]> git.karo-electronics.de Git - linux-beck.git/commitdiff
Merge branch 'drm-next-3.9' of git://people.freedesktop.org/~agd5f/linux into drm...
authorDave Airlie <airlied@redhat.com>
Wed, 20 Feb 2013 21:15:10 +0000 (07:15 +1000)
committerDave Airlie <airlied@redhat.com>
Wed, 20 Feb 2013 21:15:16 +0000 (07:15 +1000)
More drm-next bits for radeon.  Just bug fixes.

* 'drm-next-3.9' of git://people.freedesktop.org/~agd5f/linux:
  drm/radeon: properly validate the atpx interface
  drm/radeon: switch get_gpu_clock() to a callback (v2)
  drm/radeon: add a asic callback to get the xclk
  drm/radeon: Avoid NULL pointer dereference from atom_index_iio() allocation failure
  drm/radeon: remove overzealous warning in hdmi handling
  drm/radeon: fix multi-head power profile stability on BTC+ asics

1063 files changed:
Documentation/DocBook/drm.tmpl
Documentation/EDID/HOWTO.txt
Documentation/device-mapper/dm-raid.txt
Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt
Documentation/devicetree/bindings/video/display-timing.txt [new file with mode: 0644]
Documentation/filesystems/f2fs.txt
Documentation/hid/hid-sensor.txt [changed mode: 0755->0644]
Documentation/kernel-parameters.txt
Documentation/thermal/nouveau_thermal [new file with mode: 0644]
Documentation/x86/boot.txt
Documentation/x86/zero-page.txt
MAINTAINERS
Makefile
arch/arm/boot/dts/Makefile
arch/arm/boot/dts/armada-370-db.dts
arch/arm/boot/dts/armada-xp-mv78230.dtsi
arch/arm/boot/dts/armada-xp-mv78260.dtsi
arch/arm/boot/dts/armada-xp-mv78460.dtsi
arch/arm/boot/dts/at91rm9200.dtsi
arch/arm/boot/dts/at91sam9260.dtsi
arch/arm/boot/dts/at91sam9263.dtsi
arch/arm/boot/dts/at91sam9g45.dtsi
arch/arm/boot/dts/at91sam9n12.dtsi
arch/arm/boot/dts/at91sam9x5.dtsi
arch/arm/boot/dts/cros5250-common.dtsi
arch/arm/boot/dts/dove-cubox.dts
arch/arm/boot/dts/exynos5250-smdk5250.dts
arch/arm/boot/dts/kirkwood-ns2-common.dtsi
arch/arm/boot/dts/kirkwood.dtsi
arch/arm/boot/dts/kizbox.dts
arch/arm/boot/dts/sunxi.dtsi
arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
arch/arm/configs/at91_dt_defconfig
arch/arm/kernel/debug.S
arch/arm/kernel/head.S
arch/arm/kernel/hyp-stub.S
arch/arm/mach-at91/setup.c
arch/arm/mach-imx/Kconfig
arch/arm/mach-imx/clk-imx25.c
arch/arm/mach-imx/clk-imx27.c
arch/arm/mach-imx/clk-imx31.c
arch/arm/mach-imx/clk-imx35.c
arch/arm/mach-imx/clk-imx51-imx53.c
arch/arm/mach-imx/clk-imx6q.c
arch/arm/mach-imx/common.h
arch/arm/mach-imx/devices/devices-common.h
arch/arm/mach-imx/devices/platform-fsl-usb2-udc.c
arch/arm/mach-imx/devices/platform-imx-fb.c
arch/arm/mach-imx/hotplug.c
arch/arm/mach-imx/iram_alloc.c
arch/arm/mach-imx/platsmp.c
arch/arm/mach-imx/pm-imx6q.c
arch/arm/mach-integrator/pci_v3.c
arch/arm/mach-kirkwood/board-ns2.c
arch/arm/mach-mvebu/Makefile
arch/arm/mach-omap2/board-omap4panda.c
arch/arm/mach-omap2/cclock2420_data.c
arch/arm/mach-omap2/cclock2430_data.c
arch/arm/mach-omap2/cclock44xx_data.c
arch/arm/mach-omap2/devices.c
arch/arm/mach-omap2/drm.c
arch/arm/mach-omap2/omap_hwmod_44xx_data.c
arch/arm/mach-omap2/timer.c
arch/arm/mach-pxa/include/mach/mfp-pxa27x.h
arch/arm/mach-pxa/pxa27x.c
arch/arm/mach-s3c64xx/mach-crag6410-module.c
arch/arm/mach-s3c64xx/pm.c
arch/arm/mm/dma-mapping.c
arch/arm/mm/mmu.c
arch/arm/plat-versatile/headsmp.S
arch/arm/vfp/entry.S
arch/arm/vfp/vfphw.S
arch/arm64/boot/dts/Makefile
arch/arm64/include/asm/elf.h
arch/arm64/include/asm/pgtable.h
arch/arm64/include/asm/unistd32.h
arch/arm64/kernel/vdso.c
arch/arm64/kernel/vdso/gettimeofday.S
arch/avr32/include/asm/dma-mapping.h
arch/blackfin/include/asm/dma-mapping.h
arch/c6x/include/asm/dma-mapping.h
arch/cris/include/asm/dma-mapping.h
arch/frv/include/asm/dma-mapping.h
arch/ia64/kernel/ptrace.c
arch/m68k/include/asm/dma-mapping.h
arch/m68k/include/asm/pgtable_no.h
arch/m68k/include/asm/unistd.h
arch/m68k/include/uapi/asm/unistd.h
arch/m68k/kernel/syscalltable.S
arch/m68k/mm/init.c
arch/mips/bcm47xx/Kconfig
arch/mips/cavium-octeon/executive/cvmx-l2c.c
arch/mips/include/asm/dsp.h
arch/mips/include/asm/inst.h
arch/mips/include/asm/mach-pnx833x/war.h
arch/mips/include/asm/pgtable-64.h
arch/mips/include/uapi/asm/Kbuild
arch/mips/include/uapi/asm/break.h [moved from arch/mips/include/asm/break.h with 100% similarity]
arch/mips/kernel/ftrace.c
arch/mips/kernel/mcount.S
arch/mips/kernel/vpe.c
arch/mips/lantiq/irq.c
arch/mips/lib/delay.c
arch/mips/mm/ioremap.c
arch/mips/mm/mmap.c
arch/mips/netlogic/xlr/setup.c
arch/mips/pci/pci-ar71xx.c
arch/mips/pci/pci-ar724x.c
arch/mn10300/Kconfig
arch/mn10300/include/asm/dma-mapping.h
arch/parisc/include/asm/dma-mapping.h
arch/parisc/kernel/entry.S
arch/parisc/kernel/irq.c
arch/parisc/kernel/ptrace.c
arch/parisc/kernel/signal.c
arch/parisc/math-emu/cnv_float.h
arch/powerpc/kernel/entry_32.S
arch/powerpc/kernel/entry_64.S
arch/powerpc/kernel/kgdb.c
arch/powerpc/kernel/time.c
arch/powerpc/kvm/emulate.c
arch/powerpc/mm/hash_low_64.S
arch/powerpc/oprofile/op_model_power4.c
arch/powerpc/platforms/pasemi/cpufreq.c
arch/s390/Makefile
arch/s390/include/asm/pgtable.h
arch/s390/include/asm/timex.h
arch/s390/kernel/time.c
arch/s390/kvm/interrupt.c
arch/s390/kvm/kvm-s390.c
arch/sh/boards/mach-ecovec24/setup.c
arch/sh/include/asm/elf.h
arch/sh/include/asm/processor_32.h
arch/sh/include/asm/processor_64.h
arch/sh/include/uapi/asm/unistd_32.h
arch/sh/include/uapi/asm/unistd_64.h
arch/sh/kernel/syscalls_32.S
arch/sh/kernel/syscalls_64.S
arch/sh/lib/mcount.S
arch/sparc/include/uapi/asm/unistd.h
arch/sparc/kernel/pci.c
arch/sparc/kernel/pci_psycho.c
arch/sparc/kernel/pci_sabre.c
arch/sparc/kernel/pci_schizo.c
arch/sparc/kernel/systbls_32.S
arch/sparc/kernel/systbls_64.S
arch/x86/Kconfig
arch/x86/boot/Makefile
arch/x86/boot/compressed/eboot.c
arch/x86/boot/compressed/head_32.S
arch/x86/boot/compressed/head_64.S
arch/x86/boot/header.S
arch/x86/boot/setup.ld
arch/x86/boot/tools/build.c
arch/x86/ia32/ia32entry.S
arch/x86/include/asm/efi.h
arch/x86/include/asm/uv/uv.h
arch/x86/include/uapi/asm/bootparam.h
arch/x86/kernel/cpu/intel_cacheinfo.c
arch/x86/kernel/cpu/perf_event.c
arch/x86/kernel/cpu/perf_event_intel.c
arch/x86/kernel/cpu/perf_event_p6.c
arch/x86/kernel/entry_32.S
arch/x86/kernel/entry_64.S
arch/x86/kernel/head_32.S
arch/x86/kernel/msr.c
arch/x86/kernel/pci-dma.c
arch/x86/kernel/reboot.c
arch/x86/kernel/setup.c
arch/x86/kernel/step.c
arch/x86/platform/efi/efi.c
arch/x86/platform/efi/efi_64.c
arch/x86/platform/uv/tlb_uv.c
arch/x86/tools/insn_sanity.c
arch/x86/tools/relocs.c
arch/x86/xen/smp.c
arch/xtensa/include/asm/dma-mapping.h
drivers/acpi/apei/apei-base.c
drivers/acpi/glue.c
drivers/acpi/osl.c
drivers/acpi/processor_idle.c
drivers/acpi/processor_perflib.c
drivers/ata/ahci.c
drivers/ata/libahci.c
drivers/ata/libata-core.c
drivers/ata/libata-eh.c
drivers/base/cpu.c
drivers/base/firmware_class.c
drivers/base/regmap/regmap-debugfs.c
drivers/base/regmap/regmap.c
drivers/block/virtio_blk.c
drivers/bluetooth/ath3k.c
drivers/bluetooth/btusb.c
drivers/char/agp/intel-gtt.c
drivers/clk/mvebu/clk-cpu.c
drivers/cpufreq/Kconfig.x86
drivers/cpufreq/acpi-cpufreq.c
drivers/cpufreq/cpufreq-cpu0.c
drivers/cpufreq/omap-cpufreq.c
drivers/cpuidle/cpuidle.c
drivers/cpuidle/driver.c
drivers/cpuidle/governors/menu.c
drivers/cpuidle/sysfs.c
drivers/devfreq/devfreq.c
drivers/devfreq/exynos4_bus.c
drivers/dma/imx-dma.c
drivers/dma/ioat/dma_v3.c
drivers/dma/tegra20-apb-dma.c
drivers/edac/edac_mc.c
drivers/edac/edac_pci_sysfs.c
drivers/firmware/dmi_scan.c
drivers/firmware/efivars.c
drivers/firmware/iscsi_ibft_find.c
drivers/gpio/gpio-mvebu.c
drivers/gpio/gpio-samsung.c
drivers/gpu/Makefile
drivers/gpu/drm/Kconfig
drivers/gpu/drm/Makefile
drivers/gpu/drm/ast/ast_drv.c
drivers/gpu/drm/ast/ast_drv.h
drivers/gpu/drm/ast/ast_fb.c
drivers/gpu/drm/ast/ast_main.c
drivers/gpu/drm/cirrus/cirrus_fbdev.c
drivers/gpu/drm/cirrus/cirrus_main.c
drivers/gpu/drm/drm_crtc.c
drivers/gpu/drm/drm_edid.c
drivers/gpu/drm/drm_fb_cma_helper.c
drivers/gpu/drm/drm_fb_helper.c
drivers/gpu/drm/drm_fops.c
drivers/gpu/drm/drm_irq.c
drivers/gpu/drm/drm_mm.c
drivers/gpu/drm/drm_modes.c
drivers/gpu/drm/drm_pci.c
drivers/gpu/drm/drm_prime.c
drivers/gpu/drm/drm_usb.c
drivers/gpu/drm/exynos/exynos_drm_fbdev.c
drivers/gpu/drm/gma500/framebuffer.c
drivers/gpu/drm/gma500/psb_device.c
drivers/gpu/drm/gma500/psb_drv.c
drivers/gpu/drm/i2c/Kconfig [new file with mode: 0644]
drivers/gpu/drm/i2c/ch7006_drv.c
drivers/gpu/drm/i915/Makefile
drivers/gpu/drm/i915/i915_debugfs.c
drivers/gpu/drm/i915/i915_dma.c
drivers/gpu/drm/i915/i915_drv.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem.c
drivers/gpu/drm/i915/i915_gem_context.c
drivers/gpu/drm/i915/i915_gem_dmabuf.c
drivers/gpu/drm/i915/i915_gem_evict.c
drivers/gpu/drm/i915/i915_gem_execbuffer.c
drivers/gpu/drm/i915/i915_gem_gtt.c
drivers/gpu/drm/i915/i915_gem_stolen.c
drivers/gpu/drm/i915/i915_gem_tiling.c
drivers/gpu/drm/i915/i915_irq.c
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/i915_suspend.c
drivers/gpu/drm/i915/i915_ums.c [new file with mode: 0644]
drivers/gpu/drm/i915/intel_crt.c
drivers/gpu/drm/i915/intel_ddi.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_dvo.c
drivers/gpu/drm/i915/intel_fb.c
drivers/gpu/drm/i915/intel_hdmi.c
drivers/gpu/drm/i915/intel_i2c.c
drivers/gpu/drm/i915/intel_lvds.c
drivers/gpu/drm/i915/intel_modes.c
drivers/gpu/drm/i915/intel_opregion.c
drivers/gpu/drm/i915/intel_overlay.c
drivers/gpu/drm/i915/intel_panel.c
drivers/gpu/drm/i915/intel_pm.c
drivers/gpu/drm/i915/intel_ringbuffer.c
drivers/gpu/drm/i915/intel_ringbuffer.h
drivers/gpu/drm/i915/intel_sdvo.c
drivers/gpu/drm/i915/intel_sprite.c
drivers/gpu/drm/i915/intel_tv.c
drivers/gpu/drm/mgag200/mgag200_fb.c
drivers/gpu/drm/mgag200/mgag200_main.c
drivers/gpu/drm/nouveau/Kconfig
drivers/gpu/drm/nouveau/Makefile
drivers/gpu/drm/nouveau/core/core/client.c
drivers/gpu/drm/nouveau/core/core/enum.c
drivers/gpu/drm/nouveau/core/core/event.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/copy/nva3.c
drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c
drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c
drivers/gpu/drm/nouveau/core/engine/disp/base.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/disp/dport.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/disp/dport.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/disp/nv04.c
drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
drivers/gpu/drm/nouveau/core/engine/disp/nv50.h
drivers/gpu/drm/nouveau/core/engine/disp/nv84.c
drivers/gpu/drm/nouveau/core/engine/disp/nv94.c
drivers/gpu/drm/nouveau/core/engine/disp/nva0.c
drivers/gpu/drm/nouveau/core/engine/disp/nva3.c
drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c
drivers/gpu/drm/nouveau/core/engine/disp/nve0.c
drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c
drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c
drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c
drivers/gpu/drm/nouveau/core/engine/fifo/base.c
drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c
drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c
drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c
drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c
drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c
drivers/gpu/drm/nouveau/core/engine/graph/nv04.c
drivers/gpu/drm/nouveau/core/engine/graph/nv10.c
drivers/gpu/drm/nouveau/core/engine/graph/nv20.c
drivers/gpu/drm/nouveau/core/engine/graph/nv40.c
drivers/gpu/drm/nouveau/core/engine/graph/nv50.c
drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c
drivers/gpu/drm/nouveau/core/engine/graph/nve0.c
drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c
drivers/gpu/drm/nouveau/core/engine/software/nv50.c
drivers/gpu/drm/nouveau/core/engine/software/nvc0.c
drivers/gpu/drm/nouveau/core/include/core/class.h
drivers/gpu/drm/nouveau/core/include/core/client.h
drivers/gpu/drm/nouveau/core/include/core/device.h
drivers/gpu/drm/nouveau/core/include/core/enum.h
drivers/gpu/drm/nouveau/core/include/core/event.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/include/core/object.h
drivers/gpu/drm/nouveau/core/include/core/printk.h
drivers/gpu/drm/nouveau/core/include/engine/disp.h
drivers/gpu/drm/nouveau/core/include/engine/fifo.h
drivers/gpu/drm/nouveau/core/include/engine/software.h
drivers/gpu/drm/nouveau/core/include/subdev/bios/dcb.h
drivers/gpu/drm/nouveau/core/include/subdev/bios/gpio.h
drivers/gpu/drm/nouveau/core/include/subdev/bios/i2c.h
drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h
drivers/gpu/drm/nouveau/core/include/subdev/bios/xpio.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/include/subdev/bus.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/include/subdev/gpio.h
drivers/gpu/drm/nouveau/core/include/subdev/i2c.h
drivers/gpu/drm/nouveau/core/include/subdev/therm.h
drivers/gpu/drm/nouveau/core/include/subdev/timer.h
drivers/gpu/drm/nouveau/core/os.h
drivers/gpu/drm/nouveau/core/subdev/bios/base.c
drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c
drivers/gpu/drm/nouveau/core/subdev/bios/extdev.c
drivers/gpu/drm/nouveau/core/subdev/bios/gpio.c
drivers/gpu/drm/nouveau/core/subdev/bios/i2c.c
drivers/gpu/drm/nouveau/core/subdev/bios/init.c
drivers/gpu/drm/nouveau/core/subdev/bios/therm.c
drivers/gpu/drm/nouveau/core/subdev/bios/xpio.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/bus/nv04.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/bus/nv31.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/bus/nv50.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/bus/nvc0.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/device/base.c
drivers/gpu/drm/nouveau/core/subdev/device/nv04.c
drivers/gpu/drm/nouveau/core/subdev/device/nv10.c
drivers/gpu/drm/nouveau/core/subdev/device/nv20.c
drivers/gpu/drm/nouveau/core/subdev/device/nv30.c
drivers/gpu/drm/nouveau/core/subdev/device/nv40.c
drivers/gpu/drm/nouveau/core/subdev/device/nv50.c
drivers/gpu/drm/nouveau/core/subdev/device/nvc0.c
drivers/gpu/drm/nouveau/core/subdev/device/nve0.c
drivers/gpu/drm/nouveau/core/subdev/devinit/nv50.c
drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c
drivers/gpu/drm/nouveau/core/subdev/gpio/base.c
drivers/gpu/drm/nouveau/core/subdev/gpio/nv10.c
drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c
drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c
drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c
drivers/gpu/drm/nouveau/core/subdev/i2c/base.c
drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c
drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/mc/nv04.c
drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c
drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c
drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c
drivers/gpu/drm/nouveau/core/subdev/mxm/mxms.c
drivers/gpu/drm/nouveau/core/subdev/therm/base.c
drivers/gpu/drm/nouveau/core/subdev/therm/fan.c
drivers/gpu/drm/nouveau/core/subdev/therm/fannil.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/therm/fantog.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/therm/ic.c
drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c
drivers/gpu/drm/nouveau/core/subdev/therm/nv50.c
drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/therm/priv.h
drivers/gpu/drm/nouveau/core/subdev/therm/temp.c
drivers/gpu/drm/nouveau/core/subdev/timer/nv04.c
drivers/gpu/drm/nouveau/nouveau_acpi.h
drivers/gpu/drm/nouveau/nouveau_backlight.c
drivers/gpu/drm/nouveau/nouveau_bios.c
drivers/gpu/drm/nouveau/nouveau_bios.h
drivers/gpu/drm/nouveau/nouveau_bo.c
drivers/gpu/drm/nouveau/nouveau_bo.h
drivers/gpu/drm/nouveau/nouveau_chan.c
drivers/gpu/drm/nouveau/nouveau_connector.c
drivers/gpu/drm/nouveau/nouveau_connector.h
drivers/gpu/drm/nouveau/nouveau_debugfs.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nouveau_debugfs.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/nouveau_display.c
drivers/gpu/drm/nouveau/nouveau_display.h
drivers/gpu/drm/nouveau/nouveau_dma.h
drivers/gpu/drm/nouveau/nouveau_dp.c
drivers/gpu/drm/nouveau/nouveau_drm.c
drivers/gpu/drm/nouveau/nouveau_drm.h
drivers/gpu/drm/nouveau/nouveau_encoder.h
drivers/gpu/drm/nouveau/nouveau_fbcon.c
drivers/gpu/drm/nouveau/nouveau_fence.c
drivers/gpu/drm/nouveau/nouveau_fence.h
drivers/gpu/drm/nouveau/nouveau_gem.c
drivers/gpu/drm/nouveau/nouveau_gem.h
drivers/gpu/drm/nouveau/nouveau_pm.c
drivers/gpu/drm/nouveau/nouveau_prime.c
drivers/gpu/drm/nouveau/nv04_dfp.c
drivers/gpu/drm/nouveau/nv04_display.c
drivers/gpu/drm/nouveau/nv04_display.h
drivers/gpu/drm/nouveau/nv04_fence.c
drivers/gpu/drm/nouveau/nv10_fence.c
drivers/gpu/drm/nouveau/nv10_fence.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/nv17_fence.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nv17_tv.c
drivers/gpu/drm/nouveau/nv50_display.c
drivers/gpu/drm/nouveau/nv50_fence.c
drivers/gpu/drm/nouveau/nv84_fence.c
drivers/gpu/drm/nouveau/nvc0_fence.c
drivers/gpu/drm/omapdrm/Kconfig [moved from drivers/staging/omapdrm/Kconfig with 100% similarity]
drivers/gpu/drm/omapdrm/Makefile [moved from drivers/staging/omapdrm/Makefile with 96% similarity]
drivers/gpu/drm/omapdrm/TODO [new file with mode: 0644]
drivers/gpu/drm/omapdrm/omap_connector.c [moved from drivers/staging/omapdrm/omap_connector.c with 73% similarity]
drivers/gpu/drm/omapdrm/omap_crtc.c [new file with mode: 0644]
drivers/gpu/drm/omapdrm/omap_debugfs.c [moved from drivers/staging/omapdrm/omap_debugfs.c with 90% similarity]
drivers/gpu/drm/omapdrm/omap_dmm_priv.h [moved from drivers/staging/omapdrm/omap_dmm_priv.h with 96% similarity]
drivers/gpu/drm/omapdrm/omap_dmm_tiler.c [moved from drivers/staging/omapdrm/omap_dmm_tiler.c with 86% similarity]
drivers/gpu/drm/omapdrm/omap_dmm_tiler.h [moved from drivers/staging/omapdrm/omap_dmm_tiler.h with 100% similarity]
drivers/gpu/drm/omapdrm/omap_drv.c [moved from drivers/staging/omapdrm/omap_drv.c with 55% similarity]
drivers/gpu/drm/omapdrm/omap_drv.h [moved from drivers/staging/omapdrm/omap_drv.h with 66% similarity]
drivers/gpu/drm/omapdrm/omap_encoder.c [moved from drivers/staging/omapdrm/omap_encoder.c with 59% similarity]
drivers/gpu/drm/omapdrm/omap_fb.c [moved from drivers/staging/omapdrm/omap_fb.c with 99% similarity]
drivers/gpu/drm/omapdrm/omap_fbdev.c [moved from drivers/staging/omapdrm/omap_fbdev.c with 95% similarity]
drivers/gpu/drm/omapdrm/omap_gem.c [moved from drivers/staging/omapdrm/omap_gem.c with 97% similarity]
drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c [moved from drivers/staging/omapdrm/omap_gem_dmabuf.c with 97% similarity]
drivers/gpu/drm/omapdrm/omap_gem_helpers.c [moved from drivers/staging/omapdrm/omap_gem_helpers.c with 98% similarity]
drivers/gpu/drm/omapdrm/omap_irq.c [new file with mode: 0644]
drivers/gpu/drm/omapdrm/omap_plane.c [new file with mode: 0644]
drivers/gpu/drm/omapdrm/tcm-sita.c [moved from drivers/staging/omapdrm/tcm-sita.c with 100% similarity]
drivers/gpu/drm/omapdrm/tcm-sita.h [moved from drivers/staging/omapdrm/tcm-sita.h with 100% similarity]
drivers/gpu/drm/omapdrm/tcm.h [moved from drivers/staging/omapdrm/tcm.h with 99% similarity]
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_cursor.c
drivers/gpu/drm/radeon/radeon_device.c
drivers/gpu/drm/radeon/radeon_display.c
drivers/gpu/drm/radeon/radeon_drv.c
drivers/gpu/drm/radeon/radeon_fb.c
drivers/gpu/drm/radeon/radeon_prime.c
drivers/gpu/drm/radeon/radeon_ttm.c
drivers/gpu/drm/shmobile/shmob_drm_drv.c
drivers/gpu/drm/tegra/fb.c
drivers/gpu/drm/ttm/ttm_bo.c
drivers/gpu/drm/ttm/ttm_execbuf_util.c
drivers/gpu/drm/udl/udl_drv.h
drivers/gpu/drm/udl/udl_fb.c
drivers/gpu/drm/udl/udl_transfer.c
drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
drivers/gpu/stub/Kconfig [deleted file]
drivers/gpu/stub/Makefile [deleted file]
drivers/gpu/stub/poulsbo.c [deleted file]
drivers/gpu/vga/vga_switcheroo.c
drivers/hid/hid-ids.h
drivers/hid/i2c-hid/i2c-hid.c
drivers/hid/usbhid/hid-quirks.c
drivers/hv/hv_balloon.c
drivers/hwmon/vexpress.c
drivers/i2c/busses/i2c-designware-core.c
drivers/i2c/busses/i2c-mxs.c
drivers/i2c/busses/i2c-omap.c
drivers/i2c/busses/i2c-sirf.c
drivers/i2c/muxes/i2c-mux-pinctrl.c
drivers/idle/intel_idle.c
drivers/iio/accel/Kconfig
drivers/iio/adc/ad7266.c
drivers/iio/adc/at91_adc.c
drivers/iio/adc/max1363.c
drivers/iio/common/hid-sensors/Kconfig
drivers/iio/common/hid-sensors/Makefile
drivers/iio/dac/ad5380.c
drivers/iio/dac/ad5446.c
drivers/iio/dac/ad5504.c
drivers/iio/dac/ad5624r_spi.c
drivers/iio/dac/ad5686.c
drivers/iio/dac/ad5791.c
drivers/iio/frequency/adf4350.c
drivers/iio/gyro/Kconfig
drivers/iio/light/Kconfig
drivers/iio/magnetometer/Kconfig
drivers/iommu/amd_iommu_init.c
drivers/iommu/intel-iommu.c
drivers/isdn/gigaset/capi.c
drivers/md/dm-raid.c
drivers/md/dm-thin.c
drivers/md/dm.c
drivers/media/i2c/m5mols/m5mols_core.c
drivers/media/platform/coda.c
drivers/media/platform/omap3isp/ispvideo.c
drivers/media/platform/s5p-fimc/fimc-mdevice.c
drivers/media/platform/s5p-mfc/s5p_mfc.c
drivers/media/radio/radio-keene.c
drivers/media/radio/radio-si4713.c
drivers/media/radio/radio-wl1273.c
drivers/media/radio/wl128x/fmdrv_v4l2.c
drivers/media/usb/gspca/kinect.c
drivers/media/usb/gspca/sonixb.c
drivers/media/usb/gspca/sonixj.c
drivers/media/usb/uvc/uvc_ctrl.c
drivers/media/usb/uvc/uvc_v4l2.c
drivers/media/v4l2-core/videobuf2-core.c
drivers/mfd/Kconfig
drivers/mfd/ab8500-core.c
drivers/mfd/arizona-core.c
drivers/mfd/arizona-irq.c
drivers/mfd/da9052-i2c.c
drivers/mfd/db8500-prcmu.c
drivers/mfd/max77686.c
drivers/mfd/max77693.c
drivers/mfd/pcf50633-core.c
drivers/mfd/rtl8411.c
drivers/mfd/rts5209.c
drivers/mfd/rts5229.c
drivers/mfd/rtsx_pcr.c
drivers/mfd/tc3589x.c
drivers/mfd/twl4030-power.c
drivers/mfd/vexpress-config.c
drivers/mfd/vexpress-sysreg.c
drivers/mfd/wm5102-tables.c
drivers/misc/atmel-ssc.c
drivers/misc/mei/amthif.c
drivers/misc/ti-st/st_kim.c
drivers/mmc/host/mvsdio.c
drivers/mmc/host/rtsx_pci_sdmmc.c
drivers/mtd/devices/Kconfig
drivers/mtd/maps/physmap_of.c
drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c
drivers/mtd/nand/davinci_nand.c
drivers/mtd/nand/nand_base.c
drivers/net/can/c_can/c_can.c
drivers/net/can/pch_can.c
drivers/net/can/ti_hecc.c
drivers/net/ethernet/3com/3c574_cs.c
drivers/net/ethernet/adi/Kconfig
drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
drivers/net/ethernet/broadcom/tg3.c
drivers/net/ethernet/calxeda/xgmac.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
drivers/net/ethernet/emulex/benet/be.h
drivers/net/ethernet/emulex/benet/be_main.c
drivers/net/ethernet/intel/ixgbe/Makefile
drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c
drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
drivers/net/ethernet/mellanox/mlx4/en_tx.c
drivers/net/ethernet/mellanox/mlx4/main.c
drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c
drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
drivers/net/ethernet/qlogic/qlge/qlge_main.c
drivers/net/ethernet/realtek/r8169.c
drivers/net/ethernet/xilinx/Kconfig
drivers/net/ethernet/xilinx/xilinx_axienet_main.c
drivers/net/hyperv/hyperv_net.h
drivers/net/hyperv/netvsc_drv.c
drivers/net/loopback.c
drivers/net/macvlan.c
drivers/net/phy/icplus.c
drivers/net/phy/marvell.c
drivers/net/tun.c
drivers/net/usb/cdc_mbim.c
drivers/net/usb/cdc_ncm.c
drivers/net/usb/dm9601.c
drivers/net/usb/qmi_wwan.c
drivers/net/usb/usbnet.c
drivers/net/virtio_net.c
drivers/net/wireless/ath/Kconfig
drivers/net/wireless/ath/Makefile
drivers/net/wireless/ath/ath9k/ar9003_calib.c
drivers/net/wireless/ath/ath9k/ar9003_phy.c
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/beacon.c
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/debug.h
drivers/net/wireless/ath/ath9k/htc_hst.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/recv.c
drivers/net/wireless/ath/wil6210/Kconfig [new file with mode: 0644]
drivers/net/wireless/ath/wil6210/Makefile [new file with mode: 0644]
drivers/net/wireless/ath/wil6210/cfg80211.c [new file with mode: 0644]
drivers/net/wireless/ath/wil6210/dbg_hexdump.h [new file with mode: 0644]
drivers/net/wireless/ath/wil6210/debugfs.c [new file with mode: 0644]
drivers/net/wireless/ath/wil6210/interrupt.c [new file with mode: 0644]
drivers/net/wireless/ath/wil6210/main.c [new file with mode: 0644]
drivers/net/wireless/ath/wil6210/netdev.c [new file with mode: 0644]
drivers/net/wireless/ath/wil6210/pcie_bus.c [new file with mode: 0644]
drivers/net/wireless/ath/wil6210/txrx.c [new file with mode: 0644]
drivers/net/wireless/ath/wil6210/txrx.h [new file with mode: 0644]
drivers/net/wireless/ath/wil6210/wil6210.h [new file with mode: 0644]
drivers/net/wireless/ath/wil6210/wmi.c [new file with mode: 0644]
drivers/net/wireless/ath/wil6210/wmi.h [new file with mode: 0644]
drivers/net/wireless/b43/b43.h
drivers/net/wireless/b43/main.c
drivers/net/wireless/b43/main.h
drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
drivers/net/wireless/iwlegacy/3945-mac.c
drivers/net/wireless/iwlegacy/common.c
drivers/net/wireless/iwlwifi/dvm/tx.c
drivers/net/wireless/iwlwifi/pcie/rx.c
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/pcie.c
drivers/net/wireless/mwifiex/sta_ioctl.c
drivers/net/wireless/mwl8k.c
drivers/net/wireless/rtlwifi/Kconfig
drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c
drivers/net/wireless/rtlwifi/rtl8723ae/phy.c
drivers/pci/hotplug/pciehp.h
drivers/pci/hotplug/pciehp_core.c
drivers/pci/hotplug/pciehp_ctrl.c
drivers/pci/hotplug/pciehp_hpc.c
drivers/pci/hotplug/shpchp.h
drivers/pci/hotplug/shpchp_core.c
drivers/pci/hotplug/shpchp_ctrl.c
drivers/pci/iov.c
drivers/pci/pcie/Kconfig
drivers/pci/pcie/aer/aerdrv_core.c
drivers/pci/pcie/aspm.c
drivers/pinctrl/Kconfig
drivers/pinctrl/mvebu/pinctrl-dove.c
drivers/pinctrl/mvebu/pinctrl-kirkwood.c
drivers/pinctrl/pinctrl-exynos5440.c
drivers/pinctrl/pinctrl-mxs.c
drivers/pinctrl/pinctrl-nomadik.c
drivers/pinctrl/pinctrl-single.c
drivers/platform/x86/ibm_rtl.c
drivers/platform/x86/samsung-laptop.c
drivers/regulator/core.c
drivers/regulator/dbx500-prcmu.c
drivers/regulator/max8997.c
drivers/regulator/max8998.c
drivers/regulator/s5m8767.c
drivers/regulator/tps80031-regulator.c
drivers/rtc/rtc-da9055.c
drivers/rtc/rtc-isl1208.c
drivers/rtc/rtc-pl031.c
drivers/rtc/rtc-vt8500.c
drivers/s390/cio/chsc.c
drivers/scsi/isci/init.c
drivers/sh/clk/cpg.c
drivers/staging/Kconfig
drivers/staging/Makefile
drivers/staging/comedi/Kconfig
drivers/staging/comedi/comedi_fops.c
drivers/staging/comedi/drivers/comedi_test.c
drivers/staging/comedi/drivers/ni_pcimio.c
drivers/staging/fwserial/Kconfig
drivers/staging/fwserial/TODO
drivers/staging/fwserial/fwserial.c
drivers/staging/fwserial/fwserial.h
drivers/staging/iio/adc/mxs-lradc.c
drivers/staging/iio/gyro/Kconfig
drivers/staging/iio/gyro/adis16080_core.c
drivers/staging/imx-drm/imx-drm-core.c
drivers/staging/imx-drm/ipu-v3/ipu-common.c
drivers/staging/imx-drm/ipuv3-crtc.c
drivers/staging/omapdrm/TODO [deleted file]
drivers/staging/omapdrm/omap_crtc.c [deleted file]
drivers/staging/omapdrm/omap_plane.c [deleted file]
drivers/staging/rtl8187se/r8180_core.c
drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c
drivers/staging/rtl8192e/rtl8192e/rtl_core.c
drivers/staging/rtl8712/usb_intf.c
drivers/staging/sb105x/Kconfig
drivers/staging/sb105x/sb_pci_mp.c
drivers/staging/speakup/synth.c
drivers/staging/tidspbridge/core/_tiomap.h
drivers/staging/tidspbridge/core/dsp-clock.c
drivers/staging/tidspbridge/core/wdt.c
drivers/staging/vme/devices/vme_pio2_core.c
drivers/staging/vt6656/bssdb.h
drivers/staging/vt6656/int.h
drivers/staging/vt6656/iocmd.h
drivers/staging/vt6656/iowpa.h
drivers/staging/wlan-ng/cfg80211.c
drivers/staging/wlan-ng/prism2mgmt.c
drivers/staging/zram/zram_drv.c
drivers/target/iscsi/iscsi_target_erl2.c
drivers/target/target_core_alua.c
drivers/target/target_core_device.c
drivers/target/target_core_fabric_configfs.c
drivers/target/target_core_pr.c
drivers/target/target_core_sbc.c
drivers/target/target_core_spc.c
drivers/target/target_core_transport.c
drivers/target/tcm_fc/tfc_sess.c
drivers/tty/pty.c
drivers/tty/serial/8250/8250.c
drivers/tty/serial/8250/8250.h
drivers/tty/serial/8250/8250_dw.c
drivers/tty/serial/8250/8250_pci.c
drivers/tty/serial/ifx6x60.c
drivers/tty/serial/mxs-auart.c
drivers/tty/serial/samsung.c
drivers/tty/serial/vt8500_serial.c
drivers/tty/vt/vt.c
drivers/usb/Kconfig
drivers/usb/chipidea/host.c
drivers/usb/class/cdc-acm.c
drivers/usb/core/hcd.c
drivers/usb/core/hub.c
drivers/usb/core/quirks.c
drivers/usb/dwc3/debugfs.c
drivers/usb/dwc3/gadget.c
drivers/usb/gadget/amd5536udc.c
drivers/usb/gadget/dummy_hcd.c
drivers/usb/gadget/f_fs.c
drivers/usb/gadget/fsl_mxc_udc.c
drivers/usb/gadget/fsl_udc_core.c
drivers/usb/gadget/fsl_usb2_udc.h
drivers/usb/gadget/mv_udc_core.c
drivers/usb/gadget/s3c-hsotg.c
drivers/usb/gadget/tcm_usb_gadget.c
drivers/usb/gadget/u_serial.c
drivers/usb/host/Kconfig
drivers/usb/host/Makefile
drivers/usb/host/ehci-fsl.c
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-hub.c
drivers/usb/host/ehci-mv.c
drivers/usb/host/ehci-mxc.c
drivers/usb/host/ehci-pci.c
drivers/usb/host/ehci-q.c
drivers/usb/host/ehci-sched.c
drivers/usb/host/ehci-timer.c
drivers/usb/host/ehci.h
drivers/usb/host/fsl-mph-dr-of.c
drivers/usb/host/imx21-hcd.c
drivers/usb/host/ohci-tmio.c
drivers/usb/host/pci-quirks.c
drivers/usb/host/uhci-hcd.c
drivers/usb/host/uhci-hub.c
drivers/usb/host/xhci-hub.c
drivers/usb/host/xhci-mem.c
drivers/usb/host/xhci-ring.c
drivers/usb/host/xhci.c
drivers/usb/misc/usbtest.c
drivers/usb/musb/cppi_dma.c
drivers/usb/musb/musb_core.c
drivers/usb/musb/musb_dsps.c
drivers/usb/otg/Kconfig
drivers/usb/otg/mv_otg.c
drivers/usb/renesas_usbhs/mod_gadget.c
drivers/usb/renesas_usbhs/mod_host.c
drivers/usb/serial/cp210x.c
drivers/usb/serial/ftdi_sio.c
drivers/usb/serial/ftdi_sio_ids.h
drivers/usb/serial/io_ti.c
drivers/usb/serial/option.c
drivers/usb/serial/qcserial.c
drivers/usb/storage/initializers.c
drivers/usb/storage/initializers.h
drivers/usb/storage/unusual_devs.h
drivers/usb/storage/usb.c
drivers/usb/storage/usual-tables.c
drivers/vfio/pci/vfio_pci_rdwr.c
drivers/vhost/tcm_vhost.c
drivers/video/Kconfig
drivers/video/Makefile
drivers/video/console/fbcon.c
drivers/video/console/vgacon.c
drivers/video/display_timing.c [new file with mode: 0644]
drivers/video/fbmem.c
drivers/video/fbmon.c
drivers/video/fbsysfs.c
drivers/video/imxfb.c
drivers/video/of_display_timing.c [new file with mode: 0644]
drivers/video/of_videomode.c [new file with mode: 0644]
drivers/video/ssd1307fb.c
drivers/video/via/hw.c
drivers/video/via/hw.h
drivers/video/via/lcd.c
drivers/video/via/share.h
drivers/video/via/via_modesetting.c
drivers/video/via/via_modesetting.h
drivers/video/videomode.c [new file with mode: 0644]
drivers/xen/cpu_hotplug.c
drivers/xen/gntdev.c
drivers/xen/grant-table.c
drivers/xen/privcmd.c
drivers/xen/xen-pciback/pciback.h
fs/Kconfig
fs/btrfs/extent-tree.c
fs/btrfs/extent_map.c
fs/btrfs/extent_map.h
fs/btrfs/file-item.c
fs/btrfs/file.c
fs/btrfs/free-space-cache.c
fs/btrfs/inode.c
fs/btrfs/ioctl.c
fs/btrfs/qgroup.c
fs/btrfs/send.c
fs/btrfs/super.c
fs/btrfs/transaction.c
fs/btrfs/tree-log.c
fs/btrfs/volumes.c
fs/buffer.c
fs/cifs/cifs_dfs_ref.c
fs/cifs/connect.c
fs/debugfs/inode.c
fs/dlm/user.c
fs/exec.c
fs/f2fs/acl.c
fs/f2fs/checkpoint.c
fs/f2fs/data.c
fs/f2fs/debug.c
fs/f2fs/dir.c
fs/f2fs/f2fs.h
fs/f2fs/file.c
fs/f2fs/gc.c
fs/f2fs/inode.c
fs/f2fs/node.c
fs/f2fs/recovery.c
fs/f2fs/segment.c
fs/f2fs/super.c
fs/f2fs/xattr.c
fs/fuse/Kconfig
fs/fuse/cuse.c
fs/fuse/dev.c
fs/fuse/file.c
fs/gfs2/lock_dlm.c
fs/jbd/journal.c
fs/nfs/namespace.c
fs/nfs/nfs4client.c
fs/nfs/nfs4state.c
fs/nfs/super.c
fs/nilfs2/ioctl.c
fs/seq_file.c
fs/udf/super.c
fs/xfs/xfs_aops.c
fs/xfs/xfs_bmap.c
fs/xfs/xfs_buf.c
fs/xfs/xfs_buf.h
fs/xfs/xfs_buf_item.c
fs/xfs/xfs_buf_item.h
fs/xfs/xfs_dfrag.c
fs/xfs/xfs_dir2_block.c
fs/xfs/xfs_iomap.c
fs/xfs/xfs_mount.c
fs/xfs/xfs_qm_syscalls.c
fs/xfs/xfs_trace.h
fs/xfs/xfs_trans_buf.c
include/asm-generic/dma-mapping-broken.h
include/asm-generic/pgtable.h
include/asm-generic/syscalls.h
include/drm/drmP.h
include/drm/drm_crtc.h
include/drm/drm_fb_helper.h
include/drm/drm_mm.h
include/drm/intel-gtt.h
include/drm/ttm/ttm_bo_driver.h
include/linux/ata.h
include/linux/audit.h
include/linux/compaction.h
include/linux/console.h
include/linux/cpu_rmap.h
include/linux/cpuidle.h
include/linux/efi.h
include/linux/fb.h
include/linux/init.h
include/linux/interrupt.h
include/linux/libata.h
include/linux/lockdep.h
include/linux/memcontrol.h
include/linux/mfd/abx500.h
include/linux/mfd/abx500/ab8500-bm.h
include/linux/mfd/da9052/da9052.h
include/linux/mfd/da9052/reg.h
include/linux/mfd/rtsx_common.h
include/linux/mfd/rtsx_pci.h
include/linux/mm.h
include/linux/mmu_notifier.h
include/linux/module.h
include/linux/netdevice.h
include/linux/platform_data/imx-iram.h [moved from arch/arm/mach-imx/iram.h with 100% similarity]
include/linux/ptrace.h
include/linux/rbtree_augmented.h
include/linux/rwsem.h
include/linux/sched.h
include/linux/security.h
include/linux/usb.h
include/linux/usb/hcd.h
include/linux/usb/usbnet.h
include/linux/vt_kern.h
include/net/ip.h
include/net/netfilter/nf_conntrack_core.h
include/sound/cs4271.h
include/sound/soc.h
include/target/target_core_base.h
include/uapi/drm/i915_drm.h
include/uapi/drm/omap_drm.h [moved from drivers/staging/omapdrm/omap_drm.h with 99% similarity]
include/uapi/linux/audit.h
include/uapi/linux/serial_core.h
include/uapi/linux/usb/ch9.h
include/video/display_timing.h [new file with mode: 0644]
include/video/of_display_timing.h [new file with mode: 0644]
include/video/of_videomode.h [new file with mode: 0644]
include/video/videomode.h [new file with mode: 0644]
init/Kconfig
init/do_mounts_initrd.c
init/main.c
kernel/async.c
kernel/audit.c
kernel/audit_tree.c
kernel/audit_watch.c
kernel/auditfilter.c
kernel/auditsc.c
kernel/compat.c
kernel/debug/kdb/kdb_main.c
kernel/events/core.c
kernel/fork.c
kernel/module.c
kernel/ptrace.c
kernel/rcutree_plugin.h
kernel/rwsem.c
kernel/sched/core.c
kernel/sched/debug.c
kernel/sched/fair.c
kernel/sched/rt.c
kernel/signal.c
kernel/smp.c
kernel/trace/ftrace.c
kernel/trace/trace.c
lib/bug.c
lib/cpu_rmap.c
lib/digsig.c
lib/rbtree.c
mm/bootmem.c
mm/compaction.c
mm/huge_memory.c
mm/hugetlb.c
mm/internal.h
mm/memblock.c
mm/migrate.c
mm/mmap.c
mm/page_alloc.c
net/batman-adv/distributed-arp-table.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/hidp/core.c
net/bluetooth/l2cap_core.c
net/bluetooth/sco.c
net/core/dev.c
net/core/request_sock.c
net/core/scm.c
net/core/skbuff.c
net/ipv4/ah4.c
net/ipv4/datagram.c
net/ipv4/esp4.c
net/ipv4/ip_gre.c
net/ipv4/ip_sockglue.c
net/ipv4/ipcomp.c
net/ipv4/ping.c
net/ipv4/raw.c
net/ipv4/route.c
net/ipv4/tcp.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv4/udp.c
net/ipv6/addrconf.c
net/ipv6/ah6.c
net/ipv6/esp6.c
net/ipv6/icmp.c
net/ipv6/ip6_output.c
net/ipv6/ip6mr.c
net/mac80211/cfg.c
net/mac80211/chan.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/mesh.c
net/mac80211/mesh.h
net/mac80211/mesh_hwmp.c
net/mac80211/mlme.c
net/mac80211/offchannel.c
net/mac80211/scan.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/tx.c
net/netfilter/nf_conntrack_core.c
net/netfilter/nf_conntrack_standalone.c
net/netfilter/x_tables.c
net/netfilter/xt_CT.c
net/sctp/outqueue.c
net/sctp/sm_statefuns.c
net/sctp/sysctl.c
net/sunrpc/clnt.c
net/sunrpc/sched.c
net/sunrpc/xprt.c
net/wireless/core.c
net/xfrm/xfrm_policy.c
net/xfrm/xfrm_replay.c
samples/seccomp/Makefile
scripts/checkpatch.pl
security/capability.c
security/device_cgroup.c
security/integrity/evm/evm_crypto.c
security/security.c
security/selinux/hooks.c
security/selinux/include/classmap.h
security/selinux/include/objsec.h
sound/arm/pxa2xx-ac97-lib.c
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_intel.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/rme9652/hdspm.c
sound/soc/codecs/arizona.c
sound/soc/codecs/arizona.h
sound/soc/codecs/cs4271.c
sound/soc/codecs/cs42l52.c
sound/soc/codecs/lm49453.c
sound/soc/codecs/sgtl5000.c
sound/soc/codecs/sta529.c
sound/soc/codecs/wm2000.c
sound/soc/codecs/wm2200.c
sound/soc/codecs/wm5100.c
sound/soc/codecs/wm5102.c
sound/soc/codecs/wm5110.c
sound/soc/codecs/wm_adsp.c
sound/soc/fsl/Kconfig
sound/soc/fsl/Makefile
sound/soc/fsl/imx-pcm.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-pcm.c
sound/usb/mixer.c
sound/usb/mixer_maps.c
sound/usb/mixer_quirks.c
sound/usb/pcm.c
sound/usb/quirks-table.h
sound/usb/quirks.c
tools/perf/MANIFEST
tools/perf/Makefile
tools/vm/.gitignore [new file with mode: 0644]

index 4ee2304f82f92e0edd8dfb8c36140451d74342ef..51e1904ac4c723b738c6aba7c094d67188eafaea 100644 (file)
@@ -743,6 +743,10 @@ char *date;</synopsis>
           These two operations are mandatory for GEM drivers that support DRM
           PRIME.
         </para>
+        <sect4>
+          <title>DRM PRIME Helper Functions Reference</title>
+!Pdrivers/gpu/drm/drm_prime.c PRIME Helpers
+        </sect4>
       </sect3>
       <sect3 id="drm-gem-objects-mapping">
         <title>GEM Objects Mapping</title>
@@ -978,10 +982,25 @@ int max_width, max_height;</synopsis>
         If the parameters are deemed valid, drivers then create, initialize and
         return an instance of struct <structname>drm_framebuffer</structname>.
         If desired the instance can be embedded in a larger driver-specific
-        structure. The new instance is initialized with a call to
-        <function>drm_framebuffer_init</function> which takes a pointer to DRM
-        frame buffer operations (struct
-        <structname>drm_framebuffer_funcs</structname>). Frame buffer operations are
+       structure. Drivers must fill its <structfield>width</structfield>,
+       <structfield>height</structfield>, <structfield>pitches</structfield>,
+        <structfield>offsets</structfield>, <structfield>depth</structfield>,
+        <structfield>bits_per_pixel</structfield> and
+        <structfield>pixel_format</structfield> fields from the values passed
+        through the <parameter>drm_mode_fb_cmd2</parameter> argument. They
+        should call the <function>drm_helper_mode_fill_fb_struct</function>
+        helper function to do so.
+      </para>
+
+      <para>
+       The initailization of the new framebuffer instance is finalized with a
+       call to <function>drm_framebuffer_init</function> which takes a pointer
+       to DRM frame buffer operations (struct
+       <structname>drm_framebuffer_funcs</structname>). Note that this function
+       publishes the framebuffer and so from this point on it can be accessed
+       concurrently from other threads. Hence it must be the last step in the
+       driver's framebuffer initialization sequence. Frame buffer operations
+       are
         <itemizedlist>
           <listitem>
             <synopsis>int (*create_handle)(struct drm_framebuffer *fb,
@@ -1022,16 +1041,16 @@ int max_width, max_height;</synopsis>
         </itemizedlist>
       </para>
       <para>
-        After initializing the <structname>drm_framebuffer</structname>
-        instance drivers must fill its <structfield>width</structfield>,
-        <structfield>height</structfield>, <structfield>pitches</structfield>,
-        <structfield>offsets</structfield>, <structfield>depth</structfield>,
-        <structfield>bits_per_pixel</structfield> and
-        <structfield>pixel_format</structfield> fields from the values passed
-        through the <parameter>drm_mode_fb_cmd2</parameter> argument. They
-        should call the <function>drm_helper_mode_fill_fb_struct</function>
-        helper function to do so.
-      </para>
+       The lifetime of a drm framebuffer is controlled with a reference count,
+       drivers can grab additional references with
+       <function>drm_framebuffer_reference</function> </para> and drop them
+       again with <function>drm_framebuffer_unreference</function>. For
+       driver-private framebuffers for which the last reference is never
+       dropped (e.g. for the fbdev framebuffer when the struct
+       <structname>drm_framebuffer</structname> is embedded into the fbdev
+       helper struct) drivers can manually clean up a framebuffer at module
+       unload time with
+       <function>drm_framebuffer_unregister_private</function>.
     </sect2>
     <sect2>
       <title>Output Polling</title>
@@ -1043,6 +1062,22 @@ int max_width, max_height;</synopsis>
         operation.
       </para>
     </sect2>
+    <sect2>
+      <title>Locking</title>
+      <para>
+        Beside some lookup structures with their own locking (which is hidden
+       behind the interface functions) most of the modeset state is protected
+       by the <code>dev-&lt;mode_config.lock</code> mutex and additionally
+       per-crtc locks to allow cursor updates, pageflips and similar operations
+       to occur concurrently with background tasks like output detection.
+       Operations which cross domains like a full modeset always grab all
+       locks. Drivers there need to protect resources shared between crtcs with
+       additional locking. They also need to be careful to always grab the
+       relevant crtc locks if a modset functions touches crtc state, e.g. for
+       load detection (which does only grab the <code>mode_config.lock</code>
+       to allow concurrent screen updates on live crtcs).
+      </para>
+    </sect2>
   </sect1>
 
   <!-- Internals: kms initialization and cleanup -->
@@ -1609,6 +1644,10 @@ void intel_crt_init(struct drm_device *dev)
         make its properties available to applications.
       </para>
     </sect2>
+    <sect2>
+      <title>KMS API Functions</title>
+!Edrivers/gpu/drm/drm_crtc.c
+    </sect2>
   </sect1>
 
   <!-- Internals: kms helper functions -->
@@ -2104,6 +2143,7 @@ void intel_crt_init(struct drm_device *dev)
       <title>fbdev Helper Functions Reference</title>
 !Pdrivers/gpu/drm/drm_fb_helper.c fbdev helpers
 !Edrivers/gpu/drm/drm_fb_helper.c
+!Iinclude/drm/drm_fb_helper.h
     </sect2>
     <sect2>
       <title>Display Port Helper Functions Reference</title>
index 75a9f2a0c43dd284537d97547683f79cb57c15c2..2d0a8f09475de60152d308b2d3f96254a9fd380e 100644 (file)
@@ -28,11 +28,30 @@ Makefile environment are given here.
 To create binary EDID and C source code files from the existing data
 material, simply type "make".
 
-If you want to create your own EDID file, copy the file 1024x768.S and
-replace the settings with your own data. The CRC value in the last line
+If you want to create your own EDID file, copy the file 1024x768.S,
+replace the settings with your own data and add a new target to the
+Makefile. Please note that the EDID data structure expects the timing
+values in a different way as compared to the standard X11 format.
+
+X11:
+HTimings:  hdisp hsyncstart hsyncend htotal
+VTimings:  vdisp vsyncstart vsyncend vtotal
+
+EDID:
+#define XPIX hdisp
+#define XBLANK htotal-hdisp
+#define XOFFSET hsyncstart-hdisp
+#define XPULSE hsyncend-hsyncstart
+
+#define YPIX vdisp
+#define YBLANK vtotal-vdisp
+#define YOFFSET (63+(vsyncstart-vdisp))
+#define YPULSE (63+(vsyncend-vsyncstart))
+
+The CRC value in the last line
   #define CRC 0x55
-is a bit tricky. After a first version of the binary data set is
-created, it must be be checked with the "edid-decode" utility which will
+also is a bit tricky. After a first version of the binary data set is
+created, it must be checked with the "edid-decode" utility which will
 most probably complain about a wrong CRC. Fortunately, the utility also
 displays the correct CRC which must then be inserted into the source
 file. After the make procedure is repeated, the EDID data set is ready
index 728c38c242d631b88117958360c6f8441a72be4b..56fb62b09fc59ad757fc81de6ad6e478b3b0184b 100644 (file)
@@ -141,3 +141,4 @@ Version History
 1.2.0  Handle creation of arrays that contain failed devices.
 1.3.0  Added support for RAID 10
 1.3.1  Allow device replacement/rebuild for RAID 10
+1.3.2   Fix/improve redundancy checking for RAID10
index 3a268127b0547e08f167635d2b14a4a04e9a3ac8..bc50899e0c81f8afcd7888f5200bcb7ef5dc32d2 100644 (file)
@@ -81,7 +81,8 @@ PA31  TXD4
 Required properties for pin configuration node:
 - atmel,pins: 4 integers array, represents a group of pins mux and config
   setting. The format is atmel,pins = <PIN_BANK PIN_BANK_NUM PERIPH CONFIG>.
-  The PERIPH 0 means gpio.
+  The PERIPH 0 means gpio, PERIPH 1 is periph A, PERIPH 2 is periph B...
+  PIN_BANK 0 is pioA, PIN_BANK 1 is pioB...
 
 Bits used for CONFIG:
 PULL_UP                (1 << 0): indicate this pin need a pull up.
@@ -126,7 +127,7 @@ pinctrl@fffff400 {
                pinctrl_dbgu: dbgu-0 {
                        atmel,pins =
                                <1 14 0x1 0x0   /* PB14 periph A */
-                                1 15 0x1 0x1>; /* PB15 periph with pullup */
+                                1 15 0x1 0x1>; /* PB15 periph with pullup */
                };
        };
 };
diff --git a/Documentation/devicetree/bindings/video/display-timing.txt b/Documentation/devicetree/bindings/video/display-timing.txt
new file mode 100644 (file)
index 0000000..1500385
--- /dev/null
@@ -0,0 +1,109 @@
+display-timing bindings
+=======================
+
+display-timings node
+--------------------
+
+required properties:
+ - none
+
+optional properties:
+ - native-mode: The native mode for the display, in case multiple modes are
+               provided. When omitted, assume the first node is the native.
+
+timing subnode
+--------------
+
+required properties:
+ - hactive, vactive: display resolution
+ - hfront-porch, hback-porch, hsync-len: horizontal display timing parameters
+   in pixels
+   vfront-porch, vback-porch, vsync-len: vertical display timing parameters in
+   lines
+ - clock-frequency: display clock in Hz
+
+optional properties:
+ - hsync-active: hsync pulse is active low/high/ignored
+ - vsync-active: vsync pulse is active low/high/ignored
+ - de-active: data-enable pulse is active low/high/ignored
+ - pixelclk-active: with
+                       - active high = drive pixel data on rising edge/
+                                       sample data on falling edge
+                       - active low  = drive pixel data on falling edge/
+                                       sample data on rising edge
+                       - ignored     = ignored
+ - interlaced (bool): boolean to enable interlaced mode
+ - doublescan (bool): boolean to enable doublescan mode
+
+All the optional properties that are not bool follow the following logic:
+    <1>: high active
+    <0>: low active
+    omitted: not used on hardware
+
+There are different ways of describing the capabilities of a display. The
+devicetree representation corresponds to the one commonly found in datasheets
+for displays. If a display supports multiple signal timings, the native-mode
+can be specified.
+
+The parameters are defined as:
+
+  +----------+-------------------------------------+----------+-------+
+  |          |        â†‘                            |          |       |
+  |          |        |vback_porch                 |          |       |
+  |          |        â†“                            |          |       |
+  +----------#######################################----------+-------+
+  |          #        â†‘                            #          |       |
+  |          #        |                            #          |       |
+  |  hback   #        |                            #  hfront  | hsync |
+  |   porch  #        |       hactive              #  porch   |  len  |
+  |<-------->#<-------+--------------------------->#<-------->|<----->|
+  |          #        |                            #          |       |
+  |          #        |vactive                     #          |       |
+  |          #        |                            #          |       |
+  |          #        â†“                            #          |       |
+  +----------#######################################----------+-------+
+  |          |        â†‘                            |          |       |
+  |          |        |vfront_porch                |          |       |
+  |          |        â†“                            |          |       |
+  +----------+-------------------------------------+----------+-------+
+  |          |        â†‘                            |          |       |
+  |          |        |vsync_len                   |          |       |
+  |          |        â†“                            |          |       |
+  +----------+-------------------------------------+----------+-------+
+
+Example:
+
+       display-timings {
+               native-mode = <&timing0>;
+               timing0: 1080p24 {
+                       /* 1920x1080p24 */
+                       clock-frequency = <52000000>;
+                       hactive = <1920>;
+                       vactive = <1080>;
+                       hfront-porch = <25>;
+                       hback-porch = <25>;
+                       hsync-len = <25>;
+                       vback-porch = <2>;
+                       vfront-porch = <2>;
+                       vsync-len = <2>;
+                       hsync-active = <1>;
+               };
+       };
+
+Every required property also supports the use of ranges, so the commonly used
+datasheet description with minimum, typical and maximum values can be used.
+
+Example:
+
+       timing1: timing {
+               /* 1920x1080p24 */
+               clock-frequency = <148500000>;
+               hactive = <1920>;
+               vactive = <1080>;
+               hsync-len = <0 44 60>;
+               hfront-porch = <80 88 95>;
+               hback-porch = <100 148 160>;
+               vfront-porch = <0 4 6>;
+               vback-porch = <0 36 50>;
+               vsync-len = <0 5 6>;
+       };
index 8fbd8b46ee342c502b441d769b7334cdf954ec13..dcf338e62b71108a3a02e0198edf0eb564930121 100644 (file)
@@ -175,9 +175,9 @@ consists of multiple segments as described below.
                                             align with the zone size <-|
                  |-> align with the segment size
      _________________________________________________________________________
-    |            |            |    Node     |   Segment   |   Segment  |      |
-    | Superblock | Checkpoint |   Address   |    Info.    |   Summary  | Main |
-    |    (SB)    |   (CP)     | Table (NAT) | Table (SIT) | Area (SSA) |      |
+    |            |            |   Segment   |    Node     |   Segment  |      |
+    | Superblock | Checkpoint |    Info.    |   Address   |   Summary  | Main |
+    |    (SB)    |   (CP)     | Table (SIT) | Table (NAT) | Area (SSA) |      |
     |____________|_____2______|______N______|______N______|______N_____|__N___|
                                                                        .      .
                                                              .                .
@@ -200,14 +200,14 @@ consists of multiple segments as described below.
  : It contains file system information, bitmaps for valid NAT/SIT sets, orphan
    inode lists, and summary entries of current active segments.
 
-- Node Address Table (NAT)
- : It is composed of a block address table for all the node blocks stored in
-   Main area.
-
 - Segment Information Table (SIT)
  : It contains segment information such as valid block count and bitmap for the
    validity of all the blocks.
 
+- Node Address Table (NAT)
+ : It is composed of a block address table for all the node blocks stored in
+   Main area.
+
 - Segment Summary Area (SSA)
  : It contains summary entries which contains the owner information of all the
    data and node blocks stored in Main area.
@@ -236,13 +236,13 @@ For file system consistency, each CP points to which NAT and SIT copies are
 valid, as shown as below.
 
   +--------+----------+---------+
-  |   CP   |    NAT   |   SIT   |
+  |   CP   |    SIT   |   NAT   |
   +--------+----------+---------+
   .         .          .          .
   .            .              .              .
   .               .                 .                 .
   +-------+-------+--------+--------+--------+--------+
-  | CP #0 | CP #1 | NAT #0 | NAT #1 | SIT #0 | SIT #1 |
+  | CP #0 | CP #1 | SIT #0 | SIT #1 | NAT #0 | NAT #1 |
   +-------+-------+--------+--------+--------+--------+
      |             ^                          ^
      |             |                          |
old mode 100755 (executable)
new mode 100644 (file)
index 363e348bff9b93598587710de952a22c89f12bbe..6c723811c0a09f3ad5b2b18fa32ef12aa55cd82a 100644 (file)
@@ -2438,7 +2438,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        real-time workloads.  It can also improve energy
                        efficiency for asymmetric multiprocessors.
 
-       rcu_nocbs_poll  [KNL,BOOT]
+       rcu_nocb_poll   [KNL,BOOT]
                        Rather than requiring that offloaded CPUs
                        (specified by rcu_nocbs= above) explicitly
                        awaken the corresponding "rcuoN" kthreads,
diff --git a/Documentation/thermal/nouveau_thermal b/Documentation/thermal/nouveau_thermal
new file mode 100644 (file)
index 0000000..efceb78
--- /dev/null
@@ -0,0 +1,81 @@
+Kernel driver nouveau
+===================
+
+Supported chips:
+* NV43+
+
+Authors: Martin Peres (mupuf) <martin.peres@labri.fr>
+
+Description
+---------
+
+This driver allows to read the GPU core temperature, drive the GPU fan and
+set temperature alarms.
+
+Currently, due to the absence of in-kernel API to access HWMON drivers, Nouveau
+cannot access any of the i2c external monitoring chips it may find. If you
+have one of those, temperature and/or fan management through Nouveau's HWMON
+interface is likely not to work. This document may then not cover your situation
+entirely.
+
+Temperature management
+--------------------
+
+Temperature is exposed under as a read-only HWMON attribute temp1_input.
+
+In order to protect the GPU from overheating, Nouveau supports 4 configurable
+temperature thresholds:
+
+ * Fan_boost: Fan speed is set to 100% when reaching this temperature;
+ * Downclock: The GPU will be downclocked to reduce its power dissipation;
+ * Critical: The GPU is put on hold to further lower power dissipation;
+ * Shutdown: Shut the computer down to protect your GPU.
+
+WARNING: Some of these thresholds may not be used by Nouveau depending
+on your chipset.
+
+The default value for these thresholds comes from the GPU's vbios. These
+thresholds can be configured thanks to the following HWMON attributes:
+
+ * Fan_boost: temp1_auto_point1_temp and temp1_auto_point1_temp_hyst;
+ * Downclock: temp1_max and temp1_max_hyst;
+ * Critical: temp1_crit and temp1_crit_hyst;
+ * Shutdown: temp1_emergency and temp1_emergency_hyst.
+
+NOTE: Remember that the values are stored as milli degrees Celcius. Don't forget
+to multiply!
+
+Fan management
+------------
+
+Not all cards have a drivable fan. If you do, then the following HWMON
+attributes should be available:
+
+ * pwm1_enable: Current fan management mode (NONE, MANUAL or AUTO);
+ * pwm1: Current PWM value (power percentage);
+ * pwm1_min: The minimum PWM speed allowed;
+ * pwm1_max: The maximum PWM speed allowed (bypassed when hitting Fan_boost);
+
+You may also have the following attribute:
+
+ * fan1_input: Speed in RPM of your fan.
+
+Your fan can be driven in different modes:
+
+ * 0: The fan is left untouched;
+ * 1: The fan can be driven in manual (use pwm1 to change the speed);
+ * 2; The fan is driven automatically depending on the temperature.
+
+NOTE: Be sure to use the manual mode if you want to drive the fan speed manually
+
+NOTE2: Not all fan management modes may be supported on all chipsets. We are
+working on it.
+
+Bug reports
+---------
+
+Thermal management on Nouveau is new and may not work on all cards. If you have
+inquiries, please ping mupuf on IRC (#nouveau, freenode).
+
+Bug reports should be filled on Freedesktop's bug tracker. Please follow
+http://nouveau.freedesktop.org/wiki/Bugs
index 406d82d5d2bb1e08a8cb9e4c548ce937f448b376..e540fd67f767efdc855e3b2d01c8f8af1a14ef4d 100644 (file)
@@ -57,6 +57,10 @@ Protocol 2.10:       (Kernel 2.6.31) Added a protocol for relaxed alignment
 Protocol 2.11: (Kernel 3.6) Added a field for offset of EFI handover
                protocol entry point.
 
+Protocol 2.12: (Kernel 3.8) Added the xloadflags field and extension fields
+               to struct boot_params for for loading bzImage and ramdisk
+               above 4G in 64bit.
+
 **** MEMORY LAYOUT
 
 The traditional memory map for the kernel loader, used for Image or
@@ -182,7 +186,7 @@ Offset      Proto   Name            Meaning
 0230/4 2.05+   kernel_alignment Physical addr alignment required for kernel
 0234/1 2.05+   relocatable_kernel Whether kernel is relocatable or not
 0235/1 2.10+   min_alignment   Minimum alignment, as a power of two
-0236/2 N/A     pad3            Unused
+0236/2 2.12+   xloadflags      Boot protocol option flags
 0238/4 2.06+   cmdline_size    Maximum size of the kernel command line
 023C/4 2.07+   hardware_subarch Hardware subarchitecture
 0240/8 2.07+   hardware_subarch_data Subarchitecture-specific data
@@ -582,6 +586,27 @@ Protocol:  2.10+
   misaligned kernel.  Therefore, a loader should typically try each
   power-of-two alignment from kernel_alignment down to this alignment.
 
+Field name:     xloadflags
+Type:           read
+Offset/size:    0x236/2
+Protocol:       2.12+
+
+  This field is a bitmask.
+
+  Bit 0 (read):        XLF_KERNEL_64
+       - If 1, this kernel has the legacy 64-bit entry point at 0x200.
+
+  Bit 1 (read): XLF_CAN_BE_LOADED_ABOVE_4G
+        - If 1, kernel/boot_params/cmdline/ramdisk can be above 4G.
+
+  Bit 2 (read):        XLF_EFI_HANDOVER_32
+       - If 1, the kernel supports the 32-bit EFI handoff entry point
+          given at handover_offset.
+
+  Bit 3 (read): XLF_EFI_HANDOVER_64
+       - If 1, the kernel supports the 64-bit EFI handoff entry point
+          given at handover_offset + 0x200.
+
 Field name:    cmdline_size
 Type:          read
 Offset/size:   0x238/4
index cf5437deda81a003864f9e8a5f4e533d276a189d..199f453cb4de10016030c2dd230fb9a3a3125cee 100644 (file)
@@ -19,6 +19,9 @@ Offset        Proto   Name            Meaning
 090/010        ALL     hd1_info        hd1 disk parameter, OBSOLETE!!
 0A0/010        ALL     sys_desc_table  System description table (struct sys_desc_table)
 0B0/010        ALL     olpc_ofw_header OLPC's OpenFirmware CIF and friends
+0C0/004        ALL     ext_ramdisk_image ramdisk_image high 32bits
+0C4/004        ALL     ext_ramdisk_size  ramdisk_size high 32bits
+0C8/004        ALL     ext_cmd_line_ptr  cmd_line_ptr high 32bits
 140/080        ALL     edid_info       Video mode setup (struct edid_info)
 1C0/020        ALL     efi_info        EFI 32 information (struct efi_info)
 1E0/004        ALL     alk_mem_k       Alternative mem check, in KB
@@ -27,6 +30,7 @@ Offset        Proto   Name            Meaning
 1E9/001        ALL     eddbuf_entries  Number of entries in eddbuf (below)
 1EA/001        ALL     edd_mbr_sig_buf_entries Number of entries in edd_mbr_sig_buffer
                                (below)
+1EF/001        ALL     sentinel        Used to detect broken bootloaders
 290/040        ALL     edd_mbr_sig_buffer EDD MBR signatures
 2D0/A00        ALL     e820_map        E820 memory map table
                                (array of struct e820entry)
index 3ab0949599cdedad08b119c4cc2707b73f251f83..35a56bcd5e75c0502d01eb2c2e863b044088a972 100644 (file)
@@ -648,7 +648,7 @@ F:  arch/arm/
 
 ARM SUB-ARCHITECTURES
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
-S:     MAINTAINED
+S:     Maintained
 F:     arch/arm/mach-*/
 F:     arch/arm/plat-*/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc.git
@@ -1351,6 +1351,14 @@ W:       http://wireless.kernel.org/en/users/Drivers/ath9k
 S:     Supported
 F:     drivers/net/wireless/ath/ath9k/
 
+WILOCITY WIL6210 WIRELESS DRIVER
+M:     Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
+L:     linux-wireless@vger.kernel.org
+L:     wil6210@qca.qualcomm.com
+S:     Supported
+W:     http://wireless.kernel.org/en/users/Drivers/wil6210
+F:     drivers/net/wireless/ath/wil6210/
+
 CARL9170 LINUX COMMUNITY WIRELESS DRIVER
 M:     Christian Lamparter <chunkeey@googlemail.com>
 L:     linux-wireless@vger.kernel.org
@@ -1481,7 +1489,7 @@ AVR32 ARCHITECTURE
 M:     Haavard Skinnemoen <hskinnemoen@gmail.com>
 M:     Hans-Christian Egtvedt <egtvedt@samfundet.no>
 W:     http://www.atmel.com/products/AVR32/
-W:     http://avr32linux.org/
+W:     http://mirror.egtvedt.no/avr32linux.org/
 W:     http://avrfreaks.net/
 S:     Maintained
 F:     arch/avr32/
@@ -1964,9 +1972,9 @@ S:        Maintained
 F:     drivers/usb/host/ohci-ep93xx.c
 
 CIRRUS LOGIC CS4270 SOUND DRIVER
-M:     Timur Tabi <timur@freescale.com>
+M:     Timur Tabi <timur@tabi.org>
 L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
-S:     Supported
+S:     Odd Fixes
 F:     sound/soc/codecs/cs4270*
 
 CLEANCACHE API
@@ -2958,7 +2966,7 @@ S:        Maintained
 F:     drivers/net/ethernet/i825xx/eexpress.*
 
 ETHERNET BRIDGE
-M:     Stephen Hemminger <shemminger@vyatta.com>
+M:     Stephen Hemminger <stephen@networkplumber.org>
 L:     bridge@lists.linux-foundation.org
 L:     netdev@vger.kernel.org
 W:     http://www.linuxfoundation.org/en/Net:Bridge
@@ -3183,9 +3191,9 @@ F:        include/uapi/video/
 F:     include/uapi/linux/fb.h
 
 FREESCALE DIU FRAMEBUFFER DRIVER
-M:     Timur Tabi <timur@freescale.com>
+M:     Timur Tabi <timur@tabi.org>
 L:     linux-fbdev@vger.kernel.org
-S:     Supported
+S:     Maintained
 F:     drivers/video/fsl-diu-fb.*
 
 FREESCALE DMA DRIVER
@@ -3220,9 +3228,8 @@ F:        drivers/net/ethernet/freescale/fs_enet/
 F:     include/linux/fs_enet_pd.h
 
 FREESCALE QUICC ENGINE LIBRARY
-M:     Timur Tabi <timur@freescale.com>
 L:     linuxppc-dev@lists.ozlabs.org
-S:     Supported
+S:     Orphan
 F:     arch/powerpc/sysdev/qe_lib/
 F:     arch/powerpc/include/asm/*qe.h
 
@@ -3241,16 +3248,16 @@ S:      Maintained
 F:     drivers/net/ethernet/freescale/ucc_geth*
 
 FREESCALE QUICC ENGINE UCC UART DRIVER
-M:     Timur Tabi <timur@freescale.com>
+M:     Timur Tabi <timur@tabi.org>
 L:     linuxppc-dev@lists.ozlabs.org
-S:     Supported
+S:     Maintained
 F:     drivers/tty/serial/ucc_uart.c
 
 FREESCALE SOC SOUND DRIVERS
-M:     Timur Tabi <timur@freescale.com>
+M:     Timur Tabi <timur@tabi.org>
 L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
 L:     linuxppc-dev@lists.ozlabs.org
-S:     Supported
+S:     Maintained
 F:     sound/soc/fsl/fsl*
 F:     sound/soc/fsl/mpc8610_hpcd.c
 
@@ -4898,7 +4905,7 @@ S:        Maintained
 
 MARVELL GIGABIT ETHERNET DRIVERS (skge/sky2)
 M:     Mirko Lindner <mlindner@marvell.com>
-M:     Stephen Hemminger <shemminger@vyatta.com>
+M:     Stephen Hemminger <stephen@networkplumber.org>
 L:     netdev@vger.kernel.org
 S:     Maintained
 F:     drivers/net/ethernet/marvell/sk*
@@ -5173,7 +5180,7 @@ S:        Supported
 F:     drivers/infiniband/hw/nes/
 
 NETEM NETWORK EMULATOR
-M:     Stephen Hemminger <shemminger@vyatta.com>
+M:     Stephen Hemminger <stephen@networkplumber.org>
 L:     netem@lists.linux-foundation.org
 S:     Maintained
 F:     net/sched/sch_netem.c
@@ -5507,8 +5514,7 @@ M:        Benoît Cousson <b-cousson@ti.com>
 M:     Paul Walmsley <paul@pwsan.com>
 L:     linux-omap@vger.kernel.org
 S:     Maintained
-F:     arch/arm/mach-omap2/omap_hwmod.c
-F:     arch/arm/plat-omap/include/plat/omap_hwmod.h
+F:     arch/arm/mach-omap2/omap_hwmod.*
 
 OMAP HWMOD DATA FOR OMAP4-BASED DEVICES
 M:     Benoît Cousson <b-cousson@ti.com>
@@ -6579,7 +6585,7 @@ F:        drivers/media/platform/s3c-camif/
 F:     include/media/s3c_camif.h
 
 SERIAL DRIVERS
-M:     Alan Cox <alan@linux.intel.com>
+M:     Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 L:     linux-serial@vger.kernel.org
 S:     Maintained
 F:     drivers/tty/serial
@@ -7082,7 +7088,7 @@ F:        include/uapi/sound/
 F:     sound/
 
 SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC)
-M:     Liam Girdwood <lrg@ti.com>
+M:     Liam Girdwood <lgirdwood@gmail.com>
 M:     Mark Brown <broonie@opensource.wolfsonmicro.com>
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
 L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
@@ -7334,7 +7340,7 @@ S:        Odd Fixes
 F:     drivers/staging/speakup/
 
 STAGING - TI DSP BRIDGE DRIVERS
-M:     Omar Ramirez Luna <omar.ramirez@ti.com>
+M:     Omar Ramirez Luna <omar.ramirez@copitl.com>
 S:     Odd Fixes
 F:     drivers/staging/tidspbridge/
 
index a1667c4bcce580a25e141672daf44b550f4ee5e9..54dfde5e9f9e10dbada7845c0d1d4f974b46d4ab 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,8 @@
 VERSION = 3
 PATCHLEVEL = 8
 SUBLEVEL = 0
-EXTRAVERSION = -rc3
-NAME = Terrified Chipmunk
+EXTRAVERSION = -rc6
+NAME = Unicycling Gorilla
 
 # *DOCUMENTATION*
 # To see a list of typical targets execute "make help"
@@ -169,7 +169,7 @@ SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
                                  -e s/arm.*/arm/ -e s/sa110/arm/ \
                                  -e s/s390x/s390/ -e s/parisc64/parisc/ \
                                  -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
-                                 -e s/sh[234].*/sh/ )
+                                 -e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ )
 
 # Cross compiling and selecting different set of gcc/bin-utils
 # ---------------------------------------------------------------------------
index e44da40d984f7faa18bb8a32f51f5f1fe233f20a..5ebb44fe826a9b0b36d051272910a26775e5a16f 100644 (file)
@@ -155,6 +155,7 @@ dtb-$(CONFIG_ARCH_VT8500) += vt8500-bv07.dtb \
 dtb-$(CONFIG_ARCH_ZYNQ) += zynq-zc702.dtb
 
 targets += dtbs
+targets += $(dtb-y)
 endif
 
 # *.dtb used to be generated in the directory above. Clean out the
index 00044026ef1f470bd6bc6eb85b3409d2c0bf6556..9b82facb2561cfdb1cbc9cda25f5021668752e4a 100644 (file)
@@ -26,7 +26,7 @@
 
        memory {
                device_type = "memory";
-               reg = <0x00000000 0x20000000>; /* 512 MB */
+               reg = <0x00000000 0x40000000>; /* 1 GB */
        };
 
        soc {
index 271855a6e224758800f1146f02dd992786dab801..e041f42ed711b7e31fe93f2601fc153b2db6c2c0 100644 (file)
                };
 
                gpio0: gpio@d0018100 {
-                       compatible = "marvell,armadaxp-gpio";
-                       reg = <0xd0018100 0x40>,
-                           <0xd0018800 0x30>;
+                       compatible = "marvell,orion-gpio";
+                       reg = <0xd0018100 0x40>;
                        ngpios = <32>;
                        gpio-controller;
                        #gpio-cells = <2>;
                        interrupt-controller;
                        #interrupts-cells = <2>;
-                       interrupts = <16>, <17>, <18>, <19>;
+                       interrupts = <82>, <83>, <84>, <85>;
                };
 
                gpio1: gpio@d0018140 {
-                       compatible = "marvell,armadaxp-gpio";
-                       reg = <0xd0018140 0x40>,
-                           <0xd0018840 0x30>;
+                       compatible = "marvell,orion-gpio";
+                       reg = <0xd0018140 0x40>;
                        ngpios = <17>;
                        gpio-controller;
                        #gpio-cells = <2>;
                        interrupt-controller;
                        #interrupts-cells = <2>;
-                       interrupts = <20>, <21>, <22>;
+                       interrupts = <87>, <88>, <89>;
                };
        };
 };
index 1c1937dbce73c1d320f9689fd62fe9a05c851fd2..9e23bd8c9536d9b3d02c14a275efad5111d03f4a 100644 (file)
                };
 
                gpio0: gpio@d0018100 {
-                       compatible = "marvell,armadaxp-gpio";
-                       reg = <0xd0018100 0x40>,
-                           <0xd0018800 0x30>;
+                       compatible = "marvell,orion-gpio";
+                       reg = <0xd0018100 0x40>;
                        ngpios = <32>;
                        gpio-controller;
                        #gpio-cells = <2>;
                        interrupt-controller;
                        #interrupts-cells = <2>;
-                       interrupts = <16>, <17>, <18>, <19>;
+                       interrupts = <82>, <83>, <84>, <85>;
                };
 
                gpio1: gpio@d0018140 {
-                       compatible = "marvell,armadaxp-gpio";
-                       reg = <0xd0018140 0x40>,
-                           <0xd0018840 0x30>;
+                       compatible = "marvell,orion-gpio";
+                       reg = <0xd0018140 0x40>;
                        ngpios = <32>;
                        gpio-controller;
                        #gpio-cells = <2>;
                        interrupt-controller;
                        #interrupts-cells = <2>;
-                       interrupts = <20>, <21>, <22>, <23>;
+                       interrupts = <87>, <88>, <89>, <90>;
                };
 
                gpio2: gpio@d0018180 {
-                       compatible = "marvell,armadaxp-gpio";
-                       reg = <0xd0018180 0x40>,
-                           <0xd0018870 0x30>;
+                       compatible = "marvell,orion-gpio";
+                       reg = <0xd0018180 0x40>;
                        ngpios = <3>;
                        gpio-controller;
                        #gpio-cells = <2>;
                        interrupt-controller;
                        #interrupts-cells = <2>;
-                       interrupts = <24>;
+                       interrupts = <91>;
                };
 
                ethernet@d0034000 {
index 4905cf3a5ef85edc82dee68e0fafe978bd919f5c..965966110e3850a46eb247ca115c28ef827aa984 100644 (file)
                };
 
                gpio0: gpio@d0018100 {
-                       compatible = "marvell,armadaxp-gpio";
-                       reg = <0xd0018100 0x40>,
-                           <0xd0018800 0x30>;
+                       compatible = "marvell,orion-gpio";
+                       reg = <0xd0018100 0x40>;
                        ngpios = <32>;
                        gpio-controller;
                        #gpio-cells = <2>;
                        interrupt-controller;
                        #interrupts-cells = <2>;
-                       interrupts = <16>, <17>, <18>, <19>;
+                       interrupts = <82>, <83>, <84>, <85>;
                };
 
                gpio1: gpio@d0018140 {
-                       compatible = "marvell,armadaxp-gpio";
-                       reg = <0xd0018140 0x40>,
-                           <0xd0018840 0x30>;
+                       compatible = "marvell,orion-gpio";
+                       reg = <0xd0018140 0x40>;
                        ngpios = <32>;
                        gpio-controller;
                        #gpio-cells = <2>;
                        interrupt-controller;
                        #interrupts-cells = <2>;
-                       interrupts = <20>, <21>, <22>, <23>;
+                       interrupts = <87>, <88>, <89>, <90>;
                };
 
                gpio2: gpio@d0018180 {
-                       compatible = "marvell,armadaxp-gpio";
-                       reg = <0xd0018180 0x40>,
-                           <0xd0018870 0x30>;
+                       compatible = "marvell,orion-gpio";
+                       reg = <0xd0018180 0x40>;
                        ngpios = <3>;
                        gpio-controller;
                        #gpio-cells = <2>;
                        interrupt-controller;
                        #interrupts-cells = <2>;
-                       interrupts = <24>;
+                       interrupts = <91>;
                };
 
                ethernet@d0034000 {
index e154f242c680e0ca3db90aabf823490dcb146607..222047f1ece9bfac7ed0562540bfaff483dc89e3 100644 (file)
 
        i2c@0 {
                compatible = "i2c-gpio";
-               gpios = <&pioA 23 0 /* sda */
-                        &pioA 24 0 /* scl */
+               gpios = <&pioA 25 0 /* sda */
+                        &pioA 26 0 /* scl */
                        >;
                i2c-gpio,sda-open-drain;
                i2c-gpio,scl-open-drain;
index 68bccf41a2c6a1855c6dfd87555faabf81306cb7..cb7bcc51608d81cd51bba98ec29455973ca9daf0 100644 (file)
                                        };
                                };
 
+                               ssc0 {
+                                       pinctrl_ssc0_tx: ssc0_tx-0 {
+                                               atmel,pins =
+                                                       <1 16 0x1 0x0   /* PB16 periph A */
+                                                        1 17 0x1 0x0   /* PB17 periph A */
+                                                        1 18 0x1 0x0>; /* PB18 periph A */
+                                       };
+
+                                       pinctrl_ssc0_rx: ssc0_rx-0 {
+                                               atmel,pins =
+                                                       <1 19 0x1 0x0   /* PB19 periph A */
+                                                        1 20 0x1 0x0   /* PB20 periph A */
+                                                        1 21 0x1 0x0>; /* PB21 periph A */
+                                       };
+                               };
+
                                pioA: gpio@fffff400 {
                                        compatible = "atmel,at91rm9200-gpio";
                                        reg = <0xfffff400 0x200>;
                                compatible = "atmel,at91rm9200-ssc";
                                reg = <0xfffbc000 0x4000>;
                                interrupts = <14 4 5>;
+                               pinctrl-names = "default";
+                               pinctrl-0 = <&pinctrl_ssc0_tx &pinctrl_ssc0_rx>;
                                status = "disabled";
                        };
 
index 32ec62cf538585bf496187e0076afabd67c7be27..271d4de026e9df4ca1fc0114209e22f4da4d8cb5 100644 (file)
                                        };
                                };
 
+                               ssc0 {
+                                       pinctrl_ssc0_tx: ssc0_tx-0 {
+                                               atmel,pins =
+                                                       <1 0 0x2 0x0    /* PB0 periph B */
+                                                        1 1 0x2 0x0    /* PB1 periph B */
+                                                        1 2 0x2 0x0>;  /* PB2 periph B */
+                                       };
+
+                                       pinctrl_ssc0_rx: ssc0_rx-0 {
+                                               atmel,pins =
+                                                       <1 3 0x2 0x0    /* PB3 periph B */
+                                                        1 4 0x2 0x0    /* PB4 periph B */
+                                                        1 5 0x2 0x0>;  /* PB5 periph B */
+                                       };
+                               };
+
+                               ssc1 {
+                                       pinctrl_ssc1_tx: ssc1_tx-0 {
+                                               atmel,pins =
+                                                       <1 6 0x1 0x0    /* PB6 periph A */
+                                                        1 7 0x1 0x0    /* PB7 periph A */
+                                                        1 8 0x1 0x0>;  /* PB8 periph A */
+                                       };
+
+                                       pinctrl_ssc1_rx: ssc1_rx-0 {
+                                               atmel,pins =
+                                                       <1 9 0x1 0x0    /* PB9 periph A */
+                                                        1 10 0x1 0x0   /* PB10 periph A */
+                                                        1 11 0x1 0x0>; /* PB11 periph A */
+                                       };
+                               };
+
                                pioA: gpio@fffff200 {
                                        compatible = "atmel,at91rm9200-gpio";
                                        reg = <0xfffff200 0x200>;
                                compatible = "atmel,at91rm9200-ssc";
                                reg = <0xfff98000 0x4000>;
                                interrupts = <16 4 5>;
+                               pinctrl-names = "default";
+                               pinctrl-0 = <&pinctrl_ssc0_tx &pinctrl_ssc0_rx>;
                                status = "disabled";
                        };
 
                                compatible = "atmel,at91rm9200-ssc";
                                reg = <0xfff9c000 0x4000>;
                                interrupts = <17 4 5>;
+                               pinctrl-names = "default";
+                               pinctrl-0 = <&pinctrl_ssc1_tx &pinctrl_ssc1_rx>;
                                status = "disabled";
                        };
 
index 231858ffd850fec57eee5a91f06aee53f4c22f7f..6b1d4cab24c2a9e62537515991f109287dbc0091 100644 (file)
                                        };
                                };
 
+                               ssc0 {
+                                       pinctrl_ssc0_tx: ssc0_tx-0 {
+                                               atmel,pins =
+                                                       <3 0 0x1 0x0    /* PD0 periph A */
+                                                        3 1 0x1 0x0    /* PD1 periph A */
+                                                        3 2 0x1 0x0>;  /* PD2 periph A */
+                                       };
+
+                                       pinctrl_ssc0_rx: ssc0_rx-0 {
+                                               atmel,pins =
+                                                       <3 3 0x1 0x0    /* PD3 periph A */
+                                                        3 4 0x1 0x0    /* PD4 periph A */
+                                                        3 5 0x1 0x0>;  /* PD5 periph A */
+                                       };
+                               };
+
+                               ssc1 {
+                                       pinctrl_ssc1_tx: ssc1_tx-0 {
+                                               atmel,pins =
+                                                       <3 10 0x1 0x0   /* PD10 periph A */
+                                                        3 11 0x1 0x0   /* PD11 periph A */
+                                                        3 12 0x1 0x0>; /* PD12 periph A */
+                                       };
+
+                                       pinctrl_ssc1_rx: ssc1_rx-0 {
+                                               atmel,pins =
+                                                       <3 13 0x1 0x0   /* PD13 periph A */
+                                                        3 14 0x1 0x0   /* PD14 periph A */
+                                                        3 15 0x1 0x0>; /* PD15 periph A */
+                                       };
+                               };
+
                                pioA: gpio@fffff200 {
                                        compatible = "atmel,at91rm9200-gpio";
                                        reg = <0xfffff200 0x200>;
                                compatible = "atmel,at91sam9g45-ssc";
                                reg = <0xfff9c000 0x4000>;
                                interrupts = <16 4 5>;
+                               pinctrl-names = "default";
+                               pinctrl-0 = <&pinctrl_ssc0_tx &pinctrl_ssc0_rx>;
                                status = "disabled";
                        };
 
                                compatible = "atmel,at91sam9g45-ssc";
                                reg = <0xfffa0000 0x4000>;
                                interrupts = <17 4 5>;
+                               pinctrl-names = "default";
+                               pinctrl-0 = <&pinctrl_ssc1_tx &pinctrl_ssc1_rx>;
                                status = "disabled";
                        };
 
index e9efb34f437983370649fa096c006fe1638c1e01..80e29c605d4e53de1a8f948b5acb0eb975aee641 100644 (file)
@@ -28,6 +28,7 @@
                tcb1 = &tcb1;
                i2c0 = &i2c0;
                i2c1 = &i2c1;
+               ssc0 = &ssc0;
        };
        cpus {
                cpu@0 {
                                        };
                                };
 
+                               ssc0 {
+                                       pinctrl_ssc0_tx: ssc0_tx-0 {
+                                               atmel,pins =
+                                                       <0 24 0x2 0x0   /* PA24 periph B */
+                                                        0 25 0x2 0x0   /* PA25 periph B */
+                                                        0 26 0x2 0x0>; /* PA26 periph B */
+                                       };
+
+                                       pinctrl_ssc0_rx: ssc0_rx-0 {
+                                               atmel,pins =
+                                                       <0 27 0x2 0x0   /* PA27 periph B */
+                                                        0 28 0x2 0x0   /* PA28 periph B */
+                                                        0 29 0x2 0x0>; /* PA29 periph B */
+                                       };
+                               };
+
                                pioA: gpio@fffff400 {
                                        compatible = "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio";
                                        reg = <0xfffff400 0x200>;
                                status = "disabled";
                        };
 
+                       ssc0: ssc@f0010000 {
+                               compatible = "atmel,at91sam9g45-ssc";
+                               reg = <0xf0010000 0x4000>;
+                               interrupts = <28 4 5>;
+                               pinctrl-names = "default";
+                               pinctrl-0 = <&pinctrl_ssc0_tx &pinctrl_ssc0_rx>;
+                               status = "disabled";
+                       };
+
                        usart0: serial@f801c000 {
                                compatible = "atmel,at91sam9260-usart";
                                reg = <0xf801c000 0x4000>;
index 40ac3a4eb1abc80a3560fbf6e33183bdd9dd5e72..8ecca6948d811f827e623e48f039238dad9fe6bf 100644 (file)
                                interrupts = <1 4 7>;
                        };
 
-                       ssc0: ssc@f0010000 {
-                               compatible = "atmel,at91sam9g45-ssc";
-                               reg = <0xf0010000 0x4000>;
-                               interrupts = <28 4 5>;
-                               status = "disabled";
-                       };
-
                        tcb0: timer@f8008000 {
                                compatible = "atmel,at91sam9x5-tcb";
                                reg = <0xf8008000 0x100>;
                                                atmel,pins =
                                                        <0 3 0x1 0x0>;  /* PA3 periph A */
                                        };
+
+                                       pinctrl_usart0_sck: usart0_sck-0 {
+                                               atmel,pins =
+                                                       <0 4 0x1 0x0>;  /* PA4 periph A */
+                                       };
                                };
 
                                usart1 {
 
                                        pinctrl_usart1_rts: usart1_rts-0 {
                                                atmel,pins =
-                                                       <3 27 0x3 0x0>; /* PC27 periph C */
+                                                       <2 27 0x3 0x0>; /* PC27 periph C */
                                        };
 
                                        pinctrl_usart1_cts: usart1_cts-0 {
                                                atmel,pins =
-                                                       <3 28 0x3 0x0>; /* PC28 periph C */
+                                                       <2 28 0x3 0x0>; /* PC28 periph C */
+                                       };
+
+                                       pinctrl_usart1_sck: usart1_sck-0 {
+                                               atmel,pins =
+                                                       <2 28 0x3 0x0>; /* PC29 periph C */
                                        };
                                };
 
 
                                        pinctrl_uart2_rts: uart2_rts-0 {
                                                atmel,pins =
-                                                       <0 0 0x2 0x0>;  /* PB0 periph B */
+                                                       <1 0 0x2 0x0>;  /* PB0 periph B */
                                        };
 
                                        pinctrl_uart2_cts: uart2_cts-0 {
                                                atmel,pins =
-                                                       <0 1 0x2 0x0>;  /* PB1 periph B */
+                                                       <1 1 0x2 0x0>;  /* PB1 periph B */
+                                       };
+
+                                       pinctrl_usart2_sck: usart2_sck-0 {
+                                               atmel,pins =
+                                                       <1 2 0x2 0x0>;  /* PB2 periph B */
                                        };
                                };
 
                                usart3 {
                                        pinctrl_uart3: usart3-0 {
                                                atmel,pins =
-                                                       <3 23 0x2 0x1   /* PC22 periph B with pullup */
-                                                        3 23 0x2 0x0>; /* PC23 periph B */
+                                                       <2 23 0x2 0x1   /* PC22 periph B with pullup */
+                                                        2 23 0x2 0x0>; /* PC23 periph B */
                                        };
 
                                        pinctrl_usart3_rts: usart3_rts-0 {
                                                atmel,pins =
-                                                       <3 24 0x2 0x0>; /* PC24 periph B */
+                                                       <2 24 0x2 0x0>; /* PC24 periph B */
                                        };
 
                                        pinctrl_usart3_cts: usart3_cts-0 {
                                                atmel,pins =
-                                                       <3 25 0x2 0x0>; /* PC25 periph B */
+                                                       <2 25 0x2 0x0>; /* PC25 periph B */
+                                       };
+
+                                       pinctrl_usart3_sck: usart3_sck-0 {
+                                               atmel,pins =
+                                                       <2 26 0x2 0x0>; /* PC26 periph B */
                                        };
                                };
 
                                uart0 {
                                        pinctrl_uart0: uart0-0 {
                                                atmel,pins =
-                                                       <3 8 0x3 0x0    /* PC8 periph C */
-                                                        3 9 0x3 0x1>;  /* PC9 periph C with pullup */
+                                                       <2 8 0x3 0x0    /* PC8 periph C */
+                                                        2 9 0x3 0x1>;  /* PC9 periph C with pullup */
                                        };
                                };
 
                                uart1 {
                                        pinctrl_uart1: uart1-0 {
                                                atmel,pins =
-                                                       <3 16 0x3 0x0   /* PC16 periph C */
-                                                        3 17 0x3 0x1>; /* PC17 periph C with pullup */
+                                                       <2 16 0x3 0x0   /* PC16 periph C */
+                                                        2 17 0x3 0x1>; /* PC17 periph C with pullup */
                                        };
                                };
 
 
                                        pinctrl_macb0_rmii_mii: macb0_rmii_mii-0 {
                                                atmel,pins =
-                                                       <1 8 0x1 0x0    /* PA8 periph A */
-                                                        1 11 0x1 0x0   /* PA11 periph A */
-                                                        1 12 0x1 0x0   /* PA12 periph A */
-                                                        1 13 0x1 0x0   /* PA13 periph A */
-                                                        1 14 0x1 0x0   /* PA14 periph A */
-                                                        1 15 0x1 0x0   /* PA15 periph A */
-                                                        1 16 0x1 0x0   /* PA16 periph A */
-                                                        1 17 0x1 0x0>; /* PA17 periph A */
+                                                       <1 8 0x1 0x0    /* PB8 periph A */
+                                                        1 11 0x1 0x0   /* PB11 periph A */
+                                                        1 12 0x1 0x0   /* PB12 periph A */
+                                                        1 13 0x1 0x0   /* PB13 periph A */
+                                                        1 14 0x1 0x0   /* PB14 periph A */
+                                                        1 15 0x1 0x0   /* PB15 periph A */
+                                                        1 16 0x1 0x0   /* PB16 periph A */
+                                                        1 17 0x1 0x0>; /* PB17 periph A */
                                        };
                                };
 
                                        };
                                };
 
+                               ssc0 {
+                                       pinctrl_ssc0_tx: ssc0_tx-0 {
+                                               atmel,pins =
+                                                       <0 24 0x2 0x0   /* PA24 periph B */
+                                                        0 25 0x2 0x0   /* PA25 periph B */
+                                                        0 26 0x2 0x0>; /* PA26 periph B */
+                                       };
+
+                                       pinctrl_ssc0_rx: ssc0_rx-0 {
+                                               atmel,pins =
+                                                       <0 27 0x2 0x0   /* PA27 periph B */
+                                                        0 28 0x2 0x0   /* PA28 periph B */
+                                                        0 29 0x2 0x0>; /* PA29 periph B */
+                                       };
+                               };
+
                                pioA: gpio@fffff400 {
                                        compatible = "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio";
                                        reg = <0xfffff400 0x200>;
                                };
                        };
 
+                       ssc0: ssc@f0010000 {
+                               compatible = "atmel,at91sam9g45-ssc";
+                               reg = <0xf0010000 0x4000>;
+                               interrupts = <28 4 5>;
+                               pinctrl-names = "default";
+                               pinctrl-0 = <&pinctrl_ssc0_tx &pinctrl_ssc0_rx>;
+                               status = "disabled";
+                       };
+
                        mmc0: mmc@f0008000 {
                                compatible = "atmel,hsmci";
                                reg = <0xf0008000 0x600>;
index fddd1741743320fbdd29afbdb75133ae46673fa1..46c09801703655eb0a8fb28b8332cafa674f9b72 100644 (file)
@@ -96,8 +96,8 @@
                fifo-depth = <0x80>;
                card-detect-delay = <200>;
                samsung,dw-mshc-ciu-div = <3>;
-               samsung,dw-mshc-sdr-timing = <2 3 3>;
-               samsung,dw-mshc-ddr-timing = <1 2 3>;
+               samsung,dw-mshc-sdr-timing = <2 3>;
+               samsung,dw-mshc-ddr-timing = <1 2>;
 
                slot@0 {
                        reg = <0>;
                fifo-depth = <0x80>;
                card-detect-delay = <200>;
                samsung,dw-mshc-ciu-div = <3>;
-               samsung,dw-mshc-sdr-timing = <2 3 3>;
-               samsung,dw-mshc-ddr-timing = <1 2 3>;
+               samsung,dw-mshc-sdr-timing = <2 3>;
+               samsung,dw-mshc-ddr-timing = <1 2>;
 
                slot@0 {
                        reg = <0>;
                fifo-depth = <0x80>;
                card-detect-delay = <200>;
                samsung,dw-mshc-ciu-div = <3>;
-               samsung,dw-mshc-sdr-timing = <2 3 3>;
-               samsung,dw-mshc-ddr-timing = <1 2 3>;
+               samsung,dw-mshc-sdr-timing = <2 3>;
+               samsung,dw-mshc-ddr-timing = <1 2>;
 
                slot@0 {
                        reg = <0>;
index fed7d3f9f431071af027d72a12c92dcb6f365943..cdee96fca6e25fec9a0e24208344c03621298721 100644 (file)
 };
 
 &uart0 { status = "okay"; };
-&sdio0 { status = "okay"; };
 &sata0 { status = "okay"; };
 &i2c0 { status = "okay"; };
 
+&sdio0 {
+       status = "okay";
+       /* sdio0 card detect is connected to wrong pin on CuBox */
+       cd-gpios = <&gpio0 12 1>;
+};
+
 &spi0 {
        status = "okay";
 
 };
 
 &pinctrl {
-       pinctrl-0 = <&pmx_gpio_18>;
+       pinctrl-0 = <&pmx_gpio_12 &pmx_gpio_18>;
        pinctrl-names = "default";
 
+       pmx_gpio_12: pmx-gpio-12 {
+               marvell,pins = "mpp12";
+               marvell,function = "gpio";
+       };
+
        pmx_gpio_18: pmx-gpio-18 {
                marvell,pins = "mpp18";
                marvell,function = "gpio";
index 942d5761ca971dd7317c8398d5ddaddd0b0fce98..e05b18f3c33d67d6e5202083bf60f8aa65b4344f 100644 (file)
                fifo-depth = <0x80>;
                card-detect-delay = <200>;
                samsung,dw-mshc-ciu-div = <3>;
-               samsung,dw-mshc-sdr-timing = <2 3 3>;
-               samsung,dw-mshc-ddr-timing = <1 2 3>;
+               samsung,dw-mshc-sdr-timing = <2 3>;
+               samsung,dw-mshc-ddr-timing = <1 2>;
 
                slot@0 {
                        reg = <0>;
                fifo-depth = <0x80>;
                card-detect-delay = <200>;
                samsung,dw-mshc-ciu-div = <3>;
-               samsung,dw-mshc-sdr-timing = <2 3 3>;
-               samsung,dw-mshc-ddr-timing = <1 2 3>;
+               samsung,dw-mshc-sdr-timing = <2 3>;
+               samsung,dw-mshc-ddr-timing = <1 2>;
 
                slot@0 {
                        reg = <0>;
index 9bc6785ad228f9522c73ad0df60befa33ef798a4..77d21abfcdf73a3430f529be076cb4f17cd9e13d 100644 (file)
@@ -1,4 +1,5 @@
 /include/ "kirkwood.dtsi"
+/include/ "kirkwood-6281.dtsi"
 
 / {
        chosen {
@@ -6,6 +7,21 @@
        };
 
        ocp@f1000000 {
+               pinctrl: pinctrl@10000 {
+                       pinctrl-0 = < &pmx_spi &pmx_twsi0 &pmx_uart0
+                                       &pmx_ns2_sata0 &pmx_ns2_sata1>;
+                       pinctrl-names = "default";
+
+                       pmx_ns2_sata0: pmx-ns2-sata0 {
+                               marvell,pins = "mpp21";
+                               marvell,function = "sata0";
+                       };
+                       pmx_ns2_sata1: pmx-ns2-sata1 {
+                               marvell,pins = "mpp20";
+                               marvell,function = "sata1";
+                       };
+               };
+
                serial@12000 {
                        clock-frequency = <166666667>;
                        status = "okay";
index 110d6cbb795b384aa330c422d6409057839bb3ee..d6ab442b7011e8777efa036b551aa8e8ef438e49 100644 (file)
@@ -36,6 +36,7 @@
                        reg = <0x10100 0x40>;
                        ngpios = <32>;
                        interrupt-controller;
+                       #interrupt-cells = <2>;
                        interrupts = <35>, <36>, <37>, <38>;
                };
 
@@ -46,6 +47,7 @@
                        reg = <0x10140 0x40>;
                        ngpios = <18>;
                        interrupt-controller;
+                       #interrupt-cells = <2>;
                        interrupts = <39>, <40>, <41>;
                };
 
index e8814fe0e27760b475b0c850880e44b0923a8b50..b4dc3ed9a3ecc95949d07e79889e53a8600d66ee 100644 (file)
@@ -48,6 +48,8 @@
 
                        macb0: ethernet@fffc4000 {
                                phy-mode = "mii";
+                               pinctrl-0 = <&pinctrl_macb_rmii
+                                            &pinctrl_macb_rmii_mii_alt>;
                                status = "okay";
                        };
 
index 8bbc2bfef221a2449da1211b2b880aeb07364f94..8b36abea9f2edba7cc4390693324c829142b8a76 100644 (file)
                };
 
                uart0: uart@01c28000 {
-                       compatible = "ns8250";
+                       compatible = "snps,dw-apb-uart";
                        reg = <0x01c28000 0x400>;
                        interrupts = <1>;
                        reg-shift = <2>;
+                       reg-io-width = <4>;
                        clock-frequency = <24000000>;
                        status = "disabled";
                };
 
                uart1: uart@01c28400 {
-                       compatible = "ns8250";
+                       compatible = "snps,dw-apb-uart";
                        reg = <0x01c28400 0x400>;
                        interrupts = <2>;
                        reg-shift = <2>;
+                       reg-io-width = <4>;
                        clock-frequency = <24000000>;
                        status = "disabled";
                };
index 1fc405a9ecfb0a4e6cacd94ba9442d6ce75575fa..cf8071ad22d5fb5acfd1382f82d16e606c28e068 100644 (file)
@@ -45,7 +45,6 @@
                        reg = <1>;
                };
 
-/* A7s disabled till big.LITTLE patches are available...
                cpu2: cpu@2 {
                        device_type = "cpu";
                        compatible = "arm,cortex-a7";
@@ -63,7 +62,6 @@
                        compatible = "arm,cortex-a7";
                        reg = <0x102>;
                };
-*/
        };
 
        memory@80000000 {
index b175577d7abb5aec9541461e35407f0f9059c04a..1ea959019fcd3c19295b8a27a458198100344fe6 100644 (file)
@@ -19,6 +19,7 @@ CONFIG_SOC_AT91SAM9260=y
 CONFIG_SOC_AT91SAM9263=y
 CONFIG_SOC_AT91SAM9G45=y
 CONFIG_SOC_AT91SAM9X5=y
+CONFIG_SOC_AT91SAM9N12=y
 CONFIG_MACH_AT91SAM_DT=y
 CONFIG_AT91_PROGRAMMABLE_CLOCKS=y
 CONFIG_AT91_TIMER_HZ=128
@@ -31,7 +32,7 @@ CONFIG_ZBOOT_ROM_TEXT=0x0
 CONFIG_ZBOOT_ROM_BSS=0x0
 CONFIG_ARM_APPENDED_DTB=y
 CONFIG_ARM_ATAG_DTB_COMPAT=y
-CONFIG_CMDLINE="mem=128M console=ttyS0,115200 initrd=0x21100000,25165824 root=/dev/ram0 rw"
+CONFIG_CMDLINE="console=ttyS0,115200 initrd=0x21100000,25165824 root=/dev/ram0 rw"
 CONFIG_KEXEC=y
 CONFIG_AUTO_ZRELADDR=y
 # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
index 6809200c31fb73349df3af95cd737c0e70293ada..14f7c3b14632e22c02632faeaca9f07adcbc232f 100644 (file)
@@ -100,12 +100,14 @@ ENTRY(printch)
                b       1b
 ENDPROC(printch)
 
+#ifdef CONFIG_MMU
 ENTRY(debug_ll_addr)
                addruart r2, r3, ip
                str     r2, [r0]
                str     r3, [r1]
                mov     pc, lr
 ENDPROC(debug_ll_addr)
+#endif
 
 #else
 
index 4eee351f4668e2bbee54c3771a5389ce8900c10c..486a15ae901192fa3e26539b3d2b7003a1528e0f 100644 (file)
@@ -246,6 +246,7 @@ __create_page_tables:
 
        /*
         * Then map boot params address in r2 if specified.
+        * We map 2 sections in case the ATAGs/DTB crosses a section boundary.
         */
        mov     r0, r2, lsr #SECTION_SHIFT
        movs    r0, r0, lsl #SECTION_SHIFT
@@ -253,6 +254,8 @@ __create_page_tables:
        addne   r3, r3, #PAGE_OFFSET
        addne   r3, r4, r3, lsr #(SECTION_SHIFT - PMD_ORDER)
        orrne   r6, r7, r0
+       strne   r6, [r3], #1 << PMD_ORDER
+       addne   r6, r6, #1 << SECTION_SHIFT
        strne   r6, [r3]
 
 #ifdef CONFIG_DEBUG_LL
@@ -331,7 +334,7 @@ ENTRY(secondary_startup)
         * as it has already been validated by the primary processor.
         */
 #ifdef CONFIG_ARM_VIRT_EXT
-       bl      __hyp_stub_install
+       bl      __hyp_stub_install_secondary
 #endif
        safe_svcmode_maskall r9
 
index 65b2417aebce0beed61a7510702168cad2d9c3dc..1315c4ccfa563a04928347ec560ed618dcd3f958 100644 (file)
@@ -99,7 +99,7 @@ ENTRY(__hyp_stub_install_secondary)
         * immediately.
         */
        compare_cpu_mode_with_primary   r4, r5, r6, r7
-       bxne    lr
+       movne   pc, lr
 
        /*
         * Once we have given up on one CPU, we do not try to install the
@@ -111,7 +111,7 @@ ENTRY(__hyp_stub_install_secondary)
         */
 
        cmp     r4, #HYP_MODE
-       bxne    lr                      @ give up if the CPU is not in HYP mode
+       movne   pc, lr                  @ give up if the CPU is not in HYP mode
 
 /*
  * Configure HSCTLR to set correct exception endianness/instruction set
@@ -120,7 +120,8 @@ ENTRY(__hyp_stub_install_secondary)
  * Eventually, CPU-specific code might be needed -- assume not for now
  *
  * This code relies on the "eret" instruction to synchronize the
- * various coprocessor accesses.
+ * various coprocessor accesses. This is done when we switch to SVC
+ * (see safe_svcmode_maskall).
  */
        @ Now install the hypervisor stub:
        adr     r7, __hyp_stub_vectors
@@ -155,14 +156,7 @@ THUMB(     orr     r7, #(1 << 30)  )       @ HSCTLR.TE
 1:
 #endif
 
-       bic     r7, r4, #MODE_MASK
-       orr     r7, r7, #SVC_MODE
-THUMB( orr     r7, r7, #PSR_T_BIT      )
-       msr     spsr_cxsf, r7           @ This is SPSR_hyp.
-
-       __MSR_ELR_HYP(14)               @ msr elr_hyp, lr
-       __ERET                          @ return, switching to SVC mode
-                                       @ The boot CPU mode is left in r4.
+       bx      lr                      @ The boot CPU mode is left in r4.
 ENDPROC(__hyp_stub_install_secondary)
 
 __hyp_stub_do_trap:
@@ -200,7 +194,7 @@ ENDPROC(__hyp_get_vectors)
        @ fall through
 ENTRY(__hyp_set_vectors)
        __HVC(0)
-       bx      lr
+       mov     pc, lr
 ENDPROC(__hyp_set_vectors)
 
 #ifndef ZIMAGE
index 9ee866ce0478027b72cb001ca4cfb1a80326da3f..4b678478cf95d9f60d6a4484b4a490ee46228d45 100644 (file)
@@ -105,6 +105,8 @@ static void __init soc_detect(u32 dbgu_base)
        switch (socid) {
        case ARCH_ID_AT91RM9200:
                at91_soc_initdata.type = AT91_SOC_RM9200;
+               if (at91_soc_initdata.subtype == AT91_SOC_SUBTYPE_NONE)
+                       at91_soc_initdata.subtype = AT91_SOC_RM9200_BGA;
                at91_boot_soc = at91rm9200_soc;
                break;
 
index 3e628fd7a674d3b5f6b4782b32ef03f7548c43a2..0a2349dc70184021e268db86ef9c3cb0e966f8a3 100644 (file)
@@ -851,6 +851,7 @@ config SOC_IMX6Q
        select HAVE_CAN_FLEXCAN if CAN
        select HAVE_IMX_GPC
        select HAVE_IMX_MMDC
+       select HAVE_IMX_SRC
        select HAVE_SMP
        select MFD_SYSCON
        select PINCTRL
index b197aa73dc4b448ad603256f8ea1ea5a9be8e30c..2c570cdaae7b1f9152f6cb1e0dd122882cfac028 100644 (file)
@@ -254,9 +254,9 @@ int __init mx25_clocks_init(void)
        clk_register_clkdev(clk[ipg], "ipg", "mxc-ehci.2");
        clk_register_clkdev(clk[usbotg_ahb], "ahb", "mxc-ehci.2");
        clk_register_clkdev(clk[usb_div], "per", "mxc-ehci.2");
-       clk_register_clkdev(clk[ipg], "ipg", "fsl-usb2-udc");
-       clk_register_clkdev(clk[usbotg_ahb], "ahb", "fsl-usb2-udc");
-       clk_register_clkdev(clk[usb_div], "per", "fsl-usb2-udc");
+       clk_register_clkdev(clk[ipg], "ipg", "imx-udc-mx27");
+       clk_register_clkdev(clk[usbotg_ahb], "ahb", "imx-udc-mx27");
+       clk_register_clkdev(clk[usb_div], "per", "imx-udc-mx27");
        clk_register_clkdev(clk[nfc_ipg_per], NULL, "imx25-nand.0");
        /* i.mx25 has the i.mx35 type cspi */
        clk_register_clkdev(clk[cspi1_ipg], NULL, "imx35-cspi.0");
index 4c1d1e4efc74ed1694ef0f0eca2ab68396171426..1ffe3b534e51562aca68b587c383bb3df00b38a8 100644 (file)
@@ -236,9 +236,9 @@ int __init mx27_clocks_init(unsigned long fref)
        clk_register_clkdev(clk[lcdc_ahb_gate], "ahb", "imx21-fb.0");
        clk_register_clkdev(clk[csi_ahb_gate], "ahb", "imx27-camera.0");
        clk_register_clkdev(clk[per4_gate], "per", "imx27-camera.0");
-       clk_register_clkdev(clk[usb_div], "per", "fsl-usb2-udc");
-       clk_register_clkdev(clk[usb_ipg_gate], "ipg", "fsl-usb2-udc");
-       clk_register_clkdev(clk[usb_ahb_gate], "ahb", "fsl-usb2-udc");
+       clk_register_clkdev(clk[usb_div], "per", "imx-udc-mx27");
+       clk_register_clkdev(clk[usb_ipg_gate], "ipg", "imx-udc-mx27");
+       clk_register_clkdev(clk[usb_ahb_gate], "ahb", "imx-udc-mx27");
        clk_register_clkdev(clk[usb_div], "per", "mxc-ehci.0");
        clk_register_clkdev(clk[usb_ipg_gate], "ipg", "mxc-ehci.0");
        clk_register_clkdev(clk[usb_ahb_gate], "ahb", "mxc-ehci.0");
index 8be64e0a4ace0bec7a33f64ad2c056dfd0ad6947..16ccbd41dea9da4b830787b11e099dff7a19a141 100644 (file)
@@ -139,9 +139,9 @@ int __init mx31_clocks_init(unsigned long fref)
        clk_register_clkdev(clk[usb_div_post], "per", "mxc-ehci.2");
        clk_register_clkdev(clk[usb_gate], "ahb", "mxc-ehci.2");
        clk_register_clkdev(clk[ipg], "ipg", "mxc-ehci.2");
-       clk_register_clkdev(clk[usb_div_post], "per", "fsl-usb2-udc");
-       clk_register_clkdev(clk[usb_gate], "ahb", "fsl-usb2-udc");
-       clk_register_clkdev(clk[ipg], "ipg", "fsl-usb2-udc");
+       clk_register_clkdev(clk[usb_div_post], "per", "imx-udc-mx27");
+       clk_register_clkdev(clk[usb_gate], "ahb", "imx-udc-mx27");
+       clk_register_clkdev(clk[ipg], "ipg", "imx-udc-mx27");
        clk_register_clkdev(clk[csi_gate], NULL, "mx3-camera.0");
        /* i.mx31 has the i.mx21 type uart */
        clk_register_clkdev(clk[uart1_gate], "per", "imx21-uart.0");
index 66f3d65ea2755f0a522f2fdea52f9b9351fe5beb..f0727e80815dc7adcb7ed2f1fbfcb43723fbd91a 100644 (file)
@@ -251,9 +251,9 @@ int __init mx35_clocks_init()
        clk_register_clkdev(clk[usb_div], "per", "mxc-ehci.2");
        clk_register_clkdev(clk[ipg], "ipg", "mxc-ehci.2");
        clk_register_clkdev(clk[usbotg_gate], "ahb", "mxc-ehci.2");
-       clk_register_clkdev(clk[usb_div], "per", "fsl-usb2-udc");
-       clk_register_clkdev(clk[ipg], "ipg", "fsl-usb2-udc");
-       clk_register_clkdev(clk[usbotg_gate], "ahb", "fsl-usb2-udc");
+       clk_register_clkdev(clk[usb_div], "per", "imx-udc-mx27");
+       clk_register_clkdev(clk[ipg], "ipg", "imx-udc-mx27");
+       clk_register_clkdev(clk[usbotg_gate], "ahb", "imx-udc-mx27");
        clk_register_clkdev(clk[wdog_gate], NULL, "imx2-wdt.0");
        clk_register_clkdev(clk[nfc_div], NULL, "imx25-nand.0");
        clk_register_clkdev(clk[csi_gate], NULL, "mx3-camera.0");
index 579023f59dc1458690c91bf5059706e4dd99c6c6..fb7cb841b64c70aa8a411fd061cbf34ff6d7b480 100644 (file)
@@ -269,9 +269,9 @@ static void __init mx5_clocks_common_init(unsigned long rate_ckil,
        clk_register_clkdev(clk[usboh3_per_gate], "per", "mxc-ehci.2");
        clk_register_clkdev(clk[usboh3_gate], "ipg", "mxc-ehci.2");
        clk_register_clkdev(clk[usboh3_gate], "ahb", "mxc-ehci.2");
-       clk_register_clkdev(clk[usboh3_per_gate], "per", "fsl-usb2-udc");
-       clk_register_clkdev(clk[usboh3_gate], "ipg", "fsl-usb2-udc");
-       clk_register_clkdev(clk[usboh3_gate], "ahb", "fsl-usb2-udc");
+       clk_register_clkdev(clk[usboh3_per_gate], "per", "imx-udc-mx51");
+       clk_register_clkdev(clk[usboh3_gate], "ipg", "imx-udc-mx51");
+       clk_register_clkdev(clk[usboh3_gate], "ahb", "imx-udc-mx51");
        clk_register_clkdev(clk[nfc_gate], NULL, "imx51-nand");
        clk_register_clkdev(clk[ssi1_ipg_gate], NULL, "imx-ssi.0");
        clk_register_clkdev(clk[ssi2_ipg_gate], NULL, "imx-ssi.1");
index 7f2c10c7413abaf0786b45a29933bc60745bd12c..c0c4e723b7f5dee0a34526fd280cab2e8b3966b1 100644 (file)
@@ -436,6 +436,9 @@ int __init mx6q_clocks_init(void)
        for (i = 0; i < ARRAY_SIZE(clks_init_on); i++)
                clk_prepare_enable(clk[clks_init_on[i]]);
 
+       /* Set initial power mode */
+       imx6q_set_lpm(WAIT_CLOCKED);
+
        np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-gpt");
        base = of_iomap(np, 0);
        WARN_ON(!base);
index 7191ab4434e52b5c881e170e9e7925e918ba42f5..fa36fb84ab193f2fb8480bf3cc70d68232824844 100644 (file)
@@ -142,6 +142,7 @@ extern int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode);
 extern void imx6q_clock_map_io(void);
 
 extern void imx_cpu_die(unsigned int cpu);
+extern int imx_cpu_kill(unsigned int cpu);
 
 #ifdef CONFIG_PM
 extern void imx6q_pm_init(void);
index 6277baf1b7be74496c4f000867bc9a899d7a7adb..9bd5777ff0e78aee55115a6255925ec0804a6593 100644 (file)
@@ -63,6 +63,7 @@ struct platform_device *__init imx_add_flexcan(
 
 #include <linux/fsl_devices.h>
 struct imx_fsl_usb2_udc_data {
+       const char *devid;
        resource_size_t iobase;
        resource_size_t irq;
 };
index 37e44398197b4703b7130734d166963f9e894e01..3c06bd96e9cc5e094a183213b1bdfd957ceda1b4 100644 (file)
 #include "../hardware.h"
 #include "devices-common.h"
 
-#define imx_fsl_usb2_udc_data_entry_single(soc)                                \
+#define imx_fsl_usb2_udc_data_entry_single(soc, _devid)                        \
        {                                                               \
+               .devid = _devid,                                        \
                .iobase = soc ## _USB_OTG_BASE_ADDR,                    \
                .irq = soc ## _INT_USB_OTG,                             \
        }
 
 #ifdef CONFIG_SOC_IMX25
 const struct imx_fsl_usb2_udc_data imx25_fsl_usb2_udc_data __initconst =
-       imx_fsl_usb2_udc_data_entry_single(MX25);
+       imx_fsl_usb2_udc_data_entry_single(MX25, "imx-udc-mx27");
 #endif /* ifdef CONFIG_SOC_IMX25 */
 
 #ifdef CONFIG_SOC_IMX27
 const struct imx_fsl_usb2_udc_data imx27_fsl_usb2_udc_data __initconst =
-       imx_fsl_usb2_udc_data_entry_single(MX27);
+       imx_fsl_usb2_udc_data_entry_single(MX27, "imx-udc-mx27");
 #endif /* ifdef CONFIG_SOC_IMX27 */
 
 #ifdef CONFIG_SOC_IMX31
 const struct imx_fsl_usb2_udc_data imx31_fsl_usb2_udc_data __initconst =
-       imx_fsl_usb2_udc_data_entry_single(MX31);
+       imx_fsl_usb2_udc_data_entry_single(MX31, "imx-udc-mx27");
 #endif /* ifdef CONFIG_SOC_IMX31 */
 
 #ifdef CONFIG_SOC_IMX35
 const struct imx_fsl_usb2_udc_data imx35_fsl_usb2_udc_data __initconst =
-       imx_fsl_usb2_udc_data_entry_single(MX35);
+       imx_fsl_usb2_udc_data_entry_single(MX35, "imx-udc-mx27");
 #endif /* ifdef CONFIG_SOC_IMX35 */
 
 #ifdef CONFIG_SOC_IMX51
 const struct imx_fsl_usb2_udc_data imx51_fsl_usb2_udc_data __initconst =
-       imx_fsl_usb2_udc_data_entry_single(MX51);
+       imx_fsl_usb2_udc_data_entry_single(MX51, "imx-udc-mx51");
 #endif
 
 struct platform_device *__init imx_add_fsl_usb2_udc(
@@ -57,7 +58,7 @@ struct platform_device *__init imx_add_fsl_usb2_udc(
                        .flags = IORESOURCE_IRQ,
                },
        };
-       return imx_add_platform_device_dmamask("fsl-usb2-udc", -1,
+       return imx_add_platform_device_dmamask(data->devid, -1,
                        res, ARRAY_SIZE(res),
                        pdata, sizeof(*pdata), DMA_BIT_MASK(32));
 }
index 10b0ed39f07f9ff5cea15ca7fd5359f96506bd34..25a47c616b2d7c2b855eaf510b84b4d466b51d62 100644 (file)
@@ -54,7 +54,7 @@ struct platform_device *__init imx_add_imx_fb(
                        .flags = IORESOURCE_IRQ,
                },
        };
-       return imx_add_platform_device_dmamask("imx-fb", 0,
+       return imx_add_platform_device_dmamask(data->devid, 0,
                        res, ARRAY_SIZE(res),
                        pdata, sizeof(*pdata), DMA_BIT_MASK(32));
 }
index 3dec962b0770aa9f088829da920c76a44a9d4113..7bc5fe15dda2a3cbc5eb43bd1c93d69830c4af70 100644 (file)
@@ -46,9 +46,11 @@ static inline void cpu_enter_lowpower(void)
 void imx_cpu_die(unsigned int cpu)
 {
        cpu_enter_lowpower();
-       imx_enable_cpu(cpu, false);
+       cpu_do_idle();
+}
 
-       /* spin here until hardware takes it down */
-       while (1)
-               ;
+int imx_cpu_kill(unsigned int cpu)
+{
+       imx_enable_cpu(cpu, false);
+       return 1;
 }
index 6c80424f678e3fad821aa8ea60685485db5ddb6e..e05cf407db659c834f6c2f4d500c26242c4e2d53 100644 (file)
@@ -22,8 +22,7 @@
 #include <linux/module.h>
 #include <linux/spinlock.h>
 #include <linux/genalloc.h>
-
-#include "iram.h"
+#include "linux/platform_data/imx-iram.h"
 
 static unsigned long iram_phys_base;
 static void __iomem *iram_virt_base;
index 3777b805b76ba8645c50c41993f8ba06b550927d..66fae885c8429f3b9147d4a3c33cd6e739a0ae05 100644 (file)
@@ -92,5 +92,6 @@ struct smp_operations  imx_smp_ops __initdata = {
        .smp_boot_secondary     = imx_boot_secondary,
 #ifdef CONFIG_HOTPLUG_CPU
        .cpu_die                = imx_cpu_die,
+       .cpu_kill               = imx_cpu_kill,
 #endif
 };
index a17543da602da4d8c5504961434bba5bfa2551b2..ee42d20cba19f022d4a9ece8616e27b443d7e82e 100644 (file)
@@ -41,6 +41,7 @@ static int imx6q_pm_enter(suspend_state_t state)
                cpu_suspend(0, imx6q_suspend_finish);
                imx_smp_prepare();
                imx_gpc_post_resume();
+               imx6q_set_lpm(WAIT_CLOCKED);
                break;
        default:
                return -EINVAL;
index be50e795536d1aa2375df9957a057bac5d28e942..e7fcea7f33008b3e8ee5c3288475a726fd88c13d 100644 (file)
@@ -475,13 +475,12 @@ int __init pci_v3_setup(int nr, struct pci_sys_data *sys)
 {
        int ret = 0;
 
+       if (!ap_syscon_base)
+               return -EINVAL;
+
        if (nr == 0) {
                sys->mem_offset = PHYS_PCI_MEM_BASE;
                ret = pci_v3_setup_resources(sys);
-               /* Remap the Integrator system controller */
-               ap_syscon_base = ioremap(INTEGRATOR_SC_BASE, 0x100);
-               if (!ap_syscon_base)
-                       return -EINVAL;
        }
 
        return ret;
@@ -497,6 +496,13 @@ void __init pci_v3_preinit(void)
        unsigned int temp;
        int ret;
 
+       /* Remap the Integrator system controller */
+       ap_syscon_base = ioremap(INTEGRATOR_SC_BASE, 0x100);
+       if (!ap_syscon_base) {
+               pr_err("unable to remap the AP syscon for PCIv3\n");
+               return;
+       }
+
        pcibios_min_mem = 0x00100000;
 
        /*
index 8821720ab5a481ca048b0acb1ef01010055ffa7f..f4632a809f6895a0481ea1876e3e0fe25da36fe8 100644 (file)
 #include <linux/gpio.h>
 #include <linux/of.h>
 #include "common.h"
-#include "mpp.h"
 
 static struct mv643xx_eth_platform_data ns2_ge00_data = {
        .phy_addr       = MV643XX_ETH_PHY_ADDR(8),
 };
 
-static unsigned int ns2_mpp_config[] __initdata = {
-       MPP0_SPI_SCn,
-       MPP1_SPI_MOSI,
-       MPP2_SPI_SCK,
-       MPP3_SPI_MISO,
-       MPP4_NF_IO6,
-       MPP5_NF_IO7,
-       MPP6_SYSRST_OUTn,
-       MPP7_GPO,               /* Fan speed (bit 1) */
-       MPP8_TW0_SDA,
-       MPP9_TW0_SCK,
-       MPP10_UART0_TXD,
-       MPP11_UART0_RXD,
-       MPP12_GPO,              /* Red led */
-       MPP14_GPIO,             /* USB fuse */
-       MPP16_GPIO,             /* SATA 0 power */
-       MPP17_GPIO,             /* SATA 1 power */
-       MPP18_NF_IO0,
-       MPP19_NF_IO1,
-       MPP20_SATA1_ACTn,
-       MPP21_SATA0_ACTn,
-       MPP22_GPIO,             /* Fan speed (bit 0) */
-       MPP23_GPIO,             /* Fan power */
-       MPP24_GPIO,             /* USB mode select */
-       MPP25_GPIO,             /* Fan rotation fail */
-       MPP26_GPIO,             /* USB device vbus */
-       MPP28_GPIO,             /* USB enable host vbus */
-       MPP29_GPIO,             /* Blue led (slow register) */
-       MPP30_GPIO,             /* Blue led (command register) */
-       MPP31_GPIO,             /* Board power off */
-       MPP32_GPIO,             /* Power button (0 = Released, 1 = Pushed) */
-       MPP33_GPO,              /* Fan speed (bit 2) */
-       0
-};
-
 #define NS2_GPIO_POWER_OFF     31
 
 static void ns2_power_off(void)
@@ -71,8 +35,6 @@ void __init ns2_init(void)
        /*
         * Basic setup. Needs to be called early.
         */
-       kirkwood_mpp_conf(ns2_mpp_config);
-
        if (of_machine_is_compatible("lacie,netspace_lite_v2") ||
            of_machine_is_compatible("lacie,netspace_mini_v2"))
                ns2_ge00_data.phy_addr = MV643XX_ETH_PHY_ADDR(0);
index 5dcb369b58aa0c79e55159f9affbd8b0c2a105d2..99df4df680fda3b54e3dce605ea3793d3f6bd26b 100644 (file)
@@ -1,6 +1,8 @@
 ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \
        -I$(srctree)/arch/arm/plat-orion/include
 
+AFLAGS_coherency_ll.o          := -Wa,-march=armv7-a
+
 obj-y += system-controller.o
 obj-$(CONFIG_MACH_ARMADA_370_XP) += armada-370-xp.o irq-armada-370-xp.o addr-map.o coherency.o coherency_ll.o pmsu.o
 obj-$(CONFIG_SMP)                += platsmp.o headsmp.o
index 5c8e9cee2c2e99a4e1562b07021edb77eb36a554..769c1feee1c469049290f9101745cc28ef01742f 100644 (file)
@@ -397,6 +397,12 @@ static struct omap_board_mux board_mux[] __initdata = {
                  OMAP_PULL_ENA),
        OMAP4_MUX(ABE_MCBSP1_FSX, OMAP_MUX_MODE0 | OMAP_PIN_INPUT),
 
+       /* UART2 - BT/FM/GPS shared transport */
+       OMAP4_MUX(UART2_CTS,    OMAP_PIN_INPUT  | OMAP_MUX_MODE0),
+       OMAP4_MUX(UART2_RTS,    OMAP_PIN_OUTPUT | OMAP_MUX_MODE0),
+       OMAP4_MUX(UART2_RX,     OMAP_PIN_INPUT  | OMAP_MUX_MODE0),
+       OMAP4_MUX(UART2_TX,     OMAP_PIN_OUTPUT | OMAP_MUX_MODE0),
+
        { .reg_offset = OMAP_MUX_TERMINATOR },
 };
 
index 7e5febe456d97b0088d7dfe455124b8153f4ff41..ab7e952d207013e42888c2a7cbfe0cd4e7b1b162 100644 (file)
@@ -1935,6 +1935,8 @@ int __init omap2420_clk_init(void)
                        omap2_init_clk_hw_omap_clocks(c->lk.clk);
        }
 
+       omap2xxx_clkt_vps_late_init();
+
        omap2_clk_disable_autoidle_all();
 
        omap2_clk_enable_init_clocks(enable_init_clks,
index eda079b96c6aac0389c6539f7d93a0b0cf539e4f..eb3dab68d5362b4b6340de4fccca2b5fd8fff825 100644 (file)
@@ -2050,6 +2050,8 @@ int __init omap2430_clk_init(void)
                        omap2_init_clk_hw_omap_clocks(c->lk.clk);
        }
 
+       omap2xxx_clkt_vps_late_init();
+
        omap2_clk_disable_autoidle_all();
 
        omap2_clk_enable_init_clocks(enable_init_clks,
index 5789a5e255638d60f797a18840b3f0b01394e50a..a2cc046b47f460a83a0954ae5249e617ea091f01 100644 (file)
@@ -2026,14 +2026,13 @@ int __init omap4xxx_clk_init(void)
         * On OMAP4460 the ABE DPLL fails to turn on if in idle low-power
         * state when turning the ABE clock domain. Workaround this by
         * locking the ABE DPLL on boot.
+        * Lock the ABE DPLL in any case to avoid issues with audio.
         */
-       if (cpu_is_omap446x()) {
-               rc = clk_set_parent(&abe_dpll_refclk_mux_ck, &sys_32k_ck);
-               if (!rc)
-                       rc = clk_set_rate(&dpll_abe_ck, OMAP4_DPLL_ABE_DEFFREQ);
-               if (rc)
-                       pr_err("%s: failed to configure ABE DPLL!\n", __func__);
-       }
+       rc = clk_set_parent(&abe_dpll_refclk_mux_ck, &sys_32k_ck);
+       if (!rc)
+               rc = clk_set_rate(&dpll_abe_ck, OMAP4_DPLL_ABE_DEFFREQ);
+       if (rc)
+               pr_err("%s: failed to configure ABE DPLL!\n", __func__);
 
        return 0;
 }
index 5e304d0719a2ae763885d8e5468de14a1dad0257..626f3ea3142f55dfd0bcd5337a9758eab5d0cf71 100644 (file)
@@ -639,7 +639,7 @@ static int count_ocp2scp_devices(struct omap_ocp2scp_dev *ocp2scp_dev)
        return cnt;
 }
 
-static void omap_init_ocp2scp(void)
+static void __init omap_init_ocp2scp(void)
 {
        struct omap_hwmod       *oh;
        struct platform_device  *pdev;
index 4c7566c7e24a3f5ca90c5d320cc43a76fcbbd82a..2a2cfa88ddbfdd456b6522c678ac7321057ad517 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/platform_data/omap_drm.h>
 
+#include "soc.h"
 #include "omap_device.h"
 #include "omap_hwmod.h"
 
@@ -56,7 +57,7 @@ static int __init omap_init_drm(void)
                        oh->name);
        }
 
-       platform_data.omaprev = GET_OMAP_REVISION();
+       platform_data.omaprev = GET_OMAP_TYPE;
 
        return platform_device_register(&omap_drm_device);
 
index 129d5081ed1572146a027eae3984b84457cf01be..793f54ac7d14b7cae5117404a564d527414d3e50 100644 (file)
@@ -2132,8 +2132,12 @@ static struct omap_hwmod omap44xx_mcpdm_hwmod = {
         * currently reset very early during boot, before I2C is
         * available, so it doesn't seem that we have any choice in
         * the kernel other than to avoid resetting it.
+        *
+        * Also, McPDM needs to be configured to NO_IDLE mode when it
+        * is in used otherwise vital clocks will be gated which
+        * results 'slow motion' audio playback.
         */
-       .flags          = HWMOD_EXT_OPT_MAIN_CLK,
+       .flags          = HWMOD_EXT_OPT_MAIN_CLK | HWMOD_SWSUP_SIDLE,
        .mpu_irqs       = omap44xx_mcpdm_irqs,
        .sdma_reqs      = omap44xx_mcpdm_sdma_reqs,
        .main_clk       = "mcpdm_fck",
index 691aa674665a46cf4c8ce75c279443b82c5aff05..b8ad6e632bb84d9ed3ede854cefeeebe6b4cc00c 100644 (file)
@@ -165,15 +165,11 @@ static struct device_node * __init omap_get_timer_dt(struct of_device_id *match,
        struct device_node *np;
 
        for_each_matching_node(np, match) {
-               if (!of_device_is_available(np)) {
-                       of_node_put(np);
+               if (!of_device_is_available(np))
                        continue;
-               }
 
-               if (property && !of_get_property(np, property, NULL)) {
-                       of_node_put(np);
+               if (property && !of_get_property(np, property, NULL))
                        continue;
-               }
 
                of_add_property(np, &device_disabled);
                return np;
index a611ad3153c7b2e7e9a7e78803df0f9876bc677e..b6132aa95dc08b1ef2cc6ae0c4a5565b5213a0ab 100644 (file)
        GPIO76_LCD_PCLK,        \
        GPIO77_LCD_BIAS
 
+/* these enable a work-around for a hw bug in pxa27x during ac97 warm reset */
+#define GPIO113_AC97_nRESET_GPIO_HIGH MFP_CFG_OUT(GPIO113, AF0, DEFAULT)
+#define GPIO95_AC97_nRESET_GPIO_HIGH MFP_CFG_OUT(GPIO95, AF0, DEFAULT)
 
 extern int keypad_set_wake(unsigned int on);
 #endif /* __ASM_ARCH_MFP_PXA27X_H */
index 8047ee0effc582b421a28085624a2b58250e88e4..616cb87b61792544f470110cd6322b2d3ea9d914 100644 (file)
@@ -47,9 +47,9 @@ void pxa27x_clear_otgph(void)
 EXPORT_SYMBOL(pxa27x_clear_otgph);
 
 static unsigned long ac97_reset_config[] = {
-       GPIO113_GPIO,
+       GPIO113_AC97_nRESET_GPIO_HIGH,
        GPIO113_AC97_nRESET,
-       GPIO95_GPIO,
+       GPIO95_AC97_nRESET_GPIO_HIGH,
        GPIO95_AC97_nRESET,
 };
 
index 553059f51841874c430d74bbbe7b4e108308fca6..755c0bb119f4c1e195c61281d776dcb92a50e6d8 100644 (file)
@@ -47,7 +47,7 @@ static struct spi_board_info wm1253_devs[] = {
                .bus_num        = 0,
                .chip_select    = 0,
                .mode           = SPI_MODE_0,
-               .irq            = S3C_EINT(5),
+               .irq            = S3C_EINT(4),
                .controller_data = &wm0010_spi_csinfo,
                .platform_data = &wm0010_pdata,
        },
index 7feb426fc202d10be8f74a8992b5de212b6e4ba0..d2e1a16690bd5807f961d6804b61dddfeb029fc7 100644 (file)
@@ -338,8 +338,10 @@ int __init s3c64xx_pm_init(void)
        for (i = 0; i < ARRAY_SIZE(s3c64xx_pm_domains); i++)
                pm_genpd_init(&s3c64xx_pm_domains[i]->pd, NULL, false);
 
+#ifdef CONFIG_S3C_DEV_FB
        if (dev_get_platdata(&s3c_device_fb.dev))
                pm_genpd_add_device(&s3c64xx_pm_f.pd, &s3c_device_fb.dev);
+#endif
 
        return 0;
 }
index 6b2fb87c8698a99254bdd56c0e4c2bf146994da5..076c26d43864f8a5b3da49ac7231a62e8edde0c2 100644 (file)
@@ -774,25 +774,27 @@ static void dma_cache_maint_page(struct page *page, unsigned long offset,
        size_t size, enum dma_data_direction dir,
        void (*op)(const void *, size_t, int))
 {
+       unsigned long pfn;
+       size_t left = size;
+
+       pfn = page_to_pfn(page) + offset / PAGE_SIZE;
+       offset %= PAGE_SIZE;
+
        /*
         * A single sg entry may refer to multiple physically contiguous
         * pages.  But we still need to process highmem pages individually.
         * If highmem is not configured then the bulk of this loop gets
         * optimized out.
         */
-       size_t left = size;
        do {
                size_t len = left;
                void *vaddr;
 
+               page = pfn_to_page(pfn);
+
                if (PageHighMem(page)) {
-                       if (len + offset > PAGE_SIZE) {
-                               if (offset >= PAGE_SIZE) {
-                                       page += offset / PAGE_SIZE;
-                                       offset %= PAGE_SIZE;
-                               }
+                       if (len + offset > PAGE_SIZE)
                                len = PAGE_SIZE - offset;
-                       }
                        vaddr = kmap_high_get(page);
                        if (vaddr) {
                                vaddr += offset;
@@ -809,7 +811,7 @@ static void dma_cache_maint_page(struct page *page, unsigned long offset,
                        op(vaddr, len, dir);
                }
                offset = 0;
-               page++;
+               pfn++;
                left -= len;
        } while (left);
 }
index 9f0610243bd6cd3357c847a5e5d392353579f471..ce328c7f5c94556cdda6021e8aef1b950aba5aa2 100644 (file)
@@ -283,7 +283,7 @@ static struct mem_type mem_types[] = {
        },
        [MT_MEMORY_SO] = {
                .prot_pte  = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
-                               L_PTE_MT_UNCACHED,
+                               L_PTE_MT_UNCACHED | L_PTE_XN,
                .prot_l1   = PMD_TYPE_TABLE,
                .prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_S |
                                PMD_SECT_UNCACHED | PMD_SECT_XN,
index dd703ef09b8d9d623d83e4a131c12e87e329e838..b178d44e9eaa897c8f8a3a4bb017198cb7014cae 100644 (file)
@@ -20,7 +20,7 @@
  */
 ENTRY(versatile_secondary_startup)
        mrc     p15, 0, r0, c0, c0, 5
-       and     r0, r0, #15
+       bic     r0, #0xff000000
        adr     r4, 1f
        ldmia   r4, {r5, r6}
        sub     r4, r4, r5
index cc926c98598141a2fbcdb762148ec17782982d06..323ce1a62bbfa3465230632d5d7d35c458f380fa 100644 (file)
@@ -22,7 +22,7 @@
 @  IRQs disabled.
 @
 ENTRY(do_vfp)
-#ifdef CONFIG_PREEMPT
+#ifdef CONFIG_PREEMPT_COUNT
        ldr     r4, [r10, #TI_PREEMPT]  @ get preempt count
        add     r11, r4, #1             @ increment it
        str     r11, [r10, #TI_PREEMPT]
@@ -35,7 +35,7 @@ ENTRY(do_vfp)
 ENDPROC(do_vfp)
 
 ENTRY(vfp_null_entry)
-#ifdef CONFIG_PREEMPT
+#ifdef CONFIG_PREEMPT_COUNT
        get_thread_info r10
        ldr     r4, [r10, #TI_PREEMPT]  @ get preempt count
        sub     r11, r4, #1             @ decrement it
@@ -53,7 +53,7 @@ ENDPROC(vfp_null_entry)
 
        __INIT
 ENTRY(vfp_testing_entry)
-#ifdef CONFIG_PREEMPT
+#ifdef CONFIG_PREEMPT_COUNT
        get_thread_info r10
        ldr     r4, [r10, #TI_PREEMPT]  @ get preempt count
        sub     r11, r4, #1             @ decrement it
index ea0349f6358658065b52aa1473e877ee4fa8f5ba..dd5e56f95f3fbe3d03f7be90f1201a78a93ed568 100644 (file)
@@ -168,7 +168,7 @@ vfp_hw_state_valid:
                                        @ else it's one 32-bit instruction, so
                                        @ always subtract 4 from the following
                                        @ instruction address.
-#ifdef CONFIG_PREEMPT
+#ifdef CONFIG_PREEMPT_COUNT
        get_thread_info r10
        ldr     r4, [r10, #TI_PREEMPT]  @ get preempt count
        sub     r11, r4, #1             @ decrement it
@@ -192,7 +192,7 @@ look_for_VFP_exceptions:
        @ not recognised by VFP
 
        DBGSTR  "not VFP"
-#ifdef CONFIG_PREEMPT
+#ifdef CONFIG_PREEMPT_COUNT
        get_thread_info r10
        ldr     r4, [r10, #TI_PREEMPT]  @ get preempt count
        sub     r11, r4, #1             @ decrement it
index 801e2d7fcbc6e9bd0e4795faea842604c0c57afc..32ac0aef006879cb57b9c6bfbc25c072924b8076 100644 (file)
@@ -1,4 +1,5 @@
 targets += dtbs
+targets += $(dtb-y)
 
 dtbs: $(addprefix $(obj)/, $(dtb-y))
 
index 07fea290d7c15b211e881856bb48d943a80c337a..fe32c0e4ac010d4460184d7c8ddca2317b9d151e 100644 (file)
 
 typedef unsigned long elf_greg_t;
 
-#define ELF_NGREG (sizeof (struct pt_regs) / sizeof(elf_greg_t))
+#define ELF_NGREG (sizeof(struct user_pt_regs) / sizeof(elf_greg_t))
+#define ELF_CORE_COPY_REGS(dest, regs) \
+       *(struct user_pt_regs *)&(dest) = (regs)->user_regs;
+
 typedef elf_greg_t elf_gregset_t[ELF_NGREG];
 typedef struct user_fpsimd_state elf_fpregset_t;
 
index 64b13394950266308093f67bce6ddb7e55391428..e333a243bfccf4f6e1547ea2543e5a3fa3abd32b 100644 (file)
@@ -24,7 +24,8 @@
 /*
  * Software defined PTE bits definition.
  */
-#define PTE_VALID              (_AT(pteval_t, 1) << 0) /* pte_present() check */
+#define PTE_VALID              (_AT(pteval_t, 1) << 0)
+#define PTE_PROT_NONE          (_AT(pteval_t, 1) << 1) /* only when !PTE_VALID */
 #define PTE_FILE               (_AT(pteval_t, 1) << 2) /* only when !pte_present() */
 #define PTE_DIRTY              (_AT(pteval_t, 1) << 55)
 #define PTE_SPECIAL            (_AT(pteval_t, 1) << 56)
@@ -60,9 +61,12 @@ extern void __pgd_error(const char *file, int line, unsigned long val);
 
 extern pgprot_t pgprot_default;
 
-#define _MOD_PROT(p, b)        __pgprot(pgprot_val(p) | (b))
+#define __pgprot_modify(prot,mask,bits) \
+       __pgprot((pgprot_val(prot) & ~(mask)) | (bits))
+
+#define _MOD_PROT(p, b)                __pgprot_modify(p, 0, b)
 
-#define PAGE_NONE              _MOD_PROT(pgprot_default, PTE_NG | PTE_PXN | PTE_UXN | PTE_RDONLY)
+#define PAGE_NONE              __pgprot_modify(pgprot_default, PTE_TYPE_MASK, PTE_PROT_NONE)
 #define PAGE_SHARED            _MOD_PROT(pgprot_default, PTE_USER | PTE_NG | PTE_PXN | PTE_UXN)
 #define PAGE_SHARED_EXEC       _MOD_PROT(pgprot_default, PTE_USER | PTE_NG | PTE_PXN)
 #define PAGE_COPY              _MOD_PROT(pgprot_default, PTE_USER | PTE_NG | PTE_PXN | PTE_UXN | PTE_RDONLY)
@@ -72,7 +76,7 @@ extern pgprot_t pgprot_default;
 #define PAGE_KERNEL            _MOD_PROT(pgprot_default, PTE_PXN | PTE_UXN | PTE_DIRTY)
 #define PAGE_KERNEL_EXEC       _MOD_PROT(pgprot_default, PTE_UXN | PTE_DIRTY)
 
-#define __PAGE_NONE            __pgprot(_PAGE_DEFAULT | PTE_NG | PTE_PXN | PTE_UXN | PTE_RDONLY)
+#define __PAGE_NONE            __pgprot(((_PAGE_DEFAULT) & ~PTE_TYPE_MASK) | PTE_PROT_NONE)
 #define __PAGE_SHARED          __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN)
 #define __PAGE_SHARED_EXEC     __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN)
 #define __PAGE_COPY            __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN | PTE_RDONLY)
@@ -125,16 +129,15 @@ extern struct page *empty_zero_page;
 /*
  * The following only work if pte_present(). Undefined behaviour otherwise.
  */
-#define pte_present(pte)       (pte_val(pte) & PTE_VALID)
+#define pte_present(pte)       (pte_val(pte) & (PTE_VALID | PTE_PROT_NONE))
 #define pte_dirty(pte)         (pte_val(pte) & PTE_DIRTY)
 #define pte_young(pte)         (pte_val(pte) & PTE_AF)
 #define pte_special(pte)       (pte_val(pte) & PTE_SPECIAL)
 #define pte_write(pte)         (!(pte_val(pte) & PTE_RDONLY))
 #define pte_exec(pte)          (!(pte_val(pte) & PTE_UXN))
 
-#define pte_present_exec_user(pte) \
-       ((pte_val(pte) & (PTE_VALID | PTE_USER | PTE_UXN)) == \
-        (PTE_VALID | PTE_USER))
+#define pte_valid_user(pte) \
+       ((pte_val(pte) & (PTE_VALID | PTE_USER)) == (PTE_VALID | PTE_USER))
 
 #define PTE_BIT_FUNC(fn,op) \
 static inline pte_t pte_##fn(pte_t pte) { pte_val(pte) op; return pte; }
@@ -157,10 +160,13 @@ extern void __sync_icache_dcache(pte_t pteval, unsigned long addr);
 static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
                              pte_t *ptep, pte_t pte)
 {
-       if (pte_present_exec_user(pte))
-               __sync_icache_dcache(pte, addr);
-       if (!pte_dirty(pte))
-               pte = pte_wrprotect(pte);
+       if (pte_valid_user(pte)) {
+               if (pte_exec(pte))
+                       __sync_icache_dcache(pte, addr);
+               if (!pte_dirty(pte))
+                       pte = pte_wrprotect(pte);
+       }
+
        set_pte(ptep, pte);
 }
 
@@ -170,9 +176,6 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
 #define pte_huge(pte)          ((pte_val(pte) & PTE_TYPE_MASK) == PTE_TYPE_HUGEPAGE)
 #define pte_mkhuge(pte)                (__pte((pte_val(pte) & ~PTE_TYPE_MASK) | PTE_TYPE_HUGEPAGE))
 
-#define __pgprot_modify(prot,mask,bits)                \
-       __pgprot((pgprot_val(prot) & ~(mask)) | (bits))
-
 #define __HAVE_ARCH_PTE_SPECIAL
 
 /*
@@ -264,7 +267,8 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr)
 
 static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
 {
-       const pteval_t mask = PTE_USER | PTE_PXN | PTE_UXN | PTE_RDONLY;
+       const pteval_t mask = PTE_USER | PTE_PXN | PTE_UXN | PTE_RDONLY |
+                             PTE_PROT_NONE | PTE_VALID;
        pte_val(pte) = (pte_val(pte) & ~mask) | (pgprot_val(newprot) & mask);
        return pte;
 }
index 58432625fdb36922fa48831142e5bdf3a08d7875..5ef47ba3ed459f9f786cb79abddc2ecf762e5aaf 100644 (file)
@@ -395,8 +395,13 @@ __SYSCALL(370, sys_name_to_handle_at)
 __SYSCALL(371, compat_sys_open_by_handle_at)
 __SYSCALL(372, compat_sys_clock_adjtime)
 __SYSCALL(373, sys_syncfs)
+__SYSCALL(374, compat_sys_sendmmsg)
+__SYSCALL(375, sys_setns)
+__SYSCALL(376, compat_sys_process_vm_readv)
+__SYSCALL(377, compat_sys_process_vm_writev)
+__SYSCALL(378, sys_ni_syscall)                 /* 378 for kcmp */
 
-#define __NR_compat_syscalls           374
+#define __NR_compat_syscalls           379
 
 /*
  * Compat syscall numbers used by the AArch64 kernel.
index c958cb84d75fd4c488d585a6bec5233aa839b21c..6a389dc1bd499c5de57c1a572bb22fb1a81c1494 100644 (file)
@@ -252,10 +252,6 @@ void update_vsyscall(struct timekeeper *tk)
 
 void update_vsyscall_tz(void)
 {
-       ++vdso_data->tb_seq_count;
-       smp_wmb();
        vdso_data->tz_minuteswest       = sys_tz.tz_minuteswest;
        vdso_data->tz_dsttime           = sys_tz.tz_dsttime;
-       smp_wmb();
-       ++vdso_data->tb_seq_count;
 }
index 8bf658d974f947da714eb5e7a6812552173fe140..f0a6d10b52114953dcfd818c66ad85f6cccccbd8 100644 (file)
@@ -73,8 +73,6 @@ ENTRY(__kernel_gettimeofday)
        /* If tz is NULL, return 0. */
        cbz     x1, 3f
        ldp     w4, w5, [vdso_data, #VDSO_TZ_MINWEST]
-       seqcnt_read w9
-       seqcnt_check w9, 1b
        stp     w4, w5, [x1, #TZ_MINWEST]
 3:
        mov     x0, xzr
index aaf5199d8fcbfb69ddca12e7d48fd816f791e341..b3d18f9f3e8d466a66c44fc665e2d04f58492d4a 100644 (file)
@@ -336,4 +336,14 @@ dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
 #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
 #define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
 
+/* drivers/base/dma-mapping.c */
+extern int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
+                          void *cpu_addr, dma_addr_t dma_addr, size_t size);
+extern int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt,
+                                 void *cpu_addr, dma_addr_t dma_addr,
+                                 size_t size);
+
+#define dma_mmap_coherent(d, v, c, h, s) dma_common_mmap(d, v, c, h, s)
+#define dma_get_sgtable(d, t, v, h, s) dma_common_get_sgtable(d, t, v, h, s)
+
 #endif /* __ASM_AVR32_DMA_MAPPING_H */
index bbf461076a0a2107ddc6ea932bc615fe74fdc7a1..054d9ec57d9dc1cea3eba24655a8e24349c06cef 100644 (file)
@@ -154,4 +154,14 @@ dma_cache_sync(struct device *dev, void *vaddr, size_t size,
        _dma_sync((dma_addr_t)vaddr, size, dir);
 }
 
+/* drivers/base/dma-mapping.c */
+extern int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
+                          void *cpu_addr, dma_addr_t dma_addr, size_t size);
+extern int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt,
+                                 void *cpu_addr, dma_addr_t dma_addr,
+                                 size_t size);
+
+#define dma_mmap_coherent(d, v, c, h, s) dma_common_mmap(d, v, c, h, s)
+#define dma_get_sgtable(d, t, v, h, s) dma_common_get_sgtable(d, t, v, h, s)
+
 #endif                         /* _BLACKFIN_DMA_MAPPING_H */
index 3c694065030f506708a9717ef7bbb5faf94f6bc1..88bd0d899bdbedfac95bfc99450f9f6fbcc00ca0 100644 (file)
@@ -89,4 +89,19 @@ extern void dma_free_coherent(struct device *, size_t, void *, dma_addr_t);
 #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent((d), (s), (h), (f))
 #define dma_free_noncoherent(d, s, v, h)  dma_free_coherent((d), (s), (v), (h))
 
+/* Not supported for now */
+static inline int dma_mmap_coherent(struct device *dev,
+                                   struct vm_area_struct *vma, void *cpu_addr,
+                                   dma_addr_t dma_addr, size_t size)
+{
+       return -EINVAL;
+}
+
+static inline int dma_get_sgtable(struct device *dev, struct sg_table *sgt,
+                                 void *cpu_addr, dma_addr_t dma_addr,
+                                 size_t size)
+{
+       return -EINVAL;
+}
+
 #endif /* _ASM_C6X_DMA_MAPPING_H */
index 8588b2ccf85444206480895bb067cd8c7366a558..2f0f654f1b4407484c0a22d1b418a44e5388f04f 100644 (file)
@@ -158,5 +158,15 @@ dma_cache_sync(struct device *dev, void *vaddr, size_t size,
 {
 }
 
+/* drivers/base/dma-mapping.c */
+extern int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
+                          void *cpu_addr, dma_addr_t dma_addr, size_t size);
+extern int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt,
+                                 void *cpu_addr, dma_addr_t dma_addr,
+                                 size_t size);
+
+#define dma_mmap_coherent(d, v, c, h, s) dma_common_mmap(d, v, c, h, s)
+#define dma_get_sgtable(d, t, v, h, s) dma_common_get_sgtable(d, t, v, h, s)
+
 
 #endif
index dfb811002c640fcc790aaecf47727a1c5f83c5fa..1746a2b8e6e7287cd9ad612caa00af6bd9cd8a28 100644 (file)
@@ -132,4 +132,19 @@ void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
        flush_write_buffers();
 }
 
+/* Not supported for now */
+static inline int dma_mmap_coherent(struct device *dev,
+                                   struct vm_area_struct *vma, void *cpu_addr,
+                                   dma_addr_t dma_addr, size_t size)
+{
+       return -EINVAL;
+}
+
+static inline int dma_get_sgtable(struct device *dev, struct sg_table *sgt,
+                                 void *cpu_addr, dma_addr_t dma_addr,
+                                 size_t size)
+{
+       return -EINVAL;
+}
+
 #endif  /* _ASM_DMA_MAPPING_H */
index 4265ff64219b20eade95390e8cc2f47ec7500fba..b7a5fffe0924e03c860f50afcebc5bc0fc4ea7a3 100644 (file)
@@ -672,33 +672,6 @@ ptrace_attach_sync_user_rbs (struct task_struct *child)
        read_unlock(&tasklist_lock);
 }
 
-static inline int
-thread_matches (struct task_struct *thread, unsigned long addr)
-{
-       unsigned long thread_rbs_end;
-       struct pt_regs *thread_regs;
-
-       if (ptrace_check_attach(thread, 0) < 0)
-               /*
-                * If the thread is not in an attachable state, we'll
-                * ignore it.  The net effect is that if ADDR happens
-                * to overlap with the portion of the thread's
-                * register backing store that is currently residing
-                * on the thread's kernel stack, then ptrace() may end
-                * up accessing a stale value.  But if the thread
-                * isn't stopped, that's a problem anyhow, so we're
-                * doing as well as we can...
-                */
-               return 0;
-
-       thread_regs = task_pt_regs(thread);
-       thread_rbs_end = ia64_get_user_rbs_end(thread, thread_regs, NULL);
-       if (!on_kernel_rbs(addr, thread_regs->ar_bspstore, thread_rbs_end))
-               return 0;
-
-       return 1;       /* looks like we've got a winner */
-}
-
 /*
  * Write f32-f127 back to task->thread.fph if it has been modified.
  */
index 17f7a45948eac7473b3afb16e86b5512d58a2c4d..292805f0762ebd100351944c0d5abe9713b07cf2 100644 (file)
@@ -21,6 +21,22 @@ extern void *dma_alloc_coherent(struct device *, size_t,
 extern void dma_free_coherent(struct device *, size_t,
                              void *, dma_addr_t);
 
+static inline void *dma_alloc_attrs(struct device *dev, size_t size,
+                                   dma_addr_t *dma_handle, gfp_t flag,
+                                   struct dma_attrs *attrs)
+{
+       /* attrs is not supported and ignored */
+       return dma_alloc_coherent(dev, size, dma_handle, flag);
+}
+
+static inline void dma_free_attrs(struct device *dev, size_t size,
+                                 void *cpu_addr, dma_addr_t dma_handle,
+                                 struct dma_attrs *attrs)
+{
+       /* attrs is not supported and ignored */
+       dma_free_coherent(dev, size, cpu_addr, dma_handle);
+}
+
 static inline void *dma_alloc_noncoherent(struct device *dev, size_t size,
                                          dma_addr_t *handle, gfp_t flag)
 {
@@ -99,4 +115,14 @@ static inline int dma_mapping_error(struct device *dev, dma_addr_t handle)
 #include <asm-generic/dma-mapping-broken.h>
 #endif
 
+/* drivers/base/dma-mapping.c */
+extern int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
+                          void *cpu_addr, dma_addr_t dma_addr, size_t size);
+extern int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt,
+                                 void *cpu_addr, dma_addr_t dma_addr,
+                                 size_t size);
+
+#define dma_mmap_coherent(d, v, c, h, s) dma_common_mmap(d, v, c, h, s)
+#define dma_get_sgtable(d, t, v, h, s) dma_common_get_sgtable(d, t, v, h, s)
+
 #endif  /* _M68K_DMA_MAPPING_H */
index bf86b29fe64a2b025fe1890da80d90270e730912..037028f4ab7033c55557ade43c6a959c89f9a0b5 100644 (file)
@@ -64,6 +64,8 @@ extern unsigned int kobjsize(const void *objp);
  */
 #define        VMALLOC_START   0
 #define        VMALLOC_END     0xffffffff
+#define        KMAP_START      0
+#define        KMAP_END        0xffffffff
 
 #include <asm-generic/pgtable.h>
 
index 847994ce680456d0144cf0361085728316917b54..f9337f61466050bd48aa8f993c3d59a647e262d6 100644 (file)
@@ -4,7 +4,7 @@
 #include <uapi/asm/unistd.h>
 
 
-#define NR_syscalls            348
+#define NR_syscalls            349
 
 #define __ARCH_WANT_OLD_READDIR
 #define __ARCH_WANT_OLD_STAT
index b94bfbf907053953336eb0bc4e04e6cf269685fb..625f321001dc077fe13da02eb0844a50099653dc 100644 (file)
 #define __NR_process_vm_readv  345
 #define __NR_process_vm_writev 346
 #define __NR_kcmp              347
+#define __NR_finit_module      348
 
 #endif /* _UAPI_ASM_M68K_UNISTD_H_ */
index c30da5b3f2dbc2418085aeb6fed2940a2c175486..3f04ea0ab802750866835fcd9c3aa75ac7b28282 100644 (file)
@@ -368,4 +368,5 @@ ENTRY(sys_call_table)
        .long sys_process_vm_readv      /* 345 */
        .long sys_process_vm_writev
        .long sys_kcmp
+       .long sys_finit_module
 
index f0e05bce92f2533ee8dc78ade768595d825646df..afd8106fd83b10c2b1d51b956b178effd9a6b096 100644 (file)
 void *empty_zero_page;
 EXPORT_SYMBOL(empty_zero_page);
 
+#if !defined(CONFIG_SUN3) && !defined(CONFIG_COLDFIRE)
+extern void init_pointer_table(unsigned long ptable);
+extern pmd_t *zero_pgtable;
+#endif
+
 #ifdef CONFIG_MMU
 
 pg_data_t pg_data_map[MAX_NUMNODES];
@@ -69,9 +74,6 @@ void __init m68k_setup_node(int node)
        node_set_online(node);
 }
 
-extern void init_pointer_table(unsigned long ptable);
-extern pmd_t *zero_pgtable;
-
 #else /* CONFIG_MMU */
 
 /*
index d7af29f1fcf0079c7ccdcae5af9f0add4c8c2c80..ba611927749b9f81def164126a2b2b1d00bf42c5 100644 (file)
@@ -8,8 +8,10 @@ config BCM47XX_SSB
        select SSB_DRIVER_EXTIF
        select SSB_EMBEDDED
        select SSB_B43_PCI_BRIDGE if PCI
+       select SSB_DRIVER_PCICORE if PCI
        select SSB_PCICORE_HOSTMODE if PCI
        select SSB_DRIVER_GPIO
+       select GPIOLIB
        default y
        help
         Add support for old Broadcom BCM47xx boards with Sonics Silicon Backplane support.
@@ -25,6 +27,7 @@ config BCM47XX_BCMA
        select BCMA_HOST_PCI if PCI
        select BCMA_DRIVER_PCI_HOSTMODE if PCI
        select BCMA_DRIVER_GPIO
+       select GPIOLIB
        default y
        help
         Add support for new Broadcom BCM47xx boards with Broadcom specific Advanced Microcontroller Bus.
index 9f883bf769530d18c880416850060cd738bb0fa8..33b72144db3100625bd17991c1651ddd803ddf2b 100644 (file)
@@ -30,6 +30,7 @@
  * measurement, and debugging facilities.
  */
 
+#include <linux/compiler.h>
 #include <linux/irqflags.h>
 #include <asm/octeon/cvmx.h>
 #include <asm/octeon/cvmx-l2c.h>
@@ -285,22 +286,22 @@ uint64_t cvmx_l2c_read_perf(uint32_t counter)
  */
 static void fault_in(uint64_t addr, int len)
 {
-       volatile char *ptr;
-       volatile char dummy;
+       char *ptr;
+
        /*
         * Adjust addr and length so we get all cache lines even for
         * small ranges spanning two cache lines.
         */
        len += addr & CVMX_CACHE_LINE_MASK;
        addr &= ~CVMX_CACHE_LINE_MASK;
-       ptr = (volatile char *)cvmx_phys_to_ptr(addr);
+       ptr = cvmx_phys_to_ptr(addr);
        /*
         * Invalidate L1 cache to make sure all loads result in data
         * being in L2.
         */
        CVMX_DCACHE_INVALIDATE;
        while (len > 0) {
-               dummy += *ptr;
+               ACCESS_ONCE(*ptr);
                len -= CVMX_CACHE_LINE_SIZE;
                ptr += CVMX_CACHE_LINE_SIZE;
        }
index e9bfc0813c72e99a46b29dadb1c3556843fc31a1..7bfad0520e25731c548c949ccecc045e91bba002 100644 (file)
@@ -16,7 +16,7 @@
 #include <asm/mipsregs.h>
 
 #define DSP_DEFAULT    0x00000000
-#define DSP_MASK       0x3ff
+#define DSP_MASK       0x3f
 
 #define __enable_dsp_hazard()                                          \
 do {                                                                   \
index ab84064283db2c50d847d888b32e42a4d316dd7d..33c34adbecfa19ae457699d449d8242ed5a0f43c 100644 (file)
@@ -353,6 +353,7 @@ union mips_instruction {
        struct u_format u_format;
        struct c_format c_format;
        struct r_format r_format;
+       struct p_format p_format;
        struct f_format f_format;
        struct ma_format ma_format;
        struct b_format b_format;
index edaa06d9d492171090e10ee08ad370fae79bebe9..e410df4e1b3a650d99d8981c7b63c04d4218daf4 100644 (file)
@@ -21,4 +21,4 @@
 #define R10000_LLSC_WAR                        0
 #define MIPS34K_MISSED_ITLB_WAR                0
 
-#endif /* __ASM_MIPS_MACH_PNX8550_WAR_H */
+#endif /* __ASM_MIPS_MACH_PNX833X_WAR_H */
index c63191055e695c5a49812f3f32ed0c913d941a0a..013d5f781263e20cf3d9e93cf14b6552c4386cc8 100644 (file)
@@ -230,6 +230,7 @@ static inline void pud_clear(pud_t *pudp)
 #else
 #define pte_pfn(x)             ((unsigned long)((x).pte >> _PFN_SHIFT))
 #define pfn_pte(pfn, prot)     __pte(((pfn) << _PFN_SHIFT) | pgprot_val(prot))
+#define pfn_pmd(pfn, prot)     __pmd(((pfn) << _PFN_SHIFT) | pgprot_val(prot))
 #endif
 
 #define __pgd_offset(address)  pgd_index(address)
index a1a0452ac1853333b07dac4dc7495c9bdd2514a5..77d4fb33f75ad1dd765ba69f697ed771312b3e9a 100644 (file)
@@ -3,6 +3,7 @@ include include/uapi/asm-generic/Kbuild.asm
 
 header-y += auxvec.h
 header-y += bitsperlong.h
+header-y += break.h
 header-y += byteorder.h
 header-y += cachectl.h
 header-y += errno.h
index 6a2d758dd8e9ed0b4a0afb043d2972869620834c..83fa1460e294e02cf602cd5d611af6d02abbba39 100644 (file)
 #define MCOUNT_OFFSET_INSNS 4
 #endif
 
+/* Arch override because MIPS doesn't need to run this from stop_machine() */
+void arch_ftrace_update_code(int command)
+{
+       ftrace_modify_all_code(command);
+}
+
 /*
  * Check if the address is in kernel space
  *
@@ -89,6 +95,24 @@ static int ftrace_modify_code(unsigned long ip, unsigned int new_code)
        return 0;
 }
 
+#ifndef CONFIG_64BIT
+static int ftrace_modify_code_2(unsigned long ip, unsigned int new_code1,
+                               unsigned int new_code2)
+{
+       int faulted;
+
+       safe_store_code(new_code1, ip, faulted);
+       if (unlikely(faulted))
+               return -EFAULT;
+       ip += 4;
+       safe_store_code(new_code2, ip, faulted);
+       if (unlikely(faulted))
+               return -EFAULT;
+       flush_icache_range(ip, ip + 8); /* original ip + 12 */
+       return 0;
+}
+#endif
+
 /*
  * The details about the calling site of mcount on MIPS
  *
@@ -131,8 +155,18 @@ int ftrace_make_nop(struct module *mod,
         * needed.
         */
        new = in_kernel_space(ip) ? INSN_NOP : INSN_B_1F;
-
+#ifdef CONFIG_64BIT
        return ftrace_modify_code(ip, new);
+#else
+       /*
+        * On 32 bit MIPS platforms, gcc adds a stack adjust
+        * instruction in the delay slot after the branch to
+        * mcount and expects mcount to restore the sp on return.
+        * This is based on a legacy API and does nothing but
+        * waste instructions so it's being removed at runtime.
+        */
+       return ftrace_modify_code_2(ip, new, INSN_NOP);
+#endif
 }
 
 int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
index 4c968e7efb747d997ec20ba8ceeb500f4c12807d..1658676733576e74867347f0f7d122df0c5a023e 100644 (file)
@@ -46,9 +46,8 @@
        PTR_L   a5, PT_R9(sp)
        PTR_L   a6, PT_R10(sp)
        PTR_L   a7, PT_R11(sp)
-       PTR_ADDIU       sp, PT_SIZE
 #else
-       PTR_ADDIU       sp, (PT_SIZE + 8)
+       PTR_ADDIU       sp, PT_SIZE
 #endif
 .endm
 
@@ -69,7 +68,9 @@ NESTED(ftrace_caller, PT_SIZE, ra)
        .globl _mcount
 _mcount:
        b       ftrace_stub
-        nop
+       addiu sp,sp,8
+
+       /* When tracing is activated, it calls ftrace_caller+8 (aka here) */
        lw      t1, function_trace_stop
        bnez    t1, ftrace_stub
         nop
index eec690af6581616957089993e2decfd91f13b4fd..147cec19621d7a433429e66433fdd993c2abd62e 100644 (file)
@@ -705,7 +705,7 @@ static int vpe_run(struct vpe * v)
 
                        printk(KERN_WARNING
                               "VPE loader: TC %d is already in use.\n",
-                               t->index);
+                              v->tc->index);
                        return -ENOEXEC;
                }
        } else {
index f36acd1b38086d341458d927c4c4db99d3a40e0f..a7935bf0fecbf789299a2547abb7b0b2e8677e2b 100644 (file)
@@ -408,7 +408,7 @@ int __init icu_of_init(struct device_node *node, struct device_node *parent)
 #endif
 
        /* tell oprofile which irq to use */
-       cp0_perfcount_irq = LTQ_PERF_IRQ;
+       cp0_perfcount_irq = irq_create_mapping(ltq_domain, LTQ_PERF_IRQ);
 
        /*
         * if the timer irq is not one of the mips irqs we need to
index dc81ca8dc0dd4b0ccd81ae2edc78b059aa8fd016..288f7954988d06ef61cc94bbf29dfeab322796b8 100644 (file)
@@ -21,7 +21,7 @@ void __delay(unsigned long loops)
        "       .set    noreorder                               \n"
        "       .align  3                                       \n"
        "1:     bnez    %0, 1b                                  \n"
-#if __SIZEOF_LONG__ == 4
+#if BITS_PER_LONG == 32
        "       subu    %0, 1                                   \n"
 #else
        "       dsubu   %0, 1                                   \n"
index 7657fd21cd3fb35779ad6a37ff65c73547cedc88..cacfd31e8ec9d1121c86e81feb35fac4344a9a3b 100644 (file)
@@ -190,9 +190,3 @@ void __iounmap(const volatile void __iomem *addr)
 
 EXPORT_SYMBOL(__ioremap);
 EXPORT_SYMBOL(__iounmap);
-
-int __virt_addr_valid(const volatile void *kaddr)
-{
-       return pfn_valid(PFN_DOWN(virt_to_phys(kaddr)));
-}
-EXPORT_SYMBOL_GPL(__virt_addr_valid);
index d9be7540a6be7979a265a3ce32acbbe241b2b91c..7e5fe2790d8a212a8d0163989280918d26a46a3c 100644 (file)
@@ -192,3 +192,9 @@ unsigned long arch_randomize_brk(struct mm_struct *mm)
 
        return ret;
 }
+
+int __virt_addr_valid(const volatile void *kaddr)
+{
+       return pfn_valid(PFN_DOWN(virt_to_phys(kaddr)));
+}
+EXPORT_SYMBOL_GPL(__virt_addr_valid);
index 4e7f49d3d5a8105c45dd09a6b86999ed0506a7d7..c5ce6992ac4c7086aea4167a7460723a5d0cb7be 100644 (file)
@@ -193,8 +193,11 @@ static void nlm_init_node(void)
 
 void __init prom_init(void)
 {
-       int i, *argv, *envp;            /* passed as 32 bit ptrs */
+       int *argv, *envp;               /* passed as 32 bit ptrs */
        struct psb_info *prom_infop;
+#ifdef CONFIG_SMP
+       int i;
+#endif
 
        /* truncate to 32 bit and sign extend all args */
        argv = (int *)(long)(int)fw_arg1;
index 1552522b8718bffb86762229ef5279b93094df16..6eaa4f2d0e38cb8c85e31734c7e090ec9a491cb3 100644 (file)
@@ -24,7 +24,7 @@
 #include <asm/mach-ath79/pci.h>
 
 #define AR71XX_PCI_MEM_BASE    0x10000000
-#define AR71XX_PCI_MEM_SIZE    0x08000000
+#define AR71XX_PCI_MEM_SIZE    0x07000000
 
 #define AR71XX_PCI_WIN0_OFFS           0x10000000
 #define AR71XX_PCI_WIN1_OFFS           0x11000000
index 86d77a666458bae80c60f94c9fc405dca5f8459c..c11c75be2d7e06ab0b1232c35926a8ec3c5ba574 100644 (file)
@@ -21,7 +21,7 @@
 #define AR724X_PCI_CTRL_SIZE   0x100
 
 #define AR724X_PCI_MEM_BASE    0x10000000
-#define AR724X_PCI_MEM_SIZE    0x08000000
+#define AR724X_PCI_MEM_SIZE    0x04000000
 
 #define AR724X_PCI_REG_RESET           0x18
 #define AR724X_PCI_REG_INT_STATUS      0x4c
index aa03f2e13385fe5083a50b17ae6f8c02e3ccb269..e70001cfa05b1ee650b132f9163e72766f2fbb5c 100644 (file)
@@ -6,6 +6,7 @@ config MN10300
        select ARCH_WANT_IPC_PARSE_VERSION
        select HAVE_ARCH_TRACEHOOK
        select HAVE_ARCH_KGDB
+       select GENERIC_ATOMIC64
        select HAVE_NMI_WATCHDOG if MN10300_WD_TIMER
        select GENERIC_CLOCKEVENTS
        select MODULES_USE_ELF_RELA
index c1be4397b1edb4b059e917331c5ef8173bcf1096..a18abfc558eb1d41b99cd22798976f517196b7bd 100644 (file)
@@ -168,4 +168,19 @@ void dma_cache_sync(void *vaddr, size_t size,
        mn10300_dcache_flush_inv();
 }
 
+/* Not supported for now */
+static inline int dma_mmap_coherent(struct device *dev,
+                                   struct vm_area_struct *vma, void *cpu_addr,
+                                   dma_addr_t dma_addr, size_t size)
+{
+       return -EINVAL;
+}
+
+static inline int dma_get_sgtable(struct device *dev, struct sg_table *sgt,
+                                 void *cpu_addr, dma_addr_t dma_addr,
+                                 size_t size)
+{
+       return -EINVAL;
+}
+
 #endif
index 467bbd510eac04a5bc86a3f97f70332cf636ec19..106b395688e1d167bf764a76de70d5d58e4109d7 100644 (file)
@@ -238,4 +238,19 @@ void * sba_get_iommu(struct parisc_device *dev);
 /* At the moment, we panic on error for IOMMU resource exaustion */
 #define dma_mapping_error(dev, x)      0
 
+/* This API cannot be supported on PA-RISC */
+static inline int dma_mmap_coherent(struct device *dev,
+                                   struct vm_area_struct *vma, void *cpu_addr,
+                                   dma_addr_t dma_addr, size_t size)
+{
+       return -EINVAL;
+}
+
+static inline int dma_get_sgtable(struct device *dev, struct sg_table *sgt,
+                                 void *cpu_addr, dma_addr_t dma_addr,
+                                 size_t size)
+{
+       return -EINVAL;
+}
+
 #endif
index bfb44247d7a7b023d1953d5983a110fc69966995..eb7850b46c255e98cc9c97ef873766f793398061 100644 (file)
@@ -1865,7 +1865,7 @@ syscall_restore:
 
        /* Are we being ptraced? */
        ldw     TASK_FLAGS(%r1),%r19
-       ldi     (_TIF_SINGLESTEP|_TIF_BLOCKSTEP),%r2
+       ldi     _TIF_SYSCALL_TRACE_MASK,%r2
        and,COND(=)     %r19,%r2,%r0
        b,n     syscall_restore_rfi
 
@@ -1978,15 +1978,23 @@ syscall_restore_rfi:
        /* sr2 should be set to zero for userspace syscalls */
        STREG   %r0,TASK_PT_SR2(%r1)
 
-pt_regs_ok:
        LDREG   TASK_PT_GR31(%r1),%r2
-       depi    3,31,2,%r2                         /* ensure return to user mode. */
-       STREG   %r2,TASK_PT_IAOQ0(%r1)
+       depi    3,31,2,%r2                 /* ensure return to user mode. */
+       STREG   %r2,TASK_PT_IAOQ0(%r1)
        ldo     4(%r2),%r2
        STREG   %r2,TASK_PT_IAOQ1(%r1)
+       b       intr_restore
        copy    %r25,%r16
+
+pt_regs_ok:
+       LDREG   TASK_PT_IAOQ0(%r1),%r2
+       depi    3,31,2,%r2                 /* ensure return to user mode. */
+       STREG   %r2,TASK_PT_IAOQ0(%r1)
+       LDREG   TASK_PT_IAOQ1(%r1),%r2
+       depi    3,31,2,%r2
+       STREG   %r2,TASK_PT_IAOQ1(%r1)
        b       intr_restore
-       nop
+       copy    %r25,%r16
 
        .import schedule,code
 syscall_do_resched:
index c0b1affc06a8fdce9163cf06a384cc958705fa59..0299d63cd1128d9dc1e6d1877033565cf0cd672d 100644 (file)
@@ -410,11 +410,13 @@ void __init init_IRQ(void)
 {
        local_irq_disable();    /* PARANOID - should already be disabled */
        mtctl(~0UL, 23);        /* EIRR : clear all pending external intr */
-       claim_cpu_irqs();
 #ifdef CONFIG_SMP
-       if (!cpu_eiem)
+       if (!cpu_eiem) {
+               claim_cpu_irqs();
                cpu_eiem = EIEM_MASK(IPI_IRQ) | EIEM_MASK(TIMER_IRQ);
+       }
 #else
+       claim_cpu_irqs();
        cpu_eiem = EIEM_MASK(TIMER_IRQ);
 #endif
         set_eiem(cpu_eiem);    /* EIEM : enable all external intr */
index 857c2f545470522bb51345dfaacb4f31e971113e..534abd4936e1ecf96a8f872d9ba90f930c1aff9e 100644 (file)
@@ -26,7 +26,7 @@
 #include <asm/asm-offsets.h>
 
 /* PSW bits we allow the debugger to modify */
-#define USER_PSW_BITS  (PSW_N | PSW_V | PSW_CB)
+#define USER_PSW_BITS  (PSW_N | PSW_B | PSW_V | PSW_CB)
 
 /*
  * Called by kernel/ptrace.c when detaching..
index 53799695599872565f1ad8c387cbf0e1b91ceb3d..fd051705a407dd7396ee2205c1fecf5eebc34bb4 100644 (file)
@@ -190,8 +190,10 @@ get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size)
        DBG(1,"get_sigframe: ka = %#lx, sp = %#lx, frame_size = %#lx\n",
                        (unsigned long)ka, sp, frame_size);
        
+       /* Align alternate stack and reserve 64 bytes for the signal
+          handler's frame marker.  */
        if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! sas_ss_flags(sp))
-               sp = current->sas_ss_sp; /* Stacks grow up! */
+               sp = (current->sas_ss_sp + 0x7f) & ~0x3f; /* Stacks grow up! */
 
        DBG(1,"get_sigframe: Returning sp = %#lx\n", (unsigned long)sp);
        return (void __user *) sp; /* Stacks grow up.  Fun. */
index 9071e093164af3ee8a7907a9b26521b8360121e8..933423fa5144aca0b32a8110d88ff20abc98b90e 100644 (file)
     Sgl_isinexact_to_fix(sgl_value,exponent)
 
 #define Duint_from_sgl_mantissa(sgl_value,exponent,dresultA,dresultB)  \
-  {Sall(sgl_value) <<= SGL_EXP_LENGTH;  /*  left-justify  */           \
+  {unsigned int val = Sall(sgl_value) << SGL_EXP_LENGTH;               \
     if (exponent <= 31) {                                              \
-       Dintp1(dresultA) = 0;                                           \
-       Dintp2(dresultB) = (unsigned)Sall(sgl_value) >> (31 - exponent); \
+       Dintp1(dresultA) = 0;                                           \
+       Dintp2(dresultB) = val >> (31 - exponent);                      \
     }                                                                  \
     else {                                                             \
-       Dintp1(dresultA) = Sall(sgl_value) >> (63 - exponent);          \
-       Dintp2(dresultB) = Sall(sgl_value) << (exponent - 31);          \
+       Dintp1(dresultA) = val >> (63 - exponent);                      \
+       Dintp2(dresultB) = exponent <= 62 ? val << (exponent - 31) : 0; \
     }                                                                  \
-    Sall(sgl_value) >>= SGL_EXP_LENGTH;  /* return to original */      \
   }
 
 #define Duint_setzero(dresultA,dresultB)       \
index d22e73e4618b7924879517f1f59cb99dcd63b43e..e514de57a125333a4ce174cb399070b6cc62e3b4 100644 (file)
@@ -439,6 +439,8 @@ ret_from_fork:
 ret_from_kernel_thread:
        REST_NVGPRS(r1)
        bl      schedule_tail
+       li      r3,0
+       stw     r3,0(r1)
        mtlr    r14
        mr      r3,r15
        PPC440EP_ERR42
index b310a0573625dec8a672c267ab6158f8e90b37a6..3d990d3bd8baf5923d019208f7782d42e7f8a458 100644 (file)
@@ -664,6 +664,19 @@ resume_kernel:
        ld      r4,TI_FLAGS(r9)
        andi.   r0,r4,_TIF_NEED_RESCHED
        bne     1b
+
+       /*
+        * arch_local_irq_restore() from preempt_schedule_irq above may
+        * enable hard interrupt but we really should disable interrupts
+        * when we return from the interrupt, and so that we don't get
+        * interrupted after loading SRR0/1.
+        */
+#ifdef CONFIG_PPC_BOOK3E
+       wrteei  0
+#else
+       ld      r10,PACAKMSR(r13) /* Get kernel MSR without EE */
+       mtmsrd  r10,1             /* Update machine state */
+#endif /* CONFIG_PPC_BOOK3E */
 #endif /* CONFIG_PREEMPT */
 
        .globl  fast_exc_return_irq
index c470a40b29f5d4937883cfcd8a40dd1c6bbdcfd1..a7bc7521c0645c6eb697308f039fb74eaad6b4af 100644 (file)
@@ -154,12 +154,12 @@ static int kgdb_handle_breakpoint(struct pt_regs *regs)
 static int kgdb_singlestep(struct pt_regs *regs)
 {
        struct thread_info *thread_info, *exception_thread_info;
-       struct thread_info *backup_current_thread_info = \
-               (struct thread_info *)kmalloc(sizeof(struct thread_info), GFP_KERNEL);
+       struct thread_info *backup_current_thread_info;
 
        if (user_mode(regs))
                return 0;
 
+       backup_current_thread_info = (struct thread_info *)kmalloc(sizeof(struct thread_info), GFP_KERNEL);
        /*
         * On Book E and perhaps other processors, singlestep is handled on
         * the critical exception stack.  This causes current_thread_info()
@@ -185,6 +185,7 @@ static int kgdb_singlestep(struct pt_regs *regs)
                /* Restore current_thread_info lastly. */
                memcpy(exception_thread_info, backup_current_thread_info, sizeof *thread_info);
 
+       kfree(backup_current_thread_info);
        return 1;
 }
 
index 6f6b1cccc91662037115e69dc072ac1f7a376be5..127361e093f49931a87f3fdbc8cad4c88eae5679 100644 (file)
@@ -494,10 +494,15 @@ void timer_interrupt(struct pt_regs * regs)
        set_dec(DECREMENTER_MAX);
 
        /* Some implementations of hotplug will get timer interrupts while
-        * offline, just ignore these
+        * offline, just ignore these and we also need to set
+        * decrementers_next_tb as MAX to make sure __check_irq_replay
+        * don't replay timer interrupt when return, otherwise we'll trap
+        * here infinitely :(
         */
-       if (!cpu_online(smp_processor_id()))
+       if (!cpu_online(smp_processor_id())) {
+               *next_tb = ~(u64)0;
                return;
+       }
 
        /* Conditionally hard-enable interrupts now that the DEC has been
         * bumped to its maximum value
index b0855e5d8905ee50e6422ca9edfc37aa952f6b21..9d9cddc5b346ff9518009539c08804b1cf0f9682 100644 (file)
@@ -39,6 +39,7 @@
 #define OP_31_XOP_TRAP      4
 #define OP_31_XOP_LWZX      23
 #define OP_31_XOP_TRAP_64   68
+#define OP_31_XOP_DCBF      86
 #define OP_31_XOP_LBZX      87
 #define OP_31_XOP_STWX      151
 #define OP_31_XOP_STBX      215
@@ -374,6 +375,7 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
                        emulated = kvmppc_emulate_mtspr(vcpu, sprn, rs);
                        break;
 
+               case OP_31_XOP_DCBF:
                case OP_31_XOP_DCBI:
                        /* Do nothing. The guest is performing dcbi because
                         * hardware DMA is not snooped by the dcache, but
index 56585086413a4f9876c70eb6b6ea9f3cf1c1ef69..7443481a315c4922063a5f8801c62ee9bfb7691f 100644 (file)
@@ -115,11 +115,13 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_1T_SEGMENT)
        sldi    r29,r5,SID_SHIFT - VPN_SHIFT
        rldicl  r28,r3,64 - VPN_SHIFT,64 - (SID_SHIFT - VPN_SHIFT)
        or      r29,r28,r29
-
-       /* Calculate hash value for primary slot and store it in r28 */
-       rldicl  r5,r5,0,25              /* vsid & 0x0000007fffffffff */
-       rldicl  r0,r3,64-12,48          /* (ea >> 12) & 0xffff */
-       xor     r28,r5,r0
+       /*
+        * Calculate hash value for primary slot and store it in r28
+        * r3 = va, r5 = vsid
+        * r0 = (va >> 12) & ((1ul << (28 - 12)) -1)
+        */
+       rldicl  r0,r3,64-12,48
+       xor     r28,r5,r0               /* hash */
        b       4f
 
 3:     /* Calc vpn and put it in r29 */
@@ -130,11 +132,12 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_1T_SEGMENT)
        /*
         * calculate hash value for primary slot and
         * store it in r28 for 1T segment
+        * r3 = va, r5 = vsid
         */
-       rldic   r28,r5,25,25            /* (vsid << 25) & 0x7fffffffff */
-       clrldi  r5,r5,40                /* vsid & 0xffffff */
-       rldicl  r0,r3,64-12,36          /* (ea >> 12) & 0xfffffff */
-       xor     r28,r28,r5
+       sldi    r28,r5,25               /* vsid << 25 */
+       /* r0 =  (va >> 12) & ((1ul << (40 - 12)) -1) */
+       rldicl  r0,r3,64-12,36
+       xor     r28,r28,r5              /* vsid ^ ( vsid << 25) */
        xor     r28,r28,r0              /* hash */
 
        /* Convert linux PTE bits into HW equivalents */
@@ -407,11 +410,13 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_1T_SEGMENT)
         */
        rldicl  r28,r3,64 - VPN_SHIFT,64 - (SID_SHIFT - VPN_SHIFT)
        or      r29,r28,r29
-
-       /* Calculate hash value for primary slot and store it in r28 */
-       rldicl  r5,r5,0,25              /* vsid & 0x0000007fffffffff */
-       rldicl  r0,r3,64-12,48          /* (ea >> 12) & 0xffff */
-       xor     r28,r5,r0
+       /*
+        * Calculate hash value for primary slot and store it in r28
+        * r3 = va, r5 = vsid
+        * r0 = (va >> 12) & ((1ul << (28 - 12)) -1)
+        */
+       rldicl  r0,r3,64-12,48
+       xor     r28,r5,r0               /* hash */
        b       4f
 
 3:     /* Calc vpn and put it in r29 */
@@ -426,11 +431,12 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_1T_SEGMENT)
        /*
         * Calculate hash value for primary slot and
         * store it in r28  for 1T segment
+        * r3 = va, r5 = vsid
         */
-       rldic   r28,r5,25,25            /* (vsid << 25) & 0x7fffffffff */
-       clrldi  r5,r5,40                /* vsid & 0xffffff */
-       rldicl  r0,r3,64-12,36          /* (ea >> 12) & 0xfffffff */
-       xor     r28,r28,r5
+       sldi    r28,r5,25               /* vsid << 25 */
+       /* r0 = (va >> 12) & ((1ul << (40 - 12)) -1) */
+       rldicl  r0,r3,64-12,36
+       xor     r28,r28,r5              /* vsid ^ ( vsid << 25) */
        xor     r28,r28,r0              /* hash */
 
        /* Convert linux PTE bits into HW equivalents */
@@ -752,25 +758,27 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_1T_SEGMENT)
        rldicl  r28,r3,64 - VPN_SHIFT,64 - (SID_SHIFT - VPN_SHIFT)
        or      r29,r28,r29
 
-       /* Calculate hash value for primary slot and store it in r28 */
-       rldicl  r5,r5,0,25              /* vsid & 0x0000007fffffffff */
-       rldicl  r0,r3,64-16,52          /* (ea >> 16) & 0xfff */
-       xor     r28,r5,r0
+       /* Calculate hash value for primary slot and store it in r28
+        * r3 = va, r5 = vsid
+        * r0 = (va >> 16) & ((1ul << (28 - 16)) -1)
+        */
+       rldicl  r0,r3,64-16,52
+       xor     r28,r5,r0               /* hash */
        b       4f
 
 3:     /* Calc vpn and put it in r29 */
        sldi    r29,r5,SID_SHIFT_1T - VPN_SHIFT
        rldicl  r28,r3,64 - VPN_SHIFT,64 - (SID_SHIFT_1T - VPN_SHIFT)
        or      r29,r28,r29
-
        /*
         * calculate hash value for primary slot and
         * store it in r28 for 1T segment
+        * r3 = va, r5 = vsid
         */
-       rldic   r28,r5,25,25            /* (vsid << 25) & 0x7fffffffff */
-       clrldi  r5,r5,40                /* vsid & 0xffffff */
-       rldicl  r0,r3,64-16,40          /* (ea >> 16) & 0xffffff */
-       xor     r28,r28,r5
+       sldi    r28,r5,25               /* vsid << 25 */
+       /* r0 = (va >> 16) & ((1ul << (40 - 16)) -1) */
+       rldicl  r0,r3,64-16,40
+       xor     r28,r28,r5              /* vsid ^ ( vsid << 25) */
        xor     r28,r28,r0              /* hash */
 
        /* Convert linux PTE bits into HW equivalents */
index 315f9495e9b2b5aa42a6c9aaba5a6307ac03ddde..f444b94935f560f6a7abf1a1de46d88cd32e71d5 100644 (file)
@@ -52,7 +52,7 @@ static int power7_marked_instr_event(u64 mmcr1)
        for (pmc = 0; pmc < 4; pmc++) {
                psel = mmcr1 & (OPROFILE_PM_PMCSEL_MSK
                                << (OPROFILE_MAX_PMC_NUM - pmc)
-                               * OPROFILE_MAX_PMC_NUM);
+                               * OPROFILE_PMSEL_FIELD_WIDTH);
                psel = (psel >> ((OPROFILE_MAX_PMC_NUM - pmc)
                                 * OPROFILE_PMSEL_FIELD_WIDTH)) & ~1ULL;
                unit = mmcr1 & (OPROFILE_PM_UNIT_MSK
index 95d00173029f52c498902a586a24917f09e99d4b..890f30e70f98fc52a39f063172cfbadc5e8713b4 100644 (file)
@@ -236,6 +236,13 @@ out:
 
 static int pas_cpufreq_cpu_exit(struct cpufreq_policy *policy)
 {
+       /*
+        * We don't support CPU hotplug. Don't unmap after the system
+        * has already made it to a running state.
+        */
+       if (system_state != SYSTEM_BOOTING)
+               return 0;
+
        if (sdcasr_mapbase)
                iounmap(sdcasr_mapbase);
        if (sdcpwr_mapbase)
index 4b8e08b56f49679b40bd4ee098bae0bb01945292..7e3ce78d42902e636de522d8f568e00e0f75701f 100644 (file)
@@ -24,8 +24,8 @@ CHECKFLAGS    += -D__s390__ -msize-long
 else
 LD_BFD         := elf64-s390
 LDFLAGS                := -m elf64_s390
-KBUILD_AFLAGS_MODULE += -fpic -D__PIC__
-KBUILD_CFLAGS_MODULE += -fpic -D__PIC__
+KBUILD_AFLAGS_MODULE += -fPIC
+KBUILD_CFLAGS_MODULE += -fPIC
 KBUILD_CFLAGS  += -m64
 KBUILD_AFLAGS  += -m64
 UTS_MACHINE    := s390x
index c928dc1938f23c8a90134c972147c9bbfecb005e..098adbb62660be234d4d2ed5171d5c0615027f31 100644 (file)
@@ -1365,6 +1365,18 @@ static inline void pmdp_invalidate(struct vm_area_struct *vma,
        __pmd_idte(address, pmdp);
 }
 
+#define __HAVE_ARCH_PMDP_SET_WRPROTECT
+static inline void pmdp_set_wrprotect(struct mm_struct *mm,
+                                     unsigned long address, pmd_t *pmdp)
+{
+       pmd_t pmd = *pmdp;
+
+       if (pmd_write(pmd)) {
+               __pmd_idte(address, pmdp);
+               set_pmd_at(mm, address, pmdp, pmd_wrprotect(pmd));
+       }
+}
+
 static inline pmd_t mk_pmd_phys(unsigned long physpage, pgprot_t pgprot)
 {
        pmd_t __pmd;
@@ -1387,10 +1399,7 @@ static inline int has_transparent_hugepage(void)
 
 static inline unsigned long pmd_pfn(pmd_t pmd)
 {
-       if (pmd_trans_huge(pmd))
-               return pmd_val(pmd) >> HPAGE_SHIFT;
-       else
-               return pmd_val(pmd) >> PAGE_SHIFT;
+       return pmd_val(pmd) >> PAGE_SHIFT;
 }
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
index fba4d66788a247c94adfba5c13ae750e19929496..4c060bb5b8eafbfc5b816411dc91f9dea1201daf 100644 (file)
@@ -128,4 +128,32 @@ static inline unsigned long long get_clock_monotonic(void)
        return get_clock_xt() - sched_clock_base_cc;
 }
 
+/**
+ * tod_to_ns - convert a TOD format value to nanoseconds
+ * @todval: to be converted TOD format value
+ * Returns: number of nanoseconds that correspond to the TOD format value
+ *
+ * Converting a 64 Bit TOD format value to nanoseconds means that the value
+ * must be divided by 4.096. In order to achieve that we multiply with 125
+ * and divide by 512:
+ *
+ *    ns = (todval * 125) >> 9;
+ *
+ * In order to avoid an overflow with the multiplication we can rewrite this.
+ * With a split todval == 2^32 * th + tl (th upper 32 bits, tl lower 32 bits)
+ * we end up with
+ *
+ *    ns = ((2^32 * th + tl) * 125 ) >> 9;
+ * -> ns = (2^23 * th * 125) + ((tl * 125) >> 9);
+ *
+ */
+static inline unsigned long long tod_to_ns(unsigned long long todval)
+{
+       unsigned long long ns;
+
+       ns = ((todval >> 32) << 23) * 125;
+       ns += ((todval & 0xffffffff) * 125) >> 9;
+       return ns;
+}
+
 #endif
index aff0e350d776cbc65d68ec11e5aa6418fe4d0651..a5f4f5a1d24b951ac8e7e10cc9af81e153208c63 100644 (file)
@@ -63,7 +63,7 @@ static DEFINE_PER_CPU(struct clock_event_device, comparators);
  */
 unsigned long long notrace __kprobes sched_clock(void)
 {
-       return (get_clock_monotonic() * 125) >> 9;
+       return tod_to_ns(get_clock_monotonic());
 }
 
 /*
index c30615e605ac6716a1f93ee8dc377f9691ee99e0..82c481ddef76286c9be88ce0e45e4df31780eb5d 100644 (file)
@@ -408,7 +408,7 @@ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu)
                return 0;
        }
 
-       sltime = ((vcpu->arch.sie_block->ckc - now)*125)>>9;
+       sltime = tod_to_ns(vcpu->arch.sie_block->ckc - now);
 
        hrtimer_start(&vcpu->arch.ckc_timer, ktime_set (0, sltime) , HRTIMER_MODE_REL);
        VCPU_EVENT(vcpu, 5, "enabled wait via clock comparator: %llx ns", sltime);
index c9011bfaabbe8594bdd6d3c63ce242a55897cff3..f090e819bf71817ccf564d8798632fc6bf2d7fb7 100644 (file)
@@ -613,7 +613,9 @@ static int __vcpu_run(struct kvm_vcpu *vcpu)
                kvm_s390_deliver_pending_interrupts(vcpu);
 
        vcpu->arch.sie_block->icptcode = 0;
+       preempt_disable();
        kvm_guest_enter();
+       preempt_enable();
        VCPU_EVENT(vcpu, 6, "entering sie flags %x",
                   atomic_read(&vcpu->arch.sie_block->cpuflags));
        trace_kvm_s390_sie_enter(vcpu,
index 3fede4556c91eee6ae7e5aa7a449675710254150..a0fa5791cd44abe9b29784e26142338c11768c1d 100644 (file)
  *                                  OFF-ON : MMC
  */
 
+/*
+ * FSI - DA7210
+ *
+ * it needs amixer settings for playing
+ *
+ * amixer set 'HeadPhone' 80
+ * amixer set 'Out Mixer Left DAC Left' on
+ * amixer set 'Out Mixer Right DAC Right' on
+ */
+
 /* Heartbeat */
 static unsigned char led_pos[] = { 0, 1, 2, 3 };
 
index 37924afa8d8a26781a2e8ebf4952144b141f9295..bf9f44f17c2983a1f1994f451ab03d3e89c05355 100644 (file)
@@ -203,9 +203,9 @@ extern void __kernel_vsyscall;
        if (vdso_enabled)                                       \
                NEW_AUX_ENT(AT_SYSINFO_EHDR, VDSO_BASE);        \
        else                                                    \
-               NEW_AUX_ENT(AT_IGNORE, 0);
+               NEW_AUX_ENT(AT_IGNORE, 0)
 #else
-#define VSYSCALL_AUX_ENT
+#define VSYSCALL_AUX_ENT       NEW_AUX_ENT(AT_IGNORE, 0)
 #endif /* CONFIG_VSYSCALL */
 
 #ifdef CONFIG_SH_FPU
index b1320d55ca305139f5087694a6c6b3c2db3a6990..e699a12cdcca0da391ee072797c74208fa93eddb 100644 (file)
@@ -39,7 +39,7 @@
 /* This decides where the kernel will search for a free chunk of vm
  * space during mmap's.
  */
-#define TASK_UNMAPPED_BASE     (TASK_SIZE / 3)
+#define TASK_UNMAPPED_BASE     PAGE_ALIGN(TASK_SIZE / 3)
 
 /*
  * Bit of SR register
index 1ee8946f09520a987a19ae477cac15087f19bf4a..1cc7d31971435ee57f9997539fcec28e061b6187 100644 (file)
@@ -47,7 +47,7 @@ pc; })
 /* This decides where the kernel will search for a free chunk of vm
  * space during mmap's.
  */
-#define TASK_UNMAPPED_BASE     (TASK_SIZE / 3)
+#define TASK_UNMAPPED_BASE     PAGE_ALIGN(TASK_SIZE / 3)
 
 /*
  * Bit of SR register
index 9e465f246dc1e49882473fdcdeb34f6520eb6b10..d13a1d6237363747117770a7620f448b08474e47 100644 (file)
 #define __NR_process_vm_readv  365
 #define __NR_process_vm_writev 366
 #define __NR_kcmp              367
+#define __NR_finit_module      368
 
-#define NR_syscalls 368
+#define NR_syscalls 369
 
 #endif /* __ASM_SH_UNISTD_32_H */
index 8e3a2edd284eadbefe740d9fea0cbcd14a27e48f..e6820c86e8c7bc1c0daf34cd9a891c45b3a5775f 100644 (file)
 #define __NR_process_vm_readv  376
 #define __NR_process_vm_writev 377
 #define __NR_kcmp              378
+#define __NR_finit_module      379
 
-#define NR_syscalls 379
+#define NR_syscalls 380
 
 #endif /* __ASM_SH_UNISTD_64_H */
index fe97ae5e56f168e642e4825fc76575a8a61811b2..734234be2f011cfb529bacecd8a0efdfaa8762ca 100644 (file)
@@ -385,3 +385,4 @@ ENTRY(sys_call_table)
        .long sys_process_vm_readv      /* 365 */
        .long sys_process_vm_writev
        .long sys_kcmp
+       .long sys_finit_module
index 5c7b1c67bdc1c04f9c858644c066063a7eaf6919..579fcb9a896be37d01421ce96e8b3fec18ff7e3b 100644 (file)
@@ -405,3 +405,4 @@ sys_call_table:
        .long sys_process_vm_readv
        .long sys_process_vm_writev
        .long sys_kcmp
+       .long sys_finit_module
index 60164e65d66551c4375121e984fd0ea28a9525fa..52aa2011d7537daa0b502d1891df4ee0184079fe 100644 (file)
@@ -294,6 +294,8 @@ stack_panic:
        .align 2
 .L_init_thread_union:
        .long   init_thread_union
+.L_ebss:
+       .long   __bss_stop
 .Lpanic:
        .long   panic
 .Lpanic_s:
index cac719d1bc5c6b8238ff0b6be5f34bc166980534..62ced589bcf78f1554bcadd4f420fad19c7ebcc1 100644 (file)
 #define __NR_process_vm_writev 339
 #define __NR_kern_features     340
 #define __NR_kcmp              341
+#define __NR_finit_module      342
 
-#define NR_syscalls            342
+#define NR_syscalls            343
 
 /* Bitmask values returned from kern_features system call.  */
 #define KERN_FEATURE_MIXED_MODE_STACK  0x00000001
index 04bacce76fe6be65cc925d8c1419d560a9eeb8d6..baf4366e2d6afe937db5fe792c3183d56da4c580 100644 (file)
@@ -378,7 +378,8 @@ static void apb_calc_first_last(u8 map, u32 *first_p, u32 *last_p)
 /* Cook up fake bus resources for SUNW,simba PCI bridges which lack
  * a proper 'ranges' property.
  */
-static void apb_fake_ranges(struct pci_dev *dev, struct pci_bus *bus,
+static void apb_fake_ranges(struct pci_dev *dev,
+                           struct pci_bus *bus,
                            struct pci_pbm_info *pbm)
 {
        struct pci_bus_region region;
@@ -403,13 +404,15 @@ static void apb_fake_ranges(struct pci_dev *dev, struct pci_bus *bus,
        pcibios_bus_to_resource(dev, res, &region);
 }
 
-static void pci_of_scan_bus(struct pci_pbm_info *pbm, struct device_node *node,
+static void pci_of_scan_bus(struct pci_pbm_info *pbm,
+                           struct device_node *node,
                            struct pci_bus *bus);
 
 #define GET_64BIT(prop, i)     ((((u64) (prop)[(i)]) << 32) | (prop)[(i)+1])
 
 static void of_scan_pci_bridge(struct pci_pbm_info *pbm,
-                              struct device_node *node, struct pci_dev *dev)
+                              struct device_node *node,
+                              struct pci_dev *dev)
 {
        struct pci_bus *bus;
        const u32 *busrange, *ranges;
@@ -500,7 +503,8 @@ after_ranges:
        pci_of_scan_bus(pbm, node, bus);
 }
 
-static void pci_of_scan_bus(struct pci_pbm_info *pbm, struct device_node *node,
+static void pci_of_scan_bus(struct pci_pbm_info *pbm,
+                           struct device_node *node,
                            struct pci_bus *bus)
 {
        struct device_node *child;
index b85238289717f95c1da83cda5b653d15533a119b..c647634ead2bc9e0eab04f6d9558579b93990f75 100644 (file)
@@ -366,7 +366,8 @@ static void pbm_config_busmastering(struct pci_pbm_info *pbm)
        pci_config_write8(addr, 64);
 }
 
-static void psycho_scan_bus(struct pci_pbm_info *pbm, struct device *parent)
+static void psycho_scan_bus(struct pci_pbm_info *pbm,
+                           struct device *parent)
 {
        pbm_config_busmastering(pbm);
        pbm->is_66mhz_capable = 0;
index 531186d7c9ab3f0f3aaffe0e1a804fb28074c554..6f00d27e8dacb348283df0f1b30f00b5755de6d9 100644 (file)
@@ -442,7 +442,8 @@ static void sabre_scan_bus(struct pci_pbm_info *pbm, struct device *parent)
        sabre_register_error_handlers(pbm);
 }
 
-static void sabre_pbm_init(struct pci_pbm_info *pbm, struct platform_device *op)
+static void sabre_pbm_init(struct pci_pbm_info *pbm,
+                          struct platform_device *op)
 {
        psycho_pbm_init_common(pbm, op, "SABRE", PBM_CHIP_TYPE_SABRE);
        pbm->pci_afsr = pbm->controller_regs + SABRE_PIOAFSR;
index 29e888158ae6a07c7711f59c4cd36ad28138548f..8f76f23dac38ec66b0afea55a5311e612f2459f0 100644 (file)
@@ -1306,8 +1306,9 @@ static void schizo_pbm_hw_init(struct pci_pbm_info *pbm)
        }
 }
 
-static int schizo_pbm_init(struct pci_pbm_info *pbm, struct platform_device *op,
-                          u32 portid, int chip_type)
+static int schizo_pbm_init(struct pci_pbm_info *pbm,
+                          struct platform_device *op, u32 portid,
+                          int chip_type)
 {
        const struct linux_prom64_registers *regs;
        struct device_node *dp = op->dev.of_node;
index 5147f574f1256a7f3304a716da22bfb2ef68d42a..6ac43c36bbbfb98ad769ba6492f66a5fbb2ab554 100644 (file)
@@ -85,4 +85,4 @@ sys_call_table:
 /*325*/        .long sys_pwritev, sys_rt_tgsigqueueinfo, sys_perf_event_open, sys_recvmmsg, sys_fanotify_init
 /*330*/        .long sys_fanotify_mark, sys_prlimit64, sys_name_to_handle_at, sys_open_by_handle_at, sys_clock_adjtime
 /*335*/        .long sys_syncfs, sys_sendmmsg, sys_setns, sys_process_vm_readv, sys_process_vm_writev
-/*340*/        .long sys_ni_syscall, sys_kcmp
+/*340*/        .long sys_ni_syscall, sys_kcmp, sys_finit_module
index cdbd9b817751472200246245e85830b504478a0f..1009ecb92678185a388d708e8b28be070669524d 100644 (file)
@@ -86,7 +86,7 @@ sys_call_table32:
        .word compat_sys_pwritev, compat_sys_rt_tgsigqueueinfo, sys_perf_event_open, compat_sys_recvmmsg, sys_fanotify_init
 /*330*/        .word sys32_fanotify_mark, sys_prlimit64, sys_name_to_handle_at, compat_sys_open_by_handle_at, compat_sys_clock_adjtime
        .word sys_syncfs, compat_sys_sendmmsg, sys_setns, compat_sys_process_vm_readv, compat_sys_process_vm_writev
-/*340*/        .word sys_kern_features, sys_kcmp
+/*340*/        .word sys_kern_features, sys_kcmp, sys_finit_module
 
 #endif /* CONFIG_COMPAT */
 
@@ -164,4 +164,4 @@ sys_call_table:
        .word sys_pwritev, sys_rt_tgsigqueueinfo, sys_perf_event_open, sys_recvmmsg, sys_fanotify_init
 /*330*/        .word sys_fanotify_mark, sys_prlimit64, sys_name_to_handle_at, sys_open_by_handle_at, sys_clock_adjtime
        .word sys_syncfs, sys_sendmmsg, sys_setns, sys_process_vm_readv, sys_process_vm_writev
-/*340*/        .word sys_kern_features, sys_kcmp
+/*340*/        .word sys_kern_features, sys_kcmp, sys_finit_module
index 79795af598105e9998f0de234b279fb6c61ca42f..225543bf45a5ca551f9609b18cf8711f729f71e7 100644 (file)
@@ -2138,6 +2138,7 @@ config OLPC_XO1_RTC
 config OLPC_XO1_SCI
        bool "OLPC XO-1 SCI extras"
        depends on OLPC && OLPC_XO1_PM
+       depends on INPUT=y
        select POWER_SUPPLY
        select GPIO_CS5535
        select MFD_CORE
index ccce0ed67dde703a80c78309252abbef17828291..379814bc41e3a956dc037a5f1d4ca23709854fbc 100644 (file)
@@ -71,7 +71,7 @@ GCOV_PROFILE := n
 $(obj)/bzImage: asflags-y  := $(SVGA_MODE)
 
 quiet_cmd_image = BUILD   $@
-cmd_image = $(obj)/tools/build $(obj)/setup.bin $(obj)/vmlinux.bin > $@
+cmd_image = $(obj)/tools/build $(obj)/setup.bin $(obj)/vmlinux.bin $(obj)/zoffset.h > $@
 
 $(obj)/bzImage: $(obj)/setup.bin $(obj)/vmlinux.bin $(obj)/tools/build FORCE
        $(call if_changed,image)
@@ -92,7 +92,7 @@ targets += voffset.h
 $(obj)/voffset.h: vmlinux FORCE
        $(call if_changed,voffset)
 
-sed-zoffset := -e 's/^\([0-9a-fA-F]*\) . \(startup_32\|input_data\|_end\|z_.*\)$$/\#define ZO_\2 0x\1/p'
+sed-zoffset := -e 's/^\([0-9a-fA-F]*\) . \(startup_32\|startup_64\|efi_pe_entry\|efi_stub_entry\|input_data\|_end\|z_.*\)$$/\#define ZO_\2 0x\1/p'
 
 quiet_cmd_zoffset = ZOFFSET $@
       cmd_zoffset = $(NM) $< | sed -n $(sed-zoffset) > $@
index b1942e222768e7c9c2707c14dbdbd9643a51a8b9..f8fa41190c3526f61ec7bb6e1dc5b0ebf08b3c4d 100644 (file)
@@ -256,10 +256,10 @@ static efi_status_t setup_efi_pci(struct boot_params *params)
        int i;
        struct setup_data *data;
 
-       data = (struct setup_data *)params->hdr.setup_data;
+       data = (struct setup_data *)(unsigned long)params->hdr.setup_data;
 
        while (data && data->next)
-               data = (struct setup_data *)data->next;
+               data = (struct setup_data *)(unsigned long)data->next;
 
        status = efi_call_phys5(sys_table->boottime->locate_handle,
                                EFI_LOCATE_BY_PROTOCOL, &pci_proto,
@@ -295,16 +295,18 @@ static efi_status_t setup_efi_pci(struct boot_params *params)
                if (!pci)
                        continue;
 
+#ifdef CONFIG_X86_64
                status = efi_call_phys4(pci->attributes, pci,
                                        EfiPciIoAttributeOperationGet, 0,
                                        &attributes);
-
+#else
+               status = efi_call_phys5(pci->attributes, pci,
+                                       EfiPciIoAttributeOperationGet, 0, 0,
+                                       &attributes);
+#endif
                if (status != EFI_SUCCESS)
                        continue;
 
-               if (!attributes & EFI_PCI_IO_ATTRIBUTE_EMBEDDED_ROM)
-                       continue;
-
                if (!pci->romimage || !pci->romsize)
                        continue;
 
@@ -345,9 +347,9 @@ static efi_status_t setup_efi_pci(struct boot_params *params)
                memcpy(rom->romdata, pci->romimage, pci->romsize);
 
                if (data)
-                       data->next = (uint64_t)rom;
+                       data->next = (unsigned long)rom;
                else
-                       params->hdr.setup_data = (uint64_t)rom;
+                       params->hdr.setup_data = (unsigned long)rom;
 
                data = (struct setup_data *)rom;
 
@@ -432,10 +434,9 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
                         * Once we've found a GOP supporting ConOut,
                         * don't bother looking any further.
                         */
+                       first_gop = gop;
                        if (conout_found)
                                break;
-
-                       first_gop = gop;
                }
        }
 
index aa4aaf1b23803e8ef0e180e3cbeedc7a4db6c556..1e3184f6072f913250b3b18371978a4fd1b3f63d 100644 (file)
@@ -35,11 +35,11 @@ ENTRY(startup_32)
 #ifdef CONFIG_EFI_STUB
        jmp     preferred_addr
 
-       .balign 0x10
        /*
         * We don't need the return address, so set up the stack so
-        * efi_main() can find its arugments.
+        * efi_main() can find its arguments.
         */
+ENTRY(efi_pe_entry)
        add     $0x4, %esp
 
        call    make_boot_params
@@ -50,8 +50,10 @@ ENTRY(startup_32)
        pushl   %eax
        pushl   %esi
        pushl   %ecx
+       sub     $0x4, %esp
 
-       .org 0x30,0x90
+ENTRY(efi_stub_entry)
+       add     $0x4, %esp
        call    efi_main
        cmpl    $0, %eax
        movl    %eax, %esi
index 2c4b171eec337619e8f2dab2b3c3b2048e622e51..f5d1aaa0dec87ce844317f2a343a90739408ac22 100644 (file)
@@ -201,12 +201,12 @@ ENTRY(startup_64)
         */
 #ifdef CONFIG_EFI_STUB
        /*
-        * The entry point for the PE/COFF executable is 0x210, so only
-        * legacy boot loaders will execute this jmp.
+        * The entry point for the PE/COFF executable is efi_pe_entry, so
+        * only legacy boot loaders will execute this jmp.
         */
        jmp     preferred_addr
 
-       .org 0x210
+ENTRY(efi_pe_entry)
        mov     %rcx, %rdi
        mov     %rdx, %rsi
        pushq   %rdi
@@ -218,7 +218,7 @@ ENTRY(startup_64)
        popq    %rsi
        popq    %rdi
 
-       .org 0x230,0x90
+ENTRY(efi_stub_entry)
        call    efi_main
        movq    %rax,%rsi
        cmpq    $0,%rax
index 8c132a625b94991c179def21973bb8ad3c3a56d5..944ce595f767621f07a47d6c89cdea9415627663 100644 (file)
@@ -21,6 +21,7 @@
 #include <asm/e820.h>
 #include <asm/page_types.h>
 #include <asm/setup.h>
+#include <asm/bootparam.h>
 #include "boot.h"
 #include "voffset.h"
 #include "zoffset.h"
@@ -255,6 +256,9 @@ section_table:
        # header, from the old boot sector.
 
        .section ".header", "a"
+       .globl  sentinel
+sentinel:      .byte 0xff, 0xff        /* Used to detect broken loaders */
+
        .globl  hdr
 hdr:
 setup_sects:   .byte 0                 /* Filled in by build.c */
@@ -279,7 +283,7 @@ _start:
        # Part 2 of the header, from the old setup.S
 
                .ascii  "HdrS"          # header signature
-               .word   0x020b          # header version number (>= 0x0105)
+               .word   0x020c          # header version number (>= 0x0105)
                                        # or else old loadlin-1.5 will fail)
                .globl realmode_swtch
 realmode_swtch:        .word   0, 0            # default_switch, SETUPSEG
@@ -297,13 +301,7 @@ type_of_loader:    .byte   0               # 0 means ancient bootloader, newer
 
 # flags, unused bits must be zero (RFU) bit within loadflags
 loadflags:
-LOADED_HIGH    = 1                     # If set, the kernel is loaded high
-CAN_USE_HEAP   = 0x80                  # If set, the loader also has set
-                                       # heap_end_ptr to tell how much
-                                       # space behind setup.S can be used for
-                                       # heap purposes.
-                                       # Only the loader knows what is free
-               .byte   LOADED_HIGH
+               .byte   LOADED_HIGH     # The kernel is to be loaded high
 
 setup_move_size: .word  0x8000         # size to move, when setup is not
                                        # loaded at 0x90000. We will move setup
@@ -369,7 +367,23 @@ relocatable_kernel:    .byte 1
 relocatable_kernel:    .byte 0
 #endif
 min_alignment:         .byte MIN_KERNEL_ALIGN_LG2      # minimum alignment
-pad3:                  .word 0
+
+xloadflags:
+#ifdef CONFIG_X86_64
+# define XLF0 XLF_KERNEL_64                    /* 64-bit kernel */
+#else
+# define XLF0 0
+#endif
+#ifdef CONFIG_EFI_STUB
+# ifdef CONFIG_X86_64
+#  define XLF23 XLF_EFI_HANDOVER_64            /* 64-bit EFI handover ok */
+# else
+#  define XLF23 XLF_EFI_HANDOVER_32            /* 32-bit EFI handover ok */
+# endif
+#else
+# define XLF23 0
+#endif
+                       .word XLF0 | XLF23
 
 cmdline_size:   .long   COMMAND_LINE_SIZE-1     #length of the command line,
                                                 #added with boot protocol
@@ -397,8 +411,13 @@ pref_address:              .quad LOAD_PHYSICAL_ADDR        # preferred load addr
 #define INIT_SIZE VO_INIT_SIZE
 #endif
 init_size:             .long INIT_SIZE         # kernel initialization size
-handover_offset:       .long 0x30              # offset to the handover
+handover_offset:
+#ifdef CONFIG_EFI_STUB
+                       .long 0x30              # offset to the handover
                                                # protocol entry point
+#else
+                       .long 0
+#endif
 
 # End of setup header #####################################################
 
index 03c0683636b6fbf859a8795262f249631e6157f9..96a6c7563538364d2dee7e307846815156c11c33 100644 (file)
@@ -13,7 +13,7 @@ SECTIONS
        .bstext         : { *(.bstext) }
        .bsdata         : { *(.bsdata) }
 
-       . = 497;
+       . = 495;
        .header         : { *(.header) }
        .entrytext      : { *(.entrytext) }
        .inittext       : { *(.inittext) }
index 4b8e165ee5723643dcd89619a341c7559eafe2bd..94c54465002003e34af8f6fb3d14a1be839fdeba 100644 (file)
@@ -52,6 +52,10 @@ int is_big_kernel;
 
 #define PECOFF_RELOC_RESERVE 0x20
 
+unsigned long efi_stub_entry;
+unsigned long efi_pe_entry;
+unsigned long startup_64;
+
 /*----------------------------------------------------------------------*/
 
 static const u32 crctab32[] = {
@@ -132,7 +136,7 @@ static void die(const char * str, ...)
 
 static void usage(void)
 {
-       die("Usage: build setup system [> image]");
+       die("Usage: build setup system [zoffset.h] [> image]");
 }
 
 #ifdef CONFIG_EFI_STUB
@@ -206,30 +210,54 @@ static void update_pecoff_text(unsigned int text_start, unsigned int file_sz)
         */
        put_unaligned_le32(file_sz - 512, &buf[pe_header + 0x1c]);
 
-#ifdef CONFIG_X86_32
        /*
-        * Address of entry point.
-        *
-        * The EFI stub entry point is +16 bytes from the start of
-        * the .text section.
+        * Address of entry point for PE/COFF executable
         */
-       put_unaligned_le32(text_start + 16, &buf[pe_header + 0x28]);
-#else
-       /*
-        * Address of entry point. startup_32 is at the beginning and
-        * the 64-bit entry point (startup_64) is always 512 bytes
-        * after. The EFI stub entry point is 16 bytes after that, as
-        * the first instruction allows legacy loaders to jump over
-        * the EFI stub initialisation
-        */
-       put_unaligned_le32(text_start + 528, &buf[pe_header + 0x28]);
-#endif /* CONFIG_X86_32 */
+       put_unaligned_le32(text_start + efi_pe_entry, &buf[pe_header + 0x28]);
 
        update_pecoff_section_header(".text", text_start, text_sz);
 }
 
 #endif /* CONFIG_EFI_STUB */
 
+
+/*
+ * Parse zoffset.h and find the entry points. We could just #include zoffset.h
+ * but that would mean tools/build would have to be rebuilt every time. It's
+ * not as if parsing it is hard...
+ */
+#define PARSE_ZOFS(p, sym) do { \
+       if (!strncmp(p, "#define ZO_" #sym " ", 11+sizeof(#sym)))       \
+               sym = strtoul(p + 11 + sizeof(#sym), NULL, 16);         \
+} while (0)
+
+static void parse_zoffset(char *fname)
+{
+       FILE *file;
+       char *p;
+       int c;
+
+       file = fopen(fname, "r");
+       if (!file)
+               die("Unable to open `%s': %m", fname);
+       c = fread(buf, 1, sizeof(buf) - 1, file);
+       if (ferror(file))
+               die("read-error on `zoffset.h'");
+       buf[c] = 0;
+
+       p = (char *)buf;
+
+       while (p && *p) {
+               PARSE_ZOFS(p, efi_stub_entry);
+               PARSE_ZOFS(p, efi_pe_entry);
+               PARSE_ZOFS(p, startup_64);
+
+               p = strchr(p, '\n');
+               while (p && (*p == '\r' || *p == '\n'))
+                       p++;
+       }
+}
+
 int main(int argc, char ** argv)
 {
        unsigned int i, sz, setup_sectors;
@@ -241,7 +269,19 @@ int main(int argc, char ** argv)
        void *kernel;
        u32 crc = 0xffffffffUL;
 
-       if (argc != 3)
+       /* Defaults for old kernel */
+#ifdef CONFIG_X86_32
+       efi_pe_entry = 0x10;
+       efi_stub_entry = 0x30;
+#else
+       efi_pe_entry = 0x210;
+       efi_stub_entry = 0x230;
+       startup_64 = 0x200;
+#endif
+
+       if (argc == 4)
+               parse_zoffset(argv[3]);
+       else if (argc != 3)
                usage();
 
        /* Copy the setup code */
@@ -299,6 +339,11 @@ int main(int argc, char ** argv)
 
 #ifdef CONFIG_EFI_STUB
        update_pecoff_text(setup_sectors * 512, sz + i + ((sys_size * 16) - sz));
+
+#ifdef CONFIG_X86_64 /* Yes, this is really how we defined it :( */
+       efi_stub_entry -= 0x200;
+#endif
+       put_unaligned_le32(efi_stub_entry, &buf[0x264]);
 #endif
 
        crc = partial_crc32(buf, i, crc);
index 102ff7cb3e4146ef1e158d718c49c1c0a6773f7b..142c4ceff1122cadb5dd92ddb0fab05c64f0c48e 100644 (file)
@@ -207,7 +207,7 @@ sysexit_from_sys_call:
        testl $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT),TI_flags+THREAD_INFO(%rsp,RIP-ARGOFFSET)
        jnz ia32_ret_from_sys_call
        TRACE_IRQS_ON
-       sti
+       ENABLE_INTERRUPTS(CLBR_NONE)
        movl %eax,%esi          /* second arg, syscall return value */
        cmpl $-MAX_ERRNO,%eax   /* is it an error ? */
        jbe 1f
@@ -217,7 +217,7 @@ sysexit_from_sys_call:
        call __audit_syscall_exit
        movq RAX-ARGOFFSET(%rsp),%rax   /* reload syscall return value */
        movl $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT),%edi
-       cli
+       DISABLE_INTERRUPTS(CLBR_NONE)
        TRACE_IRQS_OFF
        testl %edi,TI_flags+THREAD_INFO(%rsp,RIP-ARGOFFSET)
        jz \exit
index 6e8fdf5ad1135c0100c8b7a5220bb79db2359ddb..28677c55113f8cea59e59c67e7cd53cfcd615b6d 100644 (file)
@@ -94,6 +94,7 @@ extern void __iomem *efi_ioremap(unsigned long addr, unsigned long size,
 #endif /* CONFIG_X86_32 */
 
 extern int add_efi_memmap;
+extern unsigned long x86_efi_facility;
 extern void efi_set_executable(efi_memory_desc_t *md, bool executable);
 extern int efi_memblock_x86_reserve_range(void);
 extern void efi_call_phys_prelog(void);
index b47c2a82ff1546a7efcd77a820a990d1e08bef00..062921ef34e9136100d3b4820826642dd45f223b 100644 (file)
@@ -16,7 +16,7 @@ extern void uv_system_init(void);
 extern const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask,
                                                 struct mm_struct *mm,
                                                 unsigned long start,
-                                                unsigned end,
+                                                unsigned long end,
                                                 unsigned int cpu);
 
 #else  /* X86_UV */
index 92862cd902012b9fb174d03e6e2cd687787136cf..c15ddaf907107134d6cd2f8d86f554e510a6f848 100644 (file)
@@ -1,6 +1,31 @@
 #ifndef _ASM_X86_BOOTPARAM_H
 #define _ASM_X86_BOOTPARAM_H
 
+/* setup_data types */
+#define SETUP_NONE                     0
+#define SETUP_E820_EXT                 1
+#define SETUP_DTB                      2
+#define SETUP_PCI                      3
+
+/* ram_size flags */
+#define RAMDISK_IMAGE_START_MASK       0x07FF
+#define RAMDISK_PROMPT_FLAG            0x8000
+#define RAMDISK_LOAD_FLAG              0x4000
+
+/* loadflags */
+#define LOADED_HIGH    (1<<0)
+#define QUIET_FLAG     (1<<5)
+#define KEEP_SEGMENTS  (1<<6)
+#define CAN_USE_HEAP   (1<<7)
+
+/* xloadflags */
+#define XLF_KERNEL_64                  (1<<0)
+#define XLF_CAN_BE_LOADED_ABOVE_4G     (1<<1)
+#define XLF_EFI_HANDOVER_32            (1<<2)
+#define XLF_EFI_HANDOVER_64            (1<<3)
+
+#ifndef __ASSEMBLY__
+
 #include <linux/types.h>
 #include <linux/screen_info.h>
 #include <linux/apm_bios.h>
@@ -9,12 +34,6 @@
 #include <asm/ist.h>
 #include <video/edid.h>
 
-/* setup data types */
-#define SETUP_NONE                     0
-#define SETUP_E820_EXT                 1
-#define SETUP_DTB                      2
-#define SETUP_PCI                      3
-
 /* extensible setup data list node */
 struct setup_data {
        __u64 next;
@@ -28,9 +47,6 @@ struct setup_header {
        __u16   root_flags;
        __u32   syssize;
        __u16   ram_size;
-#define RAMDISK_IMAGE_START_MASK       0x07FF
-#define RAMDISK_PROMPT_FLAG            0x8000
-#define RAMDISK_LOAD_FLAG              0x4000
        __u16   vid_mode;
        __u16   root_dev;
        __u16   boot_flag;
@@ -42,10 +58,6 @@ struct setup_header {
        __u16   kernel_version;
        __u8    type_of_loader;
        __u8    loadflags;
-#define LOADED_HIGH    (1<<0)
-#define QUIET_FLAG     (1<<5)
-#define KEEP_SEGMENTS  (1<<6)
-#define CAN_USE_HEAP   (1<<7)
        __u16   setup_move_size;
        __u32   code32_start;
        __u32   ramdisk_image;
@@ -58,7 +70,8 @@ struct setup_header {
        __u32   initrd_addr_max;
        __u32   kernel_alignment;
        __u8    relocatable_kernel;
-       __u8    _pad2[3];
+       __u8    min_alignment;
+       __u16   xloadflags;
        __u32   cmdline_size;
        __u32   hardware_subarch;
        __u64   hardware_subarch_data;
@@ -106,7 +119,10 @@ struct boot_params {
        __u8  hd1_info[16];     /* obsolete! */         /* 0x090 */
        struct sys_desc_table sys_desc_table;           /* 0x0a0 */
        struct olpc_ofw_header olpc_ofw_header;         /* 0x0b0 */
-       __u8  _pad4[128];                               /* 0x0c0 */
+       __u32 ext_ramdisk_image;                        /* 0x0c0 */
+       __u32 ext_ramdisk_size;                         /* 0x0c4 */
+       __u32 ext_cmd_line_ptr;                         /* 0x0c8 */
+       __u8  _pad4[116];                               /* 0x0cc */
        struct edid_info edid_info;                     /* 0x140 */
        struct efi_info efi_info;                       /* 0x1c0 */
        __u32 alt_mem_k;                                /* 0x1e0 */
@@ -115,7 +131,20 @@ struct boot_params {
        __u8  eddbuf_entries;                           /* 0x1e9 */
        __u8  edd_mbr_sig_buf_entries;                  /* 0x1ea */
        __u8  kbd_status;                               /* 0x1eb */
-       __u8  _pad6[5];                                 /* 0x1ec */
+       __u8  _pad5[3];                                 /* 0x1ec */
+       /*
+        * The sentinel is set to a nonzero value (0xff) in header.S.
+        *
+        * A bootloader is supposed to only take setup_header and put
+        * it into a clean boot_params buffer. If it turns out that
+        * it is clumsy or too generous with the buffer, it most
+        * probably will pick up the sentinel variable too. The fact
+        * that this variable then is still 0xff will let kernel
+        * know that some variables in boot_params are invalid and
+        * kernel should zero out certain portions of boot_params.
+        */
+       __u8  sentinel;                                 /* 0x1ef */
+       __u8  _pad6[1];                                 /* 0x1f0 */
        struct setup_header hdr;    /* setup header */  /* 0x1f1 */
        __u8  _pad7[0x290-0x1f1-sizeof(struct setup_header)];
        __u32 edd_mbr_sig_buffer[EDD_MBR_SIG_MAX];      /* 0x290 */
@@ -134,6 +163,6 @@ enum {
        X86_NR_SUBARCHS,
 };
 
-
+#endif /* __ASSEMBLY__ */
 
 #endif /* _ASM_X86_BOOTPARAM_H */
index fe9edec6698a3c0c15846c0e56c1a502cea05c08..84c1309c4c0c8932b90e25bf84f7f031c3ffa5a5 100644 (file)
@@ -298,8 +298,7 @@ struct _cache_attr {
                         unsigned int);
 };
 
-#ifdef CONFIG_AMD_NB
-
+#if defined(CONFIG_AMD_NB) && defined(CONFIG_SYSFS)
 /*
  * L3 cache descriptors
  */
@@ -524,9 +523,9 @@ store_subcaches(struct _cpuid4_info *this_leaf, const char *buf, size_t count,
 static struct _cache_attr subcaches =
        __ATTR(subcaches, 0644, show_subcaches, store_subcaches);
 
-#else  /* CONFIG_AMD_NB */
+#else
 #define amd_init_l3_cache(x, y)
-#endif /* CONFIG_AMD_NB */
+#endif  /* CONFIG_AMD_NB && CONFIG_SYSFS */
 
 static int
 __cpuinit cpuid4_cache_lookup_regs(int index,
index 4428fd178bce07a3a8fd75b81c61b83fc63d9258..6774c17a55766dec97219ddf2798ac4c740a2963 100644 (file)
@@ -340,9 +340,6 @@ int x86_setup_perfctr(struct perf_event *event)
                /* BTS is currently only allowed for user-mode. */
                if (!attr->exclude_kernel)
                        return -EOPNOTSUPP;
-
-               if (!attr->exclude_guest)
-                       return -EOPNOTSUPP;
        }
 
        hwc->config |= config;
@@ -385,9 +382,6 @@ int x86_pmu_hw_config(struct perf_event *event)
        if (event->attr.precise_ip) {
                int precise = 0;
 
-               if (!event->attr.exclude_guest)
-                       return -EOPNOTSUPP;
-
                /* Support for constant skid */
                if (x86_pmu.pebs_active && !x86_pmu.pebs_broken) {
                        precise++;
index 93b9e1181f830ff91b9ad432e0d148a38b0e93c3..4914e94ad6e86a9565420dcac8d014ff679ab6fa 100644 (file)
@@ -2019,7 +2019,10 @@ __init int intel_pmu_init(void)
                break;
 
        case 28: /* Atom */
-       case 54: /* Cedariew */
+       case 38: /* Lincroft */
+       case 39: /* Penwell */
+       case 53: /* Cloverview */
+       case 54: /* Cedarview */
                memcpy(hw_cache_event_ids, atom_hw_cache_event_ids,
                       sizeof(hw_cache_event_ids));
 
@@ -2084,6 +2087,7 @@ __init int intel_pmu_init(void)
                pr_cont("SandyBridge events, ");
                break;
        case 58: /* IvyBridge */
+       case 62: /* IvyBridge EP */
                memcpy(hw_cache_event_ids, snb_hw_cache_event_ids,
                       sizeof(hw_cache_event_ids));
                memcpy(hw_cache_extra_regs, snb_hw_cache_extra_regs,
index f2af39f5dc3d96916f8665350445a3e8155f290b..4820c232a0b91421d1ee77b194a8881a13143753 100644 (file)
@@ -19,7 +19,7 @@ static const u64 p6_perfmon_event_map[] =
 
 };
 
-static __initconst u64 p6_hw_cache_event_ids
+static u64 p6_hw_cache_event_ids
                                [PERF_COUNT_HW_CACHE_MAX]
                                [PERF_COUNT_HW_CACHE_OP_MAX]
                                [PERF_COUNT_HW_CACHE_RESULT_MAX] =
index ff84d5469d772fd8ae8f5e0599e8072333eeb0c1..6ed91d9980e240b73fa824f7db00d8065d145316 100644 (file)
@@ -1065,7 +1065,6 @@ ENTRY(xen_failsafe_callback)
        lea 16(%esp),%esp
        CFI_ADJUST_CFA_OFFSET -16
        jz 5f
-       addl $16,%esp
        jmp iret_exc
 5:     pushl_cfi $-1 /* orig_ax = -1 => not a system call */
        SAVE_ALL
index 07a7a04529bc5d7849ffc21b79819edd7b23ffd9..cb3c591339aa3d4242e420d181b83f3a62ced2c3 100644 (file)
@@ -1781,6 +1781,7 @@ first_nmi:
         * Leave room for the "copied" frame
         */
        subq $(5*8), %rsp
+       CFI_ADJUST_CFA_OFFSET 5*8
 
        /* Copy the stack frame to the Saved frame */
        .rept 5
@@ -1863,10 +1864,8 @@ end_repeat_nmi:
 nmi_swapgs:
        SWAPGS_UNSAFE_STACK
 nmi_restore:
-       RESTORE_ALL 8
-
-       /* Pop the extra iret frame */
-       addq $(5*8), %rsp
+       /* Pop the extra iret frame at once */
+       RESTORE_ALL 6*8
 
        /* Clear the NMI executing stack variable */
        movq $0, 5*8(%rsp)
index 8e7f6556028f7ff50d90c73eac405d3cdcb7b881..c8932c79e78bbaad99c4969a1ea1a0ff2f7f1802 100644 (file)
@@ -300,6 +300,12 @@ ENTRY(startup_32_smp)
        leal -__PAGE_OFFSET(%ecx),%esp
 
 default_entry:
+#define CR0_STATE      (X86_CR0_PE | X86_CR0_MP | X86_CR0_ET | \
+                        X86_CR0_NE | X86_CR0_WP | X86_CR0_AM | \
+                        X86_CR0_PG)
+       movl $(CR0_STATE & ~X86_CR0_PG),%eax
+       movl %eax,%cr0
+
 /*
  *     New page tables may be in 4Mbyte page mode and may
  *     be using the global pages. 
@@ -364,8 +370,7 @@ default_entry:
  */
        movl $pa(initial_page_table), %eax
        movl %eax,%cr3          /* set the page table pointer.. */
-       movl %cr0,%eax
-       orl  $X86_CR0_PG,%eax
+       movl $CR0_STATE,%eax
        movl %eax,%cr0          /* ..and set paging (PG) bit */
        ljmp $__BOOT_CS,$1f     /* Clear prefetch and normalize %eip */
 1:
index a7c5661f84962a2f6eb80509c1791392352a6fc8..4929502c1372db979d7e1b176b22a9981e8ec96e 100644 (file)
@@ -174,6 +174,9 @@ static int msr_open(struct inode *inode, struct file *file)
        unsigned int cpu;
        struct cpuinfo_x86 *c;
 
+       if (!capable(CAP_SYS_RAWIO))
+               return -EPERM;
+
        cpu = iminor(file->f_path.dentry->d_inode);
        if (cpu >= nr_cpu_ids || !cpu_online(cpu))
                return -ENXIO;  /* No such CPU */
index 0f5dec5c80e0fccbba37474463e724970503faf2..872079a67e4d262151dfdbe74f537033be5ebcc0 100644 (file)
@@ -56,7 +56,7 @@ struct device x86_dma_fallback_dev = {
 EXPORT_SYMBOL(x86_dma_fallback_dev);
 
 /* Number of entries preallocated for DMA-API debugging */
-#define PREALLOC_DMA_DEBUG_ENTRIES       32768
+#define PREALLOC_DMA_DEBUG_ENTRIES       65536
 
 int dma_set_mask(struct device *dev, u64 mask)
 {
index 4e8ba39eaf0fd93cbca557eaecf3efcd954ee05b..76fa1e9a2b39399b1944e4a13c68a054c9a5c386 100644 (file)
@@ -584,7 +584,7 @@ static void native_machine_emergency_restart(void)
                        break;
 
                case BOOT_EFI:
-                       if (efi_enabled)
+                       if (efi_enabled(EFI_RUNTIME_SERVICES))
                                efi.reset_system(reboot_mode ?
                                                 EFI_RESET_WARM :
                                                 EFI_RESET_COLD,
index 23ddd558fbd52d1376576b200122448d94165708..8b24289cc10c0f2239623918e587810b81c43c9b 100644 (file)
@@ -610,6 +610,83 @@ static __init void reserve_ibft_region(void)
 
 static unsigned reserve_low = CONFIG_X86_RESERVE_LOW << 10;
 
+static bool __init snb_gfx_workaround_needed(void)
+{
+#ifdef CONFIG_PCI
+       int i;
+       u16 vendor, devid;
+       static const __initconst u16 snb_ids[] = {
+               0x0102,
+               0x0112,
+               0x0122,
+               0x0106,
+               0x0116,
+               0x0126,
+               0x010a,
+       };
+
+       /* Assume no if something weird is going on with PCI */
+       if (!early_pci_allowed())
+               return false;
+
+       vendor = read_pci_config_16(0, 2, 0, PCI_VENDOR_ID);
+       if (vendor != 0x8086)
+               return false;
+
+       devid = read_pci_config_16(0, 2, 0, PCI_DEVICE_ID);
+       for (i = 0; i < ARRAY_SIZE(snb_ids); i++)
+               if (devid == snb_ids[i])
+                       return true;
+#endif
+
+       return false;
+}
+
+/*
+ * Sandy Bridge graphics has trouble with certain ranges, exclude
+ * them from allocation.
+ */
+static void __init trim_snb_memory(void)
+{
+       static const __initconst unsigned long bad_pages[] = {
+               0x20050000,
+               0x20110000,
+               0x20130000,
+               0x20138000,
+               0x40004000,
+       };
+       int i;
+
+       if (!snb_gfx_workaround_needed())
+               return;
+
+       printk(KERN_DEBUG "reserving inaccessible SNB gfx pages\n");
+
+       /*
+        * Reserve all memory below the 1 MB mark that has not
+        * already been reserved.
+        */
+       memblock_reserve(0, 1<<20);
+       
+       for (i = 0; i < ARRAY_SIZE(bad_pages); i++) {
+               if (memblock_reserve(bad_pages[i], PAGE_SIZE))
+                       printk(KERN_WARNING "failed to reserve 0x%08lx\n",
+                              bad_pages[i]);
+       }
+}
+
+/*
+ * Here we put platform-specific memory range workarounds, i.e.
+ * memory known to be corrupt or otherwise in need to be reserved on
+ * specific platforms.
+ *
+ * If this gets used more widely it could use a real dispatch mechanism.
+ */
+static void __init trim_platform_memory_ranges(void)
+{
+       trim_snb_memory();
+}
+
 static void __init trim_bios_range(void)
 {
        /*
@@ -630,6 +707,7 @@ static void __init trim_bios_range(void)
         * take them out.
         */
        e820_remove_range(BIOS_BEGIN, BIOS_END - BIOS_BEGIN, E820_RAM, 1);
+
        sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map);
 }
 
@@ -729,15 +807,15 @@ void __init setup_arch(char **cmdline_p)
 #ifdef CONFIG_EFI
        if (!strncmp((char *)&boot_params.efi_info.efi_loader_signature,
                     "EL32", 4)) {
-               efi_enabled = 1;
-               efi_64bit = false;
+               set_bit(EFI_BOOT, &x86_efi_facility);
        } else if (!strncmp((char *)&boot_params.efi_info.efi_loader_signature,
                     "EL64", 4)) {
-               efi_enabled = 1;
-               efi_64bit = true;
+               set_bit(EFI_BOOT, &x86_efi_facility);
+               set_bit(EFI_64BIT, &x86_efi_facility);
        }
-       if (efi_enabled && efi_memblock_x86_reserve_range())
-               efi_enabled = 0;
+
+       if (efi_enabled(EFI_BOOT))
+               efi_memblock_x86_reserve_range();
 #endif
 
        x86_init.oem.arch_setup();
@@ -810,7 +888,7 @@ void __init setup_arch(char **cmdline_p)
 
        finish_e820_parsing();
 
-       if (efi_enabled)
+       if (efi_enabled(EFI_BOOT))
                efi_init();
 
        dmi_scan_machine();
@@ -893,7 +971,7 @@ void __init setup_arch(char **cmdline_p)
         * The EFI specification says that boot service code won't be called
         * after ExitBootServices(). This is, in fact, a lie.
         */
-       if (efi_enabled)
+       if (efi_enabled(EFI_MEMMAP))
                efi_reserve_boot_services();
 
        /* preallocate 4k for mptable mpc */
@@ -908,6 +986,8 @@ void __init setup_arch(char **cmdline_p)
 
        setup_real_mode();
 
+       trim_platform_memory_ranges();
+
        init_gbpages();
 
        /* max_pfn_mapped is updated here */
@@ -1034,7 +1114,7 @@ void __init setup_arch(char **cmdline_p)
 
 #ifdef CONFIG_VT
 #if defined(CONFIG_VGA_CONSOLE)
-       if (!efi_enabled || (efi_mem_type(0xa0000) != EFI_CONVENTIONAL_MEMORY))
+       if (!efi_enabled(EFI_BOOT) || (efi_mem_type(0xa0000) != EFI_CONVENTIONAL_MEMORY))
                conswitchp = &vga_con;
 #elif defined(CONFIG_DUMMY_CONSOLE)
        conswitchp = &dummy_con;
@@ -1051,14 +1131,14 @@ void __init setup_arch(char **cmdline_p)
        register_refined_jiffies(CLOCK_TICK_RATE);
 
 #ifdef CONFIG_EFI
-       /* Once setup is done above, disable efi_enabled on mismatched
-        * firmware/kernel archtectures since there is no support for
-        * runtime services.
+       /* Once setup is done above, unmap the EFI memory map on
+        * mismatched firmware/kernel archtectures since there is no
+        * support for runtime services.
         */
-       if (efi_enabled && IS_ENABLED(CONFIG_X86_64) != efi_64bit) {
+       if (efi_enabled(EFI_BOOT) &&
+           IS_ENABLED(CONFIG_X86_64) != efi_enabled(EFI_64BIT)) {
                pr_info("efi: Setup done, disabling due to 32/64-bit mismatch\n");
                efi_unmap_memmap();
-               efi_enabled = 0;
        }
 #endif
 }
index cd3b2438a9800b8568b0d79788ef3fdb438aa1ca..9b4d51d0c0d013274f7ba46c2e58319f0d1d9145 100644 (file)
@@ -165,10 +165,11 @@ void set_task_blockstep(struct task_struct *task, bool on)
         * Ensure irq/preemption can't change debugctl in between.
         * Note also that both TIF_BLOCKSTEP and debugctl should
         * be changed atomically wrt preemption.
-        * FIXME: this means that set/clear TIF_BLOCKSTEP is simply
-        * wrong if task != current, SIGKILL can wakeup the stopped
-        * tracee and set/clear can play with the running task, this
-        * can confuse the next __switch_to_xtra().
+        *
+        * NOTE: this means that set/clear TIF_BLOCKSTEP is only safe if
+        * task is current or it can't be running, otherwise we can race
+        * with __switch_to_xtra(). We rely on ptrace_freeze_traced() but
+        * PTRACE_KILL is not safe.
         */
        local_irq_disable();
        debugctl = get_debugctlmsr();
index ad4439145f858314dfe518cf7cd9336c5fe9c96d..77cf0090c0a3ef7890ea24d4e5e47d1d68e0b024 100644 (file)
@@ -51,9 +51,6 @@
 
 #define EFI_DEBUG      1
 
-int efi_enabled;
-EXPORT_SYMBOL(efi_enabled);
-
 struct efi __read_mostly efi = {
        .mps        = EFI_INVALID_TABLE_ADDR,
        .acpi       = EFI_INVALID_TABLE_ADDR,
@@ -69,19 +66,28 @@ EXPORT_SYMBOL(efi);
 
 struct efi_memory_map memmap;
 
-bool efi_64bit;
-
 static struct efi efi_phys __initdata;
 static efi_system_table_t efi_systab __initdata;
 
 static inline bool efi_is_native(void)
 {
-       return IS_ENABLED(CONFIG_X86_64) == efi_64bit;
+       return IS_ENABLED(CONFIG_X86_64) == efi_enabled(EFI_64BIT);
+}
+
+unsigned long x86_efi_facility;
+
+/*
+ * Returns 1 if 'facility' is enabled, 0 otherwise.
+ */
+int efi_enabled(int facility)
+{
+       return test_bit(facility, &x86_efi_facility) != 0;
 }
+EXPORT_SYMBOL(efi_enabled);
 
 static int __init setup_noefi(char *arg)
 {
-       efi_enabled = 0;
+       clear_bit(EFI_BOOT, &x86_efi_facility);
        return 0;
 }
 early_param("noefi", setup_noefi);
@@ -426,6 +432,7 @@ void __init efi_reserve_boot_services(void)
 
 void __init efi_unmap_memmap(void)
 {
+       clear_bit(EFI_MEMMAP, &x86_efi_facility);
        if (memmap.map) {
                early_iounmap(memmap.map, memmap.nr_map * memmap.desc_size);
                memmap.map = NULL;
@@ -460,7 +467,7 @@ void __init efi_free_boot_services(void)
 
 static int __init efi_systab_init(void *phys)
 {
-       if (efi_64bit) {
+       if (efi_enabled(EFI_64BIT)) {
                efi_system_table_64_t *systab64;
                u64 tmp = 0;
 
@@ -552,7 +559,7 @@ static int __init efi_config_init(u64 tables, int nr_tables)
        void *config_tables, *tablep;
        int i, sz;
 
-       if (efi_64bit)
+       if (efi_enabled(EFI_64BIT))
                sz = sizeof(efi_config_table_64_t);
        else
                sz = sizeof(efi_config_table_32_t);
@@ -572,7 +579,7 @@ static int __init efi_config_init(u64 tables, int nr_tables)
                efi_guid_t guid;
                unsigned long table;
 
-               if (efi_64bit) {
+               if (efi_enabled(EFI_64BIT)) {
                        u64 table64;
                        guid = ((efi_config_table_64_t *)tablep)->guid;
                        table64 = ((efi_config_table_64_t *)tablep)->table;
@@ -684,7 +691,6 @@ void __init efi_init(void)
        if (boot_params.efi_info.efi_systab_hi ||
            boot_params.efi_info.efi_memmap_hi) {
                pr_info("Table located above 4GB, disabling EFI.\n");
-               efi_enabled = 0;
                return;
        }
        efi_phys.systab = (efi_system_table_t *)boot_params.efi_info.efi_systab;
@@ -694,10 +700,10 @@ void __init efi_init(void)
                          ((__u64)boot_params.efi_info.efi_systab_hi<<32));
 #endif
 
-       if (efi_systab_init(efi_phys.systab)) {
-               efi_enabled = 0;
+       if (efi_systab_init(efi_phys.systab))
                return;
-       }
+
+       set_bit(EFI_SYSTEM_TABLES, &x86_efi_facility);
 
        /*
         * Show what we know for posterity
@@ -715,10 +721,10 @@ void __init efi_init(void)
                efi.systab->hdr.revision >> 16,
                efi.systab->hdr.revision & 0xffff, vendor);
 
-       if (efi_config_init(efi.systab->tables, efi.systab->nr_tables)) {
-               efi_enabled = 0;
+       if (efi_config_init(efi.systab->tables, efi.systab->nr_tables))
                return;
-       }
+
+       set_bit(EFI_CONFIG_TABLES, &x86_efi_facility);
 
        /*
         * Note: We currently don't support runtime services on an EFI
@@ -727,15 +733,17 @@ void __init efi_init(void)
 
        if (!efi_is_native())
                pr_info("No EFI runtime due to 32/64-bit mismatch with kernel\n");
-       else if (efi_runtime_init()) {
-               efi_enabled = 0;
-               return;
+       else {
+               if (efi_runtime_init())
+                       return;
+               set_bit(EFI_RUNTIME_SERVICES, &x86_efi_facility);
        }
 
-       if (efi_memmap_init()) {
-               efi_enabled = 0;
+       if (efi_memmap_init())
                return;
-       }
+
+       set_bit(EFI_MEMMAP, &x86_efi_facility);
+
 #ifdef CONFIG_X86_32
        if (efi_is_native()) {
                x86_platform.get_wallclock = efi_get_time;
@@ -941,7 +949,7 @@ void __init efi_enter_virtual_mode(void)
         *
         * Call EFI services through wrapper functions.
         */
-       efi.runtime_version = efi_systab.fw_revision;
+       efi.runtime_version = efi_systab.hdr.revision;
        efi.get_time = virt_efi_get_time;
        efi.set_time = virt_efi_set_time;
        efi.get_wakeup_time = virt_efi_get_wakeup_time;
@@ -969,6 +977,9 @@ u32 efi_mem_type(unsigned long phys_addr)
        efi_memory_desc_t *md;
        void *p;
 
+       if (!efi_enabled(EFI_MEMMAP))
+               return 0;
+
        for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
                md = p;
                if ((md->phys_addr <= phys_addr) &&
index 95fd505dfeb6e43dd37b0c41954f0b0db6535d1c..2b2003860615fbc5e24ef12e1c1fa3d855a1f09d 100644 (file)
@@ -38,7 +38,7 @@
 #include <asm/cacheflush.h>
 #include <asm/fixmap.h>
 
-static pgd_t save_pgd __initdata;
+static pgd_t *save_pgd __initdata;
 static unsigned long efi_flags __initdata;
 
 static void __init early_code_mapping_set_exec(int executable)
@@ -61,12 +61,20 @@ static void __init early_code_mapping_set_exec(int executable)
 void __init efi_call_phys_prelog(void)
 {
        unsigned long vaddress;
+       int pgd;
+       int n_pgds;
 
        early_code_mapping_set_exec(1);
        local_irq_save(efi_flags);
-       vaddress = (unsigned long)__va(0x0UL);
-       save_pgd = *pgd_offset_k(0x0UL);
-       set_pgd(pgd_offset_k(0x0UL), *pgd_offset_k(vaddress));
+
+       n_pgds = DIV_ROUND_UP((max_pfn << PAGE_SHIFT), PGDIR_SIZE);
+       save_pgd = kmalloc(n_pgds * sizeof(pgd_t), GFP_KERNEL);
+
+       for (pgd = 0; pgd < n_pgds; pgd++) {
+               save_pgd[pgd] = *pgd_offset_k(pgd * PGDIR_SIZE);
+               vaddress = (unsigned long)__va(pgd * PGDIR_SIZE);
+               set_pgd(pgd_offset_k(pgd * PGDIR_SIZE), *pgd_offset_k(vaddress));
+       }
        __flush_tlb_all();
 }
 
@@ -75,7 +83,11 @@ void __init efi_call_phys_epilog(void)
        /*
         * After the lock is released, the original page table is restored.
         */
-       set_pgd(pgd_offset_k(0x0UL), save_pgd);
+       int pgd;
+       int n_pgds = DIV_ROUND_UP((max_pfn << PAGE_SHIFT) , PGDIR_SIZE);
+       for (pgd = 0; pgd < n_pgds; pgd++)
+               set_pgd(pgd_offset_k(pgd * PGDIR_SIZE), save_pgd[pgd]);
+       kfree(save_pgd);
        __flush_tlb_all();
        local_irq_restore(efi_flags);
        early_code_mapping_set_exec(0);
index b8b3a37c80cd75e96559e67876206ad603b53741..dbbdca5f508c45bdfb34395aeeb42e500e9f1f1f 100644 (file)
@@ -1034,7 +1034,8 @@ static int set_distrib_bits(struct cpumask *flush_mask, struct bau_control *bcp,
  * globally purge translation cache of a virtual address or all TLB's
  * @cpumask: mask of all cpu's in which the address is to be removed
  * @mm: mm_struct containing virtual address range
- * @va: virtual address to be removed (or TLB_FLUSH_ALL for all TLB's on cpu)
+ * @start: start virtual address to be removed from TLB
+ * @end: end virtual address to be remove from TLB
  * @cpu: the current cpu
  *
  * This is the entry point for initiating any UV global TLB shootdown.
@@ -1056,7 +1057,7 @@ static int set_distrib_bits(struct cpumask *flush_mask, struct bau_control *bcp,
  */
 const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask,
                                struct mm_struct *mm, unsigned long start,
-                               unsigned end, unsigned int cpu)
+                               unsigned long end, unsigned int cpu)
 {
        int locals = 0;
        int remotes = 0;
@@ -1113,7 +1114,10 @@ const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask,
 
        record_send_statistics(stat, locals, hubs, remotes, bau_desc);
 
-       bau_desc->payload.address = start;
+       if (!end || (end - start) <= PAGE_SIZE)
+               bau_desc->payload.address = start;
+       else
+               bau_desc->payload.address = TLB_FLUSH_ALL;
        bau_desc->payload.sending_cpu = cpu;
        /*
         * uv_flush_send_and_wait returns 0 if all cpu's were messaged,
index cc2f8c1312862e96006a546ae355292391039958..872eb60e78064a90900e9133a29728a6c3774cee 100644 (file)
@@ -55,7 +55,7 @@ static FILE           *input_file;    /* Input file name */
 static void usage(const char *err)
 {
        if (err)
-               fprintf(stderr, "Error: %s\n\n", err);
+               fprintf(stderr, "%s: Error: %s\n\n", prog, err);
        fprintf(stderr, "Usage: %s [-y|-n|-v] [-s seed[,no]] [-m max] [-i input]\n", prog);
        fprintf(stderr, "\t-y   64bit mode\n");
        fprintf(stderr, "\t-n   32bit mode\n");
@@ -269,7 +269,13 @@ int main(int argc, char **argv)
                insns++;
        }
 
-       fprintf(stdout, "%s: decoded and checked %d %s instructions with %d errors (seed:0x%x)\n", (errors) ? "Failure" : "Success", insns, (input_file) ? "given" : "random", errors, seed);
+       fprintf(stdout, "%s: %s: decoded and checked %d %s instructions with %d errors (seed:0x%x)\n",
+               prog,
+               (errors) ? "Failure" : "Success",
+               insns,
+               (input_file) ? "given" : "random",
+               errors,
+               seed);
 
        return errors ? 1 : 0;
 }
index 5a1847d619306e5f0ed5ed5570c53c69a8ce2315..79d67bd507fa6c5bd779defa312943b1d99a47f6 100644 (file)
@@ -814,12 +814,14 @@ int main(int argc, char **argv)
        read_relocs(fp);
        if (show_absolute_syms) {
                print_absolute_symbols();
-               return 0;
+               goto out;
        }
        if (show_absolute_relocs) {
                print_absolute_relocs();
-               return 0;
+               goto out;
        }
        emit_relocs(as_text, use_real_mode);
+out:
+       fclose(fp);
        return 0;
 }
index 4f7d2599b484a67222cbfa68f7b929c35975732e..34bc4cee8887b2c2d974c99ca81b78577f719ff9 100644 (file)
@@ -432,13 +432,6 @@ static void __cpuinit xen_play_dead(void) /* used only with HOTPLUG_CPU */
        play_dead_common();
        HYPERVISOR_vcpu_op(VCPUOP_down, smp_processor_id(), NULL);
        cpu_bringup();
-       /*
-        * Balance out the preempt calls - as we are running in cpu_idle
-        * loop which has been called at bootup from cpu_bringup_and_idle.
-        * The cpucpu_bringup_and_idle called cpu_bringup which made a
-        * preempt_disable() So this preempt_enable will balance it out.
-        */
-       preempt_enable();
 }
 
 #else /* !CONFIG_HOTPLUG_CPU */
index 4acb5feba1fb7ae0bb830fed17b0391ae7a8483a..172a02a6ad146fea24ab966cf46a3612434a03da 100644 (file)
@@ -170,4 +170,19 @@ dma_cache_sync(struct device *dev, void *vaddr, size_t size,
        consistent_sync(vaddr, size, direction);
 }
 
+/* Not supported for now */
+static inline int dma_mmap_coherent(struct device *dev,
+                                   struct vm_area_struct *vma, void *cpu_addr,
+                                   dma_addr_t dma_addr, size_t size)
+{
+       return -EINVAL;
+}
+
+static inline int dma_get_sgtable(struct device *dev, struct sg_table *sgt,
+                                 void *cpu_addr, dma_addr_t dma_addr,
+                                 size_t size)
+{
+       return -EINVAL;
+}
+
 #endif /* _XTENSA_DMA_MAPPING_H */
index 00a783661d0b5548b65882f312faa4a116ff5762..46f80e2c92f7da893ff28f5993d89fa01efc323a 100644 (file)
@@ -590,6 +590,9 @@ static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr,
        if (bit_width == 32 && bit_offset == 0 && (*paddr & 0x03) == 0 &&
            *access_bit_width < 32)
                *access_bit_width = 32;
+       else if (bit_width == 64 && bit_offset == 0 && (*paddr & 0x07) == 0 &&
+           *access_bit_width < 64)
+               *access_bit_width = 64;
 
        if ((bit_width + bit_offset) > *access_bit_width) {
                pr_warning(FW_BUG APEI_PFX
index 95af6f674a6cc15b9e726d2cee7440ffa4b130fb..35da18113216c367eae553371e9164a726a70f44 100644 (file)
@@ -297,7 +297,7 @@ static int acpi_platform_notify(struct device *dev)
        if (!ret) {
                struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 
-               acpi_get_name(dev->acpi_handle, ACPI_FULL_PATHNAME, &buffer);
+               acpi_get_name(ACPI_HANDLE(dev), ACPI_FULL_PATHNAME, &buffer);
                DBG("Device %s -> %s\n", dev_name(dev), (char *)buffer.pointer);
                kfree(buffer.pointer);
        } else
index 3ff267861541f7570a5b110b7893e0cb012f5917..bd22f8667eed65e3889e61dcc357d85f5f3d1239 100644 (file)
@@ -250,7 +250,7 @@ acpi_physical_address __init acpi_os_get_root_pointer(void)
                return acpi_rsdp;
 #endif
 
-       if (efi_enabled) {
+       if (efi_enabled(EFI_CONFIG_TABLES)) {
                if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
                        return efi.acpi20;
                else if (efi.acpi != EFI_INVALID_TABLE_ADDR)
index f1a5da44591dcd1b415b57e638165318bc13e793..ed9a1cc690be5fa8cd3844394128216350904a7e 100644 (file)
@@ -958,6 +958,9 @@ static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr)
                return -EINVAL;
        }
 
+       if (!dev)
+               return -EINVAL;
+
        dev->cpu = pr->id;
 
        if (max_cstate == 0)
@@ -1149,6 +1152,7 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr)
                }
 
                /* Populate Updated C-state information */
+               acpi_processor_get_power_info(pr);
                acpi_processor_setup_cpuidle_states(pr);
 
                /* Enable all cpuidle devices */
index 836bfe0690422855c80ecfe41895a4fe667bfd14..53e7ac9403a7dc21502e90a698ef1b27ad668a37 100644 (file)
@@ -340,6 +340,13 @@ static void amd_fixup_frequency(struct acpi_processor_px *px, int i)
        if ((boot_cpu_data.x86 == 0x10 && boot_cpu_data.x86_model < 10)
            || boot_cpu_data.x86 == 0x11) {
                rdmsr(MSR_AMD_PSTATE_DEF_BASE + index, lo, hi);
+               /*
+                * MSR C001_0064+:
+                * Bit 63: PstateEn. Read-write. If set, the P-state is valid.
+                */
+               if (!(hi & BIT(31)))
+                       return;
+
                fid = lo & 0x3f;
                did = (lo >> 6) & 7;
                if (boot_cpu_data.x86 == 0x10)
index 7862d17976b7532f48204cbf4210ffb6d4984f97..49791273256626d278d6a3d797913606034157ef 100644 (file)
@@ -53,6 +53,7 @@
 
 enum {
        AHCI_PCI_BAR_STA2X11    = 0,
+       AHCI_PCI_BAR_ENMOTUS    = 2,
        AHCI_PCI_BAR_STANDARD   = 5,
 };
 
@@ -410,6 +411,9 @@ static const struct pci_device_id ahci_pci_tbl[] = {
        { PCI_VDEVICE(ASMEDIA, 0x0611), board_ahci },   /* ASM1061 */
        { PCI_VDEVICE(ASMEDIA, 0x0612), board_ahci },   /* ASM1062 */
 
+       /* Enmotus */
+       { PCI_DEVICE(0x1c44, 0x8000), board_ahci },
+
        /* Generic, PCI class code for AHCI */
        { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
          PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci },
@@ -1098,9 +1102,11 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                dev_info(&pdev->dev,
                         "PDC42819 can only drive SATA devices with this driver\n");
 
-       /* The Connext uses non-standard BAR */
+       /* Both Connext and Enmotus devices use non-standard BARs */
        if (pdev->vendor == PCI_VENDOR_ID_STMICRO && pdev->device == 0xCC06)
                ahci_pci_bar = AHCI_PCI_BAR_STA2X11;
+       else if (pdev->vendor == 0x1c44 && pdev->device == 0x8000)
+               ahci_pci_bar = AHCI_PCI_BAR_ENMOTUS;
 
        /* acquire resources */
        rc = pcim_enable_device(pdev);
index 320712a7b9eaac3174f3a96de8693b63dee5f1af..6cd7805e47cac96b6a2e6fe07444d7c491a7e45d 100644 (file)
@@ -1951,13 +1951,13 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)
        /* Use the nominal value 10 ms if the read MDAT is zero,
         * the nominal value of DETO is 20 ms.
         */
-       if (dev->sata_settings[ATA_LOG_DEVSLP_VALID] &
+       if (dev->devslp_timing[ATA_LOG_DEVSLP_VALID] &
            ATA_LOG_DEVSLP_VALID_MASK) {
-               mdat = dev->sata_settings[ATA_LOG_DEVSLP_MDAT] &
+               mdat = dev->devslp_timing[ATA_LOG_DEVSLP_MDAT] &
                       ATA_LOG_DEVSLP_MDAT_MASK;
                if (!mdat)
                        mdat = 10;
-               deto = dev->sata_settings[ATA_LOG_DEVSLP_DETO];
+               deto = dev->devslp_timing[ATA_LOG_DEVSLP_DETO];
                if (!deto)
                        deto = 20;
        } else {
index 9e8b99af400dc53539ba4e8d45d36696e4818b65..46cd3f4c6aaaf8122c82ea8f60f7e6bcbaf435c6 100644 (file)
@@ -2325,24 +2325,28 @@ int ata_dev_configure(struct ata_device *dev)
                        }
                }
 
-               /* check and mark DevSlp capability */
-               if (ata_id_has_devslp(dev->id))
-                       dev->flags |= ATA_DFLAG_DEVSLP;
-
-               /* Obtain SATA Settings page from Identify Device Data Log,
-                * which contains DevSlp timing variables etc.
-                * Exclude old devices with ata_id_has_ncq()
+               /* Check and mark DevSlp capability. Get DevSlp timing variables
+                * from SATA Settings page of Identify Device Data Log.
                 */
-               if (ata_id_has_ncq(dev->id)) {
+               if (ata_id_has_devslp(dev->id)) {
+                       u8 sata_setting[ATA_SECT_SIZE];
+                       int i, j;
+
+                       dev->flags |= ATA_DFLAG_DEVSLP;
                        err_mask = ata_read_log_page(dev,
                                                     ATA_LOG_SATA_ID_DEV_DATA,
                                                     ATA_LOG_SATA_SETTINGS,
-                                                    dev->sata_settings,
+                                                    sata_setting,
                                                     1);
                        if (err_mask)
                                ata_dev_dbg(dev,
                                            "failed to get Identify Device Data, Emask 0x%x\n",
                                            err_mask);
+                       else
+                               for (i = 0; i < ATA_LOG_DEVSLP_SIZE; i++) {
+                                       j = ATA_LOG_DEVSLP_OFFSET + i;
+                                       dev->devslp_timing[i] = sata_setting[j];
+                               }
                }
 
                dev->cdb_len = 16;
index bf039b0e97b79d7f9c9d2bae803ad84850b181b4..bcf4437214f50141e2d1bfe17a0ad02af6f4d81b 100644 (file)
@@ -2094,7 +2094,7 @@ static unsigned int ata_eh_speed_down(struct ata_device *dev,
  */
 static inline int ata_eh_worth_retry(struct ata_queued_cmd *qc)
 {
-       if (qc->flags & AC_ERR_MEDIA)
+       if (qc->err_mask & AC_ERR_MEDIA)
                return 0;       /* don't retry media errors */
        if (qc->flags & ATA_QCFLAG_IO)
                return 1;       /* otherwise retry anything from fs stack */
index 63452943abd1e2a509661f934d51be5431259b86..fb10728f63721565d282e2298f2bf14641fb8f1e 100644 (file)
@@ -224,7 +224,7 @@ static void cpu_device_release(struct device *dev)
         * by the cpu device.
         *
         * Never copy this way of doing things, or you too will be made fun of
-        * on the linux-kerenl list, you have been warned.
+        * on the linux-kernel list, you have been warned.
         */
 }
 
index d81460309182fde4177865a922f740c278c7d40c..b392b353be39016818abd096da01fb4a18839450 100644 (file)
@@ -305,7 +305,7 @@ static bool fw_read_file_contents(struct file *file, struct firmware_buf *fw_buf
        char *buf;
 
        size = fw_file_size(file);
-       if (size < 0)
+       if (size <= 0)
                return false;
        buf = vmalloc(size);
        if (!buf)
index 07aad786f817e88300af9c4d6d685391f41b3471..d9a6c94ce42338b27c98c87f8e82910fdaa20579 100644 (file)
@@ -56,6 +56,19 @@ static const struct file_operations regmap_name_fops = {
        .llseek = default_llseek,
 };
 
+static void regmap_debugfs_free_dump_cache(struct regmap *map)
+{
+       struct regmap_debugfs_off_cache *c;
+
+       while (!list_empty(&map->debugfs_off_cache)) {
+               c = list_first_entry(&map->debugfs_off_cache,
+                                    struct regmap_debugfs_off_cache,
+                                    list);
+               list_del(&c->list);
+               kfree(c);
+       }
+}
+
 /*
  * Work out where the start offset maps into register numbers, bearing
  * in mind that we suppress hidden registers.
@@ -91,8 +104,10 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
                        /* No cache entry?  Start a new one */
                        if (!c) {
                                c = kzalloc(sizeof(*c), GFP_KERNEL);
-                               if (!c)
-                                       break;
+                               if (!c) {
+                                       regmap_debugfs_free_dump_cache(map);
+                                       return base;
+                               }
                                c->min = p;
                                c->base_reg = i;
                        }
@@ -101,14 +116,32 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
                }
        }
 
+       /* Close the last entry off if we didn't scan beyond it */
+       if (c) {
+               c->max = p - 1;
+               list_add_tail(&c->list,
+                             &map->debugfs_off_cache);
+       }
+
+       /*
+        * This should never happen; we return above if we fail to
+        * allocate and we should never be in this code if there are
+        * no registers at all.
+        */
+       if (list_empty(&map->debugfs_off_cache)) {
+               WARN_ON(list_empty(&map->debugfs_off_cache));
+               return base;
+       }
+
        /* Find the relevant block */
        list_for_each_entry(c, &map->debugfs_off_cache, list) {
-               if (*pos >= c->min && *pos <= c->max) {
+               if (from >= c->min && from <= c->max) {
                        *pos = c->min;
                        return c->base_reg;
                }
 
-               ret = c->max;
+               *pos = c->min;
+               ret = c->base_reg;
        }
 
        return ret;
@@ -387,16 +420,8 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
 
 void regmap_debugfs_exit(struct regmap *map)
 {
-       struct regmap_debugfs_off_cache *c;
-
        debugfs_remove_recursive(map->debugfs);
-       while (!list_empty(&map->debugfs_off_cache)) {
-               c = list_first_entry(&map->debugfs_off_cache,
-                                    struct regmap_debugfs_off_cache,
-                                    list);
-               list_del(&c->list);
-               kfree(c);
-       }
+       regmap_debugfs_free_dump_cache(map);
        kfree(map->debugfs_name);
 }
 
index 42d5cb0f503fb825104d7a0f02f79b63ab6f63da..f00b059c057ad01e4c8b9f05e95676c3c53c0e79 100644 (file)
@@ -1106,7 +1106,7 @@ EXPORT_SYMBOL_GPL(regmap_raw_write);
  * @val_count: Number of registers to write
  *
  * This function is intended to be used for writing a large block of
- * data to be device either in single transfer or multiple transfer.
+ * data to the device either in single transfer or multiple transfer.
  *
  * A value of zero will be returned on success, a negative errno will
  * be returned in error cases.
index 9d8409c02082f5fbb75c456faeb9323b86fbe9f3..8ad21a25bc0d92cc80f48f69f4556bdcc6ef6fa2 100644 (file)
@@ -889,6 +889,7 @@ static void virtblk_remove(struct virtio_device *vdev)
 {
        struct virtio_blk *vblk = vdev->priv;
        int index = vblk->index;
+       int refc;
 
        /* Prevent config work handler from accessing the device. */
        mutex_lock(&vblk->config_lock);
@@ -903,11 +904,15 @@ static void virtblk_remove(struct virtio_device *vdev)
 
        flush_work(&vblk->config_work);
 
+       refc = atomic_read(&disk_to_dev(vblk->disk)->kobj.kref.refcount);
        put_disk(vblk->disk);
        mempool_destroy(vblk->pool);
        vdev->config->del_vqs(vdev);
        kfree(vblk);
-       ida_simple_remove(&vd_index_ida, index);
+
+       /* Only free device id if we don't have any users */
+       if (refc == 1)
+               ida_simple_remove(&vd_index_ida, index);
 }
 
 #ifdef CONFIG_PM
index b00000e8aef6f9cb06955049d3fa4faaba25e38b..33c9a44a967899ac5b46e9b3e9951b2e80ff164d 100644 (file)
@@ -77,10 +77,15 @@ static struct usb_device_id ath3k_table[] = {
        { USB_DEVICE(0x0CF3, 0x311D) },
        { USB_DEVICE(0x13d3, 0x3375) },
        { USB_DEVICE(0x04CA, 0x3005) },
+       { USB_DEVICE(0x04CA, 0x3006) },
+       { USB_DEVICE(0x04CA, 0x3008) },
        { USB_DEVICE(0x13d3, 0x3362) },
        { USB_DEVICE(0x0CF3, 0xE004) },
        { USB_DEVICE(0x0930, 0x0219) },
        { USB_DEVICE(0x0489, 0xe057) },
+       { USB_DEVICE(0x13d3, 0x3393) },
+       { USB_DEVICE(0x0489, 0xe04e) },
+       { USB_DEVICE(0x0489, 0xe056) },
 
        /* Atheros AR5BBU12 with sflash firmware */
        { USB_DEVICE(0x0489, 0xE02C) },
@@ -104,10 +109,15 @@ static struct usb_device_id ath3k_blist_tbl[] = {
        { USB_DEVICE(0x0cf3, 0x311D), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
 
        /* Atheros AR5BBU22 with sflash firmware */
        { USB_DEVICE(0x0489, 0xE03C), .driver_info = BTUSB_ATH3012 },
index a1d4ede5b892d4516b294f91aca91633864e7d43..7e351e345476c1bcc2141f95e41fe43a3e65e612 100644 (file)
@@ -135,10 +135,15 @@ static struct usb_device_id blacklist_table[] = {
        { USB_DEVICE(0x0cf3, 0x311d), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
 
        /* Atheros AR5BBU12 with sflash firmware */
        { USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
index dbd901e94ea637bcfad26cc21de52c89e10862e9..b8e2014cb9cb0681d8fa1a21d7930b13e1e2a261 100644 (file)
@@ -60,7 +60,6 @@ struct intel_gtt_driver {
 };
 
 static struct _intel_private {
-       struct intel_gtt base;
        const struct intel_gtt_driver *driver;
        struct pci_dev *pcidev; /* device one */
        struct pci_dev *bridge_dev;
@@ -75,7 +74,18 @@ static struct _intel_private {
        struct resource ifp_resource;
        int resource_valid;
        struct page *scratch_page;
+       phys_addr_t scratch_page_dma;
        int refcount;
+       /* Whether i915 needs to use the dmar apis or not. */
+       unsigned int needs_dmar : 1;
+       phys_addr_t gma_bus_addr;
+       /*  Size of memory reserved for graphics by the BIOS */
+       unsigned int stolen_size;
+       /* Total number of gtt entries. */
+       unsigned int gtt_total_entries;
+       /* Part of the gtt that is mappable by the cpu, for those chips where
+        * this is not the full gtt. */
+       unsigned int gtt_mappable_entries;
 } intel_private;
 
 #define INTEL_GTT_GEN  intel_private.driver->gen
@@ -291,15 +301,15 @@ static int intel_gtt_setup_scratch_page(void)
        get_page(page);
        set_pages_uc(page, 1);
 
-       if (intel_private.base.needs_dmar) {
+       if (intel_private.needs_dmar) {
                dma_addr = pci_map_page(intel_private.pcidev, page, 0,
                                    PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
                if (pci_dma_mapping_error(intel_private.pcidev, dma_addr))
                        return -EINVAL;
 
-               intel_private.base.scratch_page_dma = dma_addr;
+               intel_private.scratch_page_dma = dma_addr;
        } else
-               intel_private.base.scratch_page_dma = page_to_phys(page);
+               intel_private.scratch_page_dma = page_to_phys(page);
 
        intel_private.scratch_page = page;
 
@@ -506,7 +516,7 @@ static unsigned int intel_gtt_total_entries(void)
                /* On previous hardware, the GTT size was just what was
                 * required to map the aperture.
                 */
-               return intel_private.base.gtt_mappable_entries;
+               return intel_private.gtt_mappable_entries;
        }
 }
 
@@ -546,7 +556,7 @@ static unsigned int intel_gtt_mappable_entries(void)
 static void intel_gtt_teardown_scratch_page(void)
 {
        set_pages_wb(intel_private.scratch_page, 1);
-       pci_unmap_page(intel_private.pcidev, intel_private.base.scratch_page_dma,
+       pci_unmap_page(intel_private.pcidev, intel_private.scratch_page_dma,
                       PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
        put_page(intel_private.scratch_page);
        __free_page(intel_private.scratch_page);
@@ -562,6 +572,40 @@ static void intel_gtt_cleanup(void)
        intel_gtt_teardown_scratch_page();
 }
 
+/* Certain Gen5 chipsets require require idling the GPU before
+ * unmapping anything from the GTT when VT-d is enabled.
+ */
+static inline int needs_ilk_vtd_wa(void)
+{
+#ifdef CONFIG_INTEL_IOMMU
+       const unsigned short gpu_devid = intel_private.pcidev->device;
+
+       /* Query intel_iommu to see if we need the workaround. Presumably that
+        * was loaded first.
+        */
+       if ((gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB ||
+            gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG) &&
+            intel_iommu_gfx_mapped)
+               return 1;
+#endif
+       return 0;
+}
+
+static bool intel_gtt_can_wc(void)
+{
+       if (INTEL_GTT_GEN <= 2)
+               return false;
+
+       if (INTEL_GTT_GEN >= 6)
+               return false;
+
+       /* Reports of major corruption with ILK vt'd enabled */
+       if (needs_ilk_vtd_wa())
+               return false;
+
+       return true;
+}
+
 static int intel_gtt_init(void)
 {
        u32 gma_addr;
@@ -572,8 +616,8 @@ static int intel_gtt_init(void)
        if (ret != 0)
                return ret;
 
-       intel_private.base.gtt_mappable_entries = intel_gtt_mappable_entries();
-       intel_private.base.gtt_total_entries = intel_gtt_total_entries();
+       intel_private.gtt_mappable_entries = intel_gtt_mappable_entries();
+       intel_private.gtt_total_entries = intel_gtt_total_entries();
 
        /* save the PGETBL reg for resume */
        intel_private.PGETBL_save =
@@ -585,13 +629,13 @@ static int intel_gtt_init(void)
 
        dev_info(&intel_private.bridge_dev->dev,
                        "detected gtt size: %dK total, %dK mappable\n",
-                       intel_private.base.gtt_total_entries * 4,
-                       intel_private.base.gtt_mappable_entries * 4);
+                       intel_private.gtt_total_entries * 4,
+                       intel_private.gtt_mappable_entries * 4);
 
-       gtt_map_size = intel_private.base.gtt_total_entries * 4;
+       gtt_map_size = intel_private.gtt_total_entries * 4;
 
        intel_private.gtt = NULL;
-       if (INTEL_GTT_GEN < 6 && INTEL_GTT_GEN > 2)
+       if (intel_gtt_can_wc())
                intel_private.gtt = ioremap_wc(intel_private.gtt_bus_addr,
                                               gtt_map_size);
        if (intel_private.gtt == NULL)
@@ -602,13 +646,12 @@ static int intel_gtt_init(void)
                iounmap(intel_private.registers);
                return -ENOMEM;
        }
-       intel_private.base.gtt = intel_private.gtt;
 
        global_cache_flush();   /* FIXME: ? */
 
-       intel_private.base.stolen_size = intel_gtt_stolen_size();
+       intel_private.stolen_size = intel_gtt_stolen_size();
 
-       intel_private.base.needs_dmar = USE_PCI_DMA_API && INTEL_GTT_GEN > 2;
+       intel_private.needs_dmar = USE_PCI_DMA_API && INTEL_GTT_GEN > 2;
 
        ret = intel_gtt_setup_scratch_page();
        if (ret != 0) {
@@ -623,7 +666,7 @@ static int intel_gtt_init(void)
                pci_read_config_dword(intel_private.pcidev, I915_GMADDR,
                                      &gma_addr);
 
-       intel_private.base.gma_bus_addr = (gma_addr & PCI_BASE_ADDRESS_MEM_MASK);
+       intel_private.gma_bus_addr = (gma_addr & PCI_BASE_ADDRESS_MEM_MASK);
 
        return 0;
 }
@@ -634,8 +677,7 @@ static int intel_fake_agp_fetch_size(void)
        unsigned int aper_size;
        int i;
 
-       aper_size = (intel_private.base.gtt_mappable_entries << PAGE_SHIFT)
-                   / MB(1);
+       aper_size = (intel_private.gtt_mappable_entries << PAGE_SHIFT) / MB(1);
 
        for (i = 0; i < num_sizes; i++) {
                if (aper_size == intel_fake_agp_sizes[i].size) {
@@ -779,7 +821,7 @@ static int intel_fake_agp_configure(void)
            return -EIO;
 
        intel_private.clear_fake_agp = true;
-       agp_bridge->gart_bus_addr = intel_private.base.gma_bus_addr;
+       agp_bridge->gart_bus_addr = intel_private.gma_bus_addr;
 
        return 0;
 }
@@ -841,12 +883,9 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem,
 {
        int ret = -EINVAL;
 
-       if (intel_private.base.do_idle_maps)
-               return -ENODEV;
-
        if (intel_private.clear_fake_agp) {
-               int start = intel_private.base.stolen_size / PAGE_SIZE;
-               int end = intel_private.base.gtt_mappable_entries;
+               int start = intel_private.stolen_size / PAGE_SIZE;
+               int end = intel_private.gtt_mappable_entries;
                intel_gtt_clear_range(start, end - start);
                intel_private.clear_fake_agp = false;
        }
@@ -857,7 +896,7 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem,
        if (mem->page_count == 0)
                goto out;
 
-       if (pg_start + mem->page_count > intel_private.base.gtt_total_entries)
+       if (pg_start + mem->page_count > intel_private.gtt_total_entries)
                goto out_err;
 
        if (type != mem->type)
@@ -869,7 +908,7 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem,
        if (!mem->is_flushed)
                global_cache_flush();
 
-       if (intel_private.base.needs_dmar) {
+       if (intel_private.needs_dmar) {
                struct sg_table st;
 
                ret = intel_gtt_map_memory(mem->pages, mem->page_count, &st);
@@ -895,7 +934,7 @@ void intel_gtt_clear_range(unsigned int first_entry, unsigned int num_entries)
        unsigned int i;
 
        for (i = first_entry; i < (first_entry + num_entries); i++) {
-               intel_private.driver->write_entry(intel_private.base.scratch_page_dma,
+               intel_private.driver->write_entry(intel_private.scratch_page_dma,
                                                  i, 0);
        }
        readl(intel_private.gtt+i-1);
@@ -908,12 +947,9 @@ static int intel_fake_agp_remove_entries(struct agp_memory *mem,
        if (mem->page_count == 0)
                return 0;
 
-       if (intel_private.base.do_idle_maps)
-               return -ENODEV;
-
        intel_gtt_clear_range(pg_start, mem->page_count);
 
-       if (intel_private.base.needs_dmar) {
+       if (intel_private.needs_dmar) {
                intel_gtt_unmap_memory(mem->sg_list, mem->num_sg);
                mem->sg_list = NULL;
                mem->num_sg = 0;
@@ -1070,25 +1106,6 @@ static void i965_write_entry(dma_addr_t addr,
        writel(addr | pte_flags, intel_private.gtt + entry);
 }
 
-/* Certain Gen5 chipsets require require idling the GPU before
- * unmapping anything from the GTT when VT-d is enabled.
- */
-static inline int needs_idle_maps(void)
-{
-#ifdef CONFIG_INTEL_IOMMU
-       const unsigned short gpu_devid = intel_private.pcidev->device;
-
-       /* Query intel_iommu to see if we need the workaround. Presumably that
-        * was loaded first.
-        */
-       if ((gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB ||
-            gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG) &&
-            intel_iommu_gfx_mapped)
-               return 1;
-#endif
-       return 0;
-}
-
 static int i9xx_setup(void)
 {
        u32 reg_addr, gtt_addr;
@@ -1116,9 +1133,6 @@ static int i9xx_setup(void)
                break;
        }
 
-       if (needs_idle_maps())
-               intel_private.base.do_idle_maps = 1;
-
        intel_i9xx_setup_flush();
 
        return 0;
@@ -1390,9 +1404,13 @@ int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev,
 }
 EXPORT_SYMBOL(intel_gmch_probe);
 
-struct intel_gtt *intel_gtt_get(void)
+void intel_gtt_get(size_t *gtt_total, size_t *stolen_size,
+                  phys_addr_t *mappable_base, unsigned long *mappable_end)
 {
-       return &intel_private.base;
+       *gtt_total = intel_private.gtt_total_entries << PAGE_SHIFT;
+       *stolen_size = intel_private.stolen_size;
+       *mappable_base = intel_private.gma_bus_addr;
+       *mappable_end = intel_private.gtt_mappable_entries << PAGE_SHIFT;
 }
 EXPORT_SYMBOL(intel_gtt_get);
 
index ff004578a119e72d989d3c9baeb45dbf84f17b7b..9dd2551a0a41aebdb10f780b6657e9f3ce3de94c 100644 (file)
@@ -124,7 +124,7 @@ void __init of_cpu_clk_setup(struct device_node *node)
 
        clks = kzalloc(ncpus * sizeof(*clks), GFP_KERNEL);
        if (WARN_ON(!clks))
-               return;
+               goto clks_out;
 
        for_each_node_by_type(dn, "cpu") {
                struct clk_init_data init;
@@ -134,11 +134,11 @@ void __init of_cpu_clk_setup(struct device_node *node)
                int cpu, err;
 
                if (WARN_ON(!clk_name))
-                       return;
+                       goto bail_out;
 
                err = of_property_read_u32(dn, "reg", &cpu);
                if (WARN_ON(err))
-                       return;
+                       goto bail_out;
 
                sprintf(clk_name, "cpu%d", cpu);
                parent_clk = of_clk_get(node, 0);
@@ -167,6 +167,9 @@ void __init of_cpu_clk_setup(struct device_node *node)
        return;
 bail_out:
        kfree(clks);
+       while(ncpus--)
+               kfree(cpuclk[ncpus].clk_name);
+clks_out:
        kfree(cpuclk);
 }
 
index 934854ae5eb4a436205d644c76c8011a5c41de3e..7227cd7340423c595a2a1b29488e864662198634 100644 (file)
@@ -106,7 +106,7 @@ config X86_POWERNOW_K7_ACPI
 config X86_POWERNOW_K8
        tristate "AMD Opteron/Athlon64 PowerNow!"
        select CPU_FREQ_TABLE
-       depends on ACPI && ACPI_PROCESSOR
+       depends on ACPI && ACPI_PROCESSOR && X86_ACPI_CPUFREQ
        help
          This adds the CPUFreq driver for K8/early Opteron/Athlon64 processors.
          Support for K10 and newer processors is now in acpi-cpufreq.
index 0d048f6a2b23a3bf9476a658cac846ccd321bd3c..7b0d49d78c61fc6c9fd312b0d05663ad42edf099 100644 (file)
@@ -1030,4 +1030,11 @@ MODULE_PARM_DESC(acpi_pstate_strict,
 late_initcall(acpi_cpufreq_init);
 module_exit(acpi_cpufreq_exit);
 
+static const struct x86_cpu_id acpi_cpufreq_ids[] = {
+       X86_FEATURE_MATCH(X86_FEATURE_ACPI),
+       X86_FEATURE_MATCH(X86_FEATURE_HW_PSTATE),
+       {}
+};
+MODULE_DEVICE_TABLE(x86cpu, acpi_cpufreq_ids);
+
 MODULE_ALIAS("acpi");
index 52bf36d599f575fcdd0bfe4fc3788db0d492e5a8..debc5a7c8db6207feeb9c428ccc44b8fcaed8d2f 100644 (file)
@@ -71,12 +71,15 @@ static int cpu0_set_target(struct cpufreq_policy *policy,
        }
 
        if (cpu_reg) {
+               rcu_read_lock();
                opp = 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 = opp_get_voltage(opp);
+               rcu_read_unlock();
                tol = volt * voltage_tolerance / 100;
                volt_old = regulator_get_voltage(cpu_reg);
        }
@@ -236,12 +239,14 @@ static int cpu0_cpufreq_driver_init(void)
                 */
                for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)
                        ;
+               rcu_read_lock();
                opp = opp_find_freq_exact(cpu_dev,
                                freq_table[0].frequency * 1000, true);
                min_uV = opp_get_voltage(opp);
                opp = opp_find_freq_exact(cpu_dev,
                                freq_table[i-1].frequency * 1000, true);
                max_uV = 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;
index 1f3417a8322dfe8b0ca773873e058f7eb3a6c283..97102b05843fa580435ae0ae5b872d9467b577df 100644 (file)
@@ -110,13 +110,16 @@ static int omap_target(struct cpufreq_policy *policy,
        freq = ret;
 
        if (mpu_reg) {
+               rcu_read_lock();
                opp = opp_find_freq_ceil(mpu_dev, &freq);
                if (IS_ERR(opp)) {
+                       rcu_read_unlock();
                        dev_err(mpu_dev, "%s: unable to find MPU OPP for %d\n",
                                __func__, freqs.new);
                        return -EINVAL;
                }
                volt = opp_get_voltage(opp);
+               rcu_read_unlock();
                tol = volt * OPP_TOLERANCE / 100;
                volt_old = regulator_get_voltage(mpu_reg);
        }
index fb4a7dd57f94f479adf8a3dd93e35e4f348b65d3..e1f6860e069c7350d3fbb5bb499a67bfc581f760 100644 (file)
@@ -69,24 +69,15 @@ int cpuidle_play_dead(void)
 {
        struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
        struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
-       int i, dead_state = -1;
-       int power_usage = INT_MAX;
+       int i;
 
        if (!drv)
                return -ENODEV;
 
        /* Find lowest-power state that supports long-term idle */
-       for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) {
-               struct cpuidle_state *s = &drv->states[i];
-
-               if (s->power_usage < power_usage && s->enter_dead) {
-                       power_usage = s->power_usage;
-                       dead_state = i;
-               }
-       }
-
-       if (dead_state != -1)
-               return drv->states[dead_state].enter_dead(dev, dead_state);
+       for (i = drv->state_count - 1; i >= CPUIDLE_DRIVER_STATE_START; i--)
+               if (drv->states[i].enter_dead)
+                       return drv->states[i].enter_dead(dev, i);
 
        return -ENODEV;
 }
index c2b281afe0ed03ce13a3a3122d96084319a93635..422c7b69ba7c50e728fa3af5af346a85a553af8f 100644 (file)
@@ -19,34 +19,9 @@ DEFINE_SPINLOCK(cpuidle_driver_lock);
 static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu);
 static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu);
 
-static void set_power_states(struct cpuidle_driver *drv)
-{
-       int i;
-
-       /*
-        * cpuidle driver should set the drv->power_specified bit
-        * before registering if the driver provides
-        * power_usage numbers.
-        *
-        * If power_specified is not set,
-        * we fill in power_usage with decreasing values as the
-        * cpuidle code has an implicit assumption that state Cn
-        * uses less power than C(n-1).
-        *
-        * With CONFIG_ARCH_HAS_CPU_RELAX, C0 is already assigned
-        * an power value of -1.  So we use -2, -3, etc, for other
-        * c-states.
-        */
-       for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++)
-               drv->states[i].power_usage = -1 - i;
-}
-
 static void __cpuidle_driver_init(struct cpuidle_driver *drv)
 {
        drv->refcnt = 0;
-
-       if (!drv->power_specified)
-               set_power_states(drv);
 }
 
 static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu)
index 20ea33afdda10c3e80ea99e716790c4494babf3e..fe343a06b7da3278ba68c9c15c261ea14afde685 100644 (file)
@@ -312,7 +312,6 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
 {
        struct menu_device *data = &__get_cpu_var(menu_devices);
        int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
-       int power_usage = INT_MAX;
        int i;
        int multiplier;
        struct timespec t;
@@ -383,11 +382,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
                if (s->exit_latency * multiplier > data->predicted_us)
                        continue;
 
-               if (s->power_usage < power_usage) {
-                       power_usage = s->power_usage;
-                       data->last_state_idx = i;
-                       data->exit_us = s->exit_latency;
-               }
+               data->last_state_idx = i;
+               data->exit_us = s->exit_latency;
        }
 
        /* not deepest C-state chosen for low predicted residency */
index 340942946106d22489124923a2ccd766bc22f2ef..428754af62366cda8fe71dee47b128c5da602969 100644 (file)
@@ -374,7 +374,7 @@ static int cpuidle_add_state_sysfs(struct cpuidle_device *device)
        struct cpuidle_driver *drv = cpuidle_get_cpu_driver(device);
 
        /* state statistics */
-       for (i = 0; i < drv->state_count; i++) {
+       for (i = 0; i < device->state_count; i++) {
                kobj = kzalloc(sizeof(struct cpuidle_state_kobj), GFP_KERNEL);
                if (!kobj)
                        goto error_state;
index 53766f39aaddfd5db9147842b40825fb1399453b..3b367973a8028c5d1cd32bd823bcf9e005d269b6 100644 (file)
@@ -994,6 +994,11 @@ module_exit(devfreq_exit);
  * @freq:      The frequency given to target function
  * @flags:     Flags handed from devfreq framework.
  *
+ * Locking: This function must be called under rcu_read_lock(). opp is a rcu
+ * protected pointer. The reason for the same is that the opp pointer which is
+ * returned will remain valid for use with opp_get_{voltage, freq} only while
+ * under the locked area. The pointer returned must be used prior to unlocking
+ * with rcu_read_unlock() to maintain the integrity of the pointer.
  */
 struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq,
                                    u32 flags)
index 80c745e83082bf6d9dade7a81dffbb89931632a7..46d94e9e95b53ae033af012ef745822e0b7f5ccd 100644 (file)
@@ -73,6 +73,16 @@ enum busclk_level_idx {
 #define EX4210_LV_NUM  (LV_2 + 1)
 #define EX4x12_LV_NUM  (LV_4 + 1)
 
+/**
+ * struct busfreq_opp_info - opp information for bus
+ * @rate:      Frequency in hertz
+ * @volt:      Voltage in microvolts corresponding to this OPP
+ */
+struct busfreq_opp_info {
+       unsigned long rate;
+       unsigned long volt;
+};
+
 struct busfreq_data {
        enum exynos4_busf_type type;
        struct device *dev;
@@ -80,7 +90,7 @@ struct busfreq_data {
        bool disabled;
        struct regulator *vdd_int;
        struct regulator *vdd_mif; /* Exynos4412/4212 only */
-       struct opp *curr_opp;
+       struct busfreq_opp_info curr_oppinfo;
        struct exynos4_ppmu dmc[2];
 
        struct notifier_block pm_notifier;
@@ -296,13 +306,14 @@ static unsigned int exynos4x12_clkdiv_sclkip[][3] = {
 };
 
 
-static int exynos4210_set_busclk(struct busfreq_data *data, struct opp *opp)
+static int exynos4210_set_busclk(struct busfreq_data *data,
+                                struct busfreq_opp_info *oppi)
 {
        unsigned int index;
        unsigned int tmp;
 
        for (index = LV_0; index < EX4210_LV_NUM; index++)
-               if (opp_get_freq(opp) == exynos4210_busclk_table[index].clk)
+               if (oppi->rate == exynos4210_busclk_table[index].clk)
                        break;
 
        if (index == EX4210_LV_NUM)
@@ -361,13 +372,14 @@ static int exynos4210_set_busclk(struct busfreq_data *data, struct opp *opp)
        return 0;
 }
 
-static int exynos4x12_set_busclk(struct busfreq_data *data, struct opp *opp)
+static int exynos4x12_set_busclk(struct busfreq_data *data,
+                                struct busfreq_opp_info *oppi)
 {
        unsigned int index;
        unsigned int tmp;
 
        for (index = LV_0; index < EX4x12_LV_NUM; index++)
-               if (opp_get_freq(opp) == exynos4x12_mifclk_table[index].clk)
+               if (oppi->rate == exynos4x12_mifclk_table[index].clk)
                        break;
 
        if (index == EX4x12_LV_NUM)
@@ -576,11 +588,12 @@ static int exynos4x12_get_intspec(unsigned long mifclk)
        return -EINVAL;
 }
 
-static int exynos4_bus_setvolt(struct busfreq_data *data, struct opp *opp,
-                              struct opp *oldopp)
+static int exynos4_bus_setvolt(struct busfreq_data *data,
+                              struct busfreq_opp_info *oppi,
+                              struct busfreq_opp_info *oldoppi)
 {
        int err = 0, tmp;
-       unsigned long volt = opp_get_voltage(opp);
+       unsigned long volt = oppi->volt;
 
        switch (data->type) {
        case TYPE_BUSF_EXYNOS4210:
@@ -595,11 +608,11 @@ static int exynos4_bus_setvolt(struct busfreq_data *data, struct opp *opp,
                if (err)
                        break;
 
-               tmp = exynos4x12_get_intspec(opp_get_freq(opp));
+               tmp = exynos4x12_get_intspec(oppi->rate);
                if (tmp < 0) {
                        err = tmp;
                        regulator_set_voltage(data->vdd_mif,
-                                             opp_get_voltage(oldopp),
+                                             oldoppi->volt,
                                              MAX_SAFEVOLT);
                        break;
                }
@@ -609,7 +622,7 @@ static int exynos4_bus_setvolt(struct busfreq_data *data, struct opp *opp,
                /*  Try to recover */
                if (err)
                        regulator_set_voltage(data->vdd_mif,
-                                             opp_get_voltage(oldopp),
+                                             oldoppi->volt,
                                              MAX_SAFEVOLT);
                break;
        default:
@@ -626,17 +639,26 @@ static int exynos4_bus_target(struct device *dev, unsigned long *_freq,
        struct platform_device *pdev = container_of(dev, struct platform_device,
                                                    dev);
        struct busfreq_data *data = platform_get_drvdata(pdev);
-       struct opp *opp = devfreq_recommended_opp(dev, _freq, flags);
-       unsigned long freq = opp_get_freq(opp);
-       unsigned long old_freq = opp_get_freq(data->curr_opp);
+       struct opp *opp;
+       unsigned long freq;
+       unsigned long old_freq = data->curr_oppinfo.rate;
+       struct busfreq_opp_info new_oppinfo;
 
-       if (IS_ERR(opp))
+       rcu_read_lock();
+       opp = devfreq_recommended_opp(dev, _freq, flags);
+       if (IS_ERR(opp)) {
+               rcu_read_unlock();
                return PTR_ERR(opp);
+       }
+       new_oppinfo.rate = opp_get_freq(opp);
+       new_oppinfo.volt = opp_get_voltage(opp);
+       rcu_read_unlock();
+       freq = new_oppinfo.rate;
 
        if (old_freq == freq)
                return 0;
 
-       dev_dbg(dev, "targetting %lukHz %luuV\n", freq, opp_get_voltage(opp));
+       dev_dbg(dev, "targetting %lukHz %luuV\n", freq, new_oppinfo.volt);
 
        mutex_lock(&data->lock);
 
@@ -644,17 +666,18 @@ static int exynos4_bus_target(struct device *dev, unsigned long *_freq,
                goto out;
 
        if (old_freq < freq)
-               err = exynos4_bus_setvolt(data, opp, data->curr_opp);
+               err = exynos4_bus_setvolt(data, &new_oppinfo,
+                                         &data->curr_oppinfo);
        if (err)
                goto out;
 
        if (old_freq != freq) {
                switch (data->type) {
                case TYPE_BUSF_EXYNOS4210:
-                       err = exynos4210_set_busclk(data, opp);
+                       err = exynos4210_set_busclk(data, &new_oppinfo);
                        break;
                case TYPE_BUSF_EXYNOS4x12:
-                       err = exynos4x12_set_busclk(data, opp);
+                       err = exynos4x12_set_busclk(data, &new_oppinfo);
                        break;
                default:
                        err = -EINVAL;
@@ -664,11 +687,12 @@ static int exynos4_bus_target(struct device *dev, unsigned long *_freq,
                goto out;
 
        if (old_freq > freq)
-               err = exynos4_bus_setvolt(data, opp, data->curr_opp);
+               err = exynos4_bus_setvolt(data, &new_oppinfo,
+                                         &data->curr_oppinfo);
        if (err)
                goto out;
 
-       data->curr_opp = opp;
+       data->curr_oppinfo = new_oppinfo;
 out:
        mutex_unlock(&data->lock);
        return err;
@@ -702,7 +726,7 @@ static int exynos4_bus_get_dev_status(struct device *dev,
 
        exynos4_read_ppmu(data);
        busier_dmc = exynos4_get_busier_dmc(data);
-       stat->current_frequency = opp_get_freq(data->curr_opp);
+       stat->current_frequency = data->curr_oppinfo.rate;
 
        if (busier_dmc)
                addr = S5P_VA_DMC1;
@@ -933,6 +957,7 @@ static int exynos4_busfreq_pm_notifier_event(struct notifier_block *this,
        struct busfreq_data *data = container_of(this, struct busfreq_data,
                                                 pm_notifier);
        struct opp *opp;
+       struct busfreq_opp_info new_oppinfo;
        unsigned long maxfreq = ULONG_MAX;
        int err = 0;
 
@@ -943,18 +968,29 @@ static int exynos4_busfreq_pm_notifier_event(struct notifier_block *this,
 
                data->disabled = true;
 
+               rcu_read_lock();
                opp = opp_find_freq_floor(data->dev, &maxfreq);
+               if (IS_ERR(opp)) {
+                       rcu_read_unlock();
+                       dev_err(data->dev, "%s: unable to find a min freq\n",
+                               __func__);
+                       return PTR_ERR(opp);
+               }
+               new_oppinfo.rate = opp_get_freq(opp);
+               new_oppinfo.volt = opp_get_voltage(opp);
+               rcu_read_unlock();
 
-               err = exynos4_bus_setvolt(data, opp, data->curr_opp);
+               err = exynos4_bus_setvolt(data, &new_oppinfo,
+                                         &data->curr_oppinfo);
                if (err)
                        goto unlock;
 
                switch (data->type) {
                case TYPE_BUSF_EXYNOS4210:
-                       err = exynos4210_set_busclk(data, opp);
+                       err = exynos4210_set_busclk(data, &new_oppinfo);
                        break;
                case TYPE_BUSF_EXYNOS4x12:
-                       err = exynos4x12_set_busclk(data, opp);
+                       err = exynos4x12_set_busclk(data, &new_oppinfo);
                        break;
                default:
                        err = -EINVAL;
@@ -962,7 +998,7 @@ static int exynos4_busfreq_pm_notifier_event(struct notifier_block *this,
                if (err)
                        goto unlock;
 
-               data->curr_opp = opp;
+               data->curr_oppinfo = new_oppinfo;
 unlock:
                mutex_unlock(&data->lock);
                if (err)
@@ -1027,13 +1063,17 @@ static int exynos4_busfreq_probe(struct platform_device *pdev)
                }
        }
 
+       rcu_read_lock();
        opp = opp_find_freq_floor(dev, &exynos4_devfreq_profile.initial_freq);
        if (IS_ERR(opp)) {
+               rcu_read_unlock();
                dev_err(dev, "Invalid initial frequency %lu kHz.\n",
                        exynos4_devfreq_profile.initial_freq);
                return PTR_ERR(opp);
        }
-       data->curr_opp = opp;
+       data->curr_oppinfo.rate = opp_get_freq(opp);
+       data->curr_oppinfo.volt = opp_get_voltage(opp);
+       rcu_read_unlock();
 
        platform_set_drvdata(pdev, data);
 
index dbf0e6f8de8a8c64d4b29b33b9c834ae3ab904c7..a7dcf78b1ff809e4a6a865884779856330d15510 100644 (file)
@@ -684,9 +684,8 @@ static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
                        break;
                }
 
-               imxdmac->hw_chaining = 1;
-               if (!imxdma_hw_chain(imxdmac))
-                       return -EINVAL;
+               imxdmac->hw_chaining = 0;
+
                imxdmac->ccr_from_device = (mode | IMX_DMA_TYPE_FIFO) |
                        ((IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR) << 2) |
                        CCR_REN;
index e5fc944de1f025edd5492e4208ae0dce5a5a5093..3e9d66920eb3491d85b8c6027ac786a76242e107 100644 (file)
@@ -951,7 +951,7 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device)
                        goto free_resources;
                }
        }
-       dma_sync_single_for_device(dev, dest_dma, PAGE_SIZE, DMA_TO_DEVICE);
+       dma_sync_single_for_device(dev, dest_dma, PAGE_SIZE, DMA_FROM_DEVICE);
 
        /* skip validate if the capability is not present */
        if (!dma_has_cap(DMA_XOR_VAL, dma_chan->device->cap_mask))
index c39e61bc8172e5c277dfd4cdb0997cfcc08e407f..3cad856fe67f9f9518cbf79b0f02bd41c32733b8 100644 (file)
@@ -266,6 +266,7 @@ static struct tegra_dma_desc *tegra_dma_desc_get(
                if (async_tx_test_ack(&dma_desc->txd)) {
                        list_del(&dma_desc->node);
                        spin_unlock_irqrestore(&tdc->lock, flags);
+                       dma_desc->txd.flags = 0;
                        return dma_desc;
                }
        }
@@ -1050,7 +1051,9 @@ struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic(
                                        TEGRA_APBDMA_AHBSEQ_WRAP_SHIFT;
        ahb_seq |= TEGRA_APBDMA_AHBSEQ_BUS_WIDTH_32;
 
-       csr |= TEGRA_APBDMA_CSR_FLOW | TEGRA_APBDMA_CSR_IE_EOC;
+       csr |= TEGRA_APBDMA_CSR_FLOW;
+       if (flags & DMA_PREP_INTERRUPT)
+               csr |= TEGRA_APBDMA_CSR_IE_EOC;
        csr |= tdc->dma_sconfig.slave_id << TEGRA_APBDMA_CSR_REQ_SEL_SHIFT;
 
        apb_seq |= TEGRA_APBDMA_APBSEQ_WRAP_WORD_1;
@@ -1095,7 +1098,8 @@ struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic(
                mem += len;
        }
        sg_req->last_sg = true;
-       dma_desc->txd.flags = 0;
+       if (flags & DMA_CTRL_ACK)
+               dma_desc->txd.flags = DMA_CTRL_ACK;
 
        /*
         * Make sure that mode should not be conflicting with currently
index 281f566a5513d907fefc67da9237a2ec41ab8679..d1e9eb191f2bd77ff72614c0819885d31dcf473d 100644 (file)
@@ -340,7 +340,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
        /*
         * Alocate and fill the csrow/channels structs
         */
-       mci->csrows = kcalloc(sizeof(*mci->csrows), tot_csrows, GFP_KERNEL);
+       mci->csrows = kcalloc(tot_csrows, sizeof(*mci->csrows), GFP_KERNEL);
        if (!mci->csrows)
                goto error;
        for (row = 0; row < tot_csrows; row++) {
@@ -351,7 +351,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
                csr->csrow_idx = row;
                csr->mci = mci;
                csr->nr_channels = tot_channels;
-               csr->channels = kcalloc(sizeof(*csr->channels), tot_channels,
+               csr->channels = kcalloc(tot_channels, sizeof(*csr->channels),
                                        GFP_KERNEL);
                if (!csr->channels)
                        goto error;
@@ -369,7 +369,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
        /*
         * Allocate and fill the dimm structs
         */
-       mci->dimms  = kcalloc(sizeof(*mci->dimms), tot_dimms, GFP_KERNEL);
+       mci->dimms  = kcalloc(tot_dimms, sizeof(*mci->dimms), GFP_KERNEL);
        if (!mci->dimms)
                goto error;
 
index dc6e905ee1a5402126496bd06838302a1b26165f..0056c4dae9d52cc24985200dbb58b724be73c3ed 100644 (file)
@@ -256,7 +256,7 @@ static ssize_t edac_pci_dev_store(struct kobject *kobj,
        struct edac_pci_dev_attribute *edac_pci_dev;
        edac_pci_dev = (struct edac_pci_dev_attribute *)attr;
 
-       if (edac_pci_dev->show)
+       if (edac_pci_dev->store)
                return edac_pci_dev->store(edac_pci_dev->value, buffer, count);
        return -EIO;
 }
index fd3ae6290d71ecfd927cc4447a942c099bd223ea..982f1f5f5742f21da9206f62fa622ce17adefe9f 100644 (file)
@@ -471,7 +471,7 @@ void __init dmi_scan_machine(void)
        char __iomem *p, *q;
        int rc;
 
-       if (efi_enabled) {
+       if (efi_enabled(EFI_CONFIG_TABLES)) {
                if (efi.smbios == EFI_INVALID_TABLE_ADDR)
                        goto error;
 
index 7b1c37497c9a2683569f8a1ac9c959e8d5bc7636..f5596db0cf583dc16226bc629a42977de0f9fa05 100644 (file)
@@ -674,7 +674,7 @@ static int efi_status_to_err(efi_status_t status)
                err = -EACCES;
                break;
        case EFI_NOT_FOUND:
-               err = -ENOENT;
+               err = -EIO;
                break;
        default:
                err = -EINVAL;
@@ -793,6 +793,7 @@ static ssize_t efivarfs_file_write(struct file *file,
                spin_unlock(&efivars->lock);
                efivar_unregister(var);
                drop_nlink(inode);
+               d_delete(file->f_dentry);
                dput(file->f_dentry);
 
        } else {
@@ -994,7 +995,7 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry)
                list_del(&var->list);
                spin_unlock(&efivars->lock);
                efivar_unregister(var);
-               drop_nlink(dir);
+               drop_nlink(dentry->d_inode);
                dput(dentry);
                return 0;
        }
@@ -1782,7 +1783,7 @@ efivars_init(void)
        printk(KERN_INFO "EFI Variables Facility v%s %s\n", EFIVARS_VERSION,
               EFIVARS_DATE);
 
-       if (!efi_enabled)
+       if (!efi_enabled(EFI_RUNTIME_SERVICES))
                return 0;
 
        /* For now we'll register the efi directory at /sys/firmware/efi */
@@ -1822,7 +1823,7 @@ err_put:
 static void __exit
 efivars_exit(void)
 {
-       if (efi_enabled) {
+       if (efi_enabled(EFI_RUNTIME_SERVICES)) {
                unregister_efivars(&__efivars);
                kobject_put(efi_kobj);
        }
index 4da4eb9ae92604c35349ebaeb39b4a612bac3ed6..2224f1dc074b1329d7ce9c80b78bef4fffc6e98b 100644 (file)
@@ -99,7 +99,7 @@ unsigned long __init find_ibft_region(unsigned long *sizep)
        /* iBFT 1.03 section 1.4.3.1 mandates that UEFI machines will
         * only use ACPI for this */
 
-       if (!efi_enabled)
+       if (!efi_enabled(EFI_BOOT))
                find_ibft_in_mem();
 
        if (ibft_addr) {
index 7d9bd94be8d2a2108b66ac9575071e5751c06c79..6819d63cb1673647e35821ce3feede298e3e56c7 100644 (file)
@@ -547,7 +547,6 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
        mvchip->membase = devm_request_and_ioremap(&pdev->dev, res);
        if (! mvchip->membase) {
                dev_err(&pdev->dev, "Cannot ioremap\n");
-               kfree(mvchip->chip.label);
                return -ENOMEM;
        }
 
@@ -557,14 +556,12 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
                res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
                if (! res) {
                        dev_err(&pdev->dev, "Cannot get memory resource\n");
-                       kfree(mvchip->chip.label);
                        return -ENODEV;
                }
 
                mvchip->percpu_membase = devm_request_and_ioremap(&pdev->dev, res);
                if (! mvchip->percpu_membase) {
                        dev_err(&pdev->dev, "Cannot ioremap\n");
-                       kfree(mvchip->chip.label);
                        return -ENOMEM;
                }
        }
@@ -625,7 +622,6 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
        mvchip->irqbase = irq_alloc_descs(-1, 0, ngpios, -1);
        if (mvchip->irqbase < 0) {
                dev_err(&pdev->dev, "no irqs\n");
-               kfree(mvchip->chip.label);
                return -ENOMEM;
        }
 
@@ -633,7 +629,6 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
                                    mvchip->membase, handle_level_irq);
        if (! gc) {
                dev_err(&pdev->dev, "Cannot allocate generic irq_chip\n");
-               kfree(mvchip->chip.label);
                return -ENOMEM;
        }
 
@@ -668,7 +663,6 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
                irq_remove_generic_chip(gc, IRQ_MSK(ngpios), IRQ_NOREQUEST,
                                        IRQ_LEVEL | IRQ_NOPROBE);
                kfree(gc);
-               kfree(mvchip->chip.label);
                return -ENODEV;
        }
 
index 01f7fe955590c1b79a64cd088431858b7d9caea8..76be7eed79dec2683e244a0a9f60a1688241e1ab 100644 (file)
@@ -32,7 +32,6 @@
 
 #include <mach/hardware.h>
 #include <mach/map.h>
-#include <mach/regs-clock.h>
 #include <mach/regs-gpio.h>
 
 #include <plat/cpu.h>
@@ -446,7 +445,7 @@ static struct samsung_gpio_cfg s3c24xx_gpiocfg_banka = {
 };
 #endif
 
-#if defined(CONFIG_ARCH_EXYNOS4) || defined(CONFIG_ARCH_EXYNOS5)
+#if defined(CONFIG_ARCH_EXYNOS4) || defined(CONFIG_SOC_EXYNOS5250)
 static struct samsung_gpio_cfg exynos_gpio_cfg = {
        .set_pull       = exynos_gpio_setpull,
        .get_pull       = exynos_gpio_getpull,
@@ -2446,7 +2445,7 @@ static struct samsung_gpio_chip exynos4_gpios_3[] = {
 };
 #endif
 
-#ifdef CONFIG_ARCH_EXYNOS5
+#ifdef CONFIG_SOC_EXYNOS5250
 static struct samsung_gpio_chip exynos5_gpios_1[] = {
        {
                .chip   = {
@@ -2614,7 +2613,7 @@ static struct samsung_gpio_chip exynos5_gpios_1[] = {
 };
 #endif
 
-#ifdef CONFIG_ARCH_EXYNOS5
+#ifdef CONFIG_SOC_EXYNOS5250
 static struct samsung_gpio_chip exynos5_gpios_2[] = {
        {
                .chip   = {
@@ -2675,7 +2674,7 @@ static struct samsung_gpio_chip exynos5_gpios_2[] = {
 };
 #endif
 
-#ifdef CONFIG_ARCH_EXYNOS5
+#ifdef CONFIG_SOC_EXYNOS5250
 static struct samsung_gpio_chip exynos5_gpios_3[] = {
        {
                .chip   = {
@@ -2711,7 +2710,7 @@ static struct samsung_gpio_chip exynos5_gpios_3[] = {
 };
 #endif
 
-#ifdef CONFIG_ARCH_EXYNOS5
+#ifdef CONFIG_SOC_EXYNOS5250
 static struct samsung_gpio_chip exynos5_gpios_4[] = {
        {
                .chip   = {
@@ -3010,7 +3009,7 @@ static __init int samsung_gpiolib_init(void)
        int i, nr_chips;
        int group = 0;
 
-#ifdef CONFIG_PINCTRL_SAMSUNG
+#if defined(CONFIG_PINCTRL_EXYNOS) || defined(CONFIG_PINCTRL_EXYNOS5440)
        /*
        * This gpio driver includes support for device tree support and there
        * are platforms using it. In order to maintain compatibility with those
@@ -3026,6 +3025,7 @@ static __init int samsung_gpiolib_init(void)
        static const struct of_device_id exynos_pinctrl_ids[] = {
                { .compatible = "samsung,pinctrl-exynos4210", },
                { .compatible = "samsung,pinctrl-exynos4x12", },
+               { .compatible = "samsung,pinctrl-exynos5440", },
        };
        for_each_matching_node(pctrl_np, exynos_pinctrl_ids)
                if (pctrl_np && of_device_is_available(pctrl_np))
index cc9277885dd071b2da83edeae9250b6ea5985020..30879df3daead6c3e208cfed0e8e7d191d31eaa9 100644 (file)
@@ -1 +1 @@
-obj-y                  += drm/ vga/ stub/
+obj-y                  += drm/ vga/
index 3399b209260a14d20383a9c025718986760a81d6..0ce5f52ac56ee5d384c4d3139a517c5fabdee422 100644 (file)
@@ -69,6 +69,8 @@ config DRM_KMS_CMA_HELPER
        help
          Choose this if you need the KMS CMA helper functions
 
+source "drivers/gpu/drm/i2c/Kconfig"
+
 config DRM_TDFX
        tristate "3dfx Banshee/Voodoo3+"
        depends on DRM && PCI
@@ -213,3 +215,5 @@ source "drivers/gpu/drm/cirrus/Kconfig"
 source "drivers/gpu/drm/shmobile/Kconfig"
 
 source "drivers/gpu/drm/tegra/Kconfig"
+
+source "drivers/gpu/drm/omapdrm/Kconfig"
index 6f58c81cfcbcb073065bf29889c9bfce73cde2ae..b6b43cbc18e45dd056b0c09fde2ce55e6e1f50f5 100644 (file)
@@ -50,4 +50,5 @@ obj-$(CONFIG_DRM_UDL) += udl/
 obj-$(CONFIG_DRM_AST) += ast/
 obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
 obj-$(CONFIG_DRM_TEGRA) += tegra/
+obj-$(CONFIG_DRM_OMAP) += omapdrm/
 obj-y                  += i2c/
index 2d2c2f8d6dc668e29202b947461314fc4ccf71be..df0d0a08097a13e4765cd649545b855f0614ff91 100644 (file)
@@ -94,9 +94,9 @@ static int ast_drm_thaw(struct drm_device *dev)
        ast_post_gpu(dev);
 
        drm_mode_config_reset(dev);
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
        drm_helper_resume_force_mode(dev);
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
 
        console_lock();
        ast_fbdev_set_suspend(dev, 0);
index 5ccf984f063acf484a365ce9f62caca95fd093c2..528429252f0f985058d80096d3f5021027cd2751 100644 (file)
@@ -98,6 +98,8 @@ struct ast_private {
 
        struct drm_gem_object *cursor_cache;
        uint64_t cursor_cache_gpu_addr;
+       /* Acces to this cache is protected by the crtc->mutex of the only crtc
+        * we have. */
        struct ttm_bo_kmap_obj cache_kmap;
        int next_cursor;
 };
index d9ec77959dff1b01b9be38825e344a4a78212d31..34931fe7d2c527ccc8ac6b7db926e1504c2aac70 100644 (file)
@@ -40,6 +40,7 @@
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
 #include "ast_drv.h"
 
 static void ast_dirty_update(struct ast_fbdev *afbdev,
@@ -145,9 +146,10 @@ static int astfb_create_object(struct ast_fbdev *afbdev,
        return ret;
 }
 
-static int astfb_create(struct ast_fbdev *afbdev,
+static int astfb_create(struct drm_fb_helper *helper,
                        struct drm_fb_helper_surface_size *sizes)
 {
+       struct ast_fbdev *afbdev = (struct ast_fbdev *)helper;
        struct drm_device *dev = afbdev->helper.dev;
        struct drm_mode_fb_cmd2 mode_cmd;
        struct drm_framebuffer *fb;
@@ -248,26 +250,10 @@ static void ast_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
        *blue = ast_crtc->lut_b[regno] << 8;
 }
 
-static int ast_find_or_create_single(struct drm_fb_helper *helper,
-                                         struct drm_fb_helper_surface_size *sizes)
-{
-       struct ast_fbdev *afbdev = (struct ast_fbdev *)helper;
-       int new_fb = 0;
-       int ret;
-
-       if (!helper->fb) {
-               ret = astfb_create(afbdev, sizes);
-               if (ret)
-                       return ret;
-               new_fb = 1;
-       }
-       return new_fb;
-}
-
 static struct drm_fb_helper_funcs ast_fb_helper_funcs = {
        .gamma_set = ast_fb_gamma_set,
        .gamma_get = ast_fb_gamma_get,
-       .fb_probe = ast_find_or_create_single,
+       .fb_probe = astfb_create,
 };
 
 static void ast_fbdev_destroy(struct drm_device *dev,
@@ -290,6 +276,7 @@ static void ast_fbdev_destroy(struct drm_device *dev,
        drm_fb_helper_fini(&afbdev->helper);
 
        vfree(afbdev->sysram);
+       drm_framebuffer_unregister_private(&afb->base);
        drm_framebuffer_cleanup(&afb->base);
 }
 
@@ -313,6 +300,10 @@ int ast_fbdev_init(struct drm_device *dev)
        }
 
        drm_fb_helper_single_add_all_connectors(&afbdev->helper);
+
+       /* disable all the possible outputs/crtcs before entering KMS mode */
+       drm_helper_disable_unused_functions(dev);
+
        drm_fb_helper_initial_config(&afbdev->helper, 32);
        return 0;
 }
index f668e6cc0f7a54ecd0640fce21512428fb6efdb8..f60fd7bd11839f0511bf2a436f1ebc79330f27c4 100644 (file)
@@ -246,16 +246,8 @@ static void ast_user_framebuffer_destroy(struct drm_framebuffer *fb)
        kfree(fb);
 }
 
-static int ast_user_framebuffer_create_handle(struct drm_framebuffer *fb,
-                                             struct drm_file *file,
-                                             unsigned int *handle)
-{
-       return -EINVAL;
-}
-
 static const struct drm_framebuffer_funcs ast_fb_funcs = {
        .destroy = ast_user_framebuffer_destroy,
-       .create_handle = ast_user_framebuffer_create_handle,
 };
 
 
@@ -266,13 +258,13 @@ int ast_framebuffer_init(struct drm_device *dev,
 {
        int ret;
 
+       drm_helper_mode_fill_fb_struct(&ast_fb->base, mode_cmd);
+       ast_fb->obj = obj;
        ret = drm_framebuffer_init(dev, &ast_fb->base, &ast_fb_funcs);
        if (ret) {
                DRM_ERROR("framebuffer init failed %d\n", ret);
                return ret;
        }
-       drm_helper_mode_fill_fb_struct(&ast_fb->base, mode_cmd);
-       ast_fb->obj = obj;
        return 0;
 }
 
index 6c6b4c87d309ca03d9372aff618e2f6cf7e923a7..e25afccaf85bd62feb4aae4d5dfab42d49c9ed4d 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/module.h>
 #include <drm/drmP.h>
 #include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
 
 #include <linux/fb.h>
 
@@ -120,9 +121,10 @@ static int cirrusfb_create_object(struct cirrus_fbdev *afbdev,
        return ret;
 }
 
-static int cirrusfb_create(struct cirrus_fbdev *gfbdev,
+static int cirrusfb_create(struct drm_fb_helper *helper,
                           struct drm_fb_helper_surface_size *sizes)
 {
+       struct cirrus_fbdev *gfbdev = (struct cirrus_fbdev *)helper;
        struct drm_device *dev = gfbdev->helper.dev;
        struct cirrus_device *cdev = gfbdev->helper.dev->dev_private;
        struct fb_info *info;
@@ -219,23 +221,6 @@ out_iounmap:
        return ret;
 }
 
-static int cirrus_fb_find_or_create_single(struct drm_fb_helper *helper,
-                                          struct drm_fb_helper_surface_size
-                                          *sizes)
-{
-       struct cirrus_fbdev *gfbdev = (struct cirrus_fbdev *)helper;
-       int new_fb = 0;
-       int ret;
-
-       if (!helper->fb) {
-               ret = cirrusfb_create(gfbdev, sizes);
-               if (ret)
-                       return ret;
-               new_fb = 1;
-       }
-       return new_fb;
-}
-
 static int cirrus_fbdev_destroy(struct drm_device *dev,
                                struct cirrus_fbdev *gfbdev)
 {
@@ -258,6 +243,7 @@ static int cirrus_fbdev_destroy(struct drm_device *dev,
 
        vfree(gfbdev->sysram);
        drm_fb_helper_fini(&gfbdev->helper);
+       drm_framebuffer_unregister_private(&gfb->base);
        drm_framebuffer_cleanup(&gfb->base);
 
        return 0;
@@ -266,7 +252,7 @@ static int cirrus_fbdev_destroy(struct drm_device *dev,
 static struct drm_fb_helper_funcs cirrus_fb_helper_funcs = {
        .gamma_set = cirrus_crtc_fb_gamma_set,
        .gamma_get = cirrus_crtc_fb_gamma_get,
-       .fb_probe = cirrus_fb_find_or_create_single,
+       .fb_probe = cirrusfb_create,
 };
 
 int cirrus_fbdev_init(struct cirrus_device *cdev)
@@ -290,6 +276,9 @@ int cirrus_fbdev_init(struct cirrus_device *cdev)
                return ret;
        }
        drm_fb_helper_single_add_all_connectors(&gfbdev->helper);
+
+       /* disable all the possible outputs/crtcs before entering KMS mode */
+       drm_helper_disable_unused_functions(cdev->dev);
        drm_fb_helper_initial_config(&gfbdev->helper, bpp_sel);
 
        return 0;
index 6a9b12e88d467946336a8582c61c067482b3f656..35cbae8277715ad2944a00dcce1c39e4482109b6 100644 (file)
@@ -23,16 +23,8 @@ static void cirrus_user_framebuffer_destroy(struct drm_framebuffer *fb)
        kfree(fb);
 }
 
-static int cirrus_user_framebuffer_create_handle(struct drm_framebuffer *fb,
-                                                struct drm_file *file_priv,
-                                                unsigned int *handle)
-{
-       return 0;
-}
-
 static const struct drm_framebuffer_funcs cirrus_fb_funcs = {
        .destroy = cirrus_user_framebuffer_destroy,
-       .create_handle = cirrus_user_framebuffer_create_handle,
 };
 
 int cirrus_framebuffer_init(struct drm_device *dev,
@@ -42,13 +34,13 @@ int cirrus_framebuffer_init(struct drm_device *dev,
 {
        int ret;
 
+       drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd);
+       gfb->obj = obj;
        ret = drm_framebuffer_init(dev, &gfb->base, &cirrus_fb_funcs);
        if (ret) {
                DRM_ERROR("drm_framebuffer_init failed: %d\n", ret);
                return ret;
        }
-       drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd);
-       gfb->obj = obj;
        return 0;
 }
 
index f2d667b8bee2be1134eeff0c731fab56eb39e082..781aef524b3bd7f850341ca98f218f67e35d2df9 100644 (file)
 #include <drm/drm_edid.h>
 #include <drm/drm_fourcc.h>
 
+/**
+ * drm_modeset_lock_all - take all modeset locks
+ * @dev: drm device
+ *
+ * This function takes all modeset locks, suitable where a more fine-grained
+ * scheme isn't (yet) implemented.
+ */
+void drm_modeset_lock_all(struct drm_device *dev)
+{
+       struct drm_crtc *crtc;
+
+       mutex_lock(&dev->mode_config.mutex);
+
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+               mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex);
+}
+EXPORT_SYMBOL(drm_modeset_lock_all);
+
+/**
+ * drm_modeset_unlock_all - drop all modeset locks
+ * @dev: device
+ */
+void drm_modeset_unlock_all(struct drm_device *dev)
+{
+       struct drm_crtc *crtc;
+
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+               mutex_unlock(&crtc->mutex);
+
+       mutex_unlock(&dev->mode_config.mutex);
+}
+EXPORT_SYMBOL(drm_modeset_unlock_all);
+
+/**
+ * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked
+ * @dev: device
+ */
+void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
+{
+       struct drm_crtc *crtc;
+
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+               WARN_ON(!mutex_is_locked(&crtc->mutex));
+
+       WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+}
+EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
+
 /* Avoid boilerplate.  I'm tired of typing. */
 #define DRM_ENUM_NAME_FN(fnname, list)                         \
        char *fnname(int val)                                   \
@@ -203,12 +251,10 @@ char *drm_get_connector_status_name(enum drm_connector_status status)
 }
 
 /**
- * drm_mode_object_get - allocate a new identifier
+ * drm_mode_object_get - allocate a new modeset identifier
  * @dev: DRM device
- * @ptr: object pointer, used to generate unique ID
- * @type: object type
- *
- * LOCKING:
+ * @obj: object pointer, used to generate unique ID
+ * @obj_type: object type
  *
  * Create a unique identifier based on @ptr in @dev's identifier space.  Used
  * for tracking modes, CRTCs and connectors.
@@ -231,24 +277,27 @@ again:
 
        mutex_lock(&dev->mode_config.idr_mutex);
        ret = idr_get_new_above(&dev->mode_config.crtc_idr, obj, 1, &new_id);
+
+       if (!ret) {
+               /*
+                * Set up the object linking under the protection of the idr
+                * lock so that other users can't see inconsistent state.
+                */
+               obj->id = new_id;
+               obj->type = obj_type;
+       }
        mutex_unlock(&dev->mode_config.idr_mutex);
+
        if (ret == -EAGAIN)
                goto again;
-       else if (ret)
-               return ret;
 
-       obj->id = new_id;
-       obj->type = obj_type;
-       return 0;
+       return ret;
 }
 
 /**
- * drm_mode_object_put - free an identifer
+ * drm_mode_object_put - free a modeset identifer
  * @dev: DRM device
- * @id: ID to free
- *
- * LOCKING:
- * Caller must hold DRM mode_config lock.
+ * @object: object to free
  *
  * Free @id from @dev's unique identifier pool.
  */
@@ -260,11 +309,24 @@ static void drm_mode_object_put(struct drm_device *dev,
        mutex_unlock(&dev->mode_config.idr_mutex);
 }
 
+/**
+ * drm_mode_object_find - look up a drm object with static lifetime
+ * @dev: drm device
+ * @id: id of the mode object
+ * @type: type of the mode object
+ *
+ * Note that framebuffers cannot be looked up with this functions - since those
+ * are reference counted, they need special treatment.
+ */
 struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
                uint32_t id, uint32_t type)
 {
        struct drm_mode_object *obj = NULL;
 
+       /* Framebuffers are reference counted and need their own lookup
+        * function.*/
+       WARN_ON(type == DRM_MODE_OBJECT_FB);
+
        mutex_lock(&dev->mode_config.idr_mutex);
        obj = idr_find(&dev->mode_config.crtc_idr, id);
        if (!obj || (obj->type != type) || (obj->id != id))
@@ -278,13 +340,18 @@ EXPORT_SYMBOL(drm_mode_object_find);
 /**
  * drm_framebuffer_init - initialize a framebuffer
  * @dev: DRM device
- *
- * LOCKING:
- * Caller must hold mode config lock.
+ * @fb: framebuffer to be initialized
+ * @funcs: ... with these functions
  *
  * Allocates an ID for the framebuffer's parent mode object, sets its mode
  * functions & device file and adds it to the master fd list.
  *
+ * IMPORTANT:
+ * This functions publishes the fb and makes it available for concurrent access
+ * by other users. Which means by this point the fb _must_ be fully set up -
+ * since all the fb attributes are invariant over its lifetime, no further
+ * locking but only correct reference counting is required.
+ *
  * RETURNS:
  * Zero on success, error code on failure.
  */
@@ -293,16 +360,23 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
 {
        int ret;
 
+       mutex_lock(&dev->mode_config.fb_lock);
        kref_init(&fb->refcount);
+       INIT_LIST_HEAD(&fb->filp_head);
+       fb->dev = dev;
+       fb->funcs = funcs;
 
        ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB);
        if (ret)
-               return ret;
+               goto out;
+
+       /* Grab the idr reference. */
+       drm_framebuffer_reference(fb);
 
-       fb->dev = dev;
-       fb->funcs = funcs;
        dev->mode_config.num_fb++;
        list_add(&fb->head, &dev->mode_config.fb_list);
+out:
+       mutex_unlock(&dev->mode_config.fb_lock);
 
        return 0;
 }
@@ -315,23 +389,63 @@ static void drm_framebuffer_free(struct kref *kref)
        fb->funcs->destroy(fb);
 }
 
+static struct drm_framebuffer *__drm_framebuffer_lookup(struct drm_device *dev,
+                                                       uint32_t id)
+{
+       struct drm_mode_object *obj = NULL;
+       struct drm_framebuffer *fb;
+
+       mutex_lock(&dev->mode_config.idr_mutex);
+       obj = idr_find(&dev->mode_config.crtc_idr, id);
+       if (!obj || (obj->type != DRM_MODE_OBJECT_FB) || (obj->id != id))
+               fb = NULL;
+       else
+               fb = obj_to_fb(obj);
+       mutex_unlock(&dev->mode_config.idr_mutex);
+
+       return fb;
+}
+
+/**
+ * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference
+ * @dev: drm device
+ * @id: id of the fb object
+ *
+ * If successful, this grabs an additional reference to the framebuffer -
+ * callers need to make sure to eventually unreference the returned framebuffer
+ * again.
+ */
+struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
+                                              uint32_t id)
+{
+       struct drm_framebuffer *fb;
+
+       mutex_lock(&dev->mode_config.fb_lock);
+       fb = __drm_framebuffer_lookup(dev, id);
+       if (fb)
+               kref_get(&fb->refcount);
+       mutex_unlock(&dev->mode_config.fb_lock);
+
+       return fb;
+}
+EXPORT_SYMBOL(drm_framebuffer_lookup);
+
 /**
  * drm_framebuffer_unreference - unref a framebuffer
+ * @fb: framebuffer to unref
  *
- * LOCKING:
- * Caller must hold mode config lock.
+ * This functions decrements the fb's refcount and frees it if it drops to zero.
  */
 void drm_framebuffer_unreference(struct drm_framebuffer *fb)
 {
-       struct drm_device *dev = fb->dev;
        DRM_DEBUG("FB ID: %d\n", fb->base.id);
-       WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
        kref_put(&fb->refcount, drm_framebuffer_free);
 }
 EXPORT_SYMBOL(drm_framebuffer_unreference);
 
 /**
  * drm_framebuffer_reference - incr the fb refcnt
+ * @fb: framebuffer
  */
 void drm_framebuffer_reference(struct drm_framebuffer *fb)
 {
@@ -340,29 +454,74 @@ void drm_framebuffer_reference(struct drm_framebuffer *fb)
 }
 EXPORT_SYMBOL(drm_framebuffer_reference);
 
+static void drm_framebuffer_free_bug(struct kref *kref)
+{
+       BUG();
+}
+
+static void __drm_framebuffer_unreference(struct drm_framebuffer *fb)
+{
+       DRM_DEBUG("FB ID: %d\n", fb->base.id);
+       kref_put(&fb->refcount, drm_framebuffer_free_bug);
+}
+
+/* dev->mode_config.fb_lock must be held! */
+static void __drm_framebuffer_unregister(struct drm_device *dev,
+                                        struct drm_framebuffer *fb)
+{
+       mutex_lock(&dev->mode_config.idr_mutex);
+       idr_remove(&dev->mode_config.crtc_idr, fb->base.id);
+       mutex_unlock(&dev->mode_config.idr_mutex);
+
+       fb->base.id = 0;
+
+       __drm_framebuffer_unreference(fb);
+}
+
+/**
+ * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr
+ * @fb: fb to unregister
+ *
+ * Drivers need to call this when cleaning up driver-private framebuffers, e.g.
+ * those used for fbdev. Note that the caller must hold a reference of it's own,
+ * i.e. the object may not be destroyed through this call (since it'll lead to a
+ * locking inversion).
+ */
+void drm_framebuffer_unregister_private(struct drm_framebuffer *fb)
+{
+       struct drm_device *dev = fb->dev;
+
+       mutex_lock(&dev->mode_config.fb_lock);
+       /* Mark fb as reaped and drop idr ref. */
+       __drm_framebuffer_unregister(dev, fb);
+       mutex_unlock(&dev->mode_config.fb_lock);
+}
+EXPORT_SYMBOL(drm_framebuffer_unregister_private);
+
 /**
  * drm_framebuffer_cleanup - remove a framebuffer object
  * @fb: framebuffer to remove
  *
- * LOCKING:
- * Caller must hold mode config lock.
+ * Cleanup references to a user-created framebuffer. This function is intended
+ * to be used from the drivers ->destroy callback.
+ *
+ * Note that this function does not remove the fb from active usuage - if it is
+ * still used anywhere, hilarity can ensue since userspace could call getfb on
+ * the id and get back -EINVAL. Obviously no concern at driver unload time.
  *
- * Scans all the CRTCs in @dev's mode_config.  If they're using @fb, removes
- * it, setting it to NULL.
+ * Also, the framebuffer will not be removed from the lookup idr - for
+ * user-created framebuffers this will happen in in the rmfb ioctl. For
+ * driver-private objects (e.g. for fbdev) drivers need to explicitly call
+ * drm_framebuffer_unregister_private.
  */
 void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
 {
        struct drm_device *dev = fb->dev;
-       /*
-        * This could be moved to drm_framebuffer_remove(), but for
-        * debugging is nice to keep around the list of fb's that are
-        * no longer associated w/ a drm_file but are not unreferenced
-        * yet.  (i915 and omapdrm have debugfs files which will show
-        * this.)
-        */
-       drm_mode_object_put(dev, &fb->base);
+
+       mutex_lock(&dev->mode_config.fb_lock);
        list_del(&fb->head);
        dev->mode_config.num_fb--;
+       mutex_unlock(&dev->mode_config.fb_lock);
 }
 EXPORT_SYMBOL(drm_framebuffer_cleanup);
 
@@ -370,11 +529,13 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup);
  * drm_framebuffer_remove - remove and unreference a framebuffer object
  * @fb: framebuffer to remove
  *
- * LOCKING:
- * Caller must hold mode config lock.
- *
  * Scans all the CRTCs and planes in @dev's mode_config.  If they're
- * using @fb, removes it, setting it to NULL.
+ * using @fb, removes it, setting it to NULL. Then drops the reference to the
+ * passed-in framebuffer. Might take the modeset locks.
+ *
+ * Note that this function optimizes the cleanup away if the caller holds the
+ * last reference to the framebuffer. It is also guaranteed to not take the
+ * modeset locks in this case.
  */
 void drm_framebuffer_remove(struct drm_framebuffer *fb)
 {
@@ -384,33 +545,53 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
        struct drm_mode_set set;
        int ret;
 
-       /* remove from any CRTC */
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-               if (crtc->fb == fb) {
-                       /* should turn off the crtc */
-                       memset(&set, 0, sizeof(struct drm_mode_set));
-                       set.crtc = crtc;
-                       set.fb = NULL;
-                       ret = crtc->funcs->set_config(&set);
-                       if (ret)
-                               DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc);
+       WARN_ON(!list_empty(&fb->filp_head));
+
+       /*
+        * drm ABI mandates that we remove any deleted framebuffers from active
+        * useage. But since most sane clients only remove framebuffers they no
+        * longer need, try to optimize this away.
+        *
+        * Since we're holding a reference ourselves, observing a refcount of 1
+        * means that we're the last holder and can skip it. Also, the refcount
+        * can never increase from 1 again, so we don't need any barriers or
+        * locks.
+        *
+        * Note that userspace could try to race with use and instate a new
+        * usage _after_ we've cleared all current ones. End result will be an
+        * in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot
+        * in this manner.
+        */
+       if (atomic_read(&fb->refcount.refcount) > 1) {
+               drm_modeset_lock_all(dev);
+               /* remove from any CRTC */
+               list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+                       if (crtc->fb == fb) {
+                               /* should turn off the crtc */
+                               memset(&set, 0, sizeof(struct drm_mode_set));
+                               set.crtc = crtc;
+                               set.fb = NULL;
+                               ret = drm_mode_set_config_internal(&set);
+                               if (ret)
+                                       DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc);
+                       }
                }
-       }
 
-       list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
-               if (plane->fb == fb) {
-                       /* should turn off the crtc */
-                       ret = plane->funcs->disable_plane(plane);
-                       if (ret)
-                               DRM_ERROR("failed to disable plane with busy fb\n");
-                       /* disconnect the plane from the fb and crtc: */
-                       plane->fb = NULL;
-                       plane->crtc = NULL;
+               list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+                       if (plane->fb == fb) {
+                               /* should turn off the crtc */
+                               ret = plane->funcs->disable_plane(plane);
+                               if (ret)
+                                       DRM_ERROR("failed to disable plane with busy fb\n");
+                               /* disconnect the plane from the fb and crtc: */
+                               __drm_framebuffer_unreference(plane->fb);
+                               plane->fb = NULL;
+                               plane->crtc = NULL;
+                       }
                }
+               drm_modeset_unlock_all(dev);
        }
 
-       list_del(&fb->filp_head);
-
        drm_framebuffer_unreference(fb);
 }
 EXPORT_SYMBOL(drm_framebuffer_remove);
@@ -421,9 +602,6 @@ EXPORT_SYMBOL(drm_framebuffer_remove);
  * @crtc: CRTC object to init
  * @funcs: callbacks for the new CRTC
  *
- * LOCKING:
- * Takes mode_config lock.
- *
  * Inits a new object created as base part of an driver crtc object.
  *
  * RETURNS:
@@ -438,7 +616,9 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
        crtc->funcs = funcs;
        crtc->invert_dimensions = false;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
+       mutex_init(&crtc->mutex);
+       mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex);
 
        ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
        if (ret)
@@ -450,7 +630,7 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
        dev->mode_config.num_crtc++;
 
  out:
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
 
        return ret;
 }
@@ -460,9 +640,6 @@ EXPORT_SYMBOL(drm_crtc_init);
  * drm_crtc_cleanup - Cleans up the core crtc usage.
  * @crtc: CRTC to cleanup
  *
- * LOCKING:
- * Caller must hold mode config lock.
- *
  * Cleanup @crtc. Removes from drm modesetting space
  * does NOT free object, caller does that.
  */
@@ -484,9 +661,6 @@ EXPORT_SYMBOL(drm_crtc_cleanup);
  * @connector: connector the new mode
  * @mode: mode data
  *
- * LOCKING:
- * Caller must hold mode config lock.
- *
  * Add @mode to @connector's mode list for later use.
  */
 void drm_mode_probed_add(struct drm_connector *connector,
@@ -501,9 +675,6 @@ EXPORT_SYMBOL(drm_mode_probed_add);
  * @connector: connector list to modify
  * @mode: mode to remove
  *
- * LOCKING:
- * Caller must hold mode config lock.
- *
  * Remove @mode from @connector's mode list, then free it.
  */
 void drm_mode_remove(struct drm_connector *connector,
@@ -519,10 +690,7 @@ EXPORT_SYMBOL(drm_mode_remove);
  * @dev: DRM device
  * @connector: the connector to init
  * @funcs: callbacks for this connector
- * @name: user visible name of the connector
- *
- * LOCKING:
- * Takes mode config lock.
+ * @connector_type: user visible type of the connector
  *
  * Initialises a preallocated connector. Connectors should be
  * subclassed as part of driver connector objects.
@@ -537,7 +705,7 @@ int drm_connector_init(struct drm_device *dev,
 {
        int ret;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
 
        ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR);
        if (ret)
@@ -567,7 +735,7 @@ int drm_connector_init(struct drm_device *dev,
                                      dev->mode_config.dpms_property, 0);
 
  out:
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
 
        return ret;
 }
@@ -577,9 +745,6 @@ EXPORT_SYMBOL(drm_connector_init);
  * drm_connector_cleanup - cleans up an initialised connector
  * @connector: connector to cleanup
  *
- * LOCKING:
- * Takes mode config lock.
- *
  * Cleans up the connector but doesn't free the object.
  */
 void drm_connector_cleanup(struct drm_connector *connector)
@@ -596,11 +761,9 @@ void drm_connector_cleanup(struct drm_connector *connector)
        list_for_each_entry_safe(mode, t, &connector->user_modes, head)
                drm_mode_remove(connector, mode);
 
-       mutex_lock(&dev->mode_config.mutex);
        drm_mode_object_put(dev, &connector->base);
        list_del(&connector->head);
        dev->mode_config.num_connector--;
-       mutex_unlock(&dev->mode_config.mutex);
 }
 EXPORT_SYMBOL(drm_connector_cleanup);
 
@@ -622,7 +785,7 @@ int drm_encoder_init(struct drm_device *dev,
 {
        int ret;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
 
        ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
        if (ret)
@@ -636,7 +799,7 @@ int drm_encoder_init(struct drm_device *dev,
        dev->mode_config.num_encoder++;
 
  out:
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
 
        return ret;
 }
@@ -645,11 +808,11 @@ EXPORT_SYMBOL(drm_encoder_init);
 void drm_encoder_cleanup(struct drm_encoder *encoder)
 {
        struct drm_device *dev = encoder->dev;
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
        drm_mode_object_put(dev, &encoder->base);
        list_del(&encoder->head);
        dev->mode_config.num_encoder--;
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
 }
 EXPORT_SYMBOL(drm_encoder_cleanup);
 
@@ -661,7 +824,7 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
 {
        int ret;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
 
        ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
        if (ret)
@@ -695,7 +858,7 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
        }
 
  out:
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
 
        return ret;
 }
@@ -705,7 +868,7 @@ void drm_plane_cleanup(struct drm_plane *plane)
 {
        struct drm_device *dev = plane->dev;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
        kfree(plane->format_types);
        drm_mode_object_put(dev, &plane->base);
        /* if not added to a list, it must be a private plane */
@@ -713,7 +876,7 @@ void drm_plane_cleanup(struct drm_plane *plane)
                list_del(&plane->head);
                dev->mode_config.num_plane--;
        }
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
 }
 EXPORT_SYMBOL(drm_plane_cleanup);
 
@@ -721,9 +884,6 @@ EXPORT_SYMBOL(drm_plane_cleanup);
  * drm_mode_create - create a new display mode
  * @dev: DRM device
  *
- * LOCKING:
- * Caller must hold DRM mode_config lock.
- *
  * Create a new drm_display_mode, give it an ID, and return it.
  *
  * RETURNS:
@@ -751,9 +911,6 @@ EXPORT_SYMBOL(drm_mode_create);
  * @dev: DRM device
  * @mode: mode to remove
  *
- * LOCKING:
- * Caller must hold mode config lock.
- *
  * Free @mode's unique identifier, then free it.
  */
 void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode)
@@ -978,16 +1135,19 @@ EXPORT_SYMBOL(drm_mode_create_dirty_info_property);
  * drm_mode_config_init - initialize DRM mode_configuration structure
  * @dev: DRM device
  *
- * LOCKING:
- * None, should happen single threaded at init time.
- *
  * Initialize @dev's mode_config structure, used for tracking the graphics
  * configuration of @dev.
+ *
+ * Since this initializes the modeset locks, no locking is possible. Which is no
+ * problem, since this should happen single threaded at init time. It is the
+ * driver's problem to ensure this guarantee.
+ *
  */
 void drm_mode_config_init(struct drm_device *dev)
 {
        mutex_init(&dev->mode_config.mutex);
        mutex_init(&dev->mode_config.idr_mutex);
+       mutex_init(&dev->mode_config.fb_lock);
        INIT_LIST_HEAD(&dev->mode_config.fb_list);
        INIT_LIST_HEAD(&dev->mode_config.crtc_list);
        INIT_LIST_HEAD(&dev->mode_config.connector_list);
@@ -997,9 +1157,9 @@ void drm_mode_config_init(struct drm_device *dev)
        INIT_LIST_HEAD(&dev->mode_config.plane_list);
        idr_init(&dev->mode_config.crtc_idr);
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
        drm_mode_create_standard_connector_properties(dev);
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
 
        /* Just to be sure */
        dev->mode_config.num_fb = 0;
@@ -1057,12 +1217,13 @@ EXPORT_SYMBOL(drm_mode_group_init_legacy_group);
  * drm_mode_config_cleanup - free up DRM mode_config info
  * @dev: DRM device
  *
- * LOCKING:
- * Caller must hold mode config lock.
- *
  * Free up all the connectors and CRTCs associated with this DRM device, then
  * free up the framebuffers and associated buffer objects.
  *
+ * Note that since this /should/ happen single-threaded at driver/device
+ * teardown time, no locking is required. It's the driver's job to ensure that
+ * this guarantee actually holds true.
+ *
  * FIXME: cleanup any dangling user buffer objects too
  */
 void drm_mode_config_cleanup(struct drm_device *dev)
@@ -1089,6 +1250,15 @@ void drm_mode_config_cleanup(struct drm_device *dev)
                drm_property_destroy(dev, property);
        }
 
+       /*
+        * Single-threaded teardown context, so it's not required to grab the
+        * fb_lock to protect against concurrent fb_list access. Contrary, it
+        * would actually deadlock with the drm_framebuffer_cleanup function.
+        *
+        * Also, if there are any framebuffers left, that's a driver leak now,
+        * so politely WARN about this.
+        */
+       WARN_ON(!list_empty(&dev->mode_config.fb_list));
        list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) {
                drm_framebuffer_remove(fb);
        }
@@ -1112,9 +1282,6 @@ EXPORT_SYMBOL(drm_mode_config_cleanup);
  * @out: drm_mode_modeinfo struct to return to the user
  * @in: drm_display_mode to use
  *
- * LOCKING:
- * None.
- *
  * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to
  * the user.
  */
@@ -1151,9 +1318,6 @@ static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
  * @out: drm_display_mode to return to the user
  * @in: drm_mode_modeinfo to use
  *
- * LOCKING:
- * None.
- *
  * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to
  * the caller.
  *
@@ -1188,13 +1352,9 @@ static int drm_crtc_convert_umode(struct drm_display_mode *out,
 
 /**
  * drm_mode_getresources - get graphics configuration
- * @inode: inode from the ioctl
- * @filp: file * from the ioctl
- * @cmd: cmd from ioctl
- * @arg: arg from ioctl
- *
- * LOCKING:
- * Takes mode config lock.
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
  *
  * Construct a set of configuration description structures and return
  * them to the user, including CRTC, connector and framebuffer configuration.
@@ -1228,8 +1388,8 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
 
-       mutex_lock(&dev->mode_config.mutex);
 
+       mutex_lock(&file_priv->fbs_lock);
        /*
         * For the non-control nodes we need to limit the list of resources
         * by IDs in the group list for this node
@@ -1237,6 +1397,23 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
        list_for_each(lh, &file_priv->fbs)
                fb_count++;
 
+       /* handle this in 4 parts */
+       /* FBs */
+       if (card_res->count_fbs >= fb_count) {
+               copied = 0;
+               fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr;
+               list_for_each_entry(fb, &file_priv->fbs, filp_head) {
+                       if (put_user(fb->base.id, fb_id + copied)) {
+                               mutex_unlock(&file_priv->fbs_lock);
+                               return -EFAULT;
+                       }
+                       copied++;
+               }
+       }
+       card_res->count_fbs = fb_count;
+       mutex_unlock(&file_priv->fbs_lock);
+
+       drm_modeset_lock_all(dev);
        mode_group = &file_priv->master->minor->mode_group;
        if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
 
@@ -1260,21 +1437,6 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
        card_res->max_width = dev->mode_config.max_width;
        card_res->min_width = dev->mode_config.min_width;
 
-       /* handle this in 4 parts */
-       /* FBs */
-       if (card_res->count_fbs >= fb_count) {
-               copied = 0;
-               fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr;
-               list_for_each_entry(fb, &file_priv->fbs, filp_head) {
-                       if (put_user(fb->base.id, fb_id + copied)) {
-                               ret = -EFAULT;
-                               goto out;
-                       }
-                       copied++;
-               }
-       }
-       card_res->count_fbs = fb_count;
-
        /* CRTCs */
        if (card_res->count_crtcs >= crtc_count) {
                copied = 0;
@@ -1370,19 +1532,15 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
                  card_res->count_connectors, card_res->count_encoders);
 
 out:
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
        return ret;
 }
 
 /**
  * drm_mode_getcrtc - get CRTC configuration
- * @inode: inode from the ioctl
- * @filp: file * from the ioctl
- * @cmd: cmd from ioctl
- * @arg: arg from ioctl
- *
- * LOCKING:
- * Takes mode config lock.
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
  *
  * Construct a CRTC configuration structure to return to the user.
  *
@@ -1402,7 +1560,7 @@ int drm_mode_getcrtc(struct drm_device *dev,
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
 
        obj = drm_mode_object_find(dev, crtc_resp->crtc_id,
                                   DRM_MODE_OBJECT_CRTC);
@@ -1430,19 +1588,15 @@ int drm_mode_getcrtc(struct drm_device *dev,
        }
 
 out:
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
        return ret;
 }
 
 /**
  * drm_mode_getconnector - get connector configuration
- * @inode: inode from the ioctl
- * @filp: file * from the ioctl
- * @cmd: cmd from ioctl
- * @arg: arg from ioctl
- *
- * LOCKING:
- * Takes mode config lock.
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
  *
  * Construct a connector configuration structure to return to the user.
  *
@@ -1575,6 +1729,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
 
 out:
        mutex_unlock(&dev->mode_config.mutex);
+
        return ret;
 }
 
@@ -1589,7 +1744,7 @@ int drm_mode_getencoder(struct drm_device *dev, void *data,
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
        obj = drm_mode_object_find(dev, enc_resp->encoder_id,
                                   DRM_MODE_OBJECT_ENCODER);
        if (!obj) {
@@ -1608,7 +1763,7 @@ int drm_mode_getencoder(struct drm_device *dev, void *data,
        enc_resp->possible_clones = encoder->possible_clones;
 
 out:
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
        return ret;
 }
 
@@ -1618,9 +1773,6 @@ out:
  * @data: ioctl data
  * @file_priv: DRM file info
  *
- * LOCKING:
- * Takes mode config lock.
- *
  * Return an plane count and set of IDs.
  */
 int drm_mode_getplane_res(struct drm_device *dev, void *data,
@@ -1635,7 +1787,7 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data,
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
        config = &dev->mode_config;
 
        /*
@@ -1657,7 +1809,7 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data,
        plane_resp->count_planes = config->num_plane;
 
 out:
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
        return ret;
 }
 
@@ -1667,9 +1819,6 @@ out:
  * @data: ioctl data
  * @file_priv: DRM file info
  *
- * LOCKING:
- * Takes mode config lock.
- *
  * Return plane info, including formats supported, gamma size, any
  * current fb, etc.
  */
@@ -1685,7 +1834,7 @@ int drm_mode_getplane(struct drm_device *dev, void *data,
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
        obj = drm_mode_object_find(dev, plane_resp->plane_id,
                                   DRM_MODE_OBJECT_PLANE);
        if (!obj) {
@@ -1725,7 +1874,7 @@ int drm_mode_getplane(struct drm_device *dev, void *data,
        plane_resp->count_format_types = plane->format_count;
 
 out:
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
        return ret;
 }
 
@@ -1733,10 +1882,7 @@ out:
  * drm_mode_setplane - set up or tear down an plane
  * @dev: DRM device
  * @data: ioctl data*
- * @file_prive: DRM file info
- *
- * LOCKING:
- * Takes mode config lock.
+ * @file_priv: DRM file info
  *
  * Set plane info, including placement, fb, scaling, and other factors.
  * Or pass a NULL fb to disable.
@@ -1748,7 +1894,7 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
        struct drm_mode_object *obj;
        struct drm_plane *plane;
        struct drm_crtc *crtc;
-       struct drm_framebuffer *fb;
+       struct drm_framebuffer *fb = NULL, *old_fb = NULL;
        int ret = 0;
        unsigned int fb_width, fb_height;
        int i;
@@ -1756,8 +1902,6 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
 
-       mutex_lock(&dev->mode_config.mutex);
-
        /*
         * First, find the plane, crtc, and fb objects.  If not available,
         * we don't bother to call the driver.
@@ -1767,16 +1911,18 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
        if (!obj) {
                DRM_DEBUG_KMS("Unknown plane ID %d\n",
                              plane_req->plane_id);
-               ret = -ENOENT;
-               goto out;
+               return -ENOENT;
        }
        plane = obj_to_plane(obj);
 
        /* No fb means shut it down */
        if (!plane_req->fb_id) {
+               drm_modeset_lock_all(dev);
+               old_fb = plane->fb;
                plane->funcs->disable_plane(plane);
                plane->crtc = NULL;
                plane->fb = NULL;
+               drm_modeset_unlock_all(dev);
                goto out;
        }
 
@@ -1790,15 +1936,13 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
        }
        crtc = obj_to_crtc(obj);
 
-       obj = drm_mode_object_find(dev, plane_req->fb_id,
-                                  DRM_MODE_OBJECT_FB);
-       if (!obj) {
+       fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
+       if (!fb) {
                DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
                              plane_req->fb_id);
                ret = -ENOENT;
                goto out;
        }
-       fb = obj_to_fb(obj);
 
        /* Check whether this plane supports the fb pixel format. */
        for (i = 0; i < plane->format_count; i++)
@@ -1844,31 +1988,62 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
                goto out;
        }
 
+       drm_modeset_lock_all(dev);
        ret = plane->funcs->update_plane(plane, crtc, fb,
                                         plane_req->crtc_x, plane_req->crtc_y,
                                         plane_req->crtc_w, plane_req->crtc_h,
                                         plane_req->src_x, plane_req->src_y,
                                         plane_req->src_w, plane_req->src_h);
        if (!ret) {
+               old_fb = plane->fb;
                plane->crtc = crtc;
                plane->fb = fb;
+               fb = NULL;
        }
+       drm_modeset_unlock_all(dev);
 
 out:
-       mutex_unlock(&dev->mode_config.mutex);
+       if (fb)
+               drm_framebuffer_unreference(fb);
+       if (old_fb)
+               drm_framebuffer_unreference(old_fb);
 
        return ret;
 }
 
 /**
- * drm_mode_setcrtc - set CRTC configuration
- * @inode: inode from the ioctl
- * @filp: file * from the ioctl
- * @cmd: cmd from ioctl
- * @arg: arg from ioctl
+ * drm_mode_set_config_internal - helper to call ->set_config
+ * @set: modeset config to set
  *
- * LOCKING:
- * Takes mode config lock.
+ * This is a little helper to wrap internal calls to the ->set_config driver
+ * interface. The only thing it adds is correct refcounting dance.
+ */
+int drm_mode_set_config_internal(struct drm_mode_set *set)
+{
+       struct drm_crtc *crtc = set->crtc;
+       struct drm_framebuffer *fb, *old_fb;
+       int ret;
+
+       old_fb = crtc->fb;
+       fb = set->fb;
+
+       ret = crtc->funcs->set_config(set);
+       if (ret == 0) {
+               if (old_fb)
+                       drm_framebuffer_unreference(old_fb);
+               if (fb)
+                       drm_framebuffer_reference(fb);
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL(drm_mode_set_config_internal);
+
+/**
+ * drm_mode_setcrtc - set CRTC configuration
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
  *
  * Build a new CRTC configuration based on user request.
  *
@@ -1899,7 +2074,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
        if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX)
                return -ERANGE;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
        obj = drm_mode_object_find(dev, crtc_req->crtc_id,
                                   DRM_MODE_OBJECT_CRTC);
        if (!obj) {
@@ -1921,16 +2096,16 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
                                goto out;
                        }
                        fb = crtc->fb;
+                       /* Make refcounting symmetric with the lookup path. */
+                       drm_framebuffer_reference(fb);
                } else {
-                       obj = drm_mode_object_find(dev, crtc_req->fb_id,
-                                                  DRM_MODE_OBJECT_FB);
-                       if (!obj) {
+                       fb = drm_framebuffer_lookup(dev, crtc_req->fb_id);
+                       if (!fb) {
                                DRM_DEBUG_KMS("Unknown FB ID%d\n",
                                                crtc_req->fb_id);
                                ret = -EINVAL;
                                goto out;
                        }
-                       fb = obj_to_fb(obj);
                }
 
                mode = drm_mode_create(dev);
@@ -2027,12 +2202,15 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
        set.connectors = connector_set;
        set.num_connectors = crtc_req->count_connectors;
        set.fb = fb;
-       ret = crtc->funcs->set_config(&set);
+       ret = drm_mode_set_config_internal(&set);
 
 out:
+       if (fb)
+               drm_framebuffer_unreference(fb);
+
        kfree(connector_set);
        drm_mode_destroy(dev, mode);
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
        return ret;
 }
 
@@ -2050,15 +2228,14 @@ int drm_mode_cursor_ioctl(struct drm_device *dev,
        if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags))
                return -EINVAL;
 
-       mutex_lock(&dev->mode_config.mutex);
        obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC);
        if (!obj) {
                DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id);
-               ret = -EINVAL;
-               goto out;
+               return -EINVAL;
        }
        crtc = obj_to_crtc(obj);
 
+       mutex_lock(&crtc->mutex);
        if (req->flags & DRM_MODE_CURSOR_BO) {
                if (!crtc->funcs->cursor_set) {
                        ret = -ENXIO;
@@ -2078,7 +2255,8 @@ int drm_mode_cursor_ioctl(struct drm_device *dev,
                }
        }
 out:
-       mutex_unlock(&dev->mode_config.mutex);
+       mutex_unlock(&crtc->mutex);
+
        return ret;
 }
 
@@ -2089,7 +2267,7 @@ uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth)
 
        switch (bpp) {
        case 8:
-               fmt = DRM_FORMAT_RGB332;
+               fmt = DRM_FORMAT_C8;
                break;
        case 16:
                if (depth == 15)
@@ -2120,13 +2298,9 @@ EXPORT_SYMBOL(drm_mode_legacy_fb_format);
 
 /**
  * drm_mode_addfb - add an FB to the graphics configuration
- * @inode: inode from the ioctl
- * @filp: file * from the ioctl
- * @cmd: cmd from ioctl
- * @arg: arg from ioctl
- *
- * LOCKING:
- * Takes mode config lock.
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
  *
  * Add a new FB to the specified CRTC, given a user request.
  *
@@ -2161,24 +2335,19 @@ int drm_mode_addfb(struct drm_device *dev,
        if ((config->min_height > r.height) || (r.height > config->max_height))
                return -EINVAL;
 
-       mutex_lock(&dev->mode_config.mutex);
-
-       /* TODO check buffer is sufficiently large */
-       /* TODO setup destructor callback */
-
        fb = dev->mode_config.funcs->fb_create(dev, file_priv, &r);
        if (IS_ERR(fb)) {
                DRM_DEBUG_KMS("could not create framebuffer\n");
-               ret = PTR_ERR(fb);
-               goto out;
+               drm_modeset_unlock_all(dev);
+               return PTR_ERR(fb);
        }
 
+       mutex_lock(&file_priv->fbs_lock);
        or->fb_id = fb->base.id;
        list_add(&fb->filp_head, &file_priv->fbs);
        DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
+       mutex_unlock(&file_priv->fbs_lock);
 
-out:
-       mutex_unlock(&dev->mode_config.mutex);
        return ret;
 }
 
@@ -2304,13 +2473,9 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r)
 
 /**
  * drm_mode_addfb2 - add an FB to the graphics configuration
- * @inode: inode from the ioctl
- * @filp: file * from the ioctl
- * @cmd: cmd from ioctl
- * @arg: arg from ioctl
- *
- * LOCKING:
- * Takes mode config lock.
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
  *
  * Add a new FB to the specified CRTC, given a user request with format.
  *
@@ -2350,33 +2515,28 @@ int drm_mode_addfb2(struct drm_device *dev,
        if (ret)
                return ret;
 
-       mutex_lock(&dev->mode_config.mutex);
-
        fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);
        if (IS_ERR(fb)) {
                DRM_DEBUG_KMS("could not create framebuffer\n");
-               ret = PTR_ERR(fb);
-               goto out;
+               drm_modeset_unlock_all(dev);
+               return PTR_ERR(fb);
        }
 
+       mutex_lock(&file_priv->fbs_lock);
        r->fb_id = fb->base.id;
        list_add(&fb->filp_head, &file_priv->fbs);
        DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
+       mutex_unlock(&file_priv->fbs_lock);
+
 
-out:
-       mutex_unlock(&dev->mode_config.mutex);
        return ret;
 }
 
 /**
  * drm_mode_rmfb - remove an FB from the configuration
- * @inode: inode from the ioctl
- * @filp: file * from the ioctl
- * @cmd: cmd from ioctl
- * @arg: arg from ioctl
- *
- * LOCKING:
- * Takes mode config lock.
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
  *
  * Remove the FB specified by the user.
  *
@@ -2388,50 +2548,49 @@ out:
 int drm_mode_rmfb(struct drm_device *dev,
                   void *data, struct drm_file *file_priv)
 {
-       struct drm_mode_object *obj;
        struct drm_framebuffer *fb = NULL;
        struct drm_framebuffer *fbl = NULL;
        uint32_t *id = data;
-       int ret = 0;
        int found = 0;
 
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
 
-       mutex_lock(&dev->mode_config.mutex);
-       obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB);
-       /* TODO check that we really get a framebuffer back. */
-       if (!obj) {
-               ret = -EINVAL;
-               goto out;
-       }
-       fb = obj_to_fb(obj);
+       mutex_lock(&file_priv->fbs_lock);
+       mutex_lock(&dev->mode_config.fb_lock);
+       fb = __drm_framebuffer_lookup(dev, *id);
+       if (!fb)
+               goto fail_lookup;
 
        list_for_each_entry(fbl, &file_priv->fbs, filp_head)
                if (fb == fbl)
                        found = 1;
+       if (!found)
+               goto fail_lookup;
 
-       if (!found) {
-               ret = -EINVAL;
-               goto out;
-       }
+       /* Mark fb as reaped, we still have a ref from fpriv->fbs. */
+       __drm_framebuffer_unregister(dev, fb);
+
+       list_del_init(&fb->filp_head);
+       mutex_unlock(&dev->mode_config.fb_lock);
+       mutex_unlock(&file_priv->fbs_lock);
 
        drm_framebuffer_remove(fb);
 
-out:
-       mutex_unlock(&dev->mode_config.mutex);
-       return ret;
+       return 0;
+
+fail_lookup:
+       mutex_unlock(&dev->mode_config.fb_lock);
+       mutex_unlock(&file_priv->fbs_lock);
+
+       return -EINVAL;
 }
 
 /**
  * drm_mode_getfb - get FB info
- * @inode: inode from the ioctl
- * @filp: file * from the ioctl
- * @cmd: cmd from ioctl
- * @arg: arg from ioctl
- *
- * LOCKING:
- * Takes mode config lock.
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
  *
  * Lookup the FB given its ID and return info about it.
  *
@@ -2444,30 +2603,28 @@ int drm_mode_getfb(struct drm_device *dev,
                   void *data, struct drm_file *file_priv)
 {
        struct drm_mode_fb_cmd *r = data;
-       struct drm_mode_object *obj;
        struct drm_framebuffer *fb;
-       int ret = 0;
+       int ret;
 
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
 
-       mutex_lock(&dev->mode_config.mutex);
-       obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB);
-       if (!obj) {
-               ret = -EINVAL;
-               goto out;
-       }
-       fb = obj_to_fb(obj);
+       fb = drm_framebuffer_lookup(dev, r->fb_id);
+       if (!fb)
+               return -EINVAL;
 
        r->height = fb->height;
        r->width = fb->width;
        r->depth = fb->depth;
        r->bpp = fb->bits_per_pixel;
        r->pitch = fb->pitches[0];
-       fb->funcs->create_handle(fb, file_priv, &r->handle);
+       if (fb->funcs->create_handle)
+               ret = fb->funcs->create_handle(fb, file_priv, &r->handle);
+       else
+               ret = -ENODEV;
+
+       drm_framebuffer_unreference(fb);
 
-out:
-       mutex_unlock(&dev->mode_config.mutex);
        return ret;
 }
 
@@ -2477,7 +2634,6 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
        struct drm_clip_rect __user *clips_ptr;
        struct drm_clip_rect *clips = NULL;
        struct drm_mode_fb_dirty_cmd *r = data;
-       struct drm_mode_object *obj;
        struct drm_framebuffer *fb;
        unsigned flags;
        int num_clips;
@@ -2486,13 +2642,9 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
 
-       mutex_lock(&dev->mode_config.mutex);
-       obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB);
-       if (!obj) {
-               ret = -EINVAL;
-               goto out_err1;
-       }
-       fb = obj_to_fb(obj);
+       fb = drm_framebuffer_lookup(dev, r->fb_id);
+       if (!fb)
+               return -EINVAL;
 
        num_clips = r->num_clips;
        clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr;
@@ -2530,27 +2682,26 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
        }
 
        if (fb->funcs->dirty) {
+               drm_modeset_lock_all(dev);
                ret = fb->funcs->dirty(fb, file_priv, flags, r->color,
                                       clips, num_clips);
+               drm_modeset_unlock_all(dev);
        } else {
                ret = -ENOSYS;
-               goto out_err2;
        }
 
 out_err2:
        kfree(clips);
 out_err1:
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_framebuffer_unreference(fb);
+
        return ret;
 }
 
 
 /**
  * drm_fb_release - remove and free the FBs on this file
- * @filp: file * from the ioctl
- *
- * LOCKING:
- * Takes mode config lock.
+ * @priv: drm file for the ioctl
  *
  * Destroy all the FBs associated with @filp.
  *
@@ -2564,11 +2715,20 @@ void drm_fb_release(struct drm_file *priv)
        struct drm_device *dev = priv->minor->dev;
        struct drm_framebuffer *fb, *tfb;
 
-       mutex_lock(&dev->mode_config.mutex);
+       mutex_lock(&priv->fbs_lock);
        list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
+
+               mutex_lock(&dev->mode_config.fb_lock);
+               /* Mark fb as reaped, we still have a ref from fpriv->fbs. */
+               __drm_framebuffer_unregister(dev, fb);
+               mutex_unlock(&dev->mode_config.fb_lock);
+
+               list_del_init(&fb->filp_head);
+
+               /* This will also drop the fpriv->fbs reference. */
                drm_framebuffer_remove(fb);
        }
-       mutex_unlock(&dev->mode_config.mutex);
+       mutex_unlock(&priv->fbs_lock);
 }
 
 /**
@@ -2660,10 +2820,9 @@ EXPORT_SYMBOL(drm_mode_detachmode_crtc);
 
 /**
  * drm_fb_attachmode - Attach a user mode to an connector
- * @inode: inode from the ioctl
- * @filp: file * from the ioctl
- * @cmd: cmd from ioctl
- * @arg: arg from ioctl
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
  *
  * This attaches a user specified mode to an connector.
  * Called by the user via ioctl.
@@ -2684,7 +2843,7 @@ int drm_mode_attachmode_ioctl(struct drm_device *dev,
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
 
        obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR);
        if (!obj) {
@@ -2708,17 +2867,16 @@ int drm_mode_attachmode_ioctl(struct drm_device *dev,
 
        drm_mode_attachmode(dev, connector, mode);
 out:
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
        return ret;
 }
 
 
 /**
  * drm_fb_detachmode - Detach a user specified mode from an connector
- * @inode: inode from the ioctl
- * @filp: file * from the ioctl
- * @cmd: cmd from ioctl
- * @arg: arg from ioctl
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
  *
  * Called by the user via ioctl.
  *
@@ -2738,7 +2896,7 @@ int drm_mode_detachmode_ioctl(struct drm_device *dev,
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
 
        obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR);
        if (!obj) {
@@ -2755,7 +2913,7 @@ int drm_mode_detachmode_ioctl(struct drm_device *dev,
 
        ret = drm_mode_detachmode(dev, connector, &mode);
 out:
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
        return ret;
 }
 
@@ -3001,7 +3159,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
        obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY);
        if (!obj) {
                ret = -EINVAL;
@@ -3079,7 +3237,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
                out_resp->count_enum_blobs = blob_count;
        }
 done:
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
        return ret;
 }
 
@@ -3130,7 +3288,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev,
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
        obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB);
        if (!obj) {
                ret = -EINVAL;
@@ -3148,7 +3306,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev,
        out_resp->length = blob->length;
 
 done:
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
        return ret;
 }
 
@@ -3290,7 +3448,7 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
 
        obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
        if (!obj) {
@@ -3327,7 +3485,7 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
        }
        arg->count_props = props_count;
 out:
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
        return ret;
 }
 
@@ -3344,7 +3502,7 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
 
        arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
        if (!arg_obj)
@@ -3382,7 +3540,7 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
        }
 
 out:
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
        return ret;
 }
 
@@ -3444,7 +3602,7 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev,
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
        obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
        if (!obj) {
                ret = -EINVAL;
@@ -3485,7 +3643,7 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev,
        crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);
 
 out:
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
        return ret;
 
 }
@@ -3503,7 +3661,7 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev,
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
        obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
        if (!obj) {
                ret = -EINVAL;
@@ -3536,7 +3694,7 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev,
                goto out;
        }
 out:
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
        return ret;
 }
 
@@ -3546,7 +3704,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
        struct drm_mode_crtc_page_flip *page_flip = data;
        struct drm_mode_object *obj;
        struct drm_crtc *crtc;
-       struct drm_framebuffer *fb;
+       struct drm_framebuffer *fb = NULL, *old_fb = NULL;
        struct drm_pending_vblank_event *e = NULL;
        unsigned long flags;
        int hdisplay, vdisplay;
@@ -3556,12 +3714,12 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
            page_flip->reserved != 0)
                return -EINVAL;
 
-       mutex_lock(&dev->mode_config.mutex);
        obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC);
        if (!obj)
-               goto out;
+               return -EINVAL;
        crtc = obj_to_crtc(obj);
 
+       mutex_lock(&crtc->mutex);
        if (crtc->fb == NULL) {
                /* The framebuffer is currently unbound, presumably
                 * due to a hotplug event, that userspace has not
@@ -3574,10 +3732,9 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
        if (crtc->funcs->page_flip == NULL)
                goto out;
 
-       obj = drm_mode_object_find(dev, page_flip->fb_id, DRM_MODE_OBJECT_FB);
-       if (!obj)
+       fb = drm_framebuffer_lookup(dev, page_flip->fb_id);
+       if (!fb)
                goto out;
-       fb = obj_to_fb(obj);
 
        hdisplay = crtc->mode.hdisplay;
        vdisplay = crtc->mode.vdisplay;
@@ -3623,6 +3780,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
                        (void (*) (struct drm_pending_event *)) kfree;
        }
 
+       old_fb = crtc->fb;
        ret = crtc->funcs->page_flip(crtc, fb, e);
        if (ret) {
                if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
@@ -3631,10 +3789,20 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
                        spin_unlock_irqrestore(&dev->event_lock, flags);
                        kfree(e);
                }
+               /* Keep the old fb, don't unref it. */
+               old_fb = NULL;
+       } else {
+               /* Unref only the old framebuffer. */
+               fb = NULL;
        }
 
 out:
-       mutex_unlock(&dev->mode_config.mutex);
+       if (fb)
+               drm_framebuffer_unreference(fb);
+       if (old_fb)
+               drm_framebuffer_unreference(old_fb);
+       mutex_unlock(&crtc->mutex);
+
        return ret;
 }
 
@@ -3702,6 +3870,7 @@ void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth,
                          int *bpp)
 {
        switch (format) {
+       case DRM_FORMAT_C8:
        case DRM_FORMAT_RGB332:
        case DRM_FORMAT_BGR233:
                *depth = 8;
index 5a3770fbd770d63f2338fee03712917f80891146..67aa0dd68250a8ca330bc8f3005a9e924d79ff8d 100644 (file)
@@ -87,9 +87,6 @@ static struct edid_quirk {
        int product_id;
        u32 quirks;
 } edid_quirk_list[] = {
-       /* ASUS VW222S */
-       { "ACI", 0x22a2, EDID_QUIRK_FORCE_REDUCED_BLANKING },
-
        /* Acer AL1706 */
        { "ACR", 44358, EDID_QUIRK_PREFER_LARGE_60 },
        /* Acer F51 */
@@ -357,10 +354,14 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
                                break;
                        }
                }
-               if (i == 4)
+
+               if (i == 4 && print_bad_edid) {
                        dev_warn(connector->dev->dev,
                         "%s: Ignoring invalid EDID block %d.\n",
                         drm_get_connector_name(connector), j);
+
+                       connector->bad_edid_counter++;
+               }
        }
 
        if (valid_extensions != block[0x7e]) {
@@ -1483,9 +1484,11 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid,
 #define VIDEO_BLOCK     0x02
 #define VENDOR_BLOCK    0x03
 #define SPEAKER_BLOCK  0x04
+#define VIDEO_CAPABILITY_BLOCK 0x07
 #define EDID_BASIC_AUDIO       (1 << 6)
 #define EDID_CEA_YCRCB444      (1 << 5)
 #define EDID_CEA_YCRCB422      (1 << 4)
+#define EDID_CEA_VCDB_QS       (1 << 6)
 
 /**
  * Search EDID for CEA extension block.
@@ -1901,6 +1904,37 @@ end:
 }
 EXPORT_SYMBOL(drm_detect_monitor_audio);
 
+/**
+ * drm_rgb_quant_range_selectable - is RGB quantization range selectable?
+ *
+ * Check whether the monitor reports the RGB quantization range selection
+ * as supported. The AVI infoframe can then be used to inform the monitor
+ * which quantization range (full or limited) is used.
+ */
+bool drm_rgb_quant_range_selectable(struct edid *edid)
+{
+       u8 *edid_ext;
+       int i, start, end;
+
+       edid_ext = drm_find_cea_extension(edid);
+       if (!edid_ext)
+               return false;
+
+       if (cea_db_offsets(edid_ext, &start, &end))
+               return false;
+
+       for_each_cea_db(edid_ext, i, start, end) {
+               if (cea_db_tag(&edid_ext[i]) == VIDEO_CAPABILITY_BLOCK &&
+                   cea_db_payload_len(&edid_ext[i]) == 2) {
+                       DRM_DEBUG_KMS("CEA VCDB 0x%02x\n", edid_ext[i + 2]);
+                       return edid_ext[i + 2] & EDID_CEA_VCDB_QS;
+               }
+       }
+
+       return false;
+}
+EXPORT_SYMBOL(drm_rgb_quant_range_selectable);
+
 /**
  * drm_add_display_info - pull display info out if present
  * @edid: EDID data
@@ -2020,7 +2054,8 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
        num_modes += add_cvt_modes(connector, edid);
        num_modes += add_standard_modes(connector, edid);
        num_modes += add_established_modes(connector, edid);
-       num_modes += add_inferred_modes(connector, edid);
+       if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)
+               num_modes += add_inferred_modes(connector, edid);
        num_modes += add_cea_modes(connector, edid);
 
        if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
index fd9d0af4d5369bb7ebac13662a3d612188c87921..1c8549dae99a1da198a18e66eb9bb517cb0230eb 100644 (file)
@@ -85,6 +85,11 @@ static struct drm_fb_cma *drm_fb_cma_alloc(struct drm_device *dev,
        if (!fb_cma)
                return ERR_PTR(-ENOMEM);
 
+       drm_helper_mode_fill_fb_struct(&fb_cma->fb, mode_cmd);
+
+       for (i = 0; i < num_planes; i++)
+               fb_cma->obj[i] = obj[i];
+
        ret = drm_framebuffer_init(dev, &fb_cma->fb, &drm_fb_cma_funcs);
        if (ret) {
                dev_err(dev->dev, "Failed to initalize framebuffer: %d\n", ret);
@@ -92,11 +97,6 @@ static struct drm_fb_cma *drm_fb_cma_alloc(struct drm_device *dev,
                return ERR_PTR(ret);
        }
 
-       drm_helper_mode_fill_fb_struct(&fb_cma->fb, mode_cmd);
-
-       for (i = 0; i < num_planes; i++)
-               fb_cma->obj[i] = obj[i];
-
        return fb_cma;
 }
 
@@ -266,6 +266,7 @@ static int drm_fbdev_cma_create(struct drm_fb_helper *helper,
        return 0;
 
 err_drm_fb_cma_destroy:
+       drm_framebuffer_unregister_private(fb);
        drm_fb_cma_destroy(fb);
 err_framebuffer_release:
        framebuffer_release(fbi);
@@ -274,23 +275,8 @@ err_drm_gem_cma_free_object:
        return ret;
 }
 
-static int drm_fbdev_cma_probe(struct drm_fb_helper *helper,
-       struct drm_fb_helper_surface_size *sizes)
-{
-       int ret = 0;
-
-       if (!helper->fb) {
-               ret = drm_fbdev_cma_create(helper, sizes);
-               if (ret < 0)
-                       return ret;
-               ret = 1;
-       }
-
-       return ret;
-}
-
 static struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
-       .fb_probe = drm_fbdev_cma_probe,
+       .fb_probe = drm_fbdev_cma_create,
 };
 
 /**
@@ -332,6 +318,9 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
 
        }
 
+       /* disable all the possible outputs/crtcs before entering KMS mode */
+       drm_helper_disable_unused_functions(dev);
+
        ret = drm_fb_helper_initial_config(helper, preferred_bpp);
        if (ret < 0) {
                dev_err(dev->dev, "Failed to set inital hw configuration.\n");
@@ -370,8 +359,10 @@ void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma)
                framebuffer_release(info);
        }
 
-       if (fbdev_cma->fb)
+       if (fbdev_cma->fb) {
+               drm_framebuffer_unregister_private(&fbdev_cma->fb->fb);
                drm_fb_cma_destroy(&fbdev_cma->fb->fb);
+       }
 
        drm_fb_helper_fini(&fbdev_cma->fb_helper);
        kfree(fbdev_cma);
@@ -386,8 +377,13 @@ EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini);
  */
 void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma)
 {
-       if (fbdev_cma)
+       if (fbdev_cma) {
+               struct drm_device *dev = fbdev_cma->fb_helper.dev;
+
+               drm_modeset_lock_all(dev);
                drm_fb_helper_restore_fbdev_mode(&fbdev_cma->fb_helper);
+               drm_modeset_unlock_all(dev);
+       }
 }
 EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode);
 
index 954d175bd7fac65b62568c57a0203a55870cbe2e..59d6b9bf204bbd2d76b8440b3f7d435def394fe7 100644 (file)
@@ -52,9 +52,36 @@ static LIST_HEAD(kernel_fb_helper_list);
  * mode setting driver. They can be used mostly independantely from the crtc
  * helper functions used by many drivers to implement the kernel mode setting
  * interfaces.
+ *
+ * Initialization is done as a three-step process with drm_fb_helper_init(),
+ * drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config().
+ * Drivers with fancier requirements than the default beheviour can override the
+ * second step with their own code.  Teardown is done with drm_fb_helper_fini().
+ *
+ * At runtime drivers should restore the fbdev console by calling
+ * drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They
+ * should also notify the fb helper code from updates to the output
+ * configuration by calling drm_fb_helper_hotplug_event(). For easier
+ * integration with the output polling code in drm_crtc_helper.c the modeset
+ * code proves a ->output_poll_changed callback.
+ *
+ * All other functions exported by the fb helper library can be used to
+ * implement the fbdev driver interface by the driver.
  */
 
-/* simple single crtc case helper function */
+/**
+ * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev
+ *                                            emulation helper
+ * @fb_helper: fbdev initialized with drm_fb_helper_init
+ *
+ * This functions adds all the available connectors for use with the given
+ * fb_helper. This is a separate step to allow drivers to freely assign
+ * connectors to the fbdev, e.g. if some are reserved for special purposes or
+ * not adequate to be used for the fbcon.
+ *
+ * Since this is part of the initial setup before the fbdev is published, no
+ * locking is required.
+ */
 int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
 {
        struct drm_device *dev = fb_helper->dev;
@@ -163,6 +190,10 @@ static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc)
        crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);
 }
 
+/**
+ * drm_fb_helper_debug_enter - implementation for ->fb_debug_enter
+ * @info: fbdev registered by the helper
+ */
 int drm_fb_helper_debug_enter(struct fb_info *info)
 {
        struct drm_fb_helper *helper = info->par;
@@ -208,6 +239,10 @@ static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc)
        return NULL;
 }
 
+/**
+ * drm_fb_helper_debug_leave - implementation for ->fb_debug_leave
+ * @info: fbdev registered by the helper
+ */
 int drm_fb_helper_debug_leave(struct fb_info *info)
 {
        struct drm_fb_helper *helper = info->par;
@@ -239,13 +274,24 @@ int drm_fb_helper_debug_leave(struct fb_info *info)
 }
 EXPORT_SYMBOL(drm_fb_helper_debug_leave);
 
+/**
+ * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration
+ * @fb_helper: fbcon to restore
+ *
+ * This should be called from driver's drm ->lastclose callback
+ * when implementing an fbcon on top of kms using this helper. This ensures that
+ * the user isn't greeted with a black screen when e.g. X dies.
+ */
 bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
 {
        bool error = false;
        int i, ret;
+
+       drm_warn_on_modeset_not_all_locked(fb_helper->dev);
+
        for (i = 0; i < fb_helper->crtc_count; i++) {
                struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
-               ret = mode_set->crtc->funcs->set_config(mode_set);
+               ret = drm_mode_set_config_internal(mode_set);
                if (ret)
                        error = true;
        }
@@ -253,6 +299,10 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
 }
 EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode);
 
+/*
+ * restore fbcon display for all kms driver's using this helper, used for sysrq
+ * and panic handling.
+ */
 static bool drm_fb_helper_force_kernel_mode(void)
 {
        bool ret, error = false;
@@ -272,7 +322,7 @@ static bool drm_fb_helper_force_kernel_mode(void)
        return error;
 }
 
-int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
+static int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
                        void *panic_str)
 {
        /*
@@ -285,30 +335,36 @@ int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
        pr_err("panic occurred, switching back to text console\n");
        return drm_fb_helper_force_kernel_mode();
 }
-EXPORT_SYMBOL(drm_fb_helper_panic);
 
 static struct notifier_block paniced = {
        .notifier_call = drm_fb_helper_panic,
 };
 
-/**
- * drm_fb_helper_restore - restore the framebuffer console (kernel) config
- *
- * Restore's the kernel's fbcon mode, used for lastclose & panic paths.
- */
-void drm_fb_helper_restore(void)
+static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper)
 {
-       bool ret;
-       ret = drm_fb_helper_force_kernel_mode();
-       if (ret == true)
-               DRM_ERROR("Failed to restore crtc configuration\n");
+       struct drm_device *dev = fb_helper->dev;
+       struct drm_crtc *crtc;
+       int bound = 0, crtcs_bound = 0;
+
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+               if (crtc->fb)
+                       crtcs_bound++;
+               if (crtc->fb == fb_helper->fb)
+                       bound++;
+       }
+
+       if (bound < crtcs_bound)
+               return false;
+       return true;
 }
-EXPORT_SYMBOL(drm_fb_helper_restore);
 
 #ifdef CONFIG_MAGIC_SYSRQ
 static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
 {
-       drm_fb_helper_restore();
+       bool ret;
+       ret = drm_fb_helper_force_kernel_mode();
+       if (ret == true)
+               DRM_ERROR("Failed to restore crtc configuration\n");
 }
 static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
 
@@ -334,10 +390,23 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
        struct drm_connector *connector;
        int i, j;
 
+       /*
+        * fbdev->blank can be called from irq context in case of a panic.
+        * Since we already have our own special panic handler which will
+        * restore the fbdev console mode completely, just bail out early.
+        */
+       if (oops_in_progress)
+               return;
+
        /*
         * For each CRTC in this fb, turn the connectors on/off.
         */
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
+       if (!drm_fb_helper_is_bound(fb_helper)) {
+               drm_modeset_unlock_all(dev);
+               return;
+       }
+
        for (i = 0; i < fb_helper->crtc_count; i++) {
                crtc = fb_helper->crtc_info[i].mode_set.crtc;
 
@@ -352,9 +421,14 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
                                dev->mode_config.dpms_property, dpms_mode);
                }
        }
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
 }
 
+/**
+ * drm_fb_helper_blank - implementation for ->fb_blank
+ * @blank: desired blanking state
+ * @info: fbdev registered by the helper
+ */
 int drm_fb_helper_blank(int blank, struct fb_info *info)
 {
        switch (blank) {
@@ -398,6 +472,24 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
        kfree(helper->crtc_info);
 }
 
+/**
+ * drm_fb_helper_init - initialize a drm_fb_helper structure
+ * @dev: drm device
+ * @fb_helper: driver-allocated fbdev helper structure to initialize
+ * @crtc_count: maximum number of crtcs to support in this fbdev emulation
+ * @max_conn_count: max connector count
+ *
+ * This allocates the structures for the fbdev helper with the given limits.
+ * Note that this won't yet touch the hardware (through the driver interfaces)
+ * nor register the fbdev. This is only done in drm_fb_helper_initial_config()
+ * to allow driver writes more control over the exact init sequence.
+ *
+ * Drivers must set fb_helper->funcs before calling
+ * drm_fb_helper_initial_config().
+ *
+ * RETURNS:
+ * Zero if everything went ok, nonzero otherwise.
+ */
 int drm_fb_helper_init(struct drm_device *dev,
                       struct drm_fb_helper *fb_helper,
                       int crtc_count, int max_conn_count)
@@ -526,6 +618,11 @@ static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
        return 0;
 }
 
+/**
+ * drm_fb_helper_setcmap - implementation for ->fb_setcmap
+ * @cmap: cmap to set
+ * @info: fbdev registered by the helper
+ */
 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
 {
        struct drm_fb_helper *fb_helper = info->par;
@@ -565,6 +662,11 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
 }
 EXPORT_SYMBOL(drm_fb_helper_setcmap);
 
+/**
+ * drm_fb_helper_check_var - implementation for ->fb_check_var
+ * @var: screeninfo to check
+ * @info: fbdev registered by the helper
+ */
 int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
                            struct fb_info *info)
 {
@@ -657,13 +759,19 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
 }
 EXPORT_SYMBOL(drm_fb_helper_check_var);
 
-/* this will let fbcon do the mode init */
+/**
+ * drm_fb_helper_set_par - implementation for ->fb_set_par
+ * @info: fbdev registered by the helper
+ *
+ * This will let fbcon do the mode init and is called at initialization time by
+ * the fbdev core when registering the driver, and later on through the hotplug
+ * callback.
+ */
 int drm_fb_helper_set_par(struct fb_info *info)
 {
        struct drm_fb_helper *fb_helper = info->par;
        struct drm_device *dev = fb_helper->dev;
        struct fb_var_screeninfo *var = &info->var;
-       struct drm_crtc *crtc;
        int ret;
        int i;
 
@@ -672,16 +780,15 @@ int drm_fb_helper_set_par(struct fb_info *info)
                return -EINVAL;
        }
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
        for (i = 0; i < fb_helper->crtc_count; i++) {
-               crtc = fb_helper->crtc_info[i].mode_set.crtc;
-               ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set);
+               ret = drm_mode_set_config_internal(&fb_helper->crtc_info[i].mode_set);
                if (ret) {
-                       mutex_unlock(&dev->mode_config.mutex);
+                       drm_modeset_unlock_all(dev);
                        return ret;
                }
        }
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
 
        if (fb_helper->delayed_hotplug) {
                fb_helper->delayed_hotplug = false;
@@ -691,6 +798,11 @@ int drm_fb_helper_set_par(struct fb_info *info)
 }
 EXPORT_SYMBOL(drm_fb_helper_set_par);
 
+/**
+ * drm_fb_helper_pan_display - implementation for ->fb_pan_display
+ * @var: updated screen information
+ * @info: fbdev registered by the helper
+ */
 int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
                              struct fb_info *info)
 {
@@ -701,7 +813,12 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
        int ret = 0;
        int i;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
+       if (!drm_fb_helper_is_bound(fb_helper)) {
+               drm_modeset_unlock_all(dev);
+               return -EBUSY;
+       }
+
        for (i = 0; i < fb_helper->crtc_count; i++) {
                crtc = fb_helper->crtc_info[i].mode_set.crtc;
 
@@ -711,22 +828,27 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
                modeset->y = var->yoffset;
 
                if (modeset->num_connectors) {
-                       ret = crtc->funcs->set_config(modeset);
+                       ret = drm_mode_set_config_internal(modeset);
                        if (!ret) {
                                info->var.xoffset = var->xoffset;
                                info->var.yoffset = var->yoffset;
                        }
                }
        }
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
        return ret;
 }
 EXPORT_SYMBOL(drm_fb_helper_pan_display);
 
-int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
-                                 int preferred_bpp)
+/*
+ * Allocates the backing storage and sets up the fbdev info structure through
+ * the ->fb_probe callback and then registers the fbdev and sets up the panic
+ * notifier.
+ */
+static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
+                                        int preferred_bpp)
 {
-       int new_fb = 0;
+       int ret = 0;
        int crtc_count = 0;
        int i;
        struct fb_info *info;
@@ -804,27 +926,30 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
        }
 
        /* push down into drivers */
-       new_fb = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);
-       if (new_fb < 0)
-               return new_fb;
+       ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);
+       if (ret < 0)
+               return ret;
 
        info = fb_helper->fbdev;
 
-       /* set the fb pointer */
+       /*
+        * Set the fb pointer - usually drm_setup_crtcs does this for hotplug
+        * events, but at init time drm_setup_crtcs needs to be called before
+        * the fb is allocated (since we need to figure out the desired size of
+        * the fb before we can allocate it ...). Hence we need to fix things up
+        * here again.
+        */
        for (i = 0; i < fb_helper->crtc_count; i++)
-               fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
+               if (fb_helper->crtc_info[i].mode_set.num_connectors)
+                       fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
 
-       if (new_fb) {
-               info->var.pixclock = 0;
-               if (register_framebuffer(info) < 0)
-                       return -EINVAL;
 
-               dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n",
-                               info->node, info->fix.id);
+       info->var.pixclock = 0;
+       if (register_framebuffer(info) < 0)
+               return -EINVAL;
 
-       } else {
-               drm_fb_helper_set_par(info);
-       }
+       dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n",
+                       info->node, info->fix.id);
 
        /* Switch back to kernel console on panic */
        /* multi card linked list maybe */
@@ -834,13 +959,25 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
                                               &paniced);
                register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
        }
-       if (new_fb)
-               list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
+
+       list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
 
        return 0;
 }
-EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
 
+/**
+ * drm_fb_helper_fill_fix - initializes fixed fbdev information
+ * @info: fbdev registered by the helper
+ * @pitch: desired pitch
+ * @depth: desired depth
+ *
+ * Helper to fill in the fixed fbdev information useful for a non-accelerated
+ * fbdev emulations. Drivers which support acceleration methods which impose
+ * additional constraints need to set up their own limits.
+ *
+ * Drivers should call this (or their equivalent setup code) from their
+ * ->fb_probe callback.
+ */
 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
                            uint32_t depth)
 {
@@ -861,6 +998,20 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
 }
 EXPORT_SYMBOL(drm_fb_helper_fill_fix);
 
+/**
+ * drm_fb_helper_fill_var - initalizes variable fbdev information
+ * @info: fbdev instance to set up
+ * @fb_helper: fb helper instance to use as template
+ * @fb_width: desired fb width
+ * @fb_height: desired fb height
+ *
+ * Sets up the variable fbdev metainformation from the given fb helper instance
+ * and the drm framebuffer allocated in fb_helper->fb.
+ *
+ * Drivers should call this (or their equivalent setup code) from their
+ * ->fb_probe callback after having allocated the fbdev backing
+ * storage framebuffer.
+ */
 void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper,
                            uint32_t fb_width, uint32_t fb_height)
 {
@@ -1284,6 +1435,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
        for (i = 0; i < fb_helper->crtc_count; i++) {
                modeset = &fb_helper->crtc_info[i].mode_set;
                modeset->num_connectors = 0;
+               modeset->fb = NULL;
        }
 
        for (i = 0; i < fb_helper->connector_count; i++) {
@@ -1300,9 +1452,21 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
                        modeset->mode = drm_mode_duplicate(dev,
                                                           fb_crtc->desired_mode);
                        modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector;
+                       modeset->fb = fb_helper->fb;
                }
        }
 
+       /* Clear out any old modes if there are no more connected outputs. */
+       for (i = 0; i < fb_helper->crtc_count; i++) {
+               modeset = &fb_helper->crtc_info[i].mode_set;
+               if (modeset->num_connectors == 0) {
+                       BUG_ON(modeset->fb);
+                       BUG_ON(modeset->num_connectors);
+                       if (modeset->mode)
+                               drm_mode_destroy(dev, modeset->mode);
+                       modeset->mode = NULL;
+               }
+       }
 out:
        kfree(crtcs);
        kfree(modes);
@@ -1310,18 +1474,23 @@ out:
 }
 
 /**
- * drm_helper_initial_config - setup a sane initial connector configuration
+ * drm_fb_helper_initial_config - setup a sane initial connector configuration
  * @fb_helper: fb_helper device struct
  * @bpp_sel: bpp value to use for the framebuffer configuration
  *
- * LOCKING:
- * Called at init time by the driver to set up the @fb_helper initial
- * configuration, must take the mode config lock.
- *
  * Scans the CRTCs and connectors and tries to put together an initial setup.
  * At the moment, this is a cloned configuration across all heads with
  * a new framebuffer object as the backing store.
  *
+ * Note that this also registers the fbdev and so allows userspace to call into
+ * the driver through the fbdev interfaces.
+ *
+ * This function will call down into the ->fb_probe callback to let
+ * the driver allocate and initialize the fbdev info structure and the drm
+ * framebuffer used to back the fbdev. drm_fb_helper_fill_var() and
+ * drm_fb_helper_fill_fix() are provided as helpers to setup simple default
+ * values for the fbdev info structure.
+ *
  * RETURNS:
  * Zero if everything went ok, nonzero otherwise.
  */
@@ -1330,9 +1499,6 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
        struct drm_device *dev = fb_helper->dev;
        int count = 0;
 
-       /* disable all the possible outputs/crtcs before entering KMS mode */
-       drm_helper_disable_unused_functions(fb_helper->dev);
-
        drm_fb_helper_parse_command_line(fb_helper);
 
        count = drm_fb_helper_probe_connector_modes(fb_helper,
@@ -1355,12 +1521,17 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config);
  *                               probing all the outputs attached to the fb
  * @fb_helper: the drm_fb_helper
  *
- * LOCKING:
- * Called at runtime, must take mode config lock.
- *
  * Scan the connectors attached to the fb_helper and try to put together a
  * setup after *notification of a change in output configuration.
  *
+ * Called at runtime, takes the mode config locks to be able to check/change the
+ * modeset configuration. Must be run from process context (which usually means
+ * either the output polling work or a work item launched from the driver's
+ * hotplug interrupt).
+ *
+ * Note that the driver must ensure that this is only called _after_ the fb has
+ * been fully set up, i.e. after the call to drm_fb_helper_initial_config.
+ *
  * RETURNS:
  * 0 on success and a non-zero error code otherwise.
  */
@@ -1369,23 +1540,14 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
        struct drm_device *dev = fb_helper->dev;
        int count = 0;
        u32 max_width, max_height, bpp_sel;
-       int bound = 0, crtcs_bound = 0;
-       struct drm_crtc *crtc;
 
        if (!fb_helper->fb)
                return 0;
 
-       mutex_lock(&dev->mode_config.mutex);
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-               if (crtc->fb)
-                       crtcs_bound++;
-               if (crtc->fb == fb_helper->fb)
-                       bound++;
-       }
-
-       if (bound < crtcs_bound) {
+       drm_modeset_lock_all(dev);
+       if (!drm_fb_helper_is_bound(fb_helper)) {
                fb_helper->delayed_hotplug = true;
-               mutex_unlock(&dev->mode_config.mutex);
+               drm_modeset_unlock_all(dev);
                return 0;
        }
        DRM_DEBUG_KMS("\n");
@@ -1397,9 +1559,11 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
        count = drm_fb_helper_probe_connector_modes(fb_helper, max_width,
                                                    max_height);
        drm_setup_crtcs(fb_helper);
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
 
-       return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
+       drm_fb_helper_set_par(fb_helper->fbdev);
+
+       return 0;
 }
 EXPORT_SYMBOL(drm_fb_helper_hotplug_event);
 
index 133b4132983e35e366884301a443ff26f45a2a05..13fdcd10a6051ddd9359af281390748528a6885e 100644 (file)
@@ -276,6 +276,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
 
        INIT_LIST_HEAD(&priv->lhead);
        INIT_LIST_HEAD(&priv->fbs);
+       mutex_init(&priv->fbs_lock);
        INIT_LIST_HEAD(&priv->event_list);
        init_waitqueue_head(&priv->event_wait);
        priv->event_space = 4096; /* set aside 4k for event buffer */
index 19c01ca3cc76aaa2f5fe7cae45f8759104f74190..38e79927b2d7e6ead32398eac6a04eb992bf0cb1 100644 (file)
@@ -505,6 +505,7 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc)
 
        /* Valid dotclock? */
        if (dotclock > 0) {
+               int frame_size;
                /* Convert scanline length in pixels and video dot clock to
                 * line duration, frame duration and pixel duration in
                 * nanoseconds:
@@ -512,7 +513,10 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc)
                pixeldur_ns = (s64) div64_u64(1000000000, dotclock);
                linedur_ns  = (s64) div64_u64(((u64) crtc->hwmode.crtc_htotal *
                                              1000000000), dotclock);
-               framedur_ns = (s64) crtc->hwmode.crtc_vtotal * linedur_ns;
+               frame_size = crtc->hwmode.crtc_htotal *
+                               crtc->hwmode.crtc_vtotal;
+               framedur_ns = (s64) div64_u64((u64) frame_size * 1000000000,
+                                             dotclock);
        } else
                DRM_ERROR("crtc %d: Can't calculate constants, dotclock = 0!\n",
                          crtc->base.id);
@@ -1218,8 +1222,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
        int ret;
        unsigned int flags, seq, crtc, high_crtc;
 
-       if ((!drm_dev_to_irq(dev)) || (!dev->irq_enabled))
-               return -EINVAL;
+       if (drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
+               if ((!drm_dev_to_irq(dev)) || (!dev->irq_enabled))
+                       return -EINVAL;
 
        if (vblwait->request.type & _DRM_VBLANK_SIGNAL)
                return -EINVAL;
index 2aa331499f81da1dfbe8f5c51216ed0096f67951..db1e2d6f90d7221d713c04b70560d85a08ad6732 100644 (file)
@@ -102,20 +102,6 @@ int drm_mm_pre_get(struct drm_mm *mm)
 }
 EXPORT_SYMBOL(drm_mm_pre_get);
 
-static inline unsigned long drm_mm_hole_node_start(struct drm_mm_node *hole_node)
-{
-       return hole_node->start + hole_node->size;
-}
-
-static inline unsigned long drm_mm_hole_node_end(struct drm_mm_node *hole_node)
-{
-       struct drm_mm_node *next_node =
-               list_entry(hole_node->node_list.next, struct drm_mm_node,
-                          node_list);
-
-       return next_node->start;
-}
-
 static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
                                 struct drm_mm_node *node,
                                 unsigned long size, unsigned alignment,
@@ -127,7 +113,7 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
        unsigned long adj_start = hole_start;
        unsigned long adj_end = hole_end;
 
-       BUG_ON(!hole_node->hole_follows || node->allocated);
+       BUG_ON(node->allocated);
 
        if (mm->color_adjust)
                mm->color_adjust(hole_node, color, &adj_start, &adj_end);
@@ -155,12 +141,57 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
        BUG_ON(node->start + node->size > adj_end);
 
        node->hole_follows = 0;
-       if (node->start + node->size < hole_end) {
+       if (__drm_mm_hole_node_start(node) < hole_end) {
                list_add(&node->hole_stack, &mm->hole_stack);
                node->hole_follows = 1;
        }
 }
 
+struct drm_mm_node *drm_mm_create_block(struct drm_mm *mm,
+                                       unsigned long start,
+                                       unsigned long size,
+                                       bool atomic)
+{
+       struct drm_mm_node *hole, *node;
+       unsigned long end = start + size;
+       unsigned long hole_start;
+       unsigned long hole_end;
+
+       drm_mm_for_each_hole(hole, mm, hole_start, hole_end) {
+               if (hole_start > start || hole_end < end)
+                       continue;
+
+               node = drm_mm_kmalloc(mm, atomic);
+               if (unlikely(node == NULL))
+                       return NULL;
+
+               node->start = start;
+               node->size = size;
+               node->mm = mm;
+               node->allocated = 1;
+
+               INIT_LIST_HEAD(&node->hole_stack);
+               list_add(&node->node_list, &hole->node_list);
+
+               if (start == hole_start) {
+                       hole->hole_follows = 0;
+                       list_del_init(&hole->hole_stack);
+               }
+
+               node->hole_follows = 0;
+               if (end != hole_end) {
+                       list_add(&node->hole_stack, &mm->hole_stack);
+                       node->hole_follows = 1;
+               }
+
+               return node;
+       }
+
+       WARN(1, "no hole found for block 0x%lx + 0x%lx\n", start, size);
+       return NULL;
+}
+EXPORT_SYMBOL(drm_mm_create_block);
+
 struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *hole_node,
                                             unsigned long size,
                                             unsigned alignment,
@@ -253,7 +284,7 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
        BUG_ON(node->start + node->size > end);
 
        node->hole_follows = 0;
-       if (node->start + node->size < hole_end) {
+       if (__drm_mm_hole_node_start(node) < hole_end) {
                list_add(&node->hole_stack, &mm->hole_stack);
                node->hole_follows = 1;
        }
@@ -327,12 +358,13 @@ void drm_mm_remove_node(struct drm_mm_node *node)
            list_entry(node->node_list.prev, struct drm_mm_node, node_list);
 
        if (node->hole_follows) {
-               BUG_ON(drm_mm_hole_node_start(node)
-                               == drm_mm_hole_node_end(node));
+               BUG_ON(__drm_mm_hole_node_start(node) ==
+                      __drm_mm_hole_node_end(node));
                list_del(&node->hole_stack);
        } else
-               BUG_ON(drm_mm_hole_node_start(node)
-                               != drm_mm_hole_node_end(node));
+               BUG_ON(__drm_mm_hole_node_start(node) !=
+                      __drm_mm_hole_node_end(node));
+
 
        if (!prev_node->hole_follows) {
                prev_node->hole_follows = 1;
@@ -390,6 +422,8 @@ struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
 {
        struct drm_mm_node *entry;
        struct drm_mm_node *best;
+       unsigned long adj_start;
+       unsigned long adj_end;
        unsigned long best_size;
 
        BUG_ON(mm->scanned_blocks);
@@ -397,17 +431,13 @@ struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
        best = NULL;
        best_size = ~0UL;
 
-       list_for_each_entry(entry, &mm->hole_stack, hole_stack) {
-               unsigned long adj_start = drm_mm_hole_node_start(entry);
-               unsigned long adj_end = drm_mm_hole_node_end(entry);
-
+       drm_mm_for_each_hole(entry, mm, adj_start, adj_end) {
                if (mm->color_adjust) {
                        mm->color_adjust(entry, color, &adj_start, &adj_end);
                        if (adj_end <= adj_start)
                                continue;
                }
 
-               BUG_ON(!entry->hole_follows);
                if (!check_free_hole(adj_start, adj_end, size, alignment))
                        continue;
 
@@ -434,6 +464,8 @@ struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm,
 {
        struct drm_mm_node *entry;
        struct drm_mm_node *best;
+       unsigned long adj_start;
+       unsigned long adj_end;
        unsigned long best_size;
 
        BUG_ON(mm->scanned_blocks);
@@ -441,13 +473,11 @@ struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm,
        best = NULL;
        best_size = ~0UL;
 
-       list_for_each_entry(entry, &mm->hole_stack, hole_stack) {
-               unsigned long adj_start = drm_mm_hole_node_start(entry) < start ?
-                       start : drm_mm_hole_node_start(entry);
-               unsigned long adj_end = drm_mm_hole_node_end(entry) > end ?
-                       end : drm_mm_hole_node_end(entry);
-
-               BUG_ON(!entry->hole_follows);
+       drm_mm_for_each_hole(entry, mm, adj_start, adj_end) {
+               if (adj_start < start)
+                       adj_start = start;
+               if (adj_end > end)
+                       adj_end = end;
 
                if (mm->color_adjust) {
                        mm->color_adjust(entry, color, &adj_start, &adj_end);
index d8da30e90db5ba63308ddc6e7418573dff351b01..04fa6f1808d151249f38fdf2974ee365eac7a047 100644 (file)
@@ -35,6 +35,8 @@
 #include <linux/export.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
 
 /**
  * drm_mode_debug_printmodeline - debug print a mode
@@ -504,6 +506,74 @@ drm_gtf_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh,
 }
 EXPORT_SYMBOL(drm_gtf_mode);
 
+#if IS_ENABLED(CONFIG_VIDEOMODE)
+int drm_display_mode_from_videomode(const struct videomode *vm,
+                                   struct drm_display_mode *dmode)
+{
+       dmode->hdisplay = vm->hactive;
+       dmode->hsync_start = dmode->hdisplay + vm->hfront_porch;
+       dmode->hsync_end = dmode->hsync_start + vm->hsync_len;
+       dmode->htotal = dmode->hsync_end + vm->hback_porch;
+
+       dmode->vdisplay = vm->vactive;
+       dmode->vsync_start = dmode->vdisplay + vm->vfront_porch;
+       dmode->vsync_end = dmode->vsync_start + vm->vsync_len;
+       dmode->vtotal = dmode->vsync_end + vm->vback_porch;
+
+       dmode->clock = vm->pixelclock / 1000;
+
+       dmode->flags = 0;
+       if (vm->dmt_flags & VESA_DMT_HSYNC_HIGH)
+               dmode->flags |= DRM_MODE_FLAG_PHSYNC;
+       else if (vm->dmt_flags & VESA_DMT_HSYNC_LOW)
+               dmode->flags |= DRM_MODE_FLAG_NHSYNC;
+       if (vm->dmt_flags & VESA_DMT_VSYNC_HIGH)
+               dmode->flags |= DRM_MODE_FLAG_PVSYNC;
+       else if (vm->dmt_flags & VESA_DMT_VSYNC_LOW)
+               dmode->flags |= DRM_MODE_FLAG_NVSYNC;
+       if (vm->data_flags & DISPLAY_FLAGS_INTERLACED)
+               dmode->flags |= DRM_MODE_FLAG_INTERLACE;
+       if (vm->data_flags & DISPLAY_FLAGS_DOUBLESCAN)
+               dmode->flags |= DRM_MODE_FLAG_DBLSCAN;
+       drm_mode_set_name(dmode);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(drm_display_mode_from_videomode);
+#endif
+
+#if IS_ENABLED(CONFIG_OF_VIDEOMODE)
+/**
+ * of_get_drm_display_mode - get a drm_display_mode from devicetree
+ * @np: device_node with the timing specification
+ * @dmode: will be set to the return value
+ * @index: index into the list of display timings in devicetree
+ *
+ * This function is expensive and should only be used, if only one mode is to be
+ * read from DT. To get multiple modes start with of_get_display_timings and
+ * work with that instead.
+ */
+int of_get_drm_display_mode(struct device_node *np,
+                           struct drm_display_mode *dmode, int index)
+{
+       struct videomode vm;
+       int ret;
+
+       ret = of_get_videomode(np, &vm, index);
+       if (ret)
+               return ret;
+
+       drm_display_mode_from_videomode(&vm, dmode);
+
+       pr_debug("%s: got %dx%d display mode from %s\n",
+               of_node_full_name(np), vm.hactive, vm.vactive, np->name);
+       drm_mode_debug_printmodeline(dmode);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(of_get_drm_display_mode);
+#endif
+
 /**
  * drm_mode_set_name - set the name on a mode
  * @mode: name will be set in this mode
index 754bc96e10c70ffd406e51ea6327c5690376e273..bd719e936e13a028a0748f8077029f235fe0a611 100644 (file)
@@ -439,78 +439,67 @@ int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver)
        return 0;
 }
 
-#else
-
-int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver)
-{
-       return -1;
-}
-
-#endif
-
-EXPORT_SYMBOL(drm_pci_init);
-
-/*@}*/
-void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver)
-{
-       struct drm_device *dev, *tmp;
-       DRM_DEBUG("\n");
-
-       if (driver->driver_features & DRIVER_MODESET) {
-               pci_unregister_driver(pdriver);
-       } else {
-               list_for_each_entry_safe(dev, tmp, &driver->device_list, driver_item)
-                       drm_put_dev(dev);
-       }
-       DRM_INFO("Module unloaded\n");
-}
-EXPORT_SYMBOL(drm_pci_exit);
-
 int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *mask)
 {
        struct pci_dev *root;
-       int pos;
-       u32 lnkcap = 0, lnkcap2 = 0;
+       u32 lnkcap, lnkcap2;
 
        *mask = 0;
        if (!dev->pdev)
                return -EINVAL;
 
-       if (!pci_is_pcie(dev->pdev))
-               return -EINVAL;
-
        root = dev->pdev->bus->self;
 
-       pos = pci_pcie_cap(root);
-       if (!pos)
-               return -EINVAL;
-
        /* we've been informed via and serverworks don't make the cut */
        if (root->vendor == PCI_VENDOR_ID_VIA ||
            root->vendor == PCI_VENDOR_ID_SERVERWORKS)
                return -EINVAL;
 
-       pci_read_config_dword(root, pos + PCI_EXP_LNKCAP, &lnkcap);
-       pci_read_config_dword(root, pos + PCI_EXP_LNKCAP2, &lnkcap2);
+       pcie_capability_read_dword(root, PCI_EXP_LNKCAP, &lnkcap);
+       pcie_capability_read_dword(root, PCI_EXP_LNKCAP2, &lnkcap2);
 
-       lnkcap &= PCI_EXP_LNKCAP_SLS;
-       lnkcap2 &= 0xfe;
-
-       if (lnkcap2) { /* PCIE GEN 3.0 */
+       if (lnkcap2) {  /* PCIe r3.0-compliant */
                if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_2_5GB)
                        *mask |= DRM_PCIE_SPEED_25;
                if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_5_0GB)
                        *mask |= DRM_PCIE_SPEED_50;
                if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB)
                        *mask |= DRM_PCIE_SPEED_80;
-       } else {
-               if (lnkcap & 1)
+       } else {        /* pre-r3.0 */
+               if (lnkcap & PCI_EXP_LNKCAP_SLS_2_5GB)
                        *mask |= DRM_PCIE_SPEED_25;
-               if (lnkcap & 2)
-                       *mask |= DRM_PCIE_SPEED_50;
+               if (lnkcap & PCI_EXP_LNKCAP_SLS_5_0GB)
+                       *mask |= (DRM_PCIE_SPEED_25 | DRM_PCIE_SPEED_50);
        }
 
        DRM_INFO("probing gen 2 caps for device %x:%x = %x/%x\n", root->vendor, root->device, lnkcap, lnkcap2);
        return 0;
 }
 EXPORT_SYMBOL(drm_pcie_get_speed_cap_mask);
+
+#else
+
+int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver)
+{
+       return -1;
+}
+
+#endif
+
+EXPORT_SYMBOL(drm_pci_init);
+
+/*@}*/
+void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver)
+{
+       struct drm_device *dev, *tmp;
+       DRM_DEBUG("\n");
+
+       if (driver->driver_features & DRIVER_MODESET) {
+               pci_unregister_driver(pdriver);
+       } else {
+               list_for_each_entry_safe(dev, tmp, &driver->device_list, driver_item)
+                       drm_put_dev(dev);
+       }
+       DRM_INFO("Module unloaded\n");
+}
+EXPORT_SYMBOL(drm_pci_exit);
index 7f125738f44e78dc37439fe13b9bc0868e9d4baf..366910ddcfcb3cd96b33c2dcd9c1d122b01990b5 100644 (file)
@@ -53,7 +53,8 @@
  * Self-importing: if userspace is using PRIME as a replacement for flink
  * then it will get a fd->handle request for a GEM object that it created.
  * Drivers should detect this situation and return back the gem object
- * from the dma-buf private.
+ * from the dma-buf private.  Prime will do this automatically for drivers that
+ * use the drm_gem_prime_{import,export} helpers.
  */
 
 struct drm_prime_member {
@@ -62,6 +63,137 @@ struct drm_prime_member {
        uint32_t handle;
 };
 
+static struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach,
+               enum dma_data_direction dir)
+{
+       struct drm_gem_object *obj = attach->dmabuf->priv;
+       struct sg_table *sgt;
+
+       mutex_lock(&obj->dev->struct_mutex);
+
+       sgt = obj->dev->driver->gem_prime_get_sg_table(obj);
+
+       if (!IS_ERR_OR_NULL(sgt))
+               dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir);
+
+       mutex_unlock(&obj->dev->struct_mutex);
+       return sgt;
+}
+
+static void drm_gem_unmap_dma_buf(struct dma_buf_attachment *attach,
+               struct sg_table *sgt, enum dma_data_direction dir)
+{
+       dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, dir);
+       sg_free_table(sgt);
+       kfree(sgt);
+}
+
+static void drm_gem_dmabuf_release(struct dma_buf *dma_buf)
+{
+       struct drm_gem_object *obj = dma_buf->priv;
+
+       if (obj->export_dma_buf == dma_buf) {
+               /* drop the reference on the export fd holds */
+               obj->export_dma_buf = NULL;
+               drm_gem_object_unreference_unlocked(obj);
+       }
+}
+
+static void *drm_gem_dmabuf_vmap(struct dma_buf *dma_buf)
+{
+       struct drm_gem_object *obj = dma_buf->priv;
+       struct drm_device *dev = obj->dev;
+
+       return dev->driver->gem_prime_vmap(obj);
+}
+
+static void drm_gem_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr)
+{
+       struct drm_gem_object *obj = dma_buf->priv;
+       struct drm_device *dev = obj->dev;
+
+       dev->driver->gem_prime_vunmap(obj, vaddr);
+}
+
+static void *drm_gem_dmabuf_kmap_atomic(struct dma_buf *dma_buf,
+               unsigned long page_num)
+{
+       return NULL;
+}
+
+static void drm_gem_dmabuf_kunmap_atomic(struct dma_buf *dma_buf,
+               unsigned long page_num, void *addr)
+{
+
+}
+static void *drm_gem_dmabuf_kmap(struct dma_buf *dma_buf,
+               unsigned long page_num)
+{
+       return NULL;
+}
+
+static void drm_gem_dmabuf_kunmap(struct dma_buf *dma_buf,
+               unsigned long page_num, void *addr)
+{
+
+}
+
+static int drm_gem_dmabuf_mmap(struct dma_buf *dma_buf,
+               struct vm_area_struct *vma)
+{
+       return -EINVAL;
+}
+
+static const struct dma_buf_ops drm_gem_prime_dmabuf_ops =  {
+       .map_dma_buf = drm_gem_map_dma_buf,
+       .unmap_dma_buf = drm_gem_unmap_dma_buf,
+       .release = drm_gem_dmabuf_release,
+       .kmap = drm_gem_dmabuf_kmap,
+       .kmap_atomic = drm_gem_dmabuf_kmap_atomic,
+       .kunmap = drm_gem_dmabuf_kunmap,
+       .kunmap_atomic = drm_gem_dmabuf_kunmap_atomic,
+       .mmap = drm_gem_dmabuf_mmap,
+       .vmap = drm_gem_dmabuf_vmap,
+       .vunmap = drm_gem_dmabuf_vunmap,
+};
+
+/**
+ * DOC: PRIME Helpers
+ *
+ * Drivers can implement @gem_prime_export and @gem_prime_import in terms of
+ * simpler APIs by using the helper functions @drm_gem_prime_export and
+ * @drm_gem_prime_import.  These functions implement dma-buf support in terms of
+ * five lower-level driver callbacks:
+ *
+ * Export callbacks:
+ *
+ *  - @gem_prime_pin (optional): prepare a GEM object for exporting
+ *
+ *  - @gem_prime_get_sg_table: provide a scatter/gather table of pinned pages
+ *
+ *  - @gem_prime_vmap: vmap a buffer exported by your driver
+ *
+ *  - @gem_prime_vunmap: vunmap a buffer exported by your driver
+ *
+ * Import callback:
+ *
+ *  - @gem_prime_import_sg_table (import): produce a GEM object from another
+ *    driver's scatter/gather table
+ */
+
+struct dma_buf *drm_gem_prime_export(struct drm_device *dev,
+                                    struct drm_gem_object *obj, int flags)
+{
+       if (dev->driver->gem_prime_pin) {
+               int ret = dev->driver->gem_prime_pin(obj);
+               if (ret)
+                       return ERR_PTR(ret);
+       }
+       return dma_buf_export(obj, &drm_gem_prime_dmabuf_ops, obj->size,
+                             0600);
+}
+EXPORT_SYMBOL(drm_gem_prime_export);
+
 int drm_gem_prime_handle_to_fd(struct drm_device *dev,
                struct drm_file *file_priv, uint32_t handle, uint32_t flags,
                int *prime_fd)
@@ -117,6 +249,58 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev,
 }
 EXPORT_SYMBOL(drm_gem_prime_handle_to_fd);
 
+struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev,
+                                           struct dma_buf *dma_buf)
+{
+       struct dma_buf_attachment *attach;
+       struct sg_table *sgt;
+       struct drm_gem_object *obj;
+       int ret;
+
+       if (!dev->driver->gem_prime_import_sg_table)
+               return ERR_PTR(-EINVAL);
+
+       if (dma_buf->ops == &drm_gem_prime_dmabuf_ops) {
+               obj = dma_buf->priv;
+               if (obj->dev == dev) {
+                       /*
+                        * Importing dmabuf exported from out own gem increases
+                        * refcount on gem itself instead of f_count of dmabuf.
+                        */
+                       drm_gem_object_reference(obj);
+                       dma_buf_put(dma_buf);
+                       return obj;
+               }
+       }
+
+       attach = dma_buf_attach(dma_buf, dev->dev);
+       if (IS_ERR(attach))
+               return ERR_PTR(PTR_ERR(attach));
+
+       sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
+       if (IS_ERR_OR_NULL(sgt)) {
+               ret = PTR_ERR(sgt);
+               goto fail_detach;
+       }
+
+       obj = dev->driver->gem_prime_import_sg_table(dev, dma_buf->size, sgt);
+       if (IS_ERR(obj)) {
+               ret = PTR_ERR(obj);
+               goto fail_unmap;
+       }
+
+       obj->import_attach = attach;
+
+       return obj;
+
+fail_unmap:
+       dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL);
+fail_detach:
+       dma_buf_detach(dma_buf, attach);
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(drm_gem_prime_import);
+
 int drm_gem_prime_fd_to_handle(struct drm_device *dev,
                struct drm_file *file_priv, int prime_fd, uint32_t *handle)
 {
index 3cec30611417578591f165f61eb6e97c29f4c7a8..34a156f0c336cfb17e71bf68ac8f0d7c3c2d5a72 100644 (file)
@@ -18,7 +18,7 @@ int drm_get_usb_dev(struct usb_interface *interface,
 
        usbdev = interface_to_usbdev(interface);
        dev->usbdev = usbdev;
-       dev->dev = &usbdev->dev;
+       dev->dev = &interface->dev;
 
        mutex_lock(&drm_global_mutex);
 
index 71f867340a88b43a98572c1b84c6776528e919fc..68f0045f86b860d7ecd4b7477a98719a950a7f95 100644 (file)
@@ -226,36 +226,8 @@ out:
        return ret;
 }
 
-static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper,
-                                  struct drm_fb_helper_surface_size *sizes)
-{
-       int ret = 0;
-
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
-       /*
-        * with !helper->fb, it means that this funcion is called first time
-        * and after that, the helper->fb would be used as clone mode.
-        */
-       if (!helper->fb) {
-               ret = exynos_drm_fbdev_create(helper, sizes);
-               if (ret < 0) {
-                       DRM_ERROR("failed to create fbdev.\n");
-                       return ret;
-               }
-
-               /*
-                * fb_helper expects a value more than 1 if succeed
-                * because register_framebuffer() should be called.
-                */
-               ret = 1;
-       }
-
-       return ret;
-}
-
 static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
-       .fb_probe =     exynos_drm_fbdev_probe,
+       .fb_probe =     exynos_drm_fbdev_create,
 };
 
 int exynos_drm_fbdev_init(struct drm_device *dev)
@@ -295,6 +267,9 @@ int exynos_drm_fbdev_init(struct drm_device *dev)
 
        }
 
+       /* disable all the possible outputs/crtcs before entering KMS mode */
+       drm_helper_disable_unused_functions(dev);
+
        ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
        if (ret < 0) {
                DRM_ERROR("failed to set up hw configuration.\n");
@@ -326,8 +301,10 @@ static void exynos_drm_fbdev_destroy(struct drm_device *dev,
        /* release drm framebuffer and real buffer */
        if (fb_helper->fb && fb_helper->fb->funcs) {
                fb = fb_helper->fb;
-               if (fb)
+               if (fb) {
+                       drm_framebuffer_unregister_private(fb);
                        drm_framebuffer_remove(fb);
+               }
        }
 
        /* release linux framebuffer */
@@ -374,5 +351,7 @@ void exynos_drm_fbdev_restore_mode(struct drm_device *dev)
        if (!private || !private->fb_helper)
                return;
 
+       drm_modeset_lock_all(dev);
        drm_fb_helper_restore_fbdev_mode(private->fb_helper);
+       drm_modeset_unlock_all(dev);
 }
index afded54dbb10c2b8c3b1e727625a5f637002330e..2590cac84257cd79cd2c01b4b6e1b16fd2dcebdf 100644 (file)
@@ -260,13 +260,13 @@ static int psb_framebuffer_init(struct drm_device *dev,
        default:
                return -EINVAL;
        }
+       drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd);
+       fb->gtt = gt;
        ret = drm_framebuffer_init(dev, &fb->base, &psb_fb_funcs);
        if (ret) {
                dev_err(dev->dev, "framebuffer init failed: %d\n", ret);
                return ret;
        }
-       drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd);
-       fb->gtt = gt;
        return 0;
 }
 
@@ -545,9 +545,7 @@ static int psbfb_probe(struct drm_fb_helper *helper,
        struct psb_fbdev *psb_fbdev = (struct psb_fbdev *)helper;
        struct drm_device *dev = psb_fbdev->psb_fb_helper.dev;
        struct drm_psb_private *dev_priv = dev->dev_private;
-       int new_fb = 0;
        int bytespp;
-       int ret;
 
        bytespp = sizes->surface_bpp / 8;
        if (bytespp == 3)       /* no 24bit packed */
@@ -562,13 +560,7 @@ static int psbfb_probe(struct drm_fb_helper *helper,
                 sizes->surface_depth = 16;
         }
 
-       if (!helper->fb) {
-               ret = psbfb_create(psb_fbdev, sizes);
-               if (ret)
-                       return ret;
-               new_fb = 1;
-       }
-       return new_fb;
+       return psbfb_create(psb_fbdev, sizes);
 }
 
 static struct drm_fb_helper_funcs psb_fb_helper_funcs = {
@@ -590,6 +582,7 @@ static int psb_fbdev_destroy(struct drm_device *dev, struct psb_fbdev *fbdev)
                framebuffer_release(info);
        }
        drm_fb_helper_fini(&fbdev->psb_fb_helper);
+       drm_framebuffer_unregister_private(&psbfb->base);
        drm_framebuffer_cleanup(&psbfb->base);
 
        if (psbfb->gtt)
@@ -615,6 +608,10 @@ int psb_fbdev_init(struct drm_device *dev)
                                                        INTELFB_CONN_LIMIT);
 
        drm_fb_helper_single_add_all_connectors(&fbdev->psb_fb_helper);
+
+       /* disable all the possible outputs/crtcs before entering KMS mode */
+       drm_helper_disable_unused_functions(dev);
+
        drm_fb_helper_initial_config(&fbdev->psb_fb_helper, 32);
        return 0;
 }
@@ -668,30 +665,6 @@ static void psb_user_framebuffer_destroy(struct drm_framebuffer *fb)
 {
        struct psb_framebuffer *psbfb = to_psb_fb(fb);
        struct gtt_range *r = psbfb->gtt;
-       struct drm_device *dev = fb->dev;
-       struct drm_psb_private *dev_priv = dev->dev_private;
-       struct psb_fbdev *fbdev = dev_priv->fbdev;
-       struct drm_crtc *crtc;
-       int reset = 0;
-
-       /* Should never get stolen memory for a user fb */
-       WARN_ON(r->stolen);
-
-       /* Check if we are erroneously live */
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-               if (crtc->fb == fb)
-                       reset = 1;
-
-       if (reset)
-               /*
-                * Now force a sane response before we permit the DRM CRTC
-                * layer to do stupid things like blank the display. Instead
-                * we reset this framebuffer as if the user had forced a reset.
-                * We must do this before the cleanup so that the DRM layer
-                * doesn't get a chance to stick its oar in where it isn't
-                * wanted.
-                */
-               drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper);
 
        /* Let DRM do its clean up */
        drm_framebuffer_cleanup(fb);
index b58c4701c4e8f50d98c1ca9464e1399a0b4d4c94..f6f534b4197e783c6efbab8256f09321dc33179e 100644 (file)
@@ -194,7 +194,7 @@ static int psb_save_display_registers(struct drm_device *dev)
        regs->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT);
 
        /* Save crtc and output state */
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
                if (drm_helper_crtc_in_use(crtc))
                        crtc->funcs->save(crtc);
@@ -204,7 +204,7 @@ static int psb_save_display_registers(struct drm_device *dev)
                if (connector->funcs->save)
                        connector->funcs->save(connector);
 
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
        return 0;
 }
 
@@ -234,7 +234,7 @@ static int psb_restore_display_registers(struct drm_device *dev)
        /*make sure VGA plane is off. it initializes to on after reset!*/
        PSB_WVDC32(0x80000000, VGACNTRL);
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
                if (drm_helper_crtc_in_use(crtc))
                        crtc->funcs->restore(crtc);
@@ -243,7 +243,7 @@ static int psb_restore_display_registers(struct drm_device *dev)
                if (connector->funcs->restore)
                        connector->funcs->restore(connector);
 
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
        return 0;
 }
 
index dd1fbfa7e46795b5ded28464bc68779bfc41689f..111e3df9c5de380434460bd931805f4a1072f6cc 100644 (file)
@@ -149,6 +149,16 @@ static struct drm_ioctl_desc psb_ioctls[] = {
 
 static void psb_lastclose(struct drm_device *dev)
 {
+       int ret;
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       struct psb_fbdev *fbdev = dev_priv->fbdev;
+
+       drm_modeset_lock_all(dev);
+       ret = drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper);
+       if (ret)
+               DRM_DEBUG("failed to restore crtc mode\n");
+       drm_modeset_unlock_all(dev);
+
        return;
 }
 
@@ -476,7 +486,7 @@ static int psb_mode_operation_ioctl(struct drm_device *dev, void *data,
        case PSB_MODE_OPERATION_MODE_VALID:
                umode = &arg->mode;
 
-               mutex_lock(&dev->mode_config.mutex);
+               drm_modeset_lock_all(dev);
 
                obj = drm_mode_object_find(dev, obj_id,
                                        DRM_MODE_OBJECT_CONNECTOR);
@@ -525,7 +535,7 @@ static int psb_mode_operation_ioctl(struct drm_device *dev, void *data,
                if (mode)
                        drm_mode_destroy(dev, mode);
 mode_op_out:
-               mutex_unlock(&dev->mode_config.mutex);
+               drm_modeset_unlock_all(dev);
                return ret;
 
        default:
diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig
new file mode 100644 (file)
index 0000000..1611836
--- /dev/null
@@ -0,0 +1,22 @@
+menu "I2C encoder or helper chips"
+     depends on DRM && DRM_KMS_HELPER && I2C
+
+config DRM_I2C_CH7006
+       tristate "Chrontel ch7006 TV encoder"
+       default m if DRM_NOUVEAU
+       help
+         Support for Chrontel ch7006 and similar TV encoders, found
+         on some nVidia video cards.
+
+         This driver is currently only useful if you're also using
+         the nouveau driver.
+
+config DRM_I2C_SIL164
+       tristate "Silicon Image sil164 TMDS transmitter"
+       default m if DRM_NOUVEAU
+       help
+         Support for sil164 and similar single-link (or dual-link
+         when used in pairs) TMDS transmitters, used in some nVidia
+         video cards.
+
+endmenu
index b865d0728e283ee8c54ccf3022b5a4506e4eb71d..51fa323920299ebf5dc57c868054cde77955fcd0 100644 (file)
@@ -364,7 +364,7 @@ static int ch7006_encoder_set_property(struct drm_encoder *encoder,
                                .crtc = crtc,
                        };
 
-                       crtc->funcs->set_config(&modeset);
+                       drm_mode_set_config_internal(&modeset);
                }
        }
 
index 0f2c5493242b2a7ff6522f8f0424a0717f347a57..91f3ac6cef35742d931060bb49049f116ac0fa25 100644 (file)
@@ -16,6 +16,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \
          i915_gem_tiling.o \
          i915_sysfs.o \
          i915_trace_points.o \
+         i915_ums.o \
          intel_display.o \
          intel_crt.o \
          intel_lvds.o \
index 9d4a2c2adf0e0883d76d13070fa09f7aa32e1526..7c65ab83914a0ce50274797b708f2b5ba35b27ee 100644 (file)
@@ -103,7 +103,7 @@ static const char *cache_level_str(int type)
 static void
 describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
 {
-       seq_printf(m, "%p: %s%s %8zdKiB %04x %04x %d %d %d%s%s%s",
+       seq_printf(m, "%p: %s%s %8zdKiB %02x %02x %d %d %d%s%s%s",
                   &obj->base,
                   get_pin_flag(obj),
                   get_tiling_flag(obj),
@@ -125,6 +125,8 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
        if (obj->gtt_space != NULL)
                seq_printf(m, " (gtt offset: %08x, size: %08x)",
                           obj->gtt_offset, (unsigned int)obj->gtt_space->size);
+       if (obj->stolen)
+               seq_printf(m, " (stolen: %08lx)", obj->stolen->start);
        if (obj->pin_mappable || obj->fault_mappable) {
                char s[3], *t = s;
                if (obj->pin_mappable)
@@ -257,8 +259,9 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
        seq_printf(m, "%u fault mappable objects, %zu bytes\n",
                   count, size);
 
-       seq_printf(m, "%zu [%zu] gtt total\n",
-                  dev_priv->mm.gtt_total, dev_priv->mm.mappable_gtt_total);
+       seq_printf(m, "%zu [%lu] gtt total\n",
+                  dev_priv->gtt.total,
+                  dev_priv->gtt.mappable_end - dev_priv->gtt.start);
 
        mutex_unlock(&dev->struct_mutex);
 
@@ -388,7 +391,7 @@ static void i915_ring_seqno_info(struct seq_file *m,
                                 struct intel_ring_buffer *ring)
 {
        if (ring->get_seqno) {
-               seq_printf(m, "Current sequence (%s): %d\n",
+               seq_printf(m, "Current sequence (%s): %u\n",
                           ring->name, ring->get_seqno(ring, false));
        }
 }
@@ -545,11 +548,11 @@ static int i915_hws_info(struct seq_file *m, void *data)
        struct drm_device *dev = node->minor->dev;
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct intel_ring_buffer *ring;
-       const volatile u32 __iomem *hws;
+       const u32 *hws;
        int i;
 
        ring = &dev_priv->ring[(uintptr_t)node->info_ent->data];
-       hws = (volatile u32 __iomem *)ring->status_page.page_addr;
+       hws = ring->status_page.page_addr;
        if (hws == NULL)
                return 0;
 
@@ -609,7 +612,7 @@ static void print_error_buffers(struct seq_file *m,
        seq_printf(m, "%s [%d]:\n", name, count);
 
        while (count--) {
-               seq_printf(m, "  %08x %8u %04x %04x %x %x%s%s%s%s%s%s%s",
+               seq_printf(m, "  %08x %8u %02x %02x %x %x%s%s%s%s%s%s%s",
                           err->gtt_offset,
                           err->size,
                           err->read_domains,
@@ -691,7 +694,7 @@ static int i915_error_state(struct seq_file *m, void *unused)
 
        seq_printf(m, "Time: %ld s %ld us\n", error->time.tv_sec,
                   error->time.tv_usec);
-       seq_printf(m, "Kernel: " UTS_RELEASE);
+       seq_printf(m, "Kernel: " UTS_RELEASE "\n");
        seq_printf(m, "PCI ID: 0x%04x\n", dev->pci_device);
        seq_printf(m, "EIR: 0x%08x\n", error->eir);
        seq_printf(m, "IER: 0x%08x\n", error->ier);
@@ -816,11 +819,11 @@ static int i915_error_state_open(struct inode *inode, struct file *file)
 
        error_priv->dev = dev;
 
-       spin_lock_irqsave(&dev_priv->error_lock, flags);
-       error_priv->error = dev_priv->first_error;
+       spin_lock_irqsave(&dev_priv->gpu_error.lock, flags);
+       error_priv->error = dev_priv->gpu_error.first_error;
        if (error_priv->error)
                kref_get(&error_priv->error->ref);
-       spin_unlock_irqrestore(&dev_priv->error_lock, flags);
+       spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags);
 
        return single_open(file, i915_error_state, error_priv);
 }
@@ -846,6 +849,77 @@ static const struct file_operations i915_error_state_fops = {
        .release = i915_error_state_release,
 };
 
+static ssize_t
+i915_next_seqno_read(struct file *filp,
+                char __user *ubuf,
+                size_t max,
+                loff_t *ppos)
+{
+       struct drm_device *dev = filp->private_data;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       char buf[80];
+       int len;
+       int ret;
+
+       ret = mutex_lock_interruptible(&dev->struct_mutex);
+       if (ret)
+               return ret;
+
+       len = snprintf(buf, sizeof(buf),
+                      "next_seqno :  0x%x\n",
+                      dev_priv->next_seqno);
+
+       mutex_unlock(&dev->struct_mutex);
+
+       if (len > sizeof(buf))
+               len = sizeof(buf);
+
+       return simple_read_from_buffer(ubuf, max, ppos, buf, len);
+}
+
+static ssize_t
+i915_next_seqno_write(struct file *filp,
+                     const char __user *ubuf,
+                     size_t cnt,
+                     loff_t *ppos)
+{
+       struct drm_device *dev = filp->private_data;
+       char buf[20];
+       u32 val = 1;
+       int ret;
+
+       if (cnt > 0) {
+               if (cnt > sizeof(buf) - 1)
+                       return -EINVAL;
+
+               if (copy_from_user(buf, ubuf, cnt))
+                       return -EFAULT;
+               buf[cnt] = 0;
+
+               ret = kstrtouint(buf, 0, &val);
+               if (ret < 0)
+                       return ret;
+       }
+
+       ret = mutex_lock_interruptible(&dev->struct_mutex);
+       if (ret)
+               return ret;
+
+       ret = i915_gem_set_seqno(dev, val);
+
+       mutex_unlock(&dev->struct_mutex);
+
+       return ret ?: cnt;
+}
+
+static const struct file_operations i915_next_seqno_fops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read = i915_next_seqno_read,
+       .write = i915_next_seqno_write,
+       .llseek = default_llseek,
+};
+
 static int i915_rstdby_delays(struct seq_file *m, void *unused)
 {
        struct drm_info_node *node = (struct drm_info_node *) m->private;
@@ -888,7 +962,7 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
                u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS);
                u32 rp_state_limits = I915_READ(GEN6_RP_STATE_LIMITS);
                u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
-               u32 rpstat;
+               u32 rpstat, cagf;
                u32 rpupei, rpcurup, rpprevup;
                u32 rpdownei, rpcurdown, rpprevdown;
                int max_freq;
@@ -907,6 +981,11 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
                rpdownei = I915_READ(GEN6_RP_CUR_DOWN_EI);
                rpcurdown = I915_READ(GEN6_RP_CUR_DOWN);
                rpprevdown = I915_READ(GEN6_RP_PREV_DOWN);
+               if (IS_HASWELL(dev))
+                       cagf = (rpstat & HSW_CAGF_MASK) >> HSW_CAGF_SHIFT;
+               else
+                       cagf = (rpstat & GEN6_CAGF_MASK) >> GEN6_CAGF_SHIFT;
+               cagf *= GT_FREQUENCY_MULTIPLIER;
 
                gen6_gt_force_wake_put(dev_priv);
                mutex_unlock(&dev->struct_mutex);
@@ -919,8 +998,7 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
                           gt_perf_status & 0xff);
                seq_printf(m, "Render p-state limit: %d\n",
                           rp_state_limits & 0xff);
-               seq_printf(m, "CAGF: %dMHz\n", ((rpstat & GEN6_CAGF_MASK) >>
-                                               GEN6_CAGF_SHIFT) * GT_FREQUENCY_MULTIPLIER);
+               seq_printf(m, "CAGF: %dMHz\n", cagf);
                seq_printf(m, "RP CUR UP EI: %dus\n", rpupei &
                           GEN6_CURICONT_MASK);
                seq_printf(m, "RP CUR UP: %dus\n", rpcurup &
@@ -1372,28 +1450,31 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
        ifbdev = dev_priv->fbdev;
        fb = to_intel_framebuffer(ifbdev->helper.fb);
 
-       seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, obj ",
+       seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, refcount %d, obj ",
                   fb->base.width,
                   fb->base.height,
                   fb->base.depth,
-                  fb->base.bits_per_pixel);
+                  fb->base.bits_per_pixel,
+                  atomic_read(&fb->base.refcount.refcount));
        describe_obj(m, fb->obj);
        seq_printf(m, "\n");
+       mutex_unlock(&dev->mode_config.mutex);
 
+       mutex_lock(&dev->mode_config.fb_lock);
        list_for_each_entry(fb, &dev->mode_config.fb_list, base.head) {
                if (&fb->base == ifbdev->helper.fb)
                        continue;
 
-               seq_printf(m, "user size: %d x %d, depth %d, %d bpp, obj ",
+               seq_printf(m, "user size: %d x %d, depth %d, %d bpp, refcount %d, obj ",
                           fb->base.width,
                           fb->base.height,
                           fb->base.depth,
-                          fb->base.bits_per_pixel);
+                          fb->base.bits_per_pixel,
+                          atomic_read(&fb->base.refcount.refcount));
                describe_obj(m, fb->obj);
                seq_printf(m, "\n");
        }
-
-       mutex_unlock(&dev->mode_config.mutex);
+       mutex_unlock(&dev->mode_config.fb_lock);
 
        return 0;
 }
@@ -1403,7 +1484,8 @@ static int i915_context_status(struct seq_file *m, void *unused)
        struct drm_info_node *node = (struct drm_info_node *) m->private;
        struct drm_device *dev = node->minor->dev;
        drm_i915_private_t *dev_priv = dev->dev_private;
-       int ret;
+       struct intel_ring_buffer *ring;
+       int ret, i;
 
        ret = mutex_lock_interruptible(&dev->mode_config.mutex);
        if (ret)
@@ -1421,6 +1503,14 @@ static int i915_context_status(struct seq_file *m, void *unused)
                seq_printf(m, "\n");
        }
 
+       for_each_ring(ring, dev_priv, i) {
+               if (ring->default_context) {
+                       seq_printf(m, "HW default context %s ring ", ring->name);
+                       describe_obj(m, ring->default_context->obj);
+                       seq_printf(m, "\n");
+               }
+       }
+
        mutex_unlock(&dev->mode_config.mutex);
 
        return 0;
@@ -1556,7 +1646,7 @@ static int i915_dpio_info(struct seq_file *m, void *data)
                return 0;
        }
 
-       ret = mutex_lock_interruptible(&dev->mode_config.mutex);
+       ret = mutex_lock_interruptible(&dev_priv->dpio_lock);
        if (ret)
                return ret;
 
@@ -1585,7 +1675,7 @@ static int i915_dpio_info(struct seq_file *m, void *data)
        seq_printf(m, "DPIO_FASTCLK_DISABLE: 0x%08x\n",
                   intel_dpio_read(dev_priv, DPIO_FASTCLK_DISABLE));
 
-       mutex_unlock(&dev->mode_config.mutex);
+       mutex_unlock(&dev_priv->dpio_lock);
 
        return 0;
 }
@@ -1603,7 +1693,7 @@ i915_wedged_read(struct file *filp,
 
        len = snprintf(buf, sizeof(buf),
                       "wedged :  %d\n",
-                      atomic_read(&dev_priv->mm.wedged));
+                      atomic_read(&dev_priv->gpu_error.reset_counter));
 
        if (len > sizeof(buf))
                len = sizeof(buf);
@@ -1658,7 +1748,7 @@ i915_ring_stop_read(struct file *filp,
        int len;
 
        len = snprintf(buf, sizeof(buf),
-                      "0x%08x\n", dev_priv->stop_rings);
+                      "0x%08x\n", dev_priv->gpu_error.stop_rings);
 
        if (len > sizeof(buf))
                len = sizeof(buf);
@@ -1694,7 +1784,7 @@ i915_ring_stop_write(struct file *filp,
        if (ret)
                return ret;
 
-       dev_priv->stop_rings = val;
+       dev_priv->gpu_error.stop_rings = val;
        mutex_unlock(&dev->struct_mutex);
 
        return cnt;
@@ -1708,6 +1798,102 @@ static const struct file_operations i915_ring_stop_fops = {
        .llseek = default_llseek,
 };
 
+#define DROP_UNBOUND 0x1
+#define DROP_BOUND 0x2
+#define DROP_RETIRE 0x4
+#define DROP_ACTIVE 0x8
+#define DROP_ALL (DROP_UNBOUND | \
+                 DROP_BOUND | \
+                 DROP_RETIRE | \
+                 DROP_ACTIVE)
+static ssize_t
+i915_drop_caches_read(struct file *filp,
+                     char __user *ubuf,
+                     size_t max,
+                     loff_t *ppos)
+{
+       char buf[20];
+       int len;
+
+       len = snprintf(buf, sizeof(buf), "0x%08x\n", DROP_ALL);
+       if (len > sizeof(buf))
+               len = sizeof(buf);
+
+       return simple_read_from_buffer(ubuf, max, ppos, buf, len);
+}
+
+static ssize_t
+i915_drop_caches_write(struct file *filp,
+                      const char __user *ubuf,
+                      size_t cnt,
+                      loff_t *ppos)
+{
+       struct drm_device *dev = filp->private_data;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_i915_gem_object *obj, *next;
+       char buf[20];
+       int val = 0, ret;
+
+       if (cnt > 0) {
+               if (cnt > sizeof(buf) - 1)
+                       return -EINVAL;
+
+               if (copy_from_user(buf, ubuf, cnt))
+                       return -EFAULT;
+               buf[cnt] = 0;
+
+               val = simple_strtoul(buf, NULL, 0);
+       }
+
+       DRM_DEBUG_DRIVER("Dropping caches: 0x%08x\n", val);
+
+       /* No need to check and wait for gpu resets, only libdrm auto-restarts
+        * on ioctls on -EAGAIN. */
+       ret = mutex_lock_interruptible(&dev->struct_mutex);
+       if (ret)
+               return ret;
+
+       if (val & DROP_ACTIVE) {
+               ret = i915_gpu_idle(dev);
+               if (ret)
+                       goto unlock;
+       }
+
+       if (val & (DROP_RETIRE | DROP_ACTIVE))
+               i915_gem_retire_requests(dev);
+
+       if (val & DROP_BOUND) {
+               list_for_each_entry_safe(obj, next, &dev_priv->mm.inactive_list, mm_list)
+                       if (obj->pin_count == 0) {
+                               ret = i915_gem_object_unbind(obj);
+                               if (ret)
+                                       goto unlock;
+                       }
+       }
+
+       if (val & DROP_UNBOUND) {
+               list_for_each_entry_safe(obj, next, &dev_priv->mm.unbound_list, gtt_list)
+                       if (obj->pages_pin_count == 0) {
+                               ret = i915_gem_object_put_pages(obj);
+                               if (ret)
+                                       goto unlock;
+                       }
+       }
+
+unlock:
+       mutex_unlock(&dev->struct_mutex);
+
+       return ret ?: cnt;
+}
+
+static const struct file_operations i915_drop_caches_fops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read = i915_drop_caches_read,
+       .write = i915_drop_caches_write,
+       .llseek = default_llseek,
+};
+
 static ssize_t
 i915_max_freq_read(struct file *filp,
                   char __user *ubuf,
@@ -2104,12 +2290,24 @@ int i915_debugfs_init(struct drm_minor *minor)
        if (ret)
                return ret;
 
+       ret = i915_debugfs_create(minor->debugfs_root, minor,
+                                 "i915_gem_drop_caches",
+                                 &i915_drop_caches_fops);
+       if (ret)
+               return ret;
+
        ret = i915_debugfs_create(minor->debugfs_root, minor,
                                  "i915_error_state",
                                  &i915_error_state_fops);
        if (ret)
                return ret;
 
+       ret = i915_debugfs_create(minor->debugfs_root, minor,
+                                "i915_next_seqno",
+                                &i915_next_seqno_fops);
+       if (ret)
+               return ret;
+
        return drm_debugfs_create_files(i915_debugfs_list,
                                        I915_DEBUGFS_ENTRIES,
                                        minor->debugfs_root, minor);
@@ -2129,10 +2327,14 @@ void i915_debugfs_cleanup(struct drm_minor *minor)
                                 1, minor);
        drm_debugfs_remove_files((struct drm_info_list *) &i915_cache_sharing_fops,
                                 1, minor);
+       drm_debugfs_remove_files((struct drm_info_list *) &i915_drop_caches_fops,
+                                1, minor);
        drm_debugfs_remove_files((struct drm_info_list *) &i915_ring_stop_fops,
                                 1, minor);
        drm_debugfs_remove_files((struct drm_info_list *) &i915_error_state_fops,
                                 1, minor);
+       drm_debugfs_remove_files((struct drm_info_list *) &i915_next_seqno_fops,
+                                1, minor);
 }
 
 #endif /* CONFIG_DEBUG_FS */
index 99daa896105d0b221d084ab1ed4b0c839983dcdd..4fa6beb14c7741bf42a1f30a766b855d3494c406 100644 (file)
@@ -992,6 +992,12 @@ static int i915_getparam(struct drm_device *dev, void *data,
        case I915_PARAM_HAS_PINNED_BATCHES:
                value = 1;
                break;
+       case I915_PARAM_HAS_EXEC_NO_RELOC:
+               value = 1;
+               break;
+       case I915_PARAM_HAS_EXEC_HANDLE_LUT:
+               value = 1;
+               break;
        default:
                DRM_DEBUG_DRIVER("Unknown parameter %d\n",
                                 param->param);
@@ -1070,7 +1076,7 @@ static int i915_set_status_page(struct drm_device *dev, void *data,
        ring->status_page.gfx_addr = hws->addr & (0x1ffff<<12);
 
        dev_priv->dri1.gfx_hws_cpu_addr =
-               ioremap_wc(dev_priv->mm.gtt_base_addr + hws->addr, 4096);
+               ioremap_wc(dev_priv->gtt.mappable_base + hws->addr, 4096);
        if (dev_priv->dri1.gfx_hws_cpu_addr == NULL) {
                i915_dma_cleanup(dev);
                ring->status_page.gfx_addr = 0;
@@ -1297,19 +1303,21 @@ static int i915_load_modeset_init(struct drm_device *dev)
        if (ret)
                goto cleanup_vga_switcheroo;
 
+       ret = drm_irq_install(dev);
+       if (ret)
+               goto cleanup_gem_stolen;
+
+       /* Important: The output setup functions called by modeset_init need
+        * working irqs for e.g. gmbus and dp aux transfers. */
        intel_modeset_init(dev);
 
        ret = i915_gem_init(dev);
        if (ret)
-               goto cleanup_gem_stolen;
-
-       intel_modeset_gem_init(dev);
+               goto cleanup_irq;
 
        INIT_WORK(&dev_priv->console_resume_work, intel_console_resume);
 
-       ret = drm_irq_install(dev);
-       if (ret)
-               goto cleanup_gem;
+       intel_modeset_gem_init(dev);
 
        /* Always safe in the mode setting case. */
        /* FIXME: do pre/post-mode set stuff in core KMS code */
@@ -1317,7 +1325,25 @@ static int i915_load_modeset_init(struct drm_device *dev)
 
        ret = intel_fbdev_init(dev);
        if (ret)
-               goto cleanup_irq;
+               goto cleanup_gem;
+
+       /* Only enable hotplug handling once the fbdev is fully set up. */
+       intel_hpd_init(dev);
+
+       /*
+        * Some ports require correctly set-up hpd registers for detection to
+        * work properly (leading to ghost connected connector status), e.g. VGA
+        * on gm45.  Hence we can only set up the initial fbdev config after hpd
+        * irqs are fully enabled. Now we should scan for the initial config
+        * only once hotplug handling is enabled, but due to screwed-up locking
+        * around kms/fbdev init we can't protect the fdbev initial config
+        * scanning against hotplug events. Hence do this first and ignore the
+        * tiny window where we will loose hotplug notifactions.
+        */
+       intel_fbdev_initial_config(dev);
+
+       /* Only enable hotplug handling once the fbdev is fully set up. */
+       dev_priv->enable_hotplug_processing = true;
 
        drm_kms_helper_poll_init(dev);
 
@@ -1326,13 +1352,13 @@ static int i915_load_modeset_init(struct drm_device *dev)
 
        return 0;
 
-cleanup_irq:
-       drm_irq_uninstall(dev);
 cleanup_gem:
        mutex_lock(&dev->struct_mutex);
        i915_gem_cleanup_ringbuffer(dev);
        mutex_unlock(&dev->struct_mutex);
        i915_gem_cleanup_aliasing_ppgtt(dev);
+cleanup_irq:
+       drm_irq_uninstall(dev);
 cleanup_gem_stolen:
        i915_gem_cleanup_stolen(dev);
 cleanup_vga_switcheroo:
@@ -1400,9 +1426,9 @@ static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv)
        if (!ap)
                return;
 
-       ap->ranges[0].base = dev_priv->mm.gtt->gma_bus_addr;
-       ap->ranges[0].size =
-               dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT;
+       ap->ranges[0].base = dev_priv->gtt.mappable_base;
+       ap->ranges[0].size = dev_priv->gtt.mappable_end - dev_priv->gtt.start;
+
        primary =
                pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
 
@@ -1516,18 +1542,17 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
                goto put_gmch;
        }
 
-       aperture_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT;
-       dev_priv->mm.gtt_base_addr = dev_priv->mm.gtt->gma_bus_addr;
+       aperture_size = dev_priv->gtt.mappable_end;
 
-       dev_priv->mm.gtt_mapping =
-               io_mapping_create_wc(dev_priv->mm.gtt_base_addr,
+       dev_priv->gtt.mappable =
+               io_mapping_create_wc(dev_priv->gtt.mappable_base,
                                     aperture_size);
-       if (dev_priv->mm.gtt_mapping == NULL) {
+       if (dev_priv->gtt.mappable == NULL) {
                ret = -EIO;
                goto out_rmmap;
        }
 
-       i915_mtrr_setup(dev_priv, dev_priv->mm.gtt_base_addr,
+       i915_mtrr_setup(dev_priv, dev_priv->gtt.mappable_base,
                        aperture_size);
 
        /* The i915 workqueue is primarily used for batched retirement of
@@ -1580,11 +1605,12 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
                pci_enable_msi(dev->pdev);
 
        spin_lock_init(&dev_priv->irq_lock);
-       spin_lock_init(&dev_priv->error_lock);
+       spin_lock_init(&dev_priv->gpu_error.lock);
        spin_lock_init(&dev_priv->rps.lock);
-       spin_lock_init(&dev_priv->dpio_lock);
+       mutex_init(&dev_priv->dpio_lock);
 
        mutex_init(&dev_priv->rps.hw_lock);
+       mutex_init(&dev_priv->modeset_restore_lock);
 
        if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev))
                dev_priv->num_pipe = 3;
@@ -1614,9 +1640,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
        intel_opregion_init(dev);
        acpi_video_register();
 
-       setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed,
-                   (unsigned long) dev);
-
        if (IS_GEN5(dev))
                intel_gpu_ips_init(dev_priv);
 
@@ -1635,15 +1658,15 @@ out_gem_unload:
 out_mtrrfree:
        if (dev_priv->mm.gtt_mtrr >= 0) {
                mtrr_del(dev_priv->mm.gtt_mtrr,
-                        dev_priv->mm.gtt_base_addr,
+                        dev_priv->gtt.mappable_base,
                         aperture_size);
                dev_priv->mm.gtt_mtrr = -1;
        }
-       io_mapping_free(dev_priv->mm.gtt_mapping);
+       io_mapping_free(dev_priv->gtt.mappable);
 out_rmmap:
        pci_iounmap(dev->pdev, dev_priv->regs);
 put_gmch:
-       i915_gem_gtt_fini(dev);
+       dev_priv->gtt.gtt_remove(dev);
 put_bridge:
        pci_dev_put(dev_priv->bridge_dev);
 free_priv:
@@ -1673,11 +1696,11 @@ int i915_driver_unload(struct drm_device *dev)
        /* Cancel the retire work handler, which should be idle now. */
        cancel_delayed_work_sync(&dev_priv->mm.retire_work);
 
-       io_mapping_free(dev_priv->mm.gtt_mapping);
+       io_mapping_free(dev_priv->gtt.mappable);
        if (dev_priv->mm.gtt_mtrr >= 0) {
                mtrr_del(dev_priv->mm.gtt_mtrr,
-                        dev_priv->mm.gtt_base_addr,
-                        dev_priv->mm.gtt->gtt_mappable_entries * PAGE_SIZE);
+                        dev_priv->gtt.mappable_base,
+                        dev_priv->gtt.mappable_end);
                dev_priv->mm.gtt_mtrr = -1;
        }
 
@@ -1703,8 +1726,8 @@ int i915_driver_unload(struct drm_device *dev)
        }
 
        /* Free error state after interrupts are fully disabled. */
-       del_timer_sync(&dev_priv->hangcheck_timer);
-       cancel_work_sync(&dev_priv->error_work);
+       del_timer_sync(&dev_priv->gpu_error.hangcheck_timer);
+       cancel_work_sync(&dev_priv->gpu_error.work);
        i915_destroy_error_state(dev);
 
        if (dev->pdev->msi_enabled)
@@ -1723,9 +1746,6 @@ int i915_driver_unload(struct drm_device *dev)
                mutex_unlock(&dev->struct_mutex);
                i915_gem_cleanup_aliasing_ppgtt(dev);
                i915_gem_cleanup_stolen(dev);
-               drm_mm_takedown(&dev_priv->mm.stolen);
-
-               intel_cleanup_overlay(dev);
 
                if (!I915_NEED_GFX_HWS(dev))
                        i915_free_hws(dev);
@@ -1738,6 +1758,10 @@ int i915_driver_unload(struct drm_device *dev)
        intel_teardown_mchbar(dev);
 
        destroy_workqueue(dev_priv->wq);
+       pm_qos_remove_request(&dev_priv->pm_qos);
+
+       if (dev_priv->slab)
+               kmem_cache_destroy(dev_priv->slab);
 
        pci_dev_put(dev_priv->bridge_dev);
        kfree(dev->dev_private);
index 117265840b1ff2fcbd845c618acf8d3b592cd4a5..c5b8c81b94402321f599f4c3e5d99b88ee82a9e3 100644 (file)
@@ -276,6 +276,7 @@ static const struct intel_device_info intel_valleyview_m_info = {
        .has_bsd_ring = 1,
        .has_blt_ring = 1,
        .is_valleyview = 1,
+       .display_mmio_offset = VLV_DISPLAY_BASE,
 };
 
 static const struct intel_device_info intel_valleyview_d_info = {
@@ -285,6 +286,7 @@ static const struct intel_device_info intel_valleyview_d_info = {
        .has_bsd_ring = 1,
        .has_blt_ring = 1,
        .is_valleyview = 1,
+       .display_mmio_offset = VLV_DISPLAY_BASE,
 };
 
 static const struct intel_device_info intel_haswell_d_info = {
@@ -468,6 +470,13 @@ static int i915_drm_freeze(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
+       /* ignore lid events during suspend */
+       mutex_lock(&dev_priv->modeset_restore_lock);
+       dev_priv->modeset_restore = MODESET_SUSPENDED;
+       mutex_unlock(&dev_priv->modeset_restore_lock);
+
+       intel_set_power_well(dev, true);
+
        drm_kms_helper_poll_disable(dev);
 
        pci_save_state(dev->pdev);
@@ -492,9 +501,6 @@ static int i915_drm_freeze(struct drm_device *dev)
 
        intel_opregion_fini(dev);
 
-       /* Modeset on resume, not lid events */
-       dev_priv->modeset_on_lid = 0;
-
        console_lock();
        intel_fbdev_set_suspend(dev, 1);
        console_unlock();
@@ -565,12 +571,11 @@ static int __i915_drm_thaw(struct drm_device *dev)
                intel_modeset_init_hw(dev);
                intel_modeset_setup_hw_state(dev, false);
                drm_irq_install(dev);
+               intel_hpd_init(dev);
        }
 
        intel_opregion_init(dev);
 
-       dev_priv->modeset_on_lid = 0;
-
        /*
         * The console lock can be pretty contented on resume due
         * to all the printk activity.  Try to keep it out of the hot
@@ -583,6 +588,9 @@ static int __i915_drm_thaw(struct drm_device *dev)
                schedule_work(&dev_priv->console_resume_work);
        }
 
+       mutex_lock(&dev_priv->modeset_restore_lock);
+       dev_priv->modeset_restore = MODESET_DONE;
+       mutex_unlock(&dev_priv->modeset_restore_lock);
        return error;
 }
 
@@ -778,9 +786,9 @@ int intel_gpu_reset(struct drm_device *dev)
        }
 
        /* Also reset the gpu hangman. */
-       if (dev_priv->stop_rings) {
+       if (dev_priv->gpu_error.stop_rings) {
                DRM_DEBUG("Simulated gpu hang, resetting stop_rings\n");
-               dev_priv->stop_rings = 0;
+               dev_priv->gpu_error.stop_rings = 0;
                if (ret == -ENODEV) {
                        DRM_ERROR("Reset not implemented, but ignoring "
                                  "error for simulated gpu hangs\n");
@@ -819,12 +827,12 @@ int i915_reset(struct drm_device *dev)
        i915_gem_reset(dev);
 
        ret = -ENODEV;
-       if (get_seconds() - dev_priv->last_gpu_reset < 5)
+       if (get_seconds() - dev_priv->gpu_error.last_reset < 5)
                DRM_ERROR("GPU hanging too fast, declaring wedged!\n");
        else
                ret = intel_gpu_reset(dev);
 
-       dev_priv->last_gpu_reset = get_seconds();
+       dev_priv->gpu_error.last_reset = get_seconds();
        if (ret) {
                DRM_ERROR("Failed to reset chip.\n");
                mutex_unlock(&dev->struct_mutex);
@@ -870,6 +878,7 @@ int i915_reset(struct drm_device *dev)
 
                drm_irq_uninstall(dev);
                drm_irq_install(dev);
+               intel_hpd_init(dev);
        } else {
                mutex_unlock(&dev->struct_mutex);
        }
@@ -1113,102 +1122,6 @@ MODULE_LICENSE("GPL and additional rights");
        ((HAS_FORCE_WAKE((dev_priv)->dev)) && \
         ((reg) < 0x40000) &&            \
         ((reg) != FORCEWAKE))
-
-static bool IS_DISPLAYREG(u32 reg)
-{
-       /*
-        * This should make it easier to transition modules over to the
-        * new register block scheme, since we can do it incrementally.
-        */
-       if (reg >= VLV_DISPLAY_BASE)
-               return false;
-
-       if (reg >= RENDER_RING_BASE &&
-           reg < RENDER_RING_BASE + 0xff)
-               return false;
-       if (reg >= GEN6_BSD_RING_BASE &&
-           reg < GEN6_BSD_RING_BASE + 0xff)
-               return false;
-       if (reg >= BLT_RING_BASE &&
-           reg < BLT_RING_BASE + 0xff)
-               return false;
-
-       if (reg == PGTBL_ER)
-               return false;
-
-       if (reg >= IPEIR_I965 &&
-           reg < HWSTAM)
-               return false;
-
-       if (reg == MI_MODE)
-               return false;
-
-       if (reg == GFX_MODE_GEN7)
-               return false;
-
-       if (reg == RENDER_HWS_PGA_GEN7 ||
-           reg == BSD_HWS_PGA_GEN7 ||
-           reg == BLT_HWS_PGA_GEN7)
-               return false;
-
-       if (reg == GEN6_BSD_SLEEP_PSMI_CONTROL ||
-           reg == GEN6_BSD_RNCID)
-               return false;
-
-       if (reg == GEN6_BLITTER_ECOSKPD)
-               return false;
-
-       if (reg >= 0x4000c &&
-           reg <= 0x4002c)
-               return false;
-
-       if (reg >= 0x4f000 &&
-           reg <= 0x4f08f)
-               return false;
-
-       if (reg >= 0x4f100 &&
-           reg <= 0x4f11f)
-               return false;
-
-       if (reg >= VLV_MASTER_IER &&
-           reg <= GEN6_PMIER)
-               return false;
-
-       if (reg >= FENCE_REG_SANDYBRIDGE_0 &&
-           reg < (FENCE_REG_SANDYBRIDGE_0 + (16*8)))
-               return false;
-
-       if (reg >= VLV_IIR_RW &&
-           reg <= VLV_ISR)
-               return false;
-
-       if (reg == FORCEWAKE_VLV ||
-           reg == FORCEWAKE_ACK_VLV)
-               return false;
-
-       if (reg == GEN6_GDRST)
-               return false;
-
-       switch (reg) {
-       case _3D_CHICKEN3:
-       case IVB_CHICKEN3:
-       case GEN7_COMMON_SLICE_CHICKEN1:
-       case GEN7_L3CNTLREG1:
-       case GEN7_L3_CHICKEN_MODE_REGISTER:
-       case GEN7_ROW_CHICKEN2:
-       case GEN7_L3SQCREG4:
-       case GEN7_SQ_CHICKEN_MBCUNIT_CONFIG:
-       case GEN7_HALF_SLICE_CHICKEN1:
-       case GEN6_MBCTL:
-       case GEN6_UCGCTL2:
-               return false;
-       default:
-               break;
-       }
-
-       return true;
-}
-
 static void
 ilk_dummy_write(struct drm_i915_private *dev_priv)
 {
@@ -1232,8 +1145,6 @@ u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg) { \
                if (dev_priv->forcewake_count == 0) \
                        dev_priv->gt.force_wake_put(dev_priv); \
                spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags); \
-       } else if (IS_VALLEYVIEW(dev_priv->dev) && IS_DISPLAYREG(reg)) { \
-               val = read##y(dev_priv->regs + reg + 0x180000);         \
        } else { \
                val = read##y(dev_priv->regs + reg); \
        } \
@@ -1260,11 +1171,7 @@ void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val) { \
                DRM_ERROR("Unknown unclaimed register before writing to %x\n", reg); \
                I915_WRITE_NOTRACE(GEN7_ERR_INT, ERR_INT_MMIO_UNCLAIMED); \
        } \
-       if (IS_VALLEYVIEW(dev_priv->dev) && IS_DISPLAYREG(reg)) { \
-               write##y(val, dev_priv->regs + reg + 0x180000);         \
-       } else {                                                        \
-               write##y(val, dev_priv->regs + reg);                    \
-       }                                                               \
+       write##y(val, dev_priv->regs + reg); \
        if (unlikely(__fifo_ret)) { \
                gen6_gt_check_fifodbg(dev_priv); \
        } \
index 12ab3bdea54dca227e8bd5f3f62cf00a31ce85ba..e95337c974594d34c936d071975f5040c78754d1 100644 (file)
@@ -30,6 +30,8 @@
 #ifndef _I915_DRV_H_
 #define _I915_DRV_H_
 
+#include <uapi/drm/i915_drm.h>
+
 #include "i915_reg.h"
 #include "intel_bios.h"
 #include "intel_ringbuffer.h"
@@ -40,6 +42,7 @@
 #include <linux/backlight.h>
 #include <linux/intel-iommu.h>
 #include <linux/kref.h>
+#include <linux/pm_qos.h>
 
 /* General customization:
  */
@@ -83,7 +86,12 @@ enum port {
 };
 #define port_name(p) ((p) + 'A')
 
-#define I915_GEM_GPU_DOMAINS   (~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT))
+#define I915_GEM_GPU_DOMAINS \
+       (I915_GEM_DOMAIN_RENDER | \
+        I915_GEM_DOMAIN_SAMPLER | \
+        I915_GEM_DOMAIN_COMMAND | \
+        I915_GEM_DOMAIN_INSTRUCTION | \
+        I915_GEM_DOMAIN_VERTEX)
 
 #define for_each_pipe(p) for ((p) = 0; (p) < dev_priv->num_pipe; (p)++)
 
@@ -101,6 +109,19 @@ struct intel_pch_pll {
 };
 #define I915_NUM_PLLS 2
 
+/* Used by dp and fdi links */
+struct intel_link_m_n {
+       uint32_t        tu;
+       uint32_t        gmch_m;
+       uint32_t        gmch_n;
+       uint32_t        link_m;
+       uint32_t        link_n;
+};
+
+void intel_link_compute_m_n(int bpp, int nlanes,
+                           int pixel_clock, int link_clock,
+                           struct intel_link_m_n *m_n);
+
 struct intel_ddi_plls {
        int spll_refcount;
        int wrpll1_refcount;
@@ -279,6 +300,7 @@ struct drm_i915_display_funcs {
                          struct drm_i915_gem_object *obj);
        int (*update_plane)(struct drm_crtc *crtc, struct drm_framebuffer *fb,
                            int x, int y);
+       void (*hpd_irq_setup)(struct drm_device *dev);
        /* clock updates for mode set */
        /* cursor updates */
        /* render clock increase/decrease */
@@ -318,6 +340,7 @@ struct drm_i915_gt_funcs {
        DEV_INFO_FLAG(has_llc)
 
 struct intel_device_info {
+       u32 display_mmio_offset;
        u8 gen;
        u8 is_mobile:1;
        u8 is_i85x:1;
@@ -345,6 +368,50 @@ struct intel_device_info {
        u8 has_llc:1;
 };
 
+enum i915_cache_level {
+       I915_CACHE_NONE = 0,
+       I915_CACHE_LLC,
+       I915_CACHE_LLC_MLC, /* gen6+, in docs at least! */
+};
+
+/* The Graphics Translation Table is the way in which GEN hardware translates a
+ * Graphics Virtual Address into a Physical Address. In addition to the normal
+ * collateral associated with any va->pa translations GEN hardware also has a
+ * portion of the GTT which can be mapped by the CPU and remain both coherent
+ * and correct (in cases like swizzling). That region is referred to as GMADR in
+ * the spec.
+ */
+struct i915_gtt {
+       unsigned long start;            /* Start offset of used GTT */
+       size_t total;                   /* Total size GTT can map */
+       size_t stolen_size;             /* Total size of stolen memory */
+
+       unsigned long mappable_end;     /* End offset that we can CPU map */
+       struct io_mapping *mappable;    /* Mapping to our CPU mappable region */
+       phys_addr_t mappable_base;      /* PA of our GMADR */
+
+       /** "Graphics Stolen Memory" holds the global PTEs */
+       void __iomem *gsm;
+
+       bool do_idle_maps;
+       dma_addr_t scratch_page_dma;
+       struct page *scratch_page;
+
+       /* global gtt ops */
+       int (*gtt_probe)(struct drm_device *dev, size_t *gtt_total,
+                         size_t *stolen, phys_addr_t *mappable_base,
+                         unsigned long *mappable_end);
+       void (*gtt_remove)(struct drm_device *dev);
+       void (*gtt_clear_range)(struct drm_device *dev,
+                               unsigned int first_entry,
+                               unsigned int num_entries);
+       void (*gtt_insert_entries)(struct drm_device *dev,
+                                  struct sg_table *st,
+                                  unsigned int pg_start,
+                                  enum i915_cache_level cache_level);
+};
+#define gtt_total_entries(gtt) ((gtt).total >> PAGE_SHIFT)
+
 #define I915_PPGTT_PD_ENTRIES 512
 #define I915_PPGTT_PT_ENTRIES 1024
 struct i915_hw_ppgtt {
@@ -354,6 +421,16 @@ struct i915_hw_ppgtt {
        uint32_t pd_offset;
        dma_addr_t *pt_dma_addr;
        dma_addr_t scratch_page_dma_addr;
+
+       /* pte functions, mirroring the interface of the global gtt. */
+       void (*clear_range)(struct i915_hw_ppgtt *ppgtt,
+                           unsigned int first_entry,
+                           unsigned int num_entries);
+       void (*insert_entries)(struct i915_hw_ppgtt *ppgtt,
+                              struct sg_table *st,
+                              unsigned int pg_start,
+                              enum i915_cache_level cache_level);
+       void (*cleanup)(struct i915_hw_ppgtt *ppgtt);
 };
 
 
@@ -580,6 +657,9 @@ struct intel_gen6_power_mgmt {
        struct mutex hw_lock;
 };
 
+/* defined intel_pm.c */
+extern spinlock_t mchdev_lock;
+
 struct intel_ilk_power_mgmt {
        u8 cur_delay;
        u8 min_delay;
@@ -620,8 +700,162 @@ struct intel_l3_parity {
        struct work_struct error_work;
 };
 
+struct i915_gem_mm {
+       /** Memory allocator for GTT stolen memory */
+       struct drm_mm stolen;
+       /** Memory allocator for GTT */
+       struct drm_mm gtt_space;
+       /** List of all objects in gtt_space. Used to restore gtt
+        * mappings on resume */
+       struct list_head bound_list;
+       /**
+        * List of objects which are not bound to the GTT (thus
+        * are idle and not used by the GPU) but still have
+        * (presumably uncached) pages still attached.
+        */
+       struct list_head unbound_list;
+
+       /** Usable portion of the GTT for GEM */
+       unsigned long stolen_base; /* limited to low memory (32-bit) */
+
+       int gtt_mtrr;
+
+       /** PPGTT used for aliasing the PPGTT with the GTT */
+       struct i915_hw_ppgtt *aliasing_ppgtt;
+
+       struct shrinker inactive_shrinker;
+       bool shrinker_no_lock_stealing;
+
+       /**
+        * List of objects currently involved in rendering.
+        *
+        * Includes buffers having the contents of their GPU caches
+        * flushed, not necessarily primitives.  last_rendering_seqno
+        * represents when the rendering involved will be completed.
+        *
+        * A reference is held on the buffer while on this list.
+        */
+       struct list_head active_list;
+
+       /**
+        * LRU list of objects which are not in the ringbuffer and
+        * are ready to unbind, but are still in the GTT.
+        *
+        * last_rendering_seqno is 0 while an object is in this list.
+        *
+        * A reference is not held on the buffer while on this list,
+        * as merely being GTT-bound shouldn't prevent its being
+        * freed, and we'll pull it off the list in the free path.
+        */
+       struct list_head inactive_list;
+
+       /** LRU list of objects with fence regs on them. */
+       struct list_head fence_list;
+
+       /**
+        * We leave the user IRQ off as much as possible,
+        * but this means that requests will finish and never
+        * be retired once the system goes idle. Set a timer to
+        * fire periodically while the ring is running. When it
+        * fires, go retire requests.
+        */
+       struct delayed_work retire_work;
+
+       /**
+        * Are we in a non-interruptible section of code like
+        * modesetting?
+        */
+       bool interruptible;
+
+       /**
+        * Flag if the X Server, and thus DRM, is not currently in
+        * control of the device.
+        *
+        * This is set between LeaveVT and EnterVT.  It needs to be
+        * replaced with a semaphore.  It also needs to be
+        * transitioned away from for kernel modesetting.
+        */
+       int suspended;
+
+       /** Bit 6 swizzling required for X tiling */
+       uint32_t bit_6_swizzle_x;
+       /** Bit 6 swizzling required for Y tiling */
+       uint32_t bit_6_swizzle_y;
+
+       /* storage for physical objects */
+       struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT];
+
+       /* accounting, useful for userland debugging */
+       size_t object_memory;
+       u32 object_count;
+};
+
+struct i915_gpu_error {
+       /* For hangcheck timer */
+#define DRM_I915_HANGCHECK_PERIOD 1500 /* in ms */
+#define DRM_I915_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD)
+       struct timer_list hangcheck_timer;
+       int hangcheck_count;
+       uint32_t last_acthd[I915_NUM_RINGS];
+       uint32_t prev_instdone[I915_NUM_INSTDONE_REG];
+
+       /* For reset and error_state handling. */
+       spinlock_t lock;
+       /* Protected by the above dev->gpu_error.lock. */
+       struct drm_i915_error_state *first_error;
+       struct work_struct work;
+
+       unsigned long last_reset;
+
+       /**
+        * State variable and reset counter controlling the reset flow
+        *
+        * Upper bits are for the reset counter.  This counter is used by the
+        * wait_seqno code to race-free noticed that a reset event happened and
+        * that it needs to restart the entire ioctl (since most likely the
+        * seqno it waited for won't ever signal anytime soon).
+        *
+        * This is important for lock-free wait paths, where no contended lock
+        * naturally enforces the correct ordering between the bail-out of the
+        * waiter and the gpu reset work code.
+        *
+        * Lowest bit controls the reset state machine: Set means a reset is in
+        * progress. This state will (presuming we don't have any bugs) decay
+        * into either unset (successful reset) or the special WEDGED value (hw
+        * terminally sour). All waiters on the reset_queue will be woken when
+        * that happens.
+        */
+       atomic_t reset_counter;
+
+       /**
+        * Special values/flags for reset_counter
+        *
+        * Note that the code relies on
+        *      I915_WEDGED & I915_RESET_IN_PROGRESS_FLAG
+        * being true.
+        */
+#define I915_RESET_IN_PROGRESS_FLAG    1
+#define I915_WEDGED                    0xffffffff
+
+       /**
+        * Waitqueue to signal when the reset has completed. Used by clients
+        * that wait for dev_priv->mm.wedged to settle.
+        */
+       wait_queue_head_t reset_queue;
+
+       /* For gpu hang simulation. */
+       unsigned int stop_rings;
+};
+
+enum modeset_restore {
+       MODESET_ON_LID_OPEN,
+       MODESET_DONE,
+       MODESET_SUSPENDED,
+};
+
 typedef struct drm_i915_private {
        struct drm_device *dev;
+       struct kmem_cache *slab;
 
        const struct intel_device_info *info;
 
@@ -636,10 +870,11 @@ typedef struct drm_i915_private {
        /** forcewake_count is protected by gt_lock */
        unsigned forcewake_count;
        /** gt_lock is also taken in irq contexts. */
-       struct spinlock gt_lock;
+       spinlock_t gt_lock;
 
        struct intel_gmbus gmbus[GMBUS_NUM_PORTS];
 
+
        /** gmbus_mutex protects against concurrent usage of the single hw gmbus
         * controller on different i2c buses. */
        struct mutex gmbus_mutex;
@@ -649,9 +884,11 @@ typedef struct drm_i915_private {
         */
        uint32_t gpio_mmio_base;
 
+       wait_queue_head_t gmbus_wait_queue;
+
        struct pci_dev *bridge_dev;
        struct intel_ring_buffer ring[I915_NUM_RINGS];
-       uint32_t next_seqno;
+       uint32_t last_seqno, next_seqno;
 
        drm_dma_handle_t *status_page_dmah;
        struct resource mch_res;
@@ -661,31 +898,24 @@ typedef struct drm_i915_private {
        /* protects the irq masks */
        spinlock_t irq_lock;
 
+       /* To control wakeup latency, e.g. for irq-driven dp aux transfers. */
+       struct pm_qos_request pm_qos;
+
        /* DPIO indirect register protection */
-       spinlock_t dpio_lock;
+       struct mutex dpio_lock;
 
        /** Cached value of IMR to avoid reads in updating the bitfield */
        u32 pipestat[2];
        u32 irq_mask;
        u32 gt_irq_mask;
-       u32 pch_irq_mask;
 
        u32 hotplug_supported_mask;
        struct work_struct hotplug_work;
+       bool enable_hotplug_processing;
 
        int num_pipe;
        int num_pch_pll;
 
-       /* For hangcheck timer */
-#define DRM_I915_HANGCHECK_PERIOD 1500 /* in ms */
-#define DRM_I915_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD)
-       struct timer_list hangcheck_timer;
-       int hangcheck_count;
-       uint32_t last_acthd[I915_NUM_RINGS];
-       uint32_t prev_instdone[I915_NUM_INSTDONE_REG];
-
-       unsigned int stop_rings;
-
        unsigned long cfb_size;
        unsigned int cfb_fb;
        enum plane cfb_plane;
@@ -696,7 +926,7 @@ typedef struct drm_i915_private {
 
        /* overlay */
        struct intel_overlay *overlay;
-       bool sprite_scaling_enabled;
+       unsigned int sprite_scaling_enabled;
 
        /* LVDS info */
        int backlight_level;  /* restore backlight to this value */
@@ -713,7 +943,6 @@ typedef struct drm_i915_private {
        unsigned int display_clock_mode:1;
        int lvds_ssc_freq;
        unsigned int bios_lvds_val; /* initial [PCH_]LVDS reg val in VBIOS */
-       unsigned int lvds_val; /* used for checking LVDS channel mode */
        struct {
                int rate;
                int lanes;
@@ -734,11 +963,6 @@ typedef struct drm_i915_private {
 
        unsigned int fsb_freq, mem_freq, is_ddr3;
 
-       spinlock_t error_lock;
-       /* Protected by dev->error_lock. */
-       struct drm_i915_error_state *first_error;
-       struct work_struct error_work;
-       struct completion error_completion;
        struct workqueue_struct *wq;
 
        /* Display functions */
@@ -750,115 +974,12 @@ typedef struct drm_i915_private {
 
        unsigned long quirks;
 
-       /* Register state */
-       bool modeset_on_lid;
+       enum modeset_restore modeset_restore;
+       struct mutex modeset_restore_lock;
 
-       struct {
-               /** Bridge to intel-gtt-ko */
-               struct intel_gtt *gtt;
-               /** Memory allocator for GTT stolen memory */
-               struct drm_mm stolen;
-               /** Memory allocator for GTT */
-               struct drm_mm gtt_space;
-               /** List of all objects in gtt_space. Used to restore gtt
-                * mappings on resume */
-               struct list_head bound_list;
-               /**
-                * List of objects which are not bound to the GTT (thus
-                * are idle and not used by the GPU) but still have
-                * (presumably uncached) pages still attached.
-                */
-               struct list_head unbound_list;
-
-               /** Usable portion of the GTT for GEM */
-               unsigned long gtt_start;
-               unsigned long gtt_mappable_end;
-               unsigned long gtt_end;
-
-               struct io_mapping *gtt_mapping;
-               phys_addr_t gtt_base_addr;
-               int gtt_mtrr;
-
-               /** PPGTT used for aliasing the PPGTT with the GTT */
-               struct i915_hw_ppgtt *aliasing_ppgtt;
-
-               struct shrinker inactive_shrinker;
-               bool shrinker_no_lock_stealing;
-
-               /**
-                * List of objects currently involved in rendering.
-                *
-                * Includes buffers having the contents of their GPU caches
-                * flushed, not necessarily primitives.  last_rendering_seqno
-                * represents when the rendering involved will be completed.
-                *
-                * A reference is held on the buffer while on this list.
-                */
-               struct list_head active_list;
-
-               /**
-                * LRU list of objects which are not in the ringbuffer and
-                * are ready to unbind, but are still in the GTT.
-                *
-                * last_rendering_seqno is 0 while an object is in this list.
-                *
-                * A reference is not held on the buffer while on this list,
-                * as merely being GTT-bound shouldn't prevent its being
-                * freed, and we'll pull it off the list in the free path.
-                */
-               struct list_head inactive_list;
-
-               /** LRU list of objects with fence regs on them. */
-               struct list_head fence_list;
-
-               /**
-                * We leave the user IRQ off as much as possible,
-                * but this means that requests will finish and never
-                * be retired once the system goes idle. Set a timer to
-                * fire periodically while the ring is running. When it
-                * fires, go retire requests.
-                */
-               struct delayed_work retire_work;
-
-               /**
-                * Are we in a non-interruptible section of code like
-                * modesetting?
-                */
-               bool interruptible;
-
-               /**
-                * Flag if the X Server, and thus DRM, is not currently in
-                * control of the device.
-                *
-                * This is set between LeaveVT and EnterVT.  It needs to be
-                * replaced with a semaphore.  It also needs to be
-                * transitioned away from for kernel modesetting.
-                */
-               int suspended;
-
-               /**
-                * Flag if the hardware appears to be wedged.
-                *
-                * This is set when attempts to idle the device timeout.
-                * It prevents command submission from occurring and makes
-                * every pending request fail
-                */
-               atomic_t wedged;
-
-               /** Bit 6 swizzling required for X tiling */
-               uint32_t bit_6_swizzle_x;
-               /** Bit 6 swizzling required for Y tiling */
-               uint32_t bit_6_swizzle_y;
-
-               /* storage for physical objects */
-               struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT];
-
-               /* accounting, useful for userland debugging */
-               size_t gtt_total;
-               size_t mappable_gtt_total;
-               size_t object_memory;
-               u32 object_count;
-       } mm;
+       struct i915_gtt gtt;
+
+       struct i915_gem_mm mm;
 
        /* Kernel Modesetting */
 
@@ -900,7 +1021,7 @@ typedef struct drm_i915_private {
        struct drm_mm_node *compressed_fb;
        struct drm_mm_node *compressed_llb;
 
-       unsigned long last_gpu_reset;
+       struct i915_gpu_error gpu_error;
 
        /* list of fbdev register on this device */
        struct intel_fbdev *fbdev;
@@ -919,7 +1040,7 @@ typedef struct drm_i915_private {
        bool hw_contexts_disabled;
        uint32_t hw_context_size;
 
-       bool fdi_rx_polarity_reversed;
+       u32 fdi_rx_config;
 
        struct i915_suspend_saved_registers regfile;
 
@@ -940,11 +1061,7 @@ enum hdmi_force_audio {
        HDMI_AUDIO_ON,                  /* force turn on HDMI audio */
 };
 
-enum i915_cache_level {
-       I915_CACHE_NONE = 0,
-       I915_CACHE_LLC,
-       I915_CACHE_LLC_MLC, /* gen6+, in docs at least! */
-};
+#define I915_GTT_RESERVED ((struct drm_mm_node *)0x1)
 
 struct drm_i915_gem_object_ops {
        /* Interface between the GEM object and its backing storage.
@@ -971,6 +1088,8 @@ struct drm_i915_gem_object {
 
        /** Current space allocated to this object in the GTT, if any. */
        struct drm_mm_node *gtt_space;
+       /** Stolen memory for this object, instead of being backed by shmem. */
+       struct drm_mm_node *stolen;
        struct list_head gtt_list;
 
        /** This object's place on the active/inactive lists */
@@ -1096,13 +1215,6 @@ struct drm_i915_gem_object {
 
        /** for phy allocated objects */
        struct drm_i915_gem_phys_object *phys_obj;
-
-       /**
-        * Number of crtcs where this object is currently the fb, but
-        * will be page flipped away on the next vblank.  When it
-        * reaches 0, dev_priv->pending_flip_queue will be woken up.
-        */
-       atomic_t pending_flip;
 };
 #define to_gem_object(obj) (&((struct drm_i915_gem_object *)(obj))->base)
 
@@ -1141,7 +1253,7 @@ struct drm_i915_gem_request {
 
 struct drm_i915_file_private {
        struct {
-               struct spinlock lock;
+               spinlock_t lock;
                struct list_head request_list;
        } mm;
        struct idr context_idr;
@@ -1227,6 +1339,8 @@ struct drm_i915_file_private {
 
 #define HAS_PIPE_CONTROL(dev) (INTEL_INFO(dev)->gen >= 5)
 
+#define HAS_DDI(dev)           (IS_HASWELL(dev))
+
 #define INTEL_PCH_DEVICE_ID_MASK               0xff00
 #define INTEL_PCH_IBX_DEVICE_ID_TYPE           0x3b00
 #define INTEL_PCH_CPT_DEVICE_ID_TYPE           0x1c00
@@ -1323,6 +1437,7 @@ void i915_hangcheck_elapsed(unsigned long data);
 void i915_handle_error(struct drm_device *dev, bool wedged);
 
 extern void intel_irq_init(struct drm_device *dev);
+extern void intel_hpd_init(struct drm_device *dev);
 extern void intel_gt_init(struct drm_device *dev);
 extern void intel_gt_reset(struct drm_device *dev);
 
@@ -1391,18 +1506,22 @@ int i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
 int i915_gem_wait_ioctl(struct drm_device *dev, void *data,
                        struct drm_file *file_priv);
 void i915_gem_load(struct drm_device *dev);
+void *i915_gem_object_alloc(struct drm_device *dev);
+void i915_gem_object_free(struct drm_i915_gem_object *obj);
 int i915_gem_init_object(struct drm_gem_object *obj);
 void i915_gem_object_init(struct drm_i915_gem_object *obj,
                         const struct drm_i915_gem_object_ops *ops);
 struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
                                                  size_t size);
 void i915_gem_free_object(struct drm_gem_object *obj);
+
 int __must_check i915_gem_object_pin(struct drm_i915_gem_object *obj,
                                     uint32_t alignment,
                                     bool map_and_fenceable,
                                     bool nonblocking);
 void i915_gem_object_unpin(struct drm_i915_gem_object *obj);
 int __must_check i915_gem_object_unbind(struct drm_i915_gem_object *obj);
+int i915_gem_object_put_pages(struct drm_i915_gem_object *obj);
 void i915_gem_release_mmap(struct drm_i915_gem_object *obj);
 void i915_gem_lastclose(struct drm_device *dev);
 
@@ -1454,8 +1573,8 @@ i915_seqno_passed(uint32_t seq1, uint32_t seq2)
        return (int32_t)(seq1 - seq2) >= 0;
 }
 
-extern int i915_gem_get_seqno(struct drm_device *dev, u32 *seqno);
-
+int __must_check i915_gem_get_seqno(struct drm_device *dev, u32 *seqno);
+int __must_check i915_gem_set_seqno(struct drm_device *dev, u32 seqno);
 int __must_check i915_gem_object_get_fence(struct drm_i915_gem_object *obj);
 int __must_check i915_gem_object_put_fence(struct drm_i915_gem_object *obj);
 
@@ -1481,8 +1600,18 @@ i915_gem_object_unpin_fence(struct drm_i915_gem_object *obj)
 
 void i915_gem_retire_requests(struct drm_device *dev);
 void i915_gem_retire_requests_ring(struct intel_ring_buffer *ring);
-int __must_check i915_gem_check_wedge(struct drm_i915_private *dev_priv,
+int __must_check i915_gem_check_wedge(struct i915_gpu_error *error,
                                      bool interruptible);
+static inline bool i915_reset_in_progress(struct i915_gpu_error *error)
+{
+       return unlikely(atomic_read(&error->reset_counter)
+                       & I915_RESET_IN_PROGRESS_FLAG);
+}
+
+static inline bool i915_terminally_wedged(struct i915_gpu_error *error)
+{
+       return atomic_read(&error->reset_counter) == I915_WEDGED;
+}
 
 void i915_gem_reset(struct drm_device *dev);
 void i915_gem_clflush_object(struct drm_i915_gem_object *obj);
@@ -1523,9 +1652,10 @@ void i915_gem_free_all_phys_object(struct drm_device *dev);
 void i915_gem_release(struct drm_device *dev, struct drm_file *file);
 
 uint32_t
-i915_gem_get_unfenced_gtt_alignment(struct drm_device *dev,
-                                   uint32_t size,
-                                   int tiling_mode);
+i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode);
+uint32_t
+i915_gem_get_gtt_alignment(struct drm_device *dev, uint32_t size,
+                           int tiling_mode, bool fenced);
 
 int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
                                    enum i915_cache_level cache_level);
@@ -1548,7 +1678,6 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data,
                                   struct drm_file *file);
 
 /* i915_gem_gtt.c */
-int __must_check i915_gem_init_aliasing_ppgtt(struct drm_device *dev);
 void i915_gem_cleanup_aliasing_ppgtt(struct drm_device *dev);
 void i915_ppgtt_bind_object(struct i915_hw_ppgtt *ppgtt,
                            struct drm_i915_gem_object *obj,
@@ -1562,12 +1691,10 @@ void i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj,
                                enum i915_cache_level cache_level);
 void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj);
 void i915_gem_gtt_finish_object(struct drm_i915_gem_object *obj);
-void i915_gem_init_global_gtt(struct drm_device *dev,
-                             unsigned long start,
-                             unsigned long mappable_end,
-                             unsigned long end);
+void i915_gem_init_global_gtt(struct drm_device *dev);
+void i915_gem_setup_global_gtt(struct drm_device *dev, unsigned long start,
+                              unsigned long mappable_end, unsigned long end);
 int i915_gem_gtt_init(struct drm_device *dev);
-void i915_gem_gtt_fini(struct drm_device *dev);
 static inline void i915_gem_chipset_flush(struct drm_device *dev)
 {
        if (INTEL_INFO(dev)->gen < 6)
@@ -1585,9 +1712,22 @@ int i915_gem_evict_everything(struct drm_device *dev);
 
 /* i915_gem_stolen.c */
 int i915_gem_init_stolen(struct drm_device *dev);
+int i915_gem_stolen_setup_compression(struct drm_device *dev, int size);
+void i915_gem_stolen_cleanup_compression(struct drm_device *dev);
 void i915_gem_cleanup_stolen(struct drm_device *dev);
+struct drm_i915_gem_object *
+i915_gem_object_create_stolen(struct drm_device *dev, u32 size);
+void i915_gem_object_release_stolen(struct drm_i915_gem_object *obj);
 
 /* i915_gem_tiling.c */
+inline static bool i915_gem_object_needs_bit17_swizzle(struct drm_i915_gem_object *obj)
+{
+       drm_i915_private_t *dev_priv = obj->base.dev->dev_private;
+
+       return dev_priv->mm.bit_6_swizzle_x == I915_BIT_6_SWIZZLE_9_10_17 &&
+               obj->tiling_mode != I915_TILING_NONE;
+}
+
 void i915_gem_detect_bit_6_swizzle(struct drm_device *dev);
 void i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj);
 void i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj);
@@ -1613,9 +1753,9 @@ void i915_debugfs_cleanup(struct drm_minor *minor);
 extern int i915_save_state(struct drm_device *dev);
 extern int i915_restore_state(struct drm_device *dev);
 
-/* i915_suspend.c */
-extern int i915_save_state(struct drm_device *dev);
-extern int i915_restore_state(struct drm_device *dev);
+/* i915_ums.c */
+void i915_save_display_reg(struct drm_device *dev);
+void i915_restore_display_reg(struct drm_device *dev);
 
 /* i915_sysfs.c */
 void i915_setup_sysfs(struct drm_device *dev_priv);
@@ -1672,6 +1812,7 @@ extern void intel_modeset_cleanup(struct drm_device *dev);
 extern int intel_modeset_vga_set_state(struct drm_device *dev, bool state);
 extern void intel_modeset_setup_hw_state(struct drm_device *dev,
                                         bool force_restore);
+extern void i915_redisable_vga(struct drm_device *dev);
 extern bool intel_fbc_enabled(struct drm_device *dev);
 extern void intel_disable_fbc(struct drm_device *dev);
 extern bool ironlake_set_drps(struct drm_device *dev, u8 val);
@@ -1744,5 +1885,19 @@ __i915_write(64, q)
 #define POSTING_READ(reg)      (void)I915_READ_NOTRACE(reg)
 #define POSTING_READ16(reg)    (void)I915_READ16_NOTRACE(reg)
 
+/* "Broadcast RGB" property */
+#define INTEL_BROADCAST_RGB_AUTO 0
+#define INTEL_BROADCAST_RGB_FULL 1
+#define INTEL_BROADCAST_RGB_LIMITED 2
+
+static inline uint32_t i915_vgacntrl_reg(struct drm_device *dev)
+{
+       if (HAS_PCH_SPLIT(dev))
+               return CPU_VGACNTRL;
+       else if (IS_VALLEYVIEW(dev))
+               return VLV_VGACNTRL;
+       else
+               return VGACNTRL;
+}
 
 #endif
index 8febea6daa0840b08f8aae13418b859204b7bc2d..8413ffced81573c0144aafeac992218755b71cc9 100644 (file)
@@ -87,47 +87,43 @@ static void i915_gem_info_remove_obj(struct drm_i915_private *dev_priv,
 }
 
 static int
-i915_gem_wait_for_error(struct drm_device *dev)
+i915_gem_wait_for_error(struct i915_gpu_error *error)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       struct completion *x = &dev_priv->error_completion;
-       unsigned long flags;
        int ret;
 
-       if (!atomic_read(&dev_priv->mm.wedged))
+#define EXIT_COND (!i915_reset_in_progress(error))
+       if (EXIT_COND)
                return 0;
 
+       /* GPU is already declared terminally dead, give up. */
+       if (i915_terminally_wedged(error))
+               return -EIO;
+
        /*
         * Only wait 10 seconds for the gpu reset to complete to avoid hanging
         * userspace. If it takes that long something really bad is going on and
         * we should simply try to bail out and fail as gracefully as possible.
         */
-       ret = wait_for_completion_interruptible_timeout(x, 10*HZ);
+       ret = wait_event_interruptible_timeout(error->reset_queue,
+                                              EXIT_COND,
+                                              10*HZ);
        if (ret == 0) {
                DRM_ERROR("Timed out waiting for the gpu reset to complete\n");
                return -EIO;
        } else if (ret < 0) {
                return ret;
        }
+#undef EXIT_COND
 
-       if (atomic_read(&dev_priv->mm.wedged)) {
-               /* GPU is hung, bump the completion count to account for
-                * the token we just consumed so that we never hit zero and
-                * end up waiting upon a subsequent completion event that
-                * will never happen.
-                */
-               spin_lock_irqsave(&x->wait.lock, flags);
-               x->done++;
-               spin_unlock_irqrestore(&x->wait.lock, flags);
-       }
        return 0;
 }
 
 int i915_mutex_lock_interruptible(struct drm_device *dev)
 {
+       struct drm_i915_private *dev_priv = dev->dev_private;
        int ret;
 
-       ret = i915_gem_wait_for_error(dev);
+       ret = i915_gem_wait_for_error(&dev_priv->gpu_error);
        if (ret)
                return ret;
 
@@ -149,6 +145,7 @@ int
 i915_gem_init_ioctl(struct drm_device *dev, void *data,
                    struct drm_file *file)
 {
+       struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_i915_gem_init *args = data;
 
        if (drm_core_check_feature(dev, DRIVER_MODESET))
@@ -163,8 +160,9 @@ i915_gem_init_ioctl(struct drm_device *dev, void *data,
                return -ENODEV;
 
        mutex_lock(&dev->struct_mutex);
-       i915_gem_init_global_gtt(dev, args->gtt_start,
-                                args->gtt_end, args->gtt_end);
+       i915_gem_setup_global_gtt(dev, args->gtt_start, args->gtt_end,
+                                 args->gtt_end);
+       dev_priv->gtt.mappable_end = args->gtt_end;
        mutex_unlock(&dev->struct_mutex);
 
        return 0;
@@ -186,12 +184,24 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
                        pinned += obj->gtt_space->size;
        mutex_unlock(&dev->struct_mutex);
 
-       args->aper_size = dev_priv->mm.gtt_total;
+       args->aper_size = dev_priv->gtt.total;
        args->aper_available_size = args->aper_size - pinned;
 
        return 0;
 }
 
+void *i915_gem_object_alloc(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       return kmem_cache_alloc(dev_priv->slab, GFP_KERNEL | __GFP_ZERO);
+}
+
+void i915_gem_object_free(struct drm_i915_gem_object *obj)
+{
+       struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
+       kmem_cache_free(dev_priv->slab, obj);
+}
+
 static int
 i915_gem_create(struct drm_file *file,
                struct drm_device *dev,
@@ -215,7 +225,7 @@ i915_gem_create(struct drm_file *file,
        if (ret) {
                drm_gem_object_release(&obj->base);
                i915_gem_info_remove_obj(dev->dev_private, obj->base.size);
-               kfree(obj);
+               i915_gem_object_free(obj);
                return ret;
        }
 
@@ -259,14 +269,6 @@ i915_gem_create_ioctl(struct drm_device *dev, void *data,
                               args->size, &args->handle);
 }
 
-static int i915_gem_object_needs_bit17_swizzle(struct drm_i915_gem_object *obj)
-{
-       drm_i915_private_t *dev_priv = obj->base.dev->dev_private;
-
-       return dev_priv->mm.bit_6_swizzle_x == I915_BIT_6_SWIZZLE_9_10_17 &&
-               obj->tiling_mode != I915_TILING_NONE;
-}
-
 static inline int
 __copy_to_user_swizzled(char __user *cpu_vaddr,
                        const char *gpu_vaddr, int gpu_offset,
@@ -407,7 +409,6 @@ i915_gem_shmem_pread(struct drm_device *dev,
        loff_t offset;
        int shmem_page_offset, page_length, ret = 0;
        int obj_do_bit17_swizzling, page_do_bit17_swizzling;
-       int hit_slowpath = 0;
        int prefaulted = 0;
        int needs_clflush = 0;
        struct scatterlist *sg;
@@ -469,7 +470,6 @@ i915_gem_shmem_pread(struct drm_device *dev,
                if (ret == 0)
                        goto next_page;
 
-               hit_slowpath = 1;
                mutex_unlock(&dev->struct_mutex);
 
                if (!prefaulted) {
@@ -502,12 +502,6 @@ next_page:
 out:
        i915_gem_object_unpin_pages(obj);
 
-       if (hit_slowpath) {
-               /* Fixup: Kill any reinstated backing storage pages */
-               if (obj->madv == __I915_MADV_PURGED)
-                       i915_gem_object_truncate(obj);
-       }
-
        return ret;
 }
 
@@ -641,7 +635,7 @@ i915_gem_gtt_pwrite_fast(struct drm_device *dev,
                 * source page isn't available.  Return the error and we'll
                 * retry in the slow path.
                 */
-               if (fast_user_write(dev_priv->mm.gtt_mapping, page_base,
+               if (fast_user_write(dev_priv->gtt.mappable, page_base,
                                    page_offset, user_data, page_length)) {
                        ret = -EFAULT;
                        goto out_unpin;
@@ -838,12 +832,13 @@ out:
        i915_gem_object_unpin_pages(obj);
 
        if (hit_slowpath) {
-               /* Fixup: Kill any reinstated backing storage pages */
-               if (obj->madv == __I915_MADV_PURGED)
-                       i915_gem_object_truncate(obj);
-               /* and flush dirty cachelines in case the object isn't in the cpu write
-                * domain anymore. */
-               if (obj->base.write_domain != I915_GEM_DOMAIN_CPU) {
+               /*
+                * Fixup: Flush cpu caches in case we didn't flush the dirty
+                * cachelines in-line while writing and the object moved
+                * out of the cpu write domain while we've dropped the lock.
+                */
+               if (!needs_clflush_after &&
+                   obj->base.write_domain != I915_GEM_DOMAIN_CPU) {
                        i915_gem_clflush_object(obj);
                        i915_gem_chipset_flush(dev);
                }
@@ -940,26 +935,17 @@ unlock:
 }
 
 int
-i915_gem_check_wedge(struct drm_i915_private *dev_priv,
+i915_gem_check_wedge(struct i915_gpu_error *error,
                     bool interruptible)
 {
-       if (atomic_read(&dev_priv->mm.wedged)) {
-               struct completion *x = &dev_priv->error_completion;
-               bool recovery_complete;
-               unsigned long flags;
-
-               /* Give the error handler a chance to run. */
-               spin_lock_irqsave(&x->wait.lock, flags);
-               recovery_complete = x->done > 0;
-               spin_unlock_irqrestore(&x->wait.lock, flags);
-
+       if (i915_reset_in_progress(error)) {
                /* Non-interruptible callers can't handle -EAGAIN, hence return
                 * -EIO unconditionally for these. */
                if (!interruptible)
                        return -EIO;
 
-               /* Recovery complete, but still wedged means reset failure. */
-               if (recovery_complete)
+               /* Recovery complete, but the reset failed ... */
+               if (i915_terminally_wedged(error))
                        return -EIO;
 
                return -EAGAIN;
@@ -990,13 +976,22 @@ i915_gem_check_olr(struct intel_ring_buffer *ring, u32 seqno)
  * __wait_seqno - wait until execution of seqno has finished
  * @ring: the ring expected to report seqno
  * @seqno: duh!
+ * @reset_counter: reset sequence associated with the given seqno
  * @interruptible: do an interruptible wait (normally yes)
  * @timeout: in - how long to wait (NULL forever); out - how much time remaining
  *
+ * Note: It is of utmost importance that the passed in seqno and reset_counter
+ * values have been read by the caller in an smp safe manner. Where read-side
+ * locks are involved, it is sufficient to read the reset_counter before
+ * unlocking the lock that protects the seqno. For lockless tricks, the
+ * reset_counter _must_ be read before, and an appropriate smp_rmb must be
+ * inserted.
+ *
  * Returns 0 if the seqno was found within the alloted time. Else returns the
  * errno with remaining time filled in timeout argument.
  */
 static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno,
+                       unsigned reset_counter,
                        bool interruptible, struct timespec *timeout)
 {
        drm_i915_private_t *dev_priv = ring->dev->dev_private;
@@ -1026,7 +1021,8 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno,
 
 #define EXIT_COND \
        (i915_seqno_passed(ring->get_seqno(ring, false), seqno) || \
-       atomic_read(&dev_priv->mm.wedged))
+        i915_reset_in_progress(&dev_priv->gpu_error) || \
+        reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter))
        do {
                if (interruptible)
                        end = wait_event_interruptible_timeout(ring->irq_queue,
@@ -1036,7 +1032,14 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno,
                        end = wait_event_timeout(ring->irq_queue, EXIT_COND,
                                                 timeout_jiffies);
 
-               ret = i915_gem_check_wedge(dev_priv, interruptible);
+               /* We need to check whether any gpu reset happened in between
+                * the caller grabbing the seqno and now ... */
+               if (reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter))
+                       end = -EAGAIN;
+
+               /* ... but upgrade the -EGAIN to an -EIO if the gpu is truely
+                * gone. */
+               ret = i915_gem_check_wedge(&dev_priv->gpu_error, interruptible);
                if (ret)
                        end = ret;
        } while (end == 0 && wait_forever);
@@ -1082,7 +1085,7 @@ i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno)
        BUG_ON(!mutex_is_locked(&dev->struct_mutex));
        BUG_ON(seqno == 0);
 
-       ret = i915_gem_check_wedge(dev_priv, interruptible);
+       ret = i915_gem_check_wedge(&dev_priv->gpu_error, interruptible);
        if (ret)
                return ret;
 
@@ -1090,7 +1093,9 @@ i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno)
        if (ret)
                return ret;
 
-       return __wait_seqno(ring, seqno, interruptible, NULL);
+       return __wait_seqno(ring, seqno,
+                           atomic_read(&dev_priv->gpu_error.reset_counter),
+                           interruptible, NULL);
 }
 
 /**
@@ -1137,6 +1142,7 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj,
        struct drm_device *dev = obj->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_ring_buffer *ring = obj->ring;
+       unsigned reset_counter;
        u32 seqno;
        int ret;
 
@@ -1147,7 +1153,7 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj,
        if (seqno == 0)
                return 0;
 
-       ret = i915_gem_check_wedge(dev_priv, true);
+       ret = i915_gem_check_wedge(&dev_priv->gpu_error, true);
        if (ret)
                return ret;
 
@@ -1155,8 +1161,9 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj,
        if (ret)
                return ret;
 
+       reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter);
        mutex_unlock(&dev->struct_mutex);
-       ret = __wait_seqno(ring, seqno, true, NULL);
+       ret = __wait_seqno(ring, seqno, reset_counter, true, NULL);
        mutex_lock(&dev->struct_mutex);
 
        i915_gem_retire_requests_ring(ring);
@@ -1344,6 +1351,12 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 
        trace_i915_gem_object_fault(obj, page_offset, true, write);
 
+       /* Access to snoopable pages through the GTT is incoherent. */
+       if (obj->cache_level != I915_CACHE_NONE && !HAS_LLC(dev)) {
+               ret = -EINVAL;
+               goto unlock;
+       }
+
        /* Now bind it into the GTT if needed */
        ret = i915_gem_object_pin(obj, 0, true, false);
        if (ret)
@@ -1359,7 +1372,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 
        obj->fault_mappable = true;
 
-       pfn = ((dev_priv->mm.gtt_base_addr + obj->gtt_offset) >> PAGE_SHIFT) +
+       pfn = ((dev_priv->gtt.mappable_base + obj->gtt_offset) >> PAGE_SHIFT) +
                page_offset;
 
        /* Finally, remap it using the new GTT offset */
@@ -1374,7 +1387,7 @@ out:
                /* If this -EIO is due to a gpu hang, give the reset code a
                 * chance to clean up the mess. Otherwise return the proper
                 * SIGBUS. */
-               if (!atomic_read(&dev_priv->mm.wedged))
+               if (i915_terminally_wedged(&dev_priv->gpu_error))
                        return VM_FAULT_SIGBUS;
        case -EAGAIN:
                /* Give the error handler a chance to run and move the
@@ -1432,7 +1445,7 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj)
        obj->fault_mappable = false;
 }
 
-static uint32_t
+uint32_t
 i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode)
 {
        uint32_t gtt_size;
@@ -1460,16 +1473,15 @@ i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode)
  * Return the required GTT alignment for an object, taking into account
  * potential fence register mapping.
  */
-static uint32_t
-i915_gem_get_gtt_alignment(struct drm_device *dev,
-                          uint32_t size,
-                          int tiling_mode)
+uint32_t
+i915_gem_get_gtt_alignment(struct drm_device *dev, uint32_t size,
+                          int tiling_mode, bool fenced)
 {
        /*
         * Minimum alignment is 4k (GTT page size), but might be greater
         * if a fence register is needed for the object.
         */
-       if (INTEL_INFO(dev)->gen >= 4 ||
+       if (INTEL_INFO(dev)->gen >= 4 || (!fenced && IS_G33(dev)) ||
            tiling_mode == I915_TILING_NONE)
                return 4096;
 
@@ -1480,35 +1492,6 @@ i915_gem_get_gtt_alignment(struct drm_device *dev,
        return i915_gem_get_gtt_size(dev, size, tiling_mode);
 }
 
-/**
- * i915_gem_get_unfenced_gtt_alignment - return required GTT alignment for an
- *                                      unfenced object
- * @dev: the device
- * @size: size of the object
- * @tiling_mode: tiling mode of the object
- *
- * Return the required GTT alignment for an object, only taking into account
- * unfenced tiled surface requirements.
- */
-uint32_t
-i915_gem_get_unfenced_gtt_alignment(struct drm_device *dev,
-                                   uint32_t size,
-                                   int tiling_mode)
-{
-       /*
-        * Minimum alignment is 4k (GTT page size) for sane hw.
-        */
-       if (INTEL_INFO(dev)->gen >= 4 || IS_G33(dev) ||
-           tiling_mode == I915_TILING_NONE)
-               return 4096;
-
-       /* Previous hardware however needs to be aligned to a power-of-two
-        * tile height. The simplest method for determining this is to reuse
-        * the power-of-tile object size.
-        */
-       return i915_gem_get_gtt_size(dev, size, tiling_mode);
-}
-
 static int i915_gem_object_create_mmap_offset(struct drm_i915_gem_object *obj)
 {
        struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
@@ -1571,7 +1554,7 @@ i915_gem_mmap_gtt(struct drm_file *file,
                goto unlock;
        }
 
-       if (obj->base.size > dev_priv->mm.gtt_mappable_end) {
+       if (obj->base.size > dev_priv->gtt.mappable_end) {
                ret = -E2BIG;
                goto out;
        }
@@ -1689,7 +1672,7 @@ i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj)
        kfree(obj->pages);
 }
 
-static int
+int
 i915_gem_object_put_pages(struct drm_i915_gem_object *obj)
 {
        const struct drm_i915_gem_object_ops *ops = obj->ops;
@@ -1862,6 +1845,11 @@ i915_gem_object_get_pages(struct drm_i915_gem_object *obj)
        if (obj->pages)
                return 0;
 
+       if (obj->madv != I915_MADV_WILLNEED) {
+               DRM_ERROR("Attempting to obtain a purgeable object\n");
+               return -EINVAL;
+       }
+
        BUG_ON(obj->pages_pin_count);
 
        ret = ops->get_pages(obj);
@@ -1918,9 +1906,6 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj)
        BUG_ON(obj->base.write_domain & ~I915_GEM_GPU_DOMAINS);
        BUG_ON(!obj->active);
 
-       if (obj->pin_count) /* are we a framebuffer? */
-               intel_mark_fb_idle(obj);
-
        list_move_tail(&obj->mm_list, &dev_priv->mm.inactive_list);
 
        list_del_init(&obj->ring_list);
@@ -1940,30 +1925,24 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj)
 }
 
 static int
-i915_gem_handle_seqno_wrap(struct drm_device *dev)
+i915_gem_init_seqno(struct drm_device *dev, u32 seqno)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_ring_buffer *ring;
        int ret, i, j;
 
-       /* The hardware uses various monotonic 32-bit counters, if we
-        * detect that they will wraparound we need to idle the GPU
-        * and reset those counters.
-        */
-       ret = 0;
+       /* Carefully retire all requests without writing to the rings */
        for_each_ring(ring, dev_priv, i) {
-               for (j = 0; j < ARRAY_SIZE(ring->sync_seqno); j++)
-                       ret |= ring->sync_seqno[j] != 0;
+               ret = intel_ring_idle(ring);
+               if (ret)
+                       return ret;
        }
-       if (ret == 0)
-               return ret;
-
-       ret = i915_gpu_idle(dev);
-       if (ret)
-               return ret;
-
        i915_gem_retire_requests(dev);
+
+       /* Finally reset hw state */
        for_each_ring(ring, dev_priv, i) {
+               intel_ring_init_seqno(ring, seqno);
+
                for (j = 0; j < ARRAY_SIZE(ring->sync_seqno); j++)
                        ring->sync_seqno[j] = 0;
        }
@@ -1971,6 +1950,32 @@ i915_gem_handle_seqno_wrap(struct drm_device *dev)
        return 0;
 }
 
+int i915_gem_set_seqno(struct drm_device *dev, u32 seqno)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int ret;
+
+       if (seqno == 0)
+               return -EINVAL;
+
+       /* HWS page needs to be set less than what we
+        * will inject to ring
+        */
+       ret = i915_gem_init_seqno(dev, seqno - 1);
+       if (ret)
+               return ret;
+
+       /* Carefully set the last_seqno value so that wrap
+        * detection still works
+        */
+       dev_priv->next_seqno = seqno;
+       dev_priv->last_seqno = seqno - 1;
+       if (dev_priv->last_seqno == 0)
+               dev_priv->last_seqno--;
+
+       return 0;
+}
+
 int
 i915_gem_get_seqno(struct drm_device *dev, u32 *seqno)
 {
@@ -1978,14 +1983,14 @@ i915_gem_get_seqno(struct drm_device *dev, u32 *seqno)
 
        /* reserve 0 for non-seqno */
        if (dev_priv->next_seqno == 0) {
-               int ret = i915_gem_handle_seqno_wrap(dev);
+               int ret = i915_gem_init_seqno(dev, 0);
                if (ret)
                        return ret;
 
                dev_priv->next_seqno = 1;
        }
 
-       *seqno = dev_priv->next_seqno++;
+       *seqno = dev_priv->last_seqno = dev_priv->next_seqno++;
        return 0;
 }
 
@@ -2052,7 +2057,7 @@ i915_add_request(struct intel_ring_buffer *ring,
 
        if (!dev_priv->mm.suspended) {
                if (i915_enable_hangcheck) {
-                       mod_timer(&dev_priv->hangcheck_timer,
+                       mod_timer(&dev_priv->gpu_error.hangcheck_timer,
                                  round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES));
                }
                if (was_empty) {
@@ -2317,10 +2322,12 @@ i915_gem_object_flush_active(struct drm_i915_gem_object *obj)
 int
 i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
 {
+       drm_i915_private_t *dev_priv = dev->dev_private;
        struct drm_i915_gem_wait *args = data;
        struct drm_i915_gem_object *obj;
        struct intel_ring_buffer *ring = NULL;
        struct timespec timeout_stack, *timeout = NULL;
+       unsigned reset_counter;
        u32 seqno = 0;
        int ret = 0;
 
@@ -2361,9 +2368,10 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
        }
 
        drm_gem_object_unreference(&obj->base);
+       reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter);
        mutex_unlock(&dev->struct_mutex);
 
-       ret = __wait_seqno(ring, seqno, true, timeout);
+       ret = __wait_seqno(ring, seqno, reset_counter, true, timeout);
        if (timeout) {
                WARN_ON(!timespec_valid(timeout));
                args->timeout_ns = timespec_to_ns(timeout);
@@ -2427,15 +2435,15 @@ static void i915_gem_object_finish_gtt(struct drm_i915_gem_object *obj)
 {
        u32 old_write_domain, old_read_domains;
 
-       /* Act a barrier for all accesses through the GTT */
-       mb();
-
        /* Force a pagefault for domain tracking on next user access */
        i915_gem_release_mmap(obj);
 
        if ((obj->base.read_domains & I915_GEM_DOMAIN_GTT) == 0)
                return;
 
+       /* Wait for any direct GTT access to complete */
+       mb();
+
        old_read_domains = obj->base.read_domains;
        old_write_domain = obj->base.write_domain;
 
@@ -2454,7 +2462,7 @@ int
 i915_gem_object_unbind(struct drm_i915_gem_object *obj)
 {
        drm_i915_private_t *dev_priv = obj->base.dev->dev_private;
-       int ret = 0;
+       int ret;
 
        if (obj->gtt_space == NULL)
                return 0;
@@ -2521,52 +2529,38 @@ int i915_gpu_idle(struct drm_device *dev)
        return 0;
 }
 
-static void sandybridge_write_fence_reg(struct drm_device *dev, int reg,
-                                       struct drm_i915_gem_object *obj)
-{
-       drm_i915_private_t *dev_priv = dev->dev_private;
-       uint64_t val;
-
-       if (obj) {
-               u32 size = obj->gtt_space->size;
-
-               val = (uint64_t)((obj->gtt_offset + size - 4096) &
-                                0xfffff000) << 32;
-               val |= obj->gtt_offset & 0xfffff000;
-               val |= (uint64_t)((obj->stride / 128) - 1) <<
-                       SANDYBRIDGE_FENCE_PITCH_SHIFT;
-
-               if (obj->tiling_mode == I915_TILING_Y)
-                       val |= 1 << I965_FENCE_TILING_Y_SHIFT;
-               val |= I965_FENCE_REG_VALID;
-       } else
-               val = 0;
-
-       I915_WRITE64(FENCE_REG_SANDYBRIDGE_0 + reg * 8, val);
-       POSTING_READ(FENCE_REG_SANDYBRIDGE_0 + reg * 8);
-}
-
 static void i965_write_fence_reg(struct drm_device *dev, int reg,
                                 struct drm_i915_gem_object *obj)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
+       int fence_reg;
+       int fence_pitch_shift;
        uint64_t val;
 
+       if (INTEL_INFO(dev)->gen >= 6) {
+               fence_reg = FENCE_REG_SANDYBRIDGE_0;
+               fence_pitch_shift = SANDYBRIDGE_FENCE_PITCH_SHIFT;
+       } else {
+               fence_reg = FENCE_REG_965_0;
+               fence_pitch_shift = I965_FENCE_PITCH_SHIFT;
+       }
+
        if (obj) {
                u32 size = obj->gtt_space->size;
 
                val = (uint64_t)((obj->gtt_offset + size - 4096) &
                                 0xfffff000) << 32;
                val |= obj->gtt_offset & 0xfffff000;
-               val |= ((obj->stride / 128) - 1) << I965_FENCE_PITCH_SHIFT;
+               val |= (uint64_t)((obj->stride / 128) - 1) << fence_pitch_shift;
                if (obj->tiling_mode == I915_TILING_Y)
                        val |= 1 << I965_FENCE_TILING_Y_SHIFT;
                val |= I965_FENCE_REG_VALID;
        } else
                val = 0;
 
-       I915_WRITE64(FENCE_REG_965_0 + reg * 8, val);
-       POSTING_READ(FENCE_REG_965_0 + reg * 8);
+       fence_reg += reg * 8;
+       I915_WRITE64(fence_reg, val);
+       POSTING_READ(fence_reg);
 }
 
 static void i915_write_fence_reg(struct drm_device *dev, int reg,
@@ -2645,18 +2639,37 @@ static void i830_write_fence_reg(struct drm_device *dev, int reg,
        POSTING_READ(FENCE_REG_830_0 + reg * 4);
 }
 
+inline static bool i915_gem_object_needs_mb(struct drm_i915_gem_object *obj)
+{
+       return obj && obj->base.read_domains & I915_GEM_DOMAIN_GTT;
+}
+
 static void i915_gem_write_fence(struct drm_device *dev, int reg,
                                 struct drm_i915_gem_object *obj)
 {
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       /* Ensure that all CPU reads are completed before installing a fence
+        * and all writes before removing the fence.
+        */
+       if (i915_gem_object_needs_mb(dev_priv->fence_regs[reg].obj))
+               mb();
+
        switch (INTEL_INFO(dev)->gen) {
        case 7:
-       case 6: sandybridge_write_fence_reg(dev, reg, obj); break;
+       case 6:
        case 5:
        case 4: i965_write_fence_reg(dev, reg, obj); break;
        case 3: i915_write_fence_reg(dev, reg, obj); break;
        case 2: i830_write_fence_reg(dev, reg, obj); break;
-       default: break;
+       default: BUG();
        }
+
+       /* And similarly be paranoid that no direct access to this region
+        * is reordered to before the fence is installed.
+        */
+       if (i915_gem_object_needs_mb(obj))
+               mb();
 }
 
 static inline int fence_number(struct drm_i915_private *dev_priv,
@@ -2686,7 +2699,7 @@ static void i915_gem_object_update_fence(struct drm_i915_gem_object *obj,
 }
 
 static int
-i915_gem_object_flush_fence(struct drm_i915_gem_object *obj)
+i915_gem_object_wait_fence(struct drm_i915_gem_object *obj)
 {
        if (obj->last_fenced_seqno) {
                int ret = i915_wait_seqno(obj->ring, obj->last_fenced_seqno);
@@ -2696,12 +2709,6 @@ i915_gem_object_flush_fence(struct drm_i915_gem_object *obj)
                obj->last_fenced_seqno = 0;
        }
 
-       /* Ensure that all CPU reads are completed before installing a fence
-        * and all writes before removing the fence.
-        */
-       if (obj->base.read_domains & I915_GEM_DOMAIN_GTT)
-               mb();
-
        obj->fenced_gpu_access = false;
        return 0;
 }
@@ -2712,7 +2719,7 @@ i915_gem_object_put_fence(struct drm_i915_gem_object *obj)
        struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
        int ret;
 
-       ret = i915_gem_object_flush_fence(obj);
+       ret = i915_gem_object_wait_fence(obj);
        if (ret)
                return ret;
 
@@ -2786,7 +2793,7 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj)
         * will need to serialise the write to the associated fence register?
         */
        if (obj->fence_dirty) {
-               ret = i915_gem_object_flush_fence(obj);
+               ret = i915_gem_object_wait_fence(obj);
                if (ret)
                        return ret;
        }
@@ -2807,7 +2814,7 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj)
                if (reg->obj) {
                        struct drm_i915_gem_object *old = reg->obj;
 
-                       ret = i915_gem_object_flush_fence(old);
+                       ret = i915_gem_object_wait_fence(old);
                        if (ret)
                                return ret;
 
@@ -2830,7 +2837,7 @@ static bool i915_gem_valid_gtt_space(struct drm_device *dev,
 
        /* On non-LLC machines we have to be careful when putting differing
         * types of snoopable memory together to avoid the prefetcher
-        * crossing memory domains and dieing.
+        * crossing memory domains and dying.
         */
        if (HAS_LLC(dev))
                return true;
@@ -2908,21 +2915,16 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
        bool mappable, fenceable;
        int ret;
 
-       if (obj->madv != I915_MADV_WILLNEED) {
-               DRM_ERROR("Attempting to bind a purgeable object\n");
-               return -EINVAL;
-       }
-
        fence_size = i915_gem_get_gtt_size(dev,
                                           obj->base.size,
                                           obj->tiling_mode);
        fence_alignment = i915_gem_get_gtt_alignment(dev,
                                                     obj->base.size,
-                                                    obj->tiling_mode);
+                                                    obj->tiling_mode, true);
        unfenced_alignment =
-               i915_gem_get_unfenced_gtt_alignment(dev,
+               i915_gem_get_gtt_alignment(dev,
                                                    obj->base.size,
-                                                   obj->tiling_mode);
+                                                   obj->tiling_mode, false);
 
        if (alignment == 0)
                alignment = map_and_fenceable ? fence_alignment :
@@ -2938,7 +2940,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
         * before evicting everything in a vain attempt to find space.
         */
        if (obj->base.size >
-           (map_and_fenceable ? dev_priv->mm.gtt_mappable_end : dev_priv->mm.gtt_total)) {
+           (map_and_fenceable ? dev_priv->gtt.mappable_end : dev_priv->gtt.total)) {
                DRM_ERROR("Attempting to bind an object larger than the aperture\n");
                return -E2BIG;
        }
@@ -2959,7 +2961,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
        if (map_and_fenceable)
                ret = drm_mm_insert_node_in_range_generic(&dev_priv->mm.gtt_space, node,
                                                          size, alignment, obj->cache_level,
-                                                         0, dev_priv->mm.gtt_mappable_end);
+                                                         0, dev_priv->gtt.mappable_end);
        else
                ret = drm_mm_insert_node_generic(&dev_priv->mm.gtt_space, node,
                                                 size, alignment, obj->cache_level);
@@ -2999,7 +3001,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
                (node->start & (fence_alignment - 1)) == 0;
 
        mappable =
-               obj->gtt_offset + obj->base.size <= dev_priv->mm.gtt_mappable_end;
+               obj->gtt_offset + obj->base.size <= dev_priv->gtt.mappable_end;
 
        obj->map_and_fenceable = mappable && fenceable;
 
@@ -3019,6 +3021,13 @@ i915_gem_clflush_object(struct drm_i915_gem_object *obj)
        if (obj->pages == NULL)
                return;
 
+       /*
+        * Stolen memory is always coherent with the GPU as it is explicitly
+        * marked as wc by the system, or the system is cache-coherent.
+        */
+       if (obj->stolen)
+               return;
+
        /* If the GPU is snooping the contents of the CPU cache,
         * we do not need to manually clear the CPU cache lines.  However,
         * the caches are only snooped when the render cache is
@@ -3107,6 +3116,13 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write)
 
        i915_gem_object_flush_cpu_write_domain(obj);
 
+       /* Serialise direct access to this object with the barriers for
+        * coherent writes from the GPU, by effectively invalidating the
+        * GTT domain upon first access.
+        */
+       if ((obj->base.read_domains & I915_GEM_DOMAIN_GTT) == 0)
+               mb();
+
        old_write_domain = obj->base.write_domain;
        old_read_domains = obj->base.read_domains;
 
@@ -3413,11 +3429,17 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file)
        unsigned long recent_enough = jiffies - msecs_to_jiffies(20);
        struct drm_i915_gem_request *request;
        struct intel_ring_buffer *ring = NULL;
+       unsigned reset_counter;
        u32 seqno = 0;
        int ret;
 
-       if (atomic_read(&dev_priv->mm.wedged))
-               return -EIO;
+       ret = i915_gem_wait_for_error(&dev_priv->gpu_error);
+       if (ret)
+               return ret;
+
+       ret = i915_gem_check_wedge(&dev_priv->gpu_error, false);
+       if (ret)
+               return ret;
 
        spin_lock(&file_priv->mm.lock);
        list_for_each_entry(request, &file_priv->mm.request_list, client_list) {
@@ -3427,12 +3449,13 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file)
                ring = request->ring;
                seqno = request->seqno;
        }
+       reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter);
        spin_unlock(&file_priv->mm.lock);
 
        if (seqno == 0)
                return 0;
 
-       ret = __wait_seqno(ring, seqno, true, NULL);
+       ret = __wait_seqno(ring, seqno, reset_counter, true, NULL);
        if (ret == 0)
                queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, 0);
 
@@ -3706,14 +3729,14 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
 {
        struct drm_i915_gem_object *obj;
        struct address_space *mapping;
-       u32 mask;
+       gfp_t mask;
 
-       obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+       obj = i915_gem_object_alloc(dev);
        if (obj == NULL)
                return NULL;
 
        if (drm_gem_object_init(dev, &obj->base, size) != 0) {
-               kfree(obj);
+               i915_gem_object_free(obj);
                return NULL;
        }
 
@@ -3785,6 +3808,7 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj)
        obj->pages_pin_count = 0;
        i915_gem_object_put_pages(obj);
        i915_gem_object_free_mmap_offset(obj);
+       i915_gem_object_release_stolen(obj);
 
        BUG_ON(obj->pages);
 
@@ -3795,7 +3819,7 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj)
        i915_gem_info_remove_obj(dev_priv, obj->base.size);
 
        kfree(obj->bit_17);
-       kfree(obj);
+       i915_gem_object_free(obj);
 }
 
 int
@@ -3829,7 +3853,7 @@ i915_gem_idle(struct drm_device *dev)
         * And not confound mm.suspended!
         */
        dev_priv->mm.suspended = 1;
-       del_timer_sync(&dev_priv->hangcheck_timer);
+       del_timer_sync(&dev_priv->gpu_error.hangcheck_timer);
 
        i915_kernel_lost_context(dev);
        i915_gem_cleanup_ringbuffer(dev);
@@ -3848,7 +3872,7 @@ void i915_gem_l3_remap(struct drm_device *dev)
        u32 misccpctl;
        int i;
 
-       if (!IS_IVYBRIDGE(dev))
+       if (!HAS_L3_GPU_CACHE(dev))
                return;
 
        if (!dev_priv->l3_parity.remap_info)
@@ -3891,8 +3915,10 @@ void i915_gem_init_swizzling(struct drm_device *dev)
        I915_WRITE(TILECTL, I915_READ(TILECTL) | TILECTL_SWZCTL);
        if (IS_GEN6(dev))
                I915_WRITE(ARB_MODE, _MASKED_BIT_ENABLE(ARB_MODE_SWIZZLE_SNB));
-       else
+       else if (IS_GEN7(dev))
                I915_WRITE(ARB_MODE, _MASKED_BIT_ENABLE(ARB_MODE_SWIZZLE_IVB));
+       else
+               BUG();
 }
 
 static bool
@@ -3911,22 +3937,11 @@ intel_enable_blt(struct drm_device *dev)
        return true;
 }
 
-int
-i915_gem_init_hw(struct drm_device *dev)
+static int i915_gem_init_rings(struct drm_device *dev)
 {
-       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct drm_i915_private *dev_priv = dev->dev_private;
        int ret;
 
-       if (INTEL_INFO(dev)->gen < 6 && !intel_enable_gtt())
-               return -EIO;
-
-       if (IS_HASWELL(dev) && (I915_READ(0x120010) == 1))
-               I915_WRITE(0x9008, I915_READ(0x9008) | 0xf0000);
-
-       i915_gem_l3_remap(dev);
-
-       i915_gem_init_swizzling(dev);
-
        ret = intel_init_render_ring_buffer(dev);
        if (ret)
                return ret;
@@ -3943,76 +3958,59 @@ i915_gem_init_hw(struct drm_device *dev)
                        goto cleanup_bsd_ring;
        }
 
-       dev_priv->next_seqno = 1;
-
-       /*
-        * XXX: There was some w/a described somewhere suggesting loading
-        * contexts before PPGTT.
-        */
-       i915_gem_context_init(dev);
-       i915_gem_init_ppgtt(dev);
+       ret = i915_gem_set_seqno(dev, ((u32)~0 - 0x1000));
+       if (ret)
+               goto cleanup_blt_ring;
 
        return 0;
 
+cleanup_blt_ring:
+       intel_cleanup_ring_buffer(&dev_priv->ring[BCS]);
 cleanup_bsd_ring:
        intel_cleanup_ring_buffer(&dev_priv->ring[VCS]);
 cleanup_render_ring:
        intel_cleanup_ring_buffer(&dev_priv->ring[RCS]);
+
        return ret;
 }
 
-static bool
-intel_enable_ppgtt(struct drm_device *dev)
+int
+i915_gem_init_hw(struct drm_device *dev)
 {
-       if (i915_enable_ppgtt >= 0)
-               return i915_enable_ppgtt;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       int ret;
 
-#ifdef CONFIG_INTEL_IOMMU
-       /* Disable ppgtt on SNB if VT-d is on. */
-       if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped)
-               return false;
-#endif
+       if (INTEL_INFO(dev)->gen < 6 && !intel_enable_gtt())
+               return -EIO;
 
-       return true;
+       if (IS_HASWELL(dev) && (I915_READ(0x120010) == 1))
+               I915_WRITE(0x9008, I915_READ(0x9008) | 0xf0000);
+
+       i915_gem_l3_remap(dev);
+
+       i915_gem_init_swizzling(dev);
+
+       ret = i915_gem_init_rings(dev);
+       if (ret)
+               return ret;
+
+       /*
+        * XXX: There was some w/a described somewhere suggesting loading
+        * contexts before PPGTT.
+        */
+       i915_gem_context_init(dev);
+       i915_gem_init_ppgtt(dev);
+
+       return 0;
 }
 
 int i915_gem_init(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       unsigned long gtt_size, mappable_size;
        int ret;
 
-       gtt_size = dev_priv->mm.gtt->gtt_total_entries << PAGE_SHIFT;
-       mappable_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT;
-
        mutex_lock(&dev->struct_mutex);
-       if (intel_enable_ppgtt(dev) && HAS_ALIASING_PPGTT(dev)) {
-               /* PPGTT pdes are stolen from global gtt ptes, so shrink the
-                * aperture accordingly when using aliasing ppgtt. */
-               gtt_size -= I915_PPGTT_PD_ENTRIES*PAGE_SIZE;
-
-               i915_gem_init_global_gtt(dev, 0, mappable_size, gtt_size);
-
-               ret = i915_gem_init_aliasing_ppgtt(dev);
-               if (ret) {
-                       mutex_unlock(&dev->struct_mutex);
-                       return ret;
-               }
-       } else {
-               /* Let GEM Manage all of the aperture.
-                *
-                * However, leave one page at the end still bound to the scratch
-                * page.  There are a number of places where the hardware
-                * apparently prefetches past the end of the object, and we've
-                * seen multiple hangs with the GPU head pointer stuck in a
-                * batchbuffer bound at the last page of the aperture.  One page
-                * should be enough to keep any prefetching inside of the
-                * aperture.
-                */
-               i915_gem_init_global_gtt(dev, 0, mappable_size,
-                                        gtt_size);
-       }
-
+       i915_gem_init_global_gtt(dev);
        ret = i915_gem_init_hw(dev);
        mutex_unlock(&dev->struct_mutex);
        if (ret) {
@@ -4047,9 +4045,9 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data,
        if (drm_core_check_feature(dev, DRIVER_MODESET))
                return 0;
 
-       if (atomic_read(&dev_priv->mm.wedged)) {
+       if (i915_reset_in_progress(&dev_priv->gpu_error)) {
                DRM_ERROR("Reenabling wedged hardware, good luck\n");
-               atomic_set(&dev_priv->mm.wedged, 0);
+               atomic_set(&dev_priv->gpu_error.reset_counter, 0);
        }
 
        mutex_lock(&dev->struct_mutex);
@@ -4113,8 +4111,14 @@ init_ring_lists(struct intel_ring_buffer *ring)
 void
 i915_gem_load(struct drm_device *dev)
 {
-       int i;
        drm_i915_private_t *dev_priv = dev->dev_private;
+       int i;
+
+       dev_priv->slab =
+               kmem_cache_create("i915_gem_object",
+                                 sizeof(struct drm_i915_gem_object), 0,
+                                 SLAB_HWCACHE_ALIGN,
+                                 NULL);
 
        INIT_LIST_HEAD(&dev_priv->mm.active_list);
        INIT_LIST_HEAD(&dev_priv->mm.inactive_list);
@@ -4127,7 +4131,7 @@ i915_gem_load(struct drm_device *dev)
                INIT_LIST_HEAD(&dev_priv->fence_regs[i].lru_list);
        INIT_DELAYED_WORK(&dev_priv->mm.retire_work,
                          i915_gem_retire_work_handler);
-       init_completion(&dev_priv->error_completion);
+       init_waitqueue_head(&dev_priv->gpu_error.reset_queue);
 
        /* On GEN3 we really need to make sure the ARB C3 LP bit is set */
        if (IS_GEN3(dev)) {
index a3f06bcad551d844c1e3649fee266466a04ff759..21177d9df4231c548dd23a2a28009660f33960d6 100644 (file)
@@ -126,13 +126,8 @@ static int get_context_size(struct drm_device *dev)
 
 static void do_destroy(struct i915_hw_context *ctx)
 {
-       struct drm_device *dev = ctx->obj->base.dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-
        if (ctx->file_priv)
                idr_remove(&ctx->file_priv->context_idr, ctx->id);
-       else
-               BUG_ON(ctx != dev_priv->ring[RCS].default_context);
 
        drm_gem_object_unreference(&ctx->obj->base);
        kfree(ctx);
@@ -242,7 +237,6 @@ err_destroy:
 void i915_gem_context_init(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       uint32_t ctx_size;
 
        if (!HAS_HW_CONTEXTS(dev)) {
                dev_priv->hw_contexts_disabled = true;
@@ -254,11 +248,9 @@ void i915_gem_context_init(struct drm_device *dev)
            dev_priv->ring[RCS].default_context)
                return;
 
-       ctx_size = get_context_size(dev);
-       dev_priv->hw_context_size = get_context_size(dev);
-       dev_priv->hw_context_size = round_up(dev_priv->hw_context_size, 4096);
+       dev_priv->hw_context_size = round_up(get_context_size(dev), 4096);
 
-       if (ctx_size <= 0 || ctx_size > (1<<20)) {
+       if (dev_priv->hw_context_size > (1<<20)) {
                dev_priv->hw_contexts_disabled = true;
                return;
        }
index abeaafef6d7ef9986313be7ab8e0c2c5a992c065..6a5af6828624e257d4b2516ade02ce79eaf4d839 100644 (file)
@@ -281,8 +281,7 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev,
        if (IS_ERR(attach))
                return ERR_CAST(attach);
 
-
-       obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+       obj = i915_gem_object_alloc(dev);
        if (obj == NULL) {
                ret = -ENOMEM;
                goto fail_detach;
@@ -290,7 +289,7 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev,
 
        ret = drm_gem_private_object_init(dev, &obj->base, dma_buf->size);
        if (ret) {
-               kfree(obj);
+               i915_gem_object_free(obj);
                goto fail_detach;
        }
 
index 776a3225184ce48db7b00f89b8522b956bfd3bef..c86d5d9356fd086b756b9dc207b6d7c9e824f630 100644 (file)
@@ -80,7 +80,7 @@ i915_gem_evict_something(struct drm_device *dev, int min_size,
        if (mappable)
                drm_mm_init_scan_with_range(&dev_priv->mm.gtt_space,
                                            min_size, alignment, cache_level,
-                                           0, dev_priv->mm.gtt_mappable_end);
+                                           0, dev_priv->gtt.mappable_end);
        else
                drm_mm_init_scan(&dev_priv->mm.gtt_space,
                                 min_size, alignment, cache_level);
index 26d08bb5821894daad6cdd1fa79b58f86cf226e2..2f2daebd0eefd581c38415489164ed080e0d141a 100644 (file)
 #include <linux/dma_remapping.h>
 
 struct eb_objects {
+       struct list_head objects;
        int and;
-       struct hlist_head buckets[0];
+       union {
+               struct drm_i915_gem_object *lut[0];
+               struct hlist_head buckets[0];
+       };
 };
 
 static struct eb_objects *
-eb_create(int size)
+eb_create(struct drm_i915_gem_execbuffer2 *args)
 {
-       struct eb_objects *eb;
-       int count = PAGE_SIZE / sizeof(struct hlist_head) / 2;
-       BUILD_BUG_ON(!is_power_of_2(PAGE_SIZE / sizeof(struct hlist_head)));
-       while (count > size)
-               count >>= 1;
-       eb = kzalloc(count*sizeof(struct hlist_head) +
-                    sizeof(struct eb_objects),
-                    GFP_KERNEL);
-       if (eb == NULL)
-               return eb;
-
-       eb->and = count - 1;
+       struct eb_objects *eb = NULL;
+
+       if (args->flags & I915_EXEC_HANDLE_LUT) {
+               int size = args->buffer_count;
+               size *= sizeof(struct drm_i915_gem_object *);
+               size += sizeof(struct eb_objects);
+               eb = kmalloc(size, GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY);
+       }
+
+       if (eb == NULL) {
+               int size = args->buffer_count;
+               int count = PAGE_SIZE / sizeof(struct hlist_head) / 2;
+               BUILD_BUG_ON(!is_power_of_2(PAGE_SIZE / sizeof(struct hlist_head)));
+               while (count > 2*size)
+                       count >>= 1;
+               eb = kzalloc(count*sizeof(struct hlist_head) +
+                            sizeof(struct eb_objects),
+                            GFP_TEMPORARY);
+               if (eb == NULL)
+                       return eb;
+
+               eb->and = count - 1;
+       } else
+               eb->and = -args->buffer_count;
+
+       INIT_LIST_HEAD(&eb->objects);
        return eb;
 }
 
 static void
 eb_reset(struct eb_objects *eb)
 {
-       memset(eb->buckets, 0, (eb->and+1)*sizeof(struct hlist_head));
+       if (eb->and >= 0)
+               memset(eb->buckets, 0, (eb->and+1)*sizeof(struct hlist_head));
 }
 
-static void
-eb_add_object(struct eb_objects *eb, struct drm_i915_gem_object *obj)
+static int
+eb_lookup_objects(struct eb_objects *eb,
+                 struct drm_i915_gem_exec_object2 *exec,
+                 const struct drm_i915_gem_execbuffer2 *args,
+                 struct drm_file *file)
 {
-       hlist_add_head(&obj->exec_node,
-                      &eb->buckets[obj->exec_handle & eb->and]);
+       int i;
+
+       spin_lock(&file->table_lock);
+       for (i = 0; i < args->buffer_count; i++) {
+               struct drm_i915_gem_object *obj;
+
+               obj = to_intel_bo(idr_find(&file->object_idr, exec[i].handle));
+               if (obj == NULL) {
+                       spin_unlock(&file->table_lock);
+                       DRM_DEBUG("Invalid object handle %d at index %d\n",
+                                  exec[i].handle, i);
+                       return -ENOENT;
+               }
+
+               if (!list_empty(&obj->exec_list)) {
+                       spin_unlock(&file->table_lock);
+                       DRM_DEBUG("Object %p [handle %d, index %d] appears more than once in object list\n",
+                                  obj, exec[i].handle, i);
+                       return -EINVAL;
+               }
+
+               drm_gem_object_reference(&obj->base);
+               list_add_tail(&obj->exec_list, &eb->objects);
+
+               obj->exec_entry = &exec[i];
+               if (eb->and < 0) {
+                       eb->lut[i] = obj;
+               } else {
+                       uint32_t handle = args->flags & I915_EXEC_HANDLE_LUT ? i : exec[i].handle;
+                       obj->exec_handle = handle;
+                       hlist_add_head(&obj->exec_node,
+                                      &eb->buckets[handle & eb->and]);
+               }
+       }
+       spin_unlock(&file->table_lock);
+
+       return 0;
 }
 
 static struct drm_i915_gem_object *
 eb_get_object(struct eb_objects *eb, unsigned long handle)
 {
-       struct hlist_head *head;
-       struct hlist_node *node;
-       struct drm_i915_gem_object *obj;
+       if (eb->and < 0) {
+               if (handle >= -eb->and)
+                       return NULL;
+               return eb->lut[handle];
+       } else {
+               struct hlist_head *head;
+               struct hlist_node *node;
 
-       head = &eb->buckets[handle & eb->and];
-       hlist_for_each(node, head) {
-               obj = hlist_entry(node, struct drm_i915_gem_object, exec_node);
-               if (obj->exec_handle == handle)
-                       return obj;
-       }
+               head = &eb->buckets[handle & eb->and];
+               hlist_for_each(node, head) {
+                       struct drm_i915_gem_object *obj;
 
-       return NULL;
+                       obj = hlist_entry(node, struct drm_i915_gem_object, exec_node);
+                       if (obj->exec_handle == handle)
+                               return obj;
+               }
+               return NULL;
+       }
 }
 
 static void
 eb_destroy(struct eb_objects *eb)
 {
+       while (!list_empty(&eb->objects)) {
+               struct drm_i915_gem_object *obj;
+
+               obj = list_first_entry(&eb->objects,
+                                      struct drm_i915_gem_object,
+                                      exec_list);
+               list_del_init(&obj->exec_list);
+               drm_gem_object_unreference(&obj->base);
+       }
        kfree(eb);
 }
 
@@ -150,17 +222,6 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
                          reloc->write_domain);
                return ret;
        }
-       if (unlikely(reloc->write_domain && target_obj->pending_write_domain &&
-                    reloc->write_domain != target_obj->pending_write_domain)) {
-               DRM_DEBUG("Write domain conflict: "
-                         "obj %p target %d offset %d "
-                         "new %08x old %08x\n",
-                         obj, reloc->target_handle,
-                         (int) reloc->offset,
-                         reloc->write_domain,
-                         target_obj->pending_write_domain);
-               return ret;
-       }
 
        target_obj->pending_read_domains |= reloc->read_domains;
        target_obj->pending_write_domain |= reloc->write_domain;
@@ -220,7 +281,7 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
 
                /* Map the page containing the relocation we're going to perform.  */
                reloc->offset += obj->gtt_offset;
-               reloc_page = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping,
+               reloc_page = io_mapping_map_atomic_wc(dev_priv->gtt.mappable,
                                                      reloc->offset & PAGE_MASK);
                reloc_entry = (uint32_t __iomem *)
                        (reloc_page + (reloc->offset & ~PAGE_MASK));
@@ -299,8 +360,7 @@ i915_gem_execbuffer_relocate_object_slow(struct drm_i915_gem_object *obj,
 
 static int
 i915_gem_execbuffer_relocate(struct drm_device *dev,
-                            struct eb_objects *eb,
-                            struct list_head *objects)
+                            struct eb_objects *eb)
 {
        struct drm_i915_gem_object *obj;
        int ret = 0;
@@ -313,7 +373,7 @@ i915_gem_execbuffer_relocate(struct drm_device *dev,
         * lockdep complains vehemently.
         */
        pagefault_disable();
-       list_for_each_entry(obj, objects, exec_list) {
+       list_for_each_entry(obj, &eb->objects, exec_list) {
                ret = i915_gem_execbuffer_relocate_object(obj, eb);
                if (ret)
                        break;
@@ -335,7 +395,8 @@ need_reloc_mappable(struct drm_i915_gem_object *obj)
 
 static int
 i915_gem_execbuffer_reserve_object(struct drm_i915_gem_object *obj,
-                                  struct intel_ring_buffer *ring)
+                                  struct intel_ring_buffer *ring,
+                                  bool *need_reloc)
 {
        struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
        struct drm_i915_gem_exec_object2 *entry = obj->exec_entry;
@@ -376,7 +437,20 @@ i915_gem_execbuffer_reserve_object(struct drm_i915_gem_object *obj,
                obj->has_aliasing_ppgtt_mapping = 1;
        }
 
-       entry->offset = obj->gtt_offset;
+       if (entry->offset != obj->gtt_offset) {
+               entry->offset = obj->gtt_offset;
+               *need_reloc = true;
+       }
+
+       if (entry->flags & EXEC_OBJECT_WRITE) {
+               obj->base.pending_read_domains = I915_GEM_DOMAIN_RENDER;
+               obj->base.pending_write_domain = I915_GEM_DOMAIN_RENDER;
+       }
+
+       if (entry->flags & EXEC_OBJECT_NEEDS_GTT &&
+           !obj->has_global_gtt_mapping)
+               i915_gem_gtt_bind_object(obj, obj->cache_level);
+
        return 0;
 }
 
@@ -402,7 +476,8 @@ i915_gem_execbuffer_unreserve_object(struct drm_i915_gem_object *obj)
 static int
 i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
                            struct drm_file *file,
-                           struct list_head *objects)
+                           struct list_head *objects,
+                           bool *need_relocs)
 {
        struct drm_i915_gem_object *obj;
        struct list_head ordered_objects;
@@ -430,7 +505,7 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
                else
                        list_move_tail(&obj->exec_list, &ordered_objects);
 
-               obj->base.pending_read_domains = 0;
+               obj->base.pending_read_domains = I915_GEM_GPU_DOMAINS & ~I915_GEM_DOMAIN_COMMAND;
                obj->base.pending_write_domain = 0;
                obj->pending_fenced_gpu_access = false;
        }
@@ -470,7 +545,7 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
                            (need_mappable && !obj->map_and_fenceable))
                                ret = i915_gem_object_unbind(obj);
                        else
-                               ret = i915_gem_execbuffer_reserve_object(obj, ring);
+                               ret = i915_gem_execbuffer_reserve_object(obj, ring, need_relocs);
                        if (ret)
                                goto err;
                }
@@ -480,7 +555,7 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
                        if (obj->gtt_space)
                                continue;
 
-                       ret = i915_gem_execbuffer_reserve_object(obj, ring);
+                       ret = i915_gem_execbuffer_reserve_object(obj, ring, need_relocs);
                        if (ret)
                                goto err;
                }
@@ -500,21 +575,22 @@ err:              /* Decrement pin count for bound objects */
 
 static int
 i915_gem_execbuffer_relocate_slow(struct drm_device *dev,
+                                 struct drm_i915_gem_execbuffer2 *args,
                                  struct drm_file *file,
                                  struct intel_ring_buffer *ring,
-                                 struct list_head *objects,
                                  struct eb_objects *eb,
-                                 struct drm_i915_gem_exec_object2 *exec,
-                                 int count)
+                                 struct drm_i915_gem_exec_object2 *exec)
 {
        struct drm_i915_gem_relocation_entry *reloc;
        struct drm_i915_gem_object *obj;
+       bool need_relocs;
        int *reloc_offset;
        int i, total, ret;
+       int count = args->buffer_count;
 
        /* We may process another execbuffer during the unlock... */
-       while (!list_empty(objects)) {
-               obj = list_first_entry(objects,
+       while (!list_empty(&eb->objects)) {
+               obj = list_first_entry(&eb->objects,
                                       struct drm_i915_gem_object,
                                       exec_list);
                list_del_init(&obj->exec_list);
@@ -582,27 +658,16 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev,
 
        /* reacquire the objects */
        eb_reset(eb);
-       for (i = 0; i < count; i++) {
-               obj = to_intel_bo(drm_gem_object_lookup(dev, file,
-                                                       exec[i].handle));
-               if (&obj->base == NULL) {
-                       DRM_DEBUG("Invalid object handle %d at index %d\n",
-                                  exec[i].handle, i);
-                       ret = -ENOENT;
-                       goto err;
-               }
-
-               list_add_tail(&obj->exec_list, objects);
-               obj->exec_handle = exec[i].handle;
-               obj->exec_entry = &exec[i];
-               eb_add_object(eb, obj);
-       }
+       ret = eb_lookup_objects(eb, exec, args, file);
+       if (ret)
+               goto err;
 
-       ret = i915_gem_execbuffer_reserve(ring, file, objects);
+       need_relocs = (args->flags & I915_EXEC_NO_RELOC) == 0;
+       ret = i915_gem_execbuffer_reserve(ring, file, &eb->objects, &need_relocs);
        if (ret)
                goto err;
 
-       list_for_each_entry(obj, objects, exec_list) {
+       list_for_each_entry(obj, &eb->objects, exec_list) {
                int offset = obj->exec_entry - exec;
                ret = i915_gem_execbuffer_relocate_object_slow(obj, eb,
                                                               reloc + reloc_offset[offset]);
@@ -622,45 +687,12 @@ err:
        return ret;
 }
 
-static int
-i915_gem_execbuffer_wait_for_flips(struct intel_ring_buffer *ring, u32 flips)
-{
-       u32 plane, flip_mask;
-       int ret;
-
-       /* Check for any pending flips. As we only maintain a flip queue depth
-        * of 1, we can simply insert a WAIT for the next display flip prior
-        * to executing the batch and avoid stalling the CPU.
-        */
-
-       for (plane = 0; flips >> plane; plane++) {
-               if (((flips >> plane) & 1) == 0)
-                       continue;
-
-               if (plane)
-                       flip_mask = MI_WAIT_FOR_PLANE_B_FLIP;
-               else
-                       flip_mask = MI_WAIT_FOR_PLANE_A_FLIP;
-
-               ret = intel_ring_begin(ring, 2);
-               if (ret)
-                       return ret;
-
-               intel_ring_emit(ring, MI_WAIT_FOR_EVENT | flip_mask);
-               intel_ring_emit(ring, MI_NOOP);
-               intel_ring_advance(ring);
-       }
-
-       return 0;
-}
-
 static int
 i915_gem_execbuffer_move_to_gpu(struct intel_ring_buffer *ring,
                                struct list_head *objects)
 {
        struct drm_i915_gem_object *obj;
        uint32_t flush_domains = 0;
-       uint32_t flips = 0;
        int ret;
 
        list_for_each_entry(obj, objects, exec_list) {
@@ -671,18 +703,9 @@ i915_gem_execbuffer_move_to_gpu(struct intel_ring_buffer *ring,
                if (obj->base.write_domain & I915_GEM_DOMAIN_CPU)
                        i915_gem_clflush_object(obj);
 
-               if (obj->base.pending_write_domain)
-                       flips |= atomic_read(&obj->pending_flip);
-
                flush_domains |= obj->base.write_domain;
        }
 
-       if (flips) {
-               ret = i915_gem_execbuffer_wait_for_flips(ring, flips);
-               if (ret)
-                       return ret;
-       }
-
        if (flush_domains & I915_GEM_DOMAIN_CPU)
                i915_gem_chipset_flush(ring->dev);
 
@@ -698,6 +721,9 @@ i915_gem_execbuffer_move_to_gpu(struct intel_ring_buffer *ring,
 static bool
 i915_gem_check_execbuffer(struct drm_i915_gem_execbuffer2 *exec)
 {
+       if (exec->flags & __I915_EXEC_UNKNOWN_FLAGS)
+               return false;
+
        return ((exec->batch_start_offset | exec->batch_len) & 0x7) == 0;
 }
 
@@ -711,6 +737,9 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec,
                char __user *ptr = (char __user *)(uintptr_t)exec[i].relocs_ptr;
                int length; /* limited by fault_in_pages_readable() */
 
+               if (exec[i].flags & __EXEC_OBJECT_UNKNOWN_FLAGS)
+                       return -EINVAL;
+
                /* First check for malicious input causing overflow */
                if (exec[i].relocation_count >
                    INT_MAX / sizeof(struct drm_i915_gem_relocation_entry))
@@ -718,9 +747,6 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec,
 
                length = exec[i].relocation_count *
                        sizeof(struct drm_i915_gem_relocation_entry);
-               if (!access_ok(VERIFY_READ, ptr, length))
-                       return -EFAULT;
-
                /* we may also need to update the presumed offsets */
                if (!access_ok(VERIFY_WRITE, ptr, length))
                        return -EFAULT;
@@ -742,8 +768,10 @@ i915_gem_execbuffer_move_to_active(struct list_head *objects,
                u32 old_read = obj->base.read_domains;
                u32 old_write = obj->base.write_domain;
 
-               obj->base.read_domains = obj->base.pending_read_domains;
                obj->base.write_domain = obj->base.pending_write_domain;
+               if (obj->base.write_domain == 0)
+                       obj->base.pending_read_domains |= obj->base.read_domains;
+               obj->base.read_domains = obj->base.pending_read_domains;
                obj->fenced_gpu_access = obj->pending_fenced_gpu_access;
 
                i915_gem_object_move_to_active(obj, ring);
@@ -802,21 +830,18 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
                       struct drm_i915_gem_exec_object2 *exec)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
-       struct list_head objects;
        struct eb_objects *eb;
        struct drm_i915_gem_object *batch_obj;
        struct drm_clip_rect *cliprects = NULL;
        struct intel_ring_buffer *ring;
        u32 ctx_id = i915_execbuffer2_get_context_id(*args);
        u32 exec_start, exec_len;
-       u32 mask;
-       u32 flags;
+       u32 mask, flags;
        int ret, mode, i;
+       bool need_relocs;
 
-       if (!i915_gem_check_execbuffer(args)) {
-               DRM_DEBUG("execbuf with invalid offset/length\n");
+       if (!i915_gem_check_execbuffer(args))
                return -EINVAL;
-       }
 
        ret = validate_exec_list(exec, args->buffer_count);
        if (ret)
@@ -937,7 +962,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
                goto pre_mutex_err;
        }
 
-       eb = eb_create(args->buffer_count);
+       eb = eb_create(args);
        if (eb == NULL) {
                mutex_unlock(&dev->struct_mutex);
                ret = -ENOMEM;
@@ -945,51 +970,28 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
        }
 
        /* Look up object handles */
-       INIT_LIST_HEAD(&objects);
-       for (i = 0; i < args->buffer_count; i++) {
-               struct drm_i915_gem_object *obj;
-
-               obj = to_intel_bo(drm_gem_object_lookup(dev, file,
-                                                       exec[i].handle));
-               if (&obj->base == NULL) {
-                       DRM_DEBUG("Invalid object handle %d at index %d\n",
-                                  exec[i].handle, i);
-                       /* prevent error path from reading uninitialized data */
-                       ret = -ENOENT;
-                       goto err;
-               }
-
-               if (!list_empty(&obj->exec_list)) {
-                       DRM_DEBUG("Object %p [handle %d, index %d] appears more than once in object list\n",
-                                  obj, exec[i].handle, i);
-                       ret = -EINVAL;
-                       goto err;
-               }
-
-               list_add_tail(&obj->exec_list, &objects);
-               obj->exec_handle = exec[i].handle;
-               obj->exec_entry = &exec[i];
-               eb_add_object(eb, obj);
-       }
+       ret = eb_lookup_objects(eb, exec, args, file);
+       if (ret)
+               goto err;
 
        /* take note of the batch buffer before we might reorder the lists */
-       batch_obj = list_entry(objects.prev,
+       batch_obj = list_entry(eb->objects.prev,
                               struct drm_i915_gem_object,
                               exec_list);
 
        /* Move the objects en-masse into the GTT, evicting if necessary. */
-       ret = i915_gem_execbuffer_reserve(ring, file, &objects);
+       need_relocs = (args->flags & I915_EXEC_NO_RELOC) == 0;
+       ret = i915_gem_execbuffer_reserve(ring, file, &eb->objects, &need_relocs);
        if (ret)
                goto err;
 
        /* The objects are in their final locations, apply the relocations. */
-       ret = i915_gem_execbuffer_relocate(dev, eb, &objects);
+       if (need_relocs)
+               ret = i915_gem_execbuffer_relocate(dev, eb);
        if (ret) {
                if (ret == -EFAULT) {
-                       ret = i915_gem_execbuffer_relocate_slow(dev, file, ring,
-                                                               &objects, eb,
-                                                               exec,
-                                                               args->buffer_count);
+                       ret = i915_gem_execbuffer_relocate_slow(dev, args, file, ring,
+                                                               eb, exec);
                        BUG_ON(!mutex_is_locked(&dev->struct_mutex));
                }
                if (ret)
@@ -1011,7 +1013,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
        if (flags & I915_DISPATCH_SECURE && !batch_obj->has_global_gtt_mapping)
                i915_gem_gtt_bind_object(batch_obj, batch_obj->cache_level);
 
-       ret = i915_gem_execbuffer_move_to_gpu(ring, &objects);
+       ret = i915_gem_execbuffer_move_to_gpu(ring, &eb->objects);
        if (ret)
                goto err;
 
@@ -1065,20 +1067,11 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
 
        trace_i915_gem_ring_dispatch(ring, intel_ring_get_seqno(ring), flags);
 
-       i915_gem_execbuffer_move_to_active(&objects, ring);
+       i915_gem_execbuffer_move_to_active(&eb->objects, ring);
        i915_gem_execbuffer_retire_commands(dev, file, ring);
 
 err:
        eb_destroy(eb);
-       while (!list_empty(&objects)) {
-               struct drm_i915_gem_object *obj;
-
-               obj = list_first_entry(&objects,
-                                      struct drm_i915_gem_object,
-                                      exec_list);
-               list_del_init(&obj->exec_list);
-               drm_gem_object_unreference(&obj->base);
-       }
 
        mutex_unlock(&dev->struct_mutex);
 
@@ -1187,7 +1180,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,
        }
 
        exec2_list = kmalloc(sizeof(*exec2_list)*args->buffer_count,
-                            GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
+                            GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY);
        if (exec2_list == NULL)
                exec2_list = drm_malloc_ab(sizeof(*exec2_list),
                                           args->buffer_count);
index 2c150dee78a70468f96a87295cd29595cfa46a68..926a1e2dd2349ea0aa5960bd4b0e089225631083 100644 (file)
@@ -44,9 +44,9 @@ typedef uint32_t gtt_pte_t;
 #define GEN6_PTE_CACHE_LLC_MLC         (3 << 1)
 #define GEN6_PTE_ADDR_ENCODE(addr)     GEN6_GTT_ADDR_ENCODE(addr)
 
-static inline gtt_pte_t pte_encode(struct drm_device *dev,
-                                  dma_addr_t addr,
-                                  enum i915_cache_level level)
+static inline gtt_pte_t gen6_pte_encode(struct drm_device *dev,
+                                       dma_addr_t addr,
+                                       enum i915_cache_level level)
 {
        gtt_pte_t pte = GEN6_PTE_VALID;
        pte |= GEN6_PTE_ADDR_ENCODE(addr);
@@ -77,7 +77,7 @@ static inline gtt_pte_t pte_encode(struct drm_device *dev,
 }
 
 /* PPGTT support for Sandybdrige/Gen6 and later */
-static void i915_ppgtt_clear_range(struct i915_hw_ppgtt *ppgtt,
+static void gen6_ppgtt_clear_range(struct i915_hw_ppgtt *ppgtt,
                                   unsigned first_entry,
                                   unsigned num_entries)
 {
@@ -87,8 +87,9 @@ static void i915_ppgtt_clear_range(struct i915_hw_ppgtt *ppgtt,
        unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES;
        unsigned last_pte, i;
 
-       scratch_pte = pte_encode(ppgtt->dev, ppgtt->scratch_page_dma_addr,
-                                I915_CACHE_LLC);
+       scratch_pte = gen6_pte_encode(ppgtt->dev,
+                                     ppgtt->scratch_page_dma_addr,
+                                     I915_CACHE_LLC);
 
        while (num_entries) {
                last_pte = first_pte + num_entries;
@@ -108,10 +109,72 @@ static void i915_ppgtt_clear_range(struct i915_hw_ppgtt *ppgtt,
        }
 }
 
-int i915_gem_init_aliasing_ppgtt(struct drm_device *dev)
+static void gen6_ppgtt_insert_entries(struct i915_hw_ppgtt *ppgtt,
+                                     struct sg_table *pages,
+                                     unsigned first_entry,
+                                     enum i915_cache_level cache_level)
 {
+       gtt_pte_t *pt_vaddr;
+       unsigned act_pd = first_entry / I915_PPGTT_PT_ENTRIES;
+       unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES;
+       unsigned i, j, m, segment_len;
+       dma_addr_t page_addr;
+       struct scatterlist *sg;
+
+       /* init sg walking */
+       sg = pages->sgl;
+       i = 0;
+       segment_len = sg_dma_len(sg) >> PAGE_SHIFT;
+       m = 0;
+
+       while (i < pages->nents) {
+               pt_vaddr = kmap_atomic(ppgtt->pt_pages[act_pd]);
+
+               for (j = first_pte; j < I915_PPGTT_PT_ENTRIES; j++) {
+                       page_addr = sg_dma_address(sg) + (m << PAGE_SHIFT);
+                       pt_vaddr[j] = gen6_pte_encode(ppgtt->dev, page_addr,
+                                                     cache_level);
+
+                       /* grab the next page */
+                       if (++m == segment_len) {
+                               if (++i == pages->nents)
+                                       break;
+
+                               sg = sg_next(sg);
+                               segment_len = sg_dma_len(sg) >> PAGE_SHIFT;
+                               m = 0;
+                       }
+               }
+
+               kunmap_atomic(pt_vaddr);
+
+               first_pte = 0;
+               act_pd++;
+       }
+}
+
+static void gen6_ppgtt_cleanup(struct i915_hw_ppgtt *ppgtt)
+{
+       int i;
+
+       if (ppgtt->pt_dma_addr) {
+               for (i = 0; i < ppgtt->num_pd_entries; i++)
+                       pci_unmap_page(ppgtt->dev->pdev,
+                                      ppgtt->pt_dma_addr[i],
+                                      4096, PCI_DMA_BIDIRECTIONAL);
+       }
+
+       kfree(ppgtt->pt_dma_addr);
+       for (i = 0; i < ppgtt->num_pd_entries; i++)
+               __free_page(ppgtt->pt_pages[i]);
+       kfree(ppgtt->pt_pages);
+       kfree(ppgtt);
+}
+
+static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
+{
+       struct drm_device *dev = ppgtt->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct i915_hw_ppgtt *ppgtt;
        unsigned first_pd_entry_in_global_pt;
        int i;
        int ret = -ENOMEM;
@@ -119,18 +182,17 @@ int i915_gem_init_aliasing_ppgtt(struct drm_device *dev)
        /* ppgtt PDEs reside in the global gtt pagetable, which has 512*1024
         * entries. For aliasing ppgtt support we just steal them at the end for
         * now. */
-       first_pd_entry_in_global_pt = dev_priv->mm.gtt->gtt_total_entries - I915_PPGTT_PD_ENTRIES;
-
-       ppgtt = kzalloc(sizeof(*ppgtt), GFP_KERNEL);
-       if (!ppgtt)
-               return ret;
+       first_pd_entry_in_global_pt =
+               gtt_total_entries(dev_priv->gtt) - I915_PPGTT_PD_ENTRIES;
 
-       ppgtt->dev = dev;
        ppgtt->num_pd_entries = I915_PPGTT_PD_ENTRIES;
+       ppgtt->clear_range = gen6_ppgtt_clear_range;
+       ppgtt->insert_entries = gen6_ppgtt_insert_entries;
+       ppgtt->cleanup = gen6_ppgtt_cleanup;
        ppgtt->pt_pages = kzalloc(sizeof(struct page *)*ppgtt->num_pd_entries,
                                  GFP_KERNEL);
        if (!ppgtt->pt_pages)
-               goto err_ppgtt;
+               return -ENOMEM;
 
        for (i = 0; i < ppgtt->num_pd_entries; i++) {
                ppgtt->pt_pages[i] = alloc_page(GFP_KERNEL);
@@ -138,39 +200,32 @@ int i915_gem_init_aliasing_ppgtt(struct drm_device *dev)
                        goto err_pt_alloc;
        }
 
-       if (dev_priv->mm.gtt->needs_dmar) {
-               ppgtt->pt_dma_addr = kzalloc(sizeof(dma_addr_t)
-                                               *ppgtt->num_pd_entries,
-                                            GFP_KERNEL);
-               if (!ppgtt->pt_dma_addr)
-                       goto err_pt_alloc;
+       ppgtt->pt_dma_addr = kzalloc(sizeof(dma_addr_t) *ppgtt->num_pd_entries,
+                                    GFP_KERNEL);
+       if (!ppgtt->pt_dma_addr)
+               goto err_pt_alloc;
 
-               for (i = 0; i < ppgtt->num_pd_entries; i++) {
-                       dma_addr_t pt_addr;
+       for (i = 0; i < ppgtt->num_pd_entries; i++) {
+               dma_addr_t pt_addr;
 
-                       pt_addr = pci_map_page(dev->pdev, ppgtt->pt_pages[i],
-                                              0, 4096,
-                                              PCI_DMA_BIDIRECTIONAL);
+               pt_addr = pci_map_page(dev->pdev, ppgtt->pt_pages[i], 0, 4096,
+                                      PCI_DMA_BIDIRECTIONAL);
 
-                       if (pci_dma_mapping_error(dev->pdev,
-                                                 pt_addr)) {
-                               ret = -EIO;
-                               goto err_pd_pin;
+               if (pci_dma_mapping_error(dev->pdev, pt_addr)) {
+                       ret = -EIO;
+                       goto err_pd_pin;
 
-                       }
-                       ppgtt->pt_dma_addr[i] = pt_addr;
                }
+               ppgtt->pt_dma_addr[i] = pt_addr;
        }
 
-       ppgtt->scratch_page_dma_addr = dev_priv->mm.gtt->scratch_page_dma;
+       ppgtt->scratch_page_dma_addr = dev_priv->gtt.scratch_page_dma;
 
-       i915_ppgtt_clear_range(ppgtt, 0,
-                              ppgtt->num_pd_entries*I915_PPGTT_PT_ENTRIES);
+       ppgtt->clear_range(ppgtt, 0,
+                          ppgtt->num_pd_entries*I915_PPGTT_PT_ENTRIES);
 
        ppgtt->pd_offset = (first_pd_entry_in_global_pt)*sizeof(gtt_pte_t);
 
-       dev_priv->mm.aliasing_ppgtt = ppgtt;
-
        return 0;
 
 err_pd_pin:
@@ -186,94 +241,57 @@ err_pt_alloc:
                        __free_page(ppgtt->pt_pages[i]);
        }
        kfree(ppgtt->pt_pages);
-err_ppgtt:
-       kfree(ppgtt);
 
        return ret;
 }
 
-void i915_gem_cleanup_aliasing_ppgtt(struct drm_device *dev)
+static int i915_gem_init_aliasing_ppgtt(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
-       int i;
+       struct i915_hw_ppgtt *ppgtt;
+       int ret;
 
+       ppgtt = kzalloc(sizeof(*ppgtt), GFP_KERNEL);
        if (!ppgtt)
-               return;
+               return -ENOMEM;
 
-       if (ppgtt->pt_dma_addr) {
-               for (i = 0; i < ppgtt->num_pd_entries; i++)
-                       pci_unmap_page(dev->pdev, ppgtt->pt_dma_addr[i],
-                                      4096, PCI_DMA_BIDIRECTIONAL);
-       }
+       ppgtt->dev = dev;
 
-       kfree(ppgtt->pt_dma_addr);
-       for (i = 0; i < ppgtt->num_pd_entries; i++)
-               __free_page(ppgtt->pt_pages[i]);
-       kfree(ppgtt->pt_pages);
-       kfree(ppgtt);
+       ret = gen6_ppgtt_init(ppgtt);
+       if (ret)
+               kfree(ppgtt);
+       else
+               dev_priv->mm.aliasing_ppgtt = ppgtt;
+
+       return ret;
 }
 
-static void i915_ppgtt_insert_sg_entries(struct i915_hw_ppgtt *ppgtt,
-                                        const struct sg_table *pages,
-                                        unsigned first_entry,
-                                        enum i915_cache_level cache_level)
+void i915_gem_cleanup_aliasing_ppgtt(struct drm_device *dev)
 {
-       gtt_pte_t *pt_vaddr;
-       unsigned act_pd = first_entry / I915_PPGTT_PT_ENTRIES;
-       unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES;
-       unsigned i, j, m, segment_len;
-       dma_addr_t page_addr;
-       struct scatterlist *sg;
-
-       /* init sg walking */
-       sg = pages->sgl;
-       i = 0;
-       segment_len = sg_dma_len(sg) >> PAGE_SHIFT;
-       m = 0;
-
-       while (i < pages->nents) {
-               pt_vaddr = kmap_atomic(ppgtt->pt_pages[act_pd]);
-
-               for (j = first_pte; j < I915_PPGTT_PT_ENTRIES; j++) {
-                       page_addr = sg_dma_address(sg) + (m << PAGE_SHIFT);
-                       pt_vaddr[j] = pte_encode(ppgtt->dev, page_addr,
-                                                cache_level);
-
-                       /* grab the next page */
-                       if (++m == segment_len) {
-                               if (++i == pages->nents)
-                                       break;
-
-                               sg = sg_next(sg);
-                               segment_len = sg_dma_len(sg) >> PAGE_SHIFT;
-                               m = 0;
-                       }
-               }
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
 
-               kunmap_atomic(pt_vaddr);
+       if (!ppgtt)
+               return;
 
-               first_pte = 0;
-               act_pd++;
-       }
+       ppgtt->cleanup(ppgtt);
 }
 
 void i915_ppgtt_bind_object(struct i915_hw_ppgtt *ppgtt,
                            struct drm_i915_gem_object *obj,
                            enum i915_cache_level cache_level)
 {
-       i915_ppgtt_insert_sg_entries(ppgtt,
-                                    obj->pages,
-                                    obj->gtt_space->start >> PAGE_SHIFT,
-                                    cache_level);
+       ppgtt->insert_entries(ppgtt, obj->pages,
+                             obj->gtt_space->start >> PAGE_SHIFT,
+                             cache_level);
 }
 
 void i915_ppgtt_unbind_object(struct i915_hw_ppgtt *ppgtt,
                              struct drm_i915_gem_object *obj)
 {
-       i915_ppgtt_clear_range(ppgtt,
-                              obj->gtt_space->start >> PAGE_SHIFT,
-                              obj->base.size >> PAGE_SHIFT);
+       ppgtt->clear_range(ppgtt,
+                          obj->gtt_space->start >> PAGE_SHIFT,
+                          obj->base.size >> PAGE_SHIFT);
 }
 
 void i915_gem_init_ppgtt(struct drm_device *dev)
@@ -282,7 +300,7 @@ void i915_gem_init_ppgtt(struct drm_device *dev)
        uint32_t pd_offset;
        struct intel_ring_buffer *ring;
        struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
-       uint32_t __iomem *pd_addr;
+       gtt_pte_t __iomem *pd_addr;
        uint32_t pd_entry;
        int i;
 
@@ -290,15 +308,11 @@ void i915_gem_init_ppgtt(struct drm_device *dev)
                return;
 
 
-       pd_addr = dev_priv->mm.gtt->gtt + ppgtt->pd_offset/sizeof(uint32_t);
+       pd_addr = (gtt_pte_t __iomem*)dev_priv->gtt.gsm + ppgtt->pd_offset/sizeof(gtt_pte_t);
        for (i = 0; i < ppgtt->num_pd_entries; i++) {
                dma_addr_t pt_addr;
 
-               if (dev_priv->mm.gtt->needs_dmar)
-                       pt_addr = ppgtt->pt_dma_addr[i];
-               else
-                       pt_addr = page_to_phys(ppgtt->pt_pages[i]);
-
+               pt_addr = ppgtt->pt_dma_addr[i];
                pd_entry = GEN6_PDE_ADDR_ENCODE(pt_addr);
                pd_entry |= GEN6_PDE_VALID;
 
@@ -338,11 +352,27 @@ void i915_gem_init_ppgtt(struct drm_device *dev)
        }
 }
 
+extern int intel_iommu_gfx_mapped;
+/* Certain Gen5 chipsets require require idling the GPU before
+ * unmapping anything from the GTT when VT-d is enabled.
+ */
+static inline bool needs_idle_maps(struct drm_device *dev)
+{
+#ifdef CONFIG_INTEL_IOMMU
+       /* Query intel_iommu to see if we need the workaround. Presumably that
+        * was loaded first.
+        */
+       if (IS_GEN5(dev) && IS_MOBILE(dev) && intel_iommu_gfx_mapped)
+               return true;
+#endif
+       return false;
+}
+
 static bool do_idling(struct drm_i915_private *dev_priv)
 {
        bool ret = dev_priv->mm.interruptible;
 
-       if (unlikely(dev_priv->mm.gtt->do_idle_maps)) {
+       if (unlikely(dev_priv->gtt.do_idle_maps)) {
                dev_priv->mm.interruptible = false;
                if (i915_gpu_idle(dev_priv->dev)) {
                        DRM_ERROR("Couldn't idle GPU\n");
@@ -356,45 +386,18 @@ static bool do_idling(struct drm_i915_private *dev_priv)
 
 static void undo_idling(struct drm_i915_private *dev_priv, bool interruptible)
 {
-       if (unlikely(dev_priv->mm.gtt->do_idle_maps))
+       if (unlikely(dev_priv->gtt.do_idle_maps))
                dev_priv->mm.interruptible = interruptible;
 }
 
-
-static void i915_ggtt_clear_range(struct drm_device *dev,
-                                unsigned first_entry,
-                                unsigned num_entries)
-{
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       gtt_pte_t scratch_pte;
-       gtt_pte_t __iomem *gtt_base = dev_priv->mm.gtt->gtt + first_entry;
-       const int max_entries = dev_priv->mm.gtt->gtt_total_entries - first_entry;
-       int i;
-
-       if (INTEL_INFO(dev)->gen < 6) {
-               intel_gtt_clear_range(first_entry, num_entries);
-               return;
-       }
-
-       if (WARN(num_entries > max_entries,
-                "First entry = %d; Num entries = %d (max=%d)\n",
-                first_entry, num_entries, max_entries))
-               num_entries = max_entries;
-
-       scratch_pte = pte_encode(dev, dev_priv->mm.gtt->scratch_page_dma, I915_CACHE_LLC);
-       for (i = 0; i < num_entries; i++)
-               iowrite32(scratch_pte, &gtt_base[i]);
-       readl(gtt_base);
-}
-
 void i915_gem_restore_gtt_mappings(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_i915_gem_object *obj;
 
        /* First fill our portion of the GTT with scratch pages */
-       i915_ggtt_clear_range(dev, dev_priv->mm.gtt_start / PAGE_SIZE,
-                             (dev_priv->mm.gtt_end - dev_priv->mm.gtt_start) / PAGE_SIZE);
+       dev_priv->gtt.gtt_clear_range(dev, dev_priv->gtt.start / PAGE_SIZE,
+                                     dev_priv->gtt.total / PAGE_SIZE);
 
        list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) {
                i915_gem_clflush_object(obj);
@@ -423,16 +426,15 @@ int i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj)
  * within the global GTT as well as accessible by the GPU through the GMADR
  * mapped BAR (dev_priv->mm.gtt->gtt).
  */
-static void gen6_ggtt_bind_object(struct drm_i915_gem_object *obj,
-                                 enum i915_cache_level level)
+static void gen6_ggtt_insert_entries(struct drm_device *dev,
+                                    struct sg_table *st,
+                                    unsigned int first_entry,
+                                    enum i915_cache_level level)
 {
-       struct drm_device *dev = obj->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct sg_table *st = obj->pages;
        struct scatterlist *sg = st->sgl;
-       const int first_entry = obj->gtt_space->start >> PAGE_SHIFT;
-       const int max_entries = dev_priv->mm.gtt->gtt_total_entries - first_entry;
-       gtt_pte_t __iomem *gtt_entries = dev_priv->mm.gtt->gtt + first_entry;
+       gtt_pte_t __iomem *gtt_entries =
+               (gtt_pte_t __iomem *)dev_priv->gtt.gsm + first_entry;
        int unused, i = 0;
        unsigned int len, m = 0;
        dma_addr_t addr;
@@ -441,14 +443,12 @@ static void gen6_ggtt_bind_object(struct drm_i915_gem_object *obj,
                len = sg_dma_len(sg) >> PAGE_SHIFT;
                for (m = 0; m < len; m++) {
                        addr = sg_dma_address(sg) + (m << PAGE_SHIFT);
-                       iowrite32(pte_encode(dev, addr, level), &gtt_entries[i]);
+                       iowrite32(gen6_pte_encode(dev, addr, level),
+                                 &gtt_entries[i]);
                        i++;
                }
        }
 
-       BUG_ON(i > max_entries);
-       BUG_ON(i != obj->base.size / PAGE_SIZE);
-
        /* XXX: This serves as a posting read to make sure that the PTE has
         * actually been updated. There is some concern that even though
         * registers and PTEs are within the same BAR that they are potentially
@@ -456,7 +456,8 @@ static void gen6_ggtt_bind_object(struct drm_i915_gem_object *obj,
         * hardware should work, we must keep this posting read for paranoia.
         */
        if (i != 0)
-               WARN_ON(readl(&gtt_entries[i-1]) != pte_encode(dev, addr, level));
+               WARN_ON(readl(&gtt_entries[i-1])
+                       != gen6_pte_encode(dev, addr, level));
 
        /* This next bit makes the above posting read even more important. We
         * want to flush the TLBs only after we're certain all the PTE updates
@@ -466,28 +467,70 @@ static void gen6_ggtt_bind_object(struct drm_i915_gem_object *obj,
        POSTING_READ(GFX_FLSH_CNTL_GEN6);
 }
 
+static void gen6_ggtt_clear_range(struct drm_device *dev,
+                                 unsigned int first_entry,
+                                 unsigned int num_entries)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       gtt_pte_t scratch_pte;
+       gtt_pte_t __iomem *gtt_base = (gtt_pte_t __iomem *) dev_priv->gtt.gsm + first_entry;
+       const int max_entries = gtt_total_entries(dev_priv->gtt) - first_entry;
+       int i;
+
+       if (WARN(num_entries > max_entries,
+                "First entry = %d; Num entries = %d (max=%d)\n",
+                first_entry, num_entries, max_entries))
+               num_entries = max_entries;
+
+       scratch_pte = gen6_pte_encode(dev, dev_priv->gtt.scratch_page_dma,
+                                     I915_CACHE_LLC);
+       for (i = 0; i < num_entries; i++)
+               iowrite32(scratch_pte, &gtt_base[i]);
+       readl(gtt_base);
+}
+
+
+static void i915_ggtt_insert_entries(struct drm_device *dev,
+                                    struct sg_table *st,
+                                    unsigned int pg_start,
+                                    enum i915_cache_level cache_level)
+{
+       unsigned int flags = (cache_level == I915_CACHE_NONE) ?
+               AGP_USER_MEMORY : AGP_USER_CACHED_MEMORY;
+
+       intel_gtt_insert_sg_entries(st, pg_start, flags);
+
+}
+
+static void i915_ggtt_clear_range(struct drm_device *dev,
+                                 unsigned int first_entry,
+                                 unsigned int num_entries)
+{
+       intel_gtt_clear_range(first_entry, num_entries);
+}
+
+
 void i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj,
                              enum i915_cache_level cache_level)
 {
        struct drm_device *dev = obj->base.dev;
-       if (INTEL_INFO(dev)->gen < 6) {
-               unsigned int flags = (cache_level == I915_CACHE_NONE) ?
-                       AGP_USER_MEMORY : AGP_USER_CACHED_MEMORY;
-               intel_gtt_insert_sg_entries(obj->pages,
-                                           obj->gtt_space->start >> PAGE_SHIFT,
-                                           flags);
-       } else {
-               gen6_ggtt_bind_object(obj, cache_level);
-       }
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       dev_priv->gtt.gtt_insert_entries(dev, obj->pages,
+                                        obj->gtt_space->start >> PAGE_SHIFT,
+                                        cache_level);
 
        obj->has_global_gtt_mapping = 1;
 }
 
 void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj)
 {
-       i915_ggtt_clear_range(obj->base.dev,
-                             obj->gtt_space->start >> PAGE_SHIFT,
-                             obj->base.size >> PAGE_SHIFT);
+       struct drm_device *dev = obj->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       dev_priv->gtt.gtt_clear_range(obj->base.dev,
+                                     obj->gtt_space->start >> PAGE_SHIFT,
+                                     obj->base.size >> PAGE_SHIFT);
 
        obj->has_global_gtt_mapping = 0;
 }
@@ -524,27 +567,101 @@ static void i915_gtt_color_adjust(struct drm_mm_node *node,
                        *end -= 4096;
        }
 }
-
-void i915_gem_init_global_gtt(struct drm_device *dev,
-                             unsigned long start,
-                             unsigned long mappable_end,
-                             unsigned long end)
+void i915_gem_setup_global_gtt(struct drm_device *dev,
+                              unsigned long start,
+                              unsigned long mappable_end,
+                              unsigned long end)
 {
+       /* Let GEM Manage all of the aperture.
+        *
+        * However, leave one page at the end still bound to the scratch page.
+        * There are a number of places where the hardware apparently prefetches
+        * past the end of the object, and we've seen multiple hangs with the
+        * GPU head pointer stuck in a batchbuffer bound at the last page of the
+        * aperture.  One page should be enough to keep any prefetching inside
+        * of the aperture.
+        */
        drm_i915_private_t *dev_priv = dev->dev_private;
+       struct drm_mm_node *entry;
+       struct drm_i915_gem_object *obj;
+       unsigned long hole_start, hole_end;
 
-       /* Substract the guard page ... */
+       BUG_ON(mappable_end > end);
+
+       /* Subtract the guard page ... */
        drm_mm_init(&dev_priv->mm.gtt_space, start, end - start - PAGE_SIZE);
        if (!HAS_LLC(dev))
                dev_priv->mm.gtt_space.color_adjust = i915_gtt_color_adjust;
 
-       dev_priv->mm.gtt_start = start;
-       dev_priv->mm.gtt_mappable_end = mappable_end;
-       dev_priv->mm.gtt_end = end;
-       dev_priv->mm.gtt_total = end - start;
-       dev_priv->mm.mappable_gtt_total = min(end, mappable_end) - start;
+       /* Mark any preallocated objects as occupied */
+       list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) {
+               DRM_DEBUG_KMS("reserving preallocated space: %x + %zx\n",
+                             obj->gtt_offset, obj->base.size);
+
+               BUG_ON(obj->gtt_space != I915_GTT_RESERVED);
+               obj->gtt_space = drm_mm_create_block(&dev_priv->mm.gtt_space,
+                                                    obj->gtt_offset,
+                                                    obj->base.size,
+                                                    false);
+               obj->has_global_gtt_mapping = 1;
+       }
+
+       dev_priv->gtt.start = start;
+       dev_priv->gtt.total = end - start;
+
+       /* Clear any non-preallocated blocks */
+       drm_mm_for_each_hole(entry, &dev_priv->mm.gtt_space,
+                            hole_start, hole_end) {
+               DRM_DEBUG_KMS("clearing unused GTT space: [%lx, %lx]\n",
+                             hole_start, hole_end);
+               dev_priv->gtt.gtt_clear_range(dev, hole_start / PAGE_SIZE,
+                                             (hole_end-hole_start) / PAGE_SIZE);
+       }
 
-       /* ... but ensure that we clear the entire range. */
-       i915_ggtt_clear_range(dev, start / PAGE_SIZE, (end-start) / PAGE_SIZE);
+       /* And finally clear the reserved guard page */
+       dev_priv->gtt.gtt_clear_range(dev, end / PAGE_SIZE - 1, 1);
+}
+
+static bool
+intel_enable_ppgtt(struct drm_device *dev)
+{
+       if (i915_enable_ppgtt >= 0)
+               return i915_enable_ppgtt;
+
+#ifdef CONFIG_INTEL_IOMMU
+       /* Disable ppgtt on SNB if VT-d is on. */
+       if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped)
+               return false;
+#endif
+
+       return true;
+}
+
+void i915_gem_init_global_gtt(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       unsigned long gtt_size, mappable_size;
+
+       gtt_size = dev_priv->gtt.total;
+       mappable_size = dev_priv->gtt.mappable_end;
+
+       if (intel_enable_ppgtt(dev) && HAS_ALIASING_PPGTT(dev)) {
+               int ret;
+               /* PPGTT pdes are stolen from global gtt ptes, so shrink the
+                * aperture accordingly when using aliasing ppgtt. */
+               gtt_size -= I915_PPGTT_PD_ENTRIES*PAGE_SIZE;
+
+               i915_gem_setup_global_gtt(dev, 0, mappable_size, gtt_size);
+
+               ret = i915_gem_init_aliasing_ppgtt(dev);
+               if (!ret)
+                       return;
+
+               DRM_ERROR("Aliased PPGTT setup failed %d\n", ret);
+               drm_mm_takedown(&dev_priv->mm.gtt_space);
+               gtt_size += I915_PPGTT_PD_ENTRIES*PAGE_SIZE;
+       }
+       i915_gem_setup_global_gtt(dev, 0, mappable_size, gtt_size);
 }
 
 static int setup_scratch_page(struct drm_device *dev)
@@ -567,8 +684,8 @@ static int setup_scratch_page(struct drm_device *dev)
 #else
        dma_addr = page_to_phys(page);
 #endif
-       dev_priv->mm.gtt->scratch_page = page;
-       dev_priv->mm.gtt->scratch_page_dma = dma_addr;
+       dev_priv->gtt.scratch_page = page;
+       dev_priv->gtt.scratch_page_dma = dma_addr;
 
        return 0;
 }
@@ -576,11 +693,11 @@ static int setup_scratch_page(struct drm_device *dev)
 static void teardown_scratch_page(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       set_pages_wb(dev_priv->mm.gtt->scratch_page, 1);
-       pci_unmap_page(dev->pdev, dev_priv->mm.gtt->scratch_page_dma,
+       set_pages_wb(dev_priv->gtt.scratch_page, 1);
+       pci_unmap_page(dev->pdev, dev_priv->gtt.scratch_page_dma,
                       PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
-       put_page(dev_priv->mm.gtt->scratch_page);
-       __free_page(dev_priv->mm.gtt->scratch_page);
+       put_page(dev_priv->gtt.scratch_page);
+       __free_page(dev_priv->gtt.scratch_page);
 }
 
 static inline unsigned int gen6_get_total_gtt_size(u16 snb_gmch_ctl)
@@ -590,14 +707,14 @@ static inline unsigned int gen6_get_total_gtt_size(u16 snb_gmch_ctl)
        return snb_gmch_ctl << 20;
 }
 
-static inline unsigned int gen6_get_stolen_size(u16 snb_gmch_ctl)
+static inline size_t gen6_get_stolen_size(u16 snb_gmch_ctl)
 {
        snb_gmch_ctl >>= SNB_GMCH_GMS_SHIFT;
        snb_gmch_ctl &= SNB_GMCH_GMS_MASK;
        return snb_gmch_ctl << 25; /* 32 MB units */
 }
 
-static inline unsigned int gen7_get_stolen_size(u16 snb_gmch_ctl)
+static inline size_t gen7_get_stolen_size(u16 snb_gmch_ctl)
 {
        static const int stolen_decoder[] = {
                0, 0, 0, 0, 0, 32, 48, 64, 128, 256, 96, 160, 224, 352};
@@ -606,103 +723,127 @@ static inline unsigned int gen7_get_stolen_size(u16 snb_gmch_ctl)
        return stolen_decoder[snb_gmch_ctl] << 20;
 }
 
-int i915_gem_gtt_init(struct drm_device *dev)
+static int gen6_gmch_probe(struct drm_device *dev,
+                          size_t *gtt_total,
+                          size_t *stolen,
+                          phys_addr_t *mappable_base,
+                          unsigned long *mappable_end)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        phys_addr_t gtt_bus_addr;
+       unsigned int gtt_size;
        u16 snb_gmch_ctl;
        int ret;
 
-       /* On modern platforms we need not worry ourself with the legacy
-        * hostbridge query stuff. Skip it entirely
-        */
-       if (INTEL_INFO(dev)->gen < 6) {
-               ret = intel_gmch_probe(dev_priv->bridge_dev, dev->pdev, NULL);
-               if (!ret) {
-                       DRM_ERROR("failed to set up gmch\n");
-                       return -EIO;
-               }
+       *mappable_base = pci_resource_start(dev->pdev, 2);
+       *mappable_end = pci_resource_len(dev->pdev, 2);
 
-               dev_priv->mm.gtt = intel_gtt_get();
-               if (!dev_priv->mm.gtt) {
-                       DRM_ERROR("Failed to initialize GTT\n");
-                       intel_gmch_remove();
-                       return -ENODEV;
-               }
-               return 0;
+       /* 64/512MB is the current min/max we actually know of, but this is just
+        * a coarse sanity check.
+        */
+       if ((*mappable_end < (64<<20) || (*mappable_end > (512<<20)))) {
+               DRM_ERROR("Unknown GMADR size (%lx)\n",
+                         dev_priv->gtt.mappable_end);
+               return -ENXIO;
        }
 
-       dev_priv->mm.gtt = kzalloc(sizeof(*dev_priv->mm.gtt), GFP_KERNEL);
-       if (!dev_priv->mm.gtt)
-               return -ENOMEM;
-
        if (!pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(40)))
                pci_set_consistent_dma_mask(dev->pdev, DMA_BIT_MASK(40));
+       pci_read_config_word(dev->pdev, SNB_GMCH_CTRL, &snb_gmch_ctl);
+       gtt_size = gen6_get_total_gtt_size(snb_gmch_ctl);
 
-#ifdef CONFIG_INTEL_IOMMU
-       dev_priv->mm.gtt->needs_dmar = 1;
-#endif
+       if (IS_GEN7(dev))
+               *stolen = gen7_get_stolen_size(snb_gmch_ctl);
+       else
+               *stolen = gen6_get_stolen_size(snb_gmch_ctl);
+
+       *gtt_total = (gtt_size / sizeof(gtt_pte_t)) << PAGE_SHIFT;
 
        /* For GEN6+ the PTEs for the ggtt live at 2MB + BAR0 */
        gtt_bus_addr = pci_resource_start(dev->pdev, 0) + (2<<20);
-       dev_priv->mm.gtt->gma_bus_addr = pci_resource_start(dev->pdev, 2);
-
-       /* i9xx_setup */
-       pci_read_config_word(dev->pdev, SNB_GMCH_CTRL, &snb_gmch_ctl);
-       dev_priv->mm.gtt->gtt_total_entries =
-               gen6_get_total_gtt_size(snb_gmch_ctl) / sizeof(gtt_pte_t);
-       if (INTEL_INFO(dev)->gen < 7)
-               dev_priv->mm.gtt->stolen_size = gen6_get_stolen_size(snb_gmch_ctl);
-       else
-               dev_priv->mm.gtt->stolen_size = gen7_get_stolen_size(snb_gmch_ctl);
-
-       dev_priv->mm.gtt->gtt_mappable_entries = pci_resource_len(dev->pdev, 2) >> PAGE_SHIFT;
-       /* 64/512MB is the current min/max we actually know of, but this is just a
-        * coarse sanity check.
-        */
-       if ((dev_priv->mm.gtt->gtt_mappable_entries >> 8) < 64 ||
-           dev_priv->mm.gtt->gtt_mappable_entries > dev_priv->mm.gtt->gtt_total_entries) {
-               DRM_ERROR("Unknown GMADR entries (%d)\n",
-                         dev_priv->mm.gtt->gtt_mappable_entries);
-               ret = -ENXIO;
-               goto err_out;
+       dev_priv->gtt.gsm = ioremap_wc(gtt_bus_addr, gtt_size);
+       if (!dev_priv->gtt.gsm) {
+               DRM_ERROR("Failed to map the gtt page table\n");
+               return -ENOMEM;
        }
 
        ret = setup_scratch_page(dev);
-       if (ret) {
+       if (ret)
                DRM_ERROR("Scratch setup failed\n");
-               goto err_out;
-       }
 
-       dev_priv->mm.gtt->gtt = ioremap_wc(gtt_bus_addr,
-                                          dev_priv->mm.gtt->gtt_total_entries * sizeof(gtt_pte_t));
-       if (!dev_priv->mm.gtt->gtt) {
-               DRM_ERROR("Failed to map the gtt page table\n");
-               teardown_scratch_page(dev);
-               ret = -ENOMEM;
-               goto err_out;
+       dev_priv->gtt.gtt_clear_range = gen6_ggtt_clear_range;
+       dev_priv->gtt.gtt_insert_entries = gen6_ggtt_insert_entries;
+
+       return ret;
+}
+
+static void gen6_gmch_remove(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       iounmap(dev_priv->gtt.gsm);
+       teardown_scratch_page(dev_priv->dev);
+}
+
+static int i915_gmch_probe(struct drm_device *dev,
+                          size_t *gtt_total,
+                          size_t *stolen,
+                          phys_addr_t *mappable_base,
+                          unsigned long *mappable_end)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int ret;
+
+       ret = intel_gmch_probe(dev_priv->bridge_dev, dev_priv->dev->pdev, NULL);
+       if (!ret) {
+               DRM_ERROR("failed to set up gmch\n");
+               return -EIO;
        }
 
-       /* GMADR is the PCI aperture used by SW to access tiled GFX surfaces in a linear fashion. */
-       DRM_INFO("Memory usable by graphics device = %dM\n", dev_priv->mm.gtt->gtt_total_entries >> 8);
-       DRM_DEBUG_DRIVER("GMADR size = %dM\n", dev_priv->mm.gtt->gtt_mappable_entries >> 8);
-       DRM_DEBUG_DRIVER("GTT stolen size = %dM\n", dev_priv->mm.gtt->stolen_size >> 20);
+       intel_gtt_get(gtt_total, stolen, mappable_base, mappable_end);
+
+       dev_priv->gtt.do_idle_maps = needs_idle_maps(dev_priv->dev);
+       dev_priv->gtt.gtt_clear_range = i915_ggtt_clear_range;
+       dev_priv->gtt.gtt_insert_entries = i915_ggtt_insert_entries;
 
        return 0;
+}
 
-err_out:
-       kfree(dev_priv->mm.gtt);
-       if (INTEL_INFO(dev)->gen < 6)
-               intel_gmch_remove();
-       return ret;
+static void i915_gmch_remove(struct drm_device *dev)
+{
+       intel_gmch_remove();
 }
 
-void i915_gem_gtt_fini(struct drm_device *dev)
+int i915_gem_gtt_init(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       iounmap(dev_priv->mm.gtt->gtt);
-       teardown_scratch_page(dev);
-       if (INTEL_INFO(dev)->gen < 6)
-               intel_gmch_remove();
-       kfree(dev_priv->mm.gtt);
+       struct i915_gtt *gtt = &dev_priv->gtt;
+       unsigned long gtt_size;
+       int ret;
+
+       if (INTEL_INFO(dev)->gen <= 5) {
+               dev_priv->gtt.gtt_probe = i915_gmch_probe;
+               dev_priv->gtt.gtt_remove = i915_gmch_remove;
+       } else {
+               dev_priv->gtt.gtt_probe = gen6_gmch_probe;
+               dev_priv->gtt.gtt_remove = gen6_gmch_remove;
+       }
+
+       ret = dev_priv->gtt.gtt_probe(dev, &dev_priv->gtt.total,
+                                    &dev_priv->gtt.stolen_size,
+                                    &gtt->mappable_base,
+                                    &gtt->mappable_end);
+       if (ret)
+               return ret;
+
+       gtt_size = (dev_priv->gtt.total >> PAGE_SHIFT) * sizeof(gtt_pte_t);
+
+       /* GMADR is the PCI mmio aperture into the global GTT. */
+       DRM_INFO("Memory usable by graphics device = %zdM\n",
+                dev_priv->gtt.total >> 20);
+       DRM_DEBUG_DRIVER("GMADR size = %ldM\n",
+                        dev_priv->gtt.mappable_end >> 20);
+       DRM_DEBUG_DRIVER("GTT stolen size = %zdM\n",
+                        dev_priv->gtt.stolen_size >> 20);
+
+       return 0;
 }
index 8e91083b126ffd32a65d7d36165525a9ef37e1a0..69d97cbac13c5754344355e602c64ad75b768a24 100644 (file)
  * for is a boon.
  */
 
-#define PTE_ADDRESS_MASK               0xfffff000
-#define PTE_ADDRESS_MASK_HIGH          0x000000f0 /* i915+ */
-#define PTE_MAPPING_TYPE_UNCACHED      (0 << 1)
-#define PTE_MAPPING_TYPE_DCACHE                (1 << 1) /* i830 only */
-#define PTE_MAPPING_TYPE_CACHED                (3 << 1)
-#define PTE_MAPPING_TYPE_MASK          (3 << 1)
-#define PTE_VALID                      (1 << 0)
-
-/**
- * i915_stolen_to_phys - take an offset into stolen memory and turn it into
- *                       a physical one
- * @dev: drm device
- * @offset: address to translate
- *
- * Some chip functions require allocations from stolen space and need the
- * physical address of the memory in question.
- */
-static unsigned long i915_stolen_to_phys(struct drm_device *dev, u32 offset)
+static unsigned long i915_stolen_to_physical(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct pci_dev *pdev = dev_priv->bridge_dev;
        u32 base;
 
-#if 0
        /* On the machines I have tested the Graphics Base of Stolen Memory
-        * is unreliable, so compute the base by subtracting the stolen memory
-        * from the Top of Low Usable DRAM which is where the BIOS places
-        * the graphics stolen memory.
+        * is unreliable, so on those compute the base by subtracting the
+        * stolen memory from the Top of Low Usable DRAM which is where the
+        * BIOS places the graphics stolen memory.
+        *
+        * On gen2, the layout is slightly different with the Graphics Segment
+        * immediately following Top of Memory (or Top of Usable DRAM). Note
+        * it appears that TOUD is only reported by 865g, so we just use the
+        * top of memory as determined by the e820 probe.
+        *
+        * XXX gen2 requires an unavailable symbol and 945gm fails with
+        * its value of TOLUD.
         */
-       if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) {
-               /* top 32bits are reserved = 0 */
+       base = 0;
+       if (INTEL_INFO(dev)->gen >= 6) {
+               /* Read Base Data of Stolen Memory Register (BDSM) directly.
+                * Note that there is also a MCHBAR miror at 0x1080c0 or
+                * we could use device 2:0x5c instead.
+               */
+               pci_read_config_dword(pdev, 0xB0, &base);
+               base &= ~4095; /* lower bits used for locking register */
+       } else if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) {
+               /* Read Graphics Base of Stolen Memory directly */
                pci_read_config_dword(pdev, 0xA4, &base);
-       } else {
-               /* XXX presume 8xx is the same as i915 */
-               pci_bus_read_config_dword(pdev->bus, 2, 0x5C, &base);
-       }
-#else
-       if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) {
-               u16 val;
-               pci_read_config_word(pdev, 0xb0, &val);
-               base = val >> 4 << 20;
-       } else {
+#if 0
+       } else if (IS_GEN3(dev)) {
                u8 val;
+               /* Stolen is immediately below Top of Low Usable DRAM */
                pci_read_config_byte(pdev, 0x9c, &val);
                base = val >> 3 << 27;
-       }
-       base -= dev_priv->mm.gtt->stolen_size;
+               base -= dev_priv->mm.gtt->stolen_size;
+       } else {
+               /* Stolen is immediately above Top of Memory */
+               base = max_low_pfn_mapped << PAGE_SHIFT;
 #endif
+       }
 
-       return base + offset;
+       return base;
 }
 
-static void i915_warn_stolen(struct drm_device *dev)
-{
-       DRM_INFO("not enough stolen space for compressed buffer, disabling\n");
-       DRM_INFO("hint: you may be able to increase stolen memory size in the BIOS to avoid this\n");
-}
-
-static void i915_setup_compression(struct drm_device *dev, int size)
+static int i915_setup_compression(struct drm_device *dev, int size)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_mm_node *compressed_fb, *uninitialized_var(compressed_llb);
-       unsigned long cfb_base;
-       unsigned long ll_base = 0;
-
-       /* Just in case the BIOS is doing something questionable. */
-       intel_disable_fbc(dev);
 
-       compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0);
+       /* Try to over-allocate to reduce reallocations and fragmentation */
+       compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen,
+                                          size <<= 1, 4096, 0);
+       if (!compressed_fb)
+               compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen,
+                                                  size >>= 1, 4096, 0);
        if (compressed_fb)
                compressed_fb = drm_mm_get_block(compressed_fb, size, 4096);
        if (!compressed_fb)
                goto err;
 
-       cfb_base = i915_stolen_to_phys(dev, compressed_fb->start);
-       if (!cfb_base)
-               goto err_fb;
-
-       if (!(IS_GM45(dev) || HAS_PCH_SPLIT(dev))) {
+       if (HAS_PCH_SPLIT(dev))
+               I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start);
+       else if (IS_GM45(dev)) {
+               I915_WRITE(DPFC_CB_BASE, compressed_fb->start);
+       } else {
                compressed_llb = drm_mm_search_free(&dev_priv->mm.stolen,
                                                    4096, 4096, 0);
                if (compressed_llb)
@@ -129,73 +117,206 @@ static void i915_setup_compression(struct drm_device *dev, int size)
                if (!compressed_llb)
                        goto err_fb;
 
-               ll_base = i915_stolen_to_phys(dev, compressed_llb->start);
-               if (!ll_base)
-                       goto err_llb;
+               dev_priv->compressed_llb = compressed_llb;
+
+               I915_WRITE(FBC_CFB_BASE,
+                          dev_priv->mm.stolen_base + compressed_fb->start);
+               I915_WRITE(FBC_LL_BASE,
+                          dev_priv->mm.stolen_base + compressed_llb->start);
        }
 
+       dev_priv->compressed_fb = compressed_fb;
        dev_priv->cfb_size = size;
 
-       dev_priv->compressed_fb = compressed_fb;
-       if (HAS_PCH_SPLIT(dev))
-               I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start);
-       else if (IS_GM45(dev)) {
-               I915_WRITE(DPFC_CB_BASE, compressed_fb->start);
-       } else {
-               I915_WRITE(FBC_CFB_BASE, cfb_base);
-               I915_WRITE(FBC_LL_BASE, ll_base);
-               dev_priv->compressed_llb = compressed_llb;
-       }
+       DRM_DEBUG_KMS("reserved %d bytes of contiguous stolen space for FBC\n",
+                     size);
 
-       DRM_DEBUG_KMS("FBC base 0x%08lx, ll base 0x%08lx, size %dM\n",
-                     cfb_base, ll_base, size >> 20);
-       return;
+       return 0;
 
-err_llb:
-       drm_mm_put_block(compressed_llb);
 err_fb:
        drm_mm_put_block(compressed_fb);
 err:
-       dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL;
-       i915_warn_stolen(dev);
+       return -ENOSPC;
+}
+
+int i915_gem_stolen_setup_compression(struct drm_device *dev, int size)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (dev_priv->mm.stolen_base == 0)
+               return -ENODEV;
+
+       if (size < dev_priv->cfb_size)
+               return 0;
+
+       /* Release any current block */
+       i915_gem_stolen_cleanup_compression(dev);
+
+       return i915_setup_compression(dev, size);
 }
 
-static void i915_cleanup_compression(struct drm_device *dev)
+void i915_gem_stolen_cleanup_compression(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
-       drm_mm_put_block(dev_priv->compressed_fb);
+       if (dev_priv->cfb_size == 0)
+               return;
+
+       if (dev_priv->compressed_fb)
+               drm_mm_put_block(dev_priv->compressed_fb);
+
        if (dev_priv->compressed_llb)
                drm_mm_put_block(dev_priv->compressed_llb);
+
+       dev_priv->cfb_size = 0;
 }
 
 void i915_gem_cleanup_stolen(struct drm_device *dev)
 {
-       if (I915_HAS_FBC(dev) && i915_powersave)
-               i915_cleanup_compression(dev);
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       i915_gem_stolen_cleanup_compression(dev);
+       drm_mm_takedown(&dev_priv->mm.stolen);
 }
 
 int i915_gem_init_stolen(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       unsigned long prealloc_size = dev_priv->mm.gtt->stolen_size;
+
+       dev_priv->mm.stolen_base = i915_stolen_to_physical(dev);
+       if (dev_priv->mm.stolen_base == 0)
+               return 0;
+
+       DRM_DEBUG_KMS("found %zd bytes of stolen memory at %08lx\n",
+                     dev_priv->gtt.stolen_size, dev_priv->mm.stolen_base);
 
        /* Basic memrange allocator for stolen space */
-       drm_mm_init(&dev_priv->mm.stolen, 0, prealloc_size);
+       drm_mm_init(&dev_priv->mm.stolen, 0, dev_priv->gtt.stolen_size);
+
+       return 0;
+}
 
-       /* Try to set up FBC with a reasonable compressed buffer size */
-       if (I915_HAS_FBC(dev) && i915_powersave) {
-               int cfb_size;
+static struct sg_table *
+i915_pages_create_for_stolen(struct drm_device *dev,
+                            u32 offset, u32 size)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct sg_table *st;
+       struct scatterlist *sg;
+
+       DRM_DEBUG_DRIVER("offset=0x%x, size=%d\n", offset, size);
+       BUG_ON(offset > dev_priv->gtt.stolen_size - size);
 
-               /* Leave 1M for line length buffer & misc. */
+       /* We hide that we have no struct page backing our stolen object
+        * by wrapping the contiguous physical allocation with a fake
+        * dma mapping in a single scatterlist.
+        */
+
+       st = kmalloc(sizeof(*st), GFP_KERNEL);
+       if (st == NULL)
+               return NULL;
 
-               /* Try to get a 32M buffer... */
-               if (prealloc_size > (36*1024*1024))
-                       cfb_size = 32*1024*1024;
-               else /* fall back to 7/8 of the stolen space */
-                       cfb_size = prealloc_size * 7 / 8;
-               i915_setup_compression(dev, cfb_size);
+       if (sg_alloc_table(st, 1, GFP_KERNEL)) {
+               kfree(st);
+               return NULL;
        }
 
-       return 0;
+       sg = st->sgl;
+       sg->offset = offset;
+       sg->length = size;
+
+       sg_dma_address(sg) = (dma_addr_t)dev_priv->mm.stolen_base + offset;
+       sg_dma_len(sg) = size;
+
+       return st;
+}
+
+static int i915_gem_object_get_pages_stolen(struct drm_i915_gem_object *obj)
+{
+       BUG();
+       return -EINVAL;
+}
+
+static void i915_gem_object_put_pages_stolen(struct drm_i915_gem_object *obj)
+{
+       /* Should only be called during free */
+       sg_free_table(obj->pages);
+       kfree(obj->pages);
+}
+
+static const struct drm_i915_gem_object_ops i915_gem_object_stolen_ops = {
+       .get_pages = i915_gem_object_get_pages_stolen,
+       .put_pages = i915_gem_object_put_pages_stolen,
+};
+
+static struct drm_i915_gem_object *
+_i915_gem_object_create_stolen(struct drm_device *dev,
+                              struct drm_mm_node *stolen)
+{
+       struct drm_i915_gem_object *obj;
+
+       obj = i915_gem_object_alloc(dev);
+       if (obj == NULL)
+               return NULL;
+
+       if (drm_gem_private_object_init(dev, &obj->base, stolen->size))
+               goto cleanup;
+
+       i915_gem_object_init(obj, &i915_gem_object_stolen_ops);
+
+       obj->pages = i915_pages_create_for_stolen(dev,
+                                                 stolen->start, stolen->size);
+       if (obj->pages == NULL)
+               goto cleanup;
+
+       obj->has_dma_mapping = true;
+       obj->pages_pin_count = 1;
+       obj->stolen = stolen;
+
+       obj->base.write_domain = I915_GEM_DOMAIN_GTT;
+       obj->base.read_domains = I915_GEM_DOMAIN_GTT;
+       obj->cache_level = I915_CACHE_NONE;
+
+       return obj;
+
+cleanup:
+       i915_gem_object_free(obj);
+       return NULL;
+}
+
+struct drm_i915_gem_object *
+i915_gem_object_create_stolen(struct drm_device *dev, u32 size)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_i915_gem_object *obj;
+       struct drm_mm_node *stolen;
+
+       if (dev_priv->mm.stolen_base == 0)
+               return NULL;
+
+       DRM_DEBUG_KMS("creating stolen object: size=%x\n", size);
+       if (size == 0)
+               return NULL;
+
+       stolen = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0);
+       if (stolen)
+               stolen = drm_mm_get_block(stolen, size, 4096);
+       if (stolen == NULL)
+               return NULL;
+
+       obj = _i915_gem_object_create_stolen(dev, stolen);
+       if (obj)
+               return obj;
+
+       drm_mm_put_block(stolen);
+       return NULL;
+}
+
+void
+i915_gem_object_release_stolen(struct drm_i915_gem_object *obj)
+{
+       if (obj->stolen) {
+               drm_mm_put_block(obj->stolen);
+               obj->stolen = NULL;
+       }
 }
index cedbfd7b3dfab10e915fe23ea3fc43afc661c302..abcba2f5a788b4ad3727309de95548851513120a 100644 (file)
@@ -272,18 +272,7 @@ i915_gem_object_fence_ok(struct drm_i915_gem_object *obj, int tiling_mode)
                        return false;
        }
 
-       /*
-        * Previous chips need to be aligned to the size of the smallest
-        * fence register that can contain the object.
-        */
-       if (INTEL_INFO(obj->base.dev)->gen == 3)
-               size = 1024*1024;
-       else
-               size = 512*1024;
-
-       while (size < obj->base.size)
-               size <<= 1;
-
+       size = i915_gem_get_gtt_size(obj->base.dev, obj->base.size, tiling_mode);
        if (obj->gtt_space->size != size)
                return false;
 
@@ -368,15 +357,15 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
 
                obj->map_and_fenceable =
                        obj->gtt_space == NULL ||
-                       (obj->gtt_offset + obj->base.size <= dev_priv->mm.gtt_mappable_end &&
+                       (obj->gtt_offset + obj->base.size <= dev_priv->gtt.mappable_end &&
                         i915_gem_object_fence_ok(obj, args->tiling_mode));
 
                /* Rebind if we need a change of alignment */
                if (!obj->map_and_fenceable) {
                        u32 unfenced_alignment =
-                               i915_gem_get_unfenced_gtt_alignment(dev,
-                                                                   obj->base.size,
-                                                                   args->tiling_mode);
+                               i915_gem_get_gtt_alignment(dev, obj->base.size,
+                                                           args->tiling_mode,
+                                                           false);
                        if (obj->gtt_offset & (unfenced_alignment - 1))
                                ret = i915_gem_object_unbind(obj);
                }
@@ -396,6 +385,18 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
        /* we have to maintain this existing ABI... */
        args->stride = obj->stride;
        args->tiling_mode = obj->tiling_mode;
+
+       /* Try to preallocate memory required to save swizzling on put-pages */
+       if (i915_gem_object_needs_bit17_swizzle(obj)) {
+               if (obj->bit_17 == NULL) {
+                       obj->bit_17 = kmalloc(BITS_TO_LONGS(obj->base.size >> PAGE_SHIFT) *
+                                             sizeof(long), GFP_KERNEL);
+               }
+       } else {
+               kfree(obj->bit_17);
+               obj->bit_17 = NULL;
+       }
+
        drm_gem_object_unreference(&obj->base);
        mutex_unlock(&dev->struct_mutex);
 
index fe843389c7b473e2035124c38330c3a965450773..2cd97d1cc920dbf1555af5d28d97d7cf893f6567 100644 (file)
@@ -287,6 +287,10 @@ static void i915_hotplug_work_func(struct work_struct *work)
        struct drm_mode_config *mode_config = &dev->mode_config;
        struct intel_encoder *encoder;
 
+       /* HPD irq before everything is fully set up. */
+       if (!dev_priv->enable_hotplug_processing)
+               return;
+
        mutex_lock(&mode_config->mutex);
        DRM_DEBUG_KMS("running encoder hotplug functions\n");
 
@@ -300,9 +304,6 @@ static void i915_hotplug_work_func(struct work_struct *work)
        drm_helper_hpd_irq_event(dev);
 }
 
-/* defined intel_pm.c */
-extern spinlock_t mchdev_lock;
-
 static void ironlake_handle_rps_change(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
@@ -355,8 +356,8 @@ static void notify_ring(struct drm_device *dev,
 
        wake_up_all(&ring->irq_queue);
        if (i915_enable_hangcheck) {
-               dev_priv->hangcheck_count = 0;
-               mod_timer(&dev_priv->hangcheck_timer,
+               dev_priv->gpu_error.hangcheck_count = 0;
+               mod_timer(&dev_priv->gpu_error.hangcheck_timer,
                          round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES));
        }
 }
@@ -524,6 +525,20 @@ static void gen6_queue_rps_work(struct drm_i915_private *dev_priv,
        queue_work(dev_priv->wq, &dev_priv->rps.work);
 }
 
+static void gmbus_irq_handler(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private;
+
+       wake_up_all(&dev_priv->gmbus_wait_queue);
+}
+
+static void dp_aux_irq_handler(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private;
+
+       wake_up_all(&dev_priv->gmbus_wait_queue);
+}
+
 static irqreturn_t valleyview_irq_handler(int irq, void *arg)
 {
        struct drm_device *dev = (struct drm_device *) arg;
@@ -533,7 +548,6 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
        unsigned long irqflags;
        int pipe;
        u32 pipe_stats[I915_MAX_PIPES];
-       bool blc_event;
 
        atomic_inc(&dev_priv->irq_received);
 
@@ -590,8 +604,8 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
                        I915_READ(PORT_HOTPLUG_STAT);
                }
 
-               if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS)
-                       blc_event = true;
+               if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS)
+                       gmbus_irq_handler(dev);
 
                if (pm_iir & GEN6_PM_DEFERRED_EVENTS)
                        gen6_queue_rps_work(dev_priv, pm_iir);
@@ -618,8 +632,11 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir)
                                 (pch_iir & SDE_AUDIO_POWER_MASK) >>
                                 SDE_AUDIO_POWER_SHIFT);
 
+       if (pch_iir & SDE_AUX_MASK)
+               dp_aux_irq_handler(dev);
+
        if (pch_iir & SDE_GMBUS)
-               DRM_DEBUG_DRIVER("PCH GMBUS interrupt\n");
+               gmbus_irq_handler(dev);
 
        if (pch_iir & SDE_AUDIO_HDCP_MASK)
                DRM_DEBUG_DRIVER("PCH HDCP audio interrupt\n");
@@ -662,10 +679,10 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
                                 SDE_AUDIO_POWER_SHIFT_CPT);
 
        if (pch_iir & SDE_AUX_MASK_CPT)
-               DRM_DEBUG_DRIVER("AUX channel interrupt\n");
+               dp_aux_irq_handler(dev);
 
        if (pch_iir & SDE_GMBUS_CPT)
-               DRM_DEBUG_DRIVER("PCH GMBUS interrupt\n");
+               gmbus_irq_handler(dev);
 
        if (pch_iir & SDE_AUDIO_CP_REQ_CPT)
                DRM_DEBUG_DRIVER("Audio CP request interrupt\n");
@@ -703,6 +720,9 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
 
        de_iir = I915_READ(DEIIR);
        if (de_iir) {
+               if (de_iir & DE_AUX_CHANNEL_A_IVB)
+                       dp_aux_irq_handler(dev);
+
                if (de_iir & DE_GSE_IVB)
                        intel_opregion_gse_intr(dev);
 
@@ -758,7 +778,7 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
        struct drm_device *dev = (struct drm_device *) arg;
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        int ret = IRQ_NONE;
-       u32 de_iir, gt_iir, de_ier, pch_iir, pm_iir;
+       u32 de_iir, gt_iir, de_ier, pm_iir;
 
        atomic_inc(&dev_priv->irq_received);
 
@@ -769,11 +789,9 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
 
        de_iir = I915_READ(DEIIR);
        gt_iir = I915_READ(GTIIR);
-       pch_iir = I915_READ(SDEIIR);
        pm_iir = I915_READ(GEN6_PMIIR);
 
-       if (de_iir == 0 && gt_iir == 0 && pch_iir == 0 &&
-           (!IS_GEN6(dev) || pm_iir == 0))
+       if (de_iir == 0 && gt_iir == 0 && (!IS_GEN6(dev) || pm_iir == 0))
                goto done;
 
        ret = IRQ_HANDLED;
@@ -783,6 +801,9 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
        else
                snb_gt_irq_handler(dev, dev_priv, gt_iir);
 
+       if (de_iir & DE_AUX_CHANNEL_A)
+               dp_aux_irq_handler(dev);
+
        if (de_iir & DE_GSE)
                intel_opregion_gse_intr(dev);
 
@@ -804,10 +825,15 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
 
        /* check event from PCH */
        if (de_iir & DE_PCH_EVENT) {
+               u32 pch_iir = I915_READ(SDEIIR);
+
                if (HAS_PCH_CPT(dev))
                        cpt_irq_handler(dev, pch_iir);
                else
                        ibx_irq_handler(dev, pch_iir);
+
+               /* should clear PCH hotplug event before clear CPU irq */
+               I915_WRITE(SDEIIR, pch_iir);
        }
 
        if (IS_GEN5(dev) &&  de_iir & DE_PCU_EVENT)
@@ -816,8 +842,6 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
        if (IS_GEN6(dev) && pm_iir & GEN6_PM_DEFERRED_EVENTS)
                gen6_queue_rps_work(dev_priv, pm_iir);
 
-       /* should clear PCH hotplug event before clear CPU irq */
-       I915_WRITE(SDEIIR, pch_iir);
        I915_WRITE(GTIIR, gt_iir);
        I915_WRITE(DEIIR, de_iir);
        I915_WRITE(GEN6_PMIIR, pm_iir);
@@ -838,23 +862,60 @@ done:
  */
 static void i915_error_work_func(struct work_struct *work)
 {
-       drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
-                                                   error_work);
+       struct i915_gpu_error *error = container_of(work, struct i915_gpu_error,
+                                                   work);
+       drm_i915_private_t *dev_priv = container_of(error, drm_i915_private_t,
+                                                   gpu_error);
        struct drm_device *dev = dev_priv->dev;
+       struct intel_ring_buffer *ring;
        char *error_event[] = { "ERROR=1", NULL };
        char *reset_event[] = { "RESET=1", NULL };
        char *reset_done_event[] = { "ERROR=0", NULL };
+       int i, ret;
 
        kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event);
 
-       if (atomic_read(&dev_priv->mm.wedged)) {
+       /*
+        * Note that there's only one work item which does gpu resets, so we
+        * need not worry about concurrent gpu resets potentially incrementing
+        * error->reset_counter twice. We only need to take care of another
+        * racing irq/hangcheck declaring the gpu dead for a second time. A
+        * quick check for that is good enough: schedule_work ensures the
+        * correct ordering between hang detection and this work item, and since
+        * the reset in-progress bit is only ever set by code outside of this
+        * work we don't need to worry about any other races.
+        */
+       if (i915_reset_in_progress(error) && !i915_terminally_wedged(error)) {
                DRM_DEBUG_DRIVER("resetting chip\n");
-               kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event);
-               if (!i915_reset(dev)) {
-                       atomic_set(&dev_priv->mm.wedged, 0);
-                       kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event);
+               kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE,
+                                  reset_event);
+
+               ret = i915_reset(dev);
+
+               if (ret == 0) {
+                       /*
+                        * After all the gem state is reset, increment the reset
+                        * counter and wake up everyone waiting for the reset to
+                        * complete.
+                        *
+                        * Since unlock operations are a one-sided barrier only,
+                        * we need to insert a barrier here to order any seqno
+                        * updates before
+                        * the counter increment.
+                        */
+                       smp_mb__before_atomic_inc();
+                       atomic_inc(&dev_priv->gpu_error.reset_counter);
+
+                       kobject_uevent_env(&dev->primary->kdev.kobj,
+                                          KOBJ_CHANGE, reset_done_event);
+               } else {
+                       atomic_set(&error->reset_counter, I915_WEDGED);
                }
-               complete_all(&dev_priv->error_completion);
+
+               for_each_ring(ring, dev_priv, i)
+                       wake_up_all(&ring->irq_queue);
+
+               wake_up_all(&dev_priv->gpu_error.reset_queue);
        }
 }
 
@@ -915,7 +976,7 @@ i915_error_object_create(struct drm_i915_private *dev_priv,
                        goto unwind;
 
                local_irq_save(flags);
-               if (reloc_offset < dev_priv->mm.gtt_mappable_end &&
+               if (reloc_offset < dev_priv->gtt.mappable_end &&
                    src->has_global_gtt_mapping) {
                        void __iomem *s;
 
@@ -924,10 +985,18 @@ i915_error_object_create(struct drm_i915_private *dev_priv,
                         * captures what the GPU read.
                         */
 
-                       s = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping,
+                       s = io_mapping_map_atomic_wc(dev_priv->gtt.mappable,
                                                     reloc_offset);
                        memcpy_fromio(d, s, PAGE_SIZE);
                        io_mapping_unmap_atomic(s);
+               } else if (src->stolen) {
+                       unsigned long offset;
+
+                       offset = dev_priv->mm.stolen_base;
+                       offset += src->stolen->start;
+                       offset += i << PAGE_SHIFT;
+
+                       memcpy_fromio(d, (void __iomem *) offset, PAGE_SIZE);
                } else {
                        struct page *page;
                        void *s;
@@ -1074,6 +1143,8 @@ static void i915_gem_record_fences(struct drm_device *dev,
                        error->fence[i] = I915_READ(FENCE_REG_830_0 + (i * 4));
                break;
 
+       default:
+               BUG();
        }
 }
 
@@ -1222,9 +1293,9 @@ static void i915_capture_error_state(struct drm_device *dev)
        unsigned long flags;
        int i, pipe;
 
-       spin_lock_irqsave(&dev_priv->error_lock, flags);
-       error = dev_priv->first_error;
-       spin_unlock_irqrestore(&dev_priv->error_lock, flags);
+       spin_lock_irqsave(&dev_priv->gpu_error.lock, flags);
+       error = dev_priv->gpu_error.first_error;
+       spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags);
        if (error)
                return;
 
@@ -1235,7 +1306,8 @@ static void i915_capture_error_state(struct drm_device *dev)
                return;
        }
 
-       DRM_INFO("capturing error event; look for more information in /debug/dri/%d/i915_error_state\n",
+       DRM_INFO("capturing error event; look for more information in"
+                "/sys/kernel/debug/dri/%d/i915_error_state\n",
                 dev->primary->index);
 
        kref_init(&error->ref);
@@ -1318,12 +1390,12 @@ static void i915_capture_error_state(struct drm_device *dev)
        error->overlay = intel_overlay_capture_error_state(dev);
        error->display = intel_display_capture_error_state(dev);
 
-       spin_lock_irqsave(&dev_priv->error_lock, flags);
-       if (dev_priv->first_error == NULL) {
-               dev_priv->first_error = error;
+       spin_lock_irqsave(&dev_priv->gpu_error.lock, flags);
+       if (dev_priv->gpu_error.first_error == NULL) {
+               dev_priv->gpu_error.first_error = error;
                error = NULL;
        }
-       spin_unlock_irqrestore(&dev_priv->error_lock, flags);
+       spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags);
 
        if (error)
                i915_error_state_free(&error->ref);
@@ -1335,10 +1407,10 @@ void i915_destroy_error_state(struct drm_device *dev)
        struct drm_i915_error_state *error;
        unsigned long flags;
 
-       spin_lock_irqsave(&dev_priv->error_lock, flags);
-       error = dev_priv->first_error;
-       dev_priv->first_error = NULL;
-       spin_unlock_irqrestore(&dev_priv->error_lock, flags);
+       spin_lock_irqsave(&dev_priv->gpu_error.lock, flags);
+       error = dev_priv->gpu_error.first_error;
+       dev_priv->gpu_error.first_error = NULL;
+       spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags);
 
        if (error)
                kref_put(&error->ref, i915_error_state_free);
@@ -1459,17 +1531,18 @@ void i915_handle_error(struct drm_device *dev, bool wedged)
        i915_report_and_clear_eir(dev);
 
        if (wedged) {
-               INIT_COMPLETION(dev_priv->error_completion);
-               atomic_set(&dev_priv->mm.wedged, 1);
+               atomic_set_mask(I915_RESET_IN_PROGRESS_FLAG,
+                               &dev_priv->gpu_error.reset_counter);
 
                /*
-                * Wakeup waiting processes so they don't hang
+                * Wakeup waiting processes so that the reset work item
+                * doesn't deadlock trying to grab various locks.
                 */
                for_each_ring(ring, dev_priv, i)
                        wake_up_all(&ring->irq_queue);
        }
 
-       queue_work(dev_priv->wq, &dev_priv->error_work);
+       queue_work(dev_priv->wq, &dev_priv->gpu_error.work);
 }
 
 static void i915_pageflip_stall_check(struct drm_device *dev, int pipe)
@@ -1700,7 +1773,7 @@ static bool i915_hangcheck_hung(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
 
-       if (dev_priv->hangcheck_count++ > 1) {
+       if (dev_priv->gpu_error.hangcheck_count++ > 1) {
                bool hung = true;
 
                DRM_ERROR("Hangcheck timer elapsed... GPU hung\n");
@@ -1759,25 +1832,29 @@ void i915_hangcheck_elapsed(unsigned long data)
                        goto repeat;
                }
 
-               dev_priv->hangcheck_count = 0;
+               dev_priv->gpu_error.hangcheck_count = 0;
                return;
        }
 
        i915_get_extra_instdone(dev, instdone);
-       if (memcmp(dev_priv->last_acthd, acthd, sizeof(acthd)) == 0 &&
-           memcmp(dev_priv->prev_instdone, instdone, sizeof(instdone)) == 0) {
+       if (memcmp(dev_priv->gpu_error.last_acthd, acthd,
+                  sizeof(acthd)) == 0 &&
+           memcmp(dev_priv->gpu_error.prev_instdone, instdone,
+                  sizeof(instdone)) == 0) {
                if (i915_hangcheck_hung(dev))
                        return;
        } else {
-               dev_priv->hangcheck_count = 0;
+               dev_priv->gpu_error.hangcheck_count = 0;
 
-               memcpy(dev_priv->last_acthd, acthd, sizeof(acthd));
-               memcpy(dev_priv->prev_instdone, instdone, sizeof(instdone));
+               memcpy(dev_priv->gpu_error.last_acthd, acthd,
+                      sizeof(acthd));
+               memcpy(dev_priv->gpu_error.prev_instdone, instdone,
+                      sizeof(instdone));
        }
 
 repeat:
        /* Reset timer case chip hangs without another request being added */
-       mod_timer(&dev_priv->hangcheck_timer,
+       mod_timer(&dev_priv->gpu_error.hangcheck_timer,
                  round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES));
 }
 
@@ -1847,7 +1924,7 @@ static void valleyview_irq_preinstall(struct drm_device *dev)
  * This register is the same on all known PCH chips.
  */
 
-static void ironlake_enable_pch_hotplug(struct drm_device *dev)
+static void ibx_enable_hotplug(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        u32     hotplug;
@@ -1860,14 +1937,36 @@ static void ironlake_enable_pch_hotplug(struct drm_device *dev)
        I915_WRITE(PCH_PORT_HOTPLUG, hotplug);
 }
 
+static void ibx_irq_postinstall(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       u32 mask;
+
+       if (HAS_PCH_IBX(dev))
+               mask = SDE_HOTPLUG_MASK |
+                      SDE_GMBUS |
+                      SDE_AUX_MASK;
+       else
+               mask = SDE_HOTPLUG_MASK_CPT |
+                      SDE_GMBUS_CPT |
+                      SDE_AUX_MASK_CPT;
+
+       I915_WRITE(SDEIIR, I915_READ(SDEIIR));
+       I915_WRITE(SDEIMR, ~mask);
+       I915_WRITE(SDEIER, mask);
+       POSTING_READ(SDEIER);
+
+       ibx_enable_hotplug(dev);
+}
+
 static int ironlake_irq_postinstall(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        /* enable kind of interrupts always enabled */
        u32 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT |
-                          DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE;
+                          DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE |
+                          DE_AUX_CHANNEL_A;
        u32 render_irqs;
-       u32 hotplug_mask;
 
        dev_priv->irq_mask = ~display_mask;
 
@@ -1895,27 +1994,7 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
        I915_WRITE(GTIER, render_irqs);
        POSTING_READ(GTIER);
 
-       if (HAS_PCH_CPT(dev)) {
-               hotplug_mask = (SDE_CRT_HOTPLUG_CPT |
-                               SDE_PORTB_HOTPLUG_CPT |
-                               SDE_PORTC_HOTPLUG_CPT |
-                               SDE_PORTD_HOTPLUG_CPT);
-       } else {
-               hotplug_mask = (SDE_CRT_HOTPLUG |
-                               SDE_PORTB_HOTPLUG |
-                               SDE_PORTC_HOTPLUG |
-                               SDE_PORTD_HOTPLUG |
-                               SDE_AUX_MASK);
-       }
-
-       dev_priv->pch_irq_mask = ~hotplug_mask;
-
-       I915_WRITE(SDEIIR, I915_READ(SDEIIR));
-       I915_WRITE(SDEIMR, dev_priv->pch_irq_mask);
-       I915_WRITE(SDEIER, hotplug_mask);
-       POSTING_READ(SDEIER);
-
-       ironlake_enable_pch_hotplug(dev);
+       ibx_irq_postinstall(dev);
 
        if (IS_IRONLAKE_M(dev)) {
                /* Clear & enable PCU event interrupts */
@@ -1935,9 +2014,9 @@ static int ivybridge_irq_postinstall(struct drm_device *dev)
                DE_MASTER_IRQ_CONTROL | DE_GSE_IVB | DE_PCH_EVENT_IVB |
                DE_PLANEC_FLIP_DONE_IVB |
                DE_PLANEB_FLIP_DONE_IVB |
-               DE_PLANEA_FLIP_DONE_IVB;
+               DE_PLANEA_FLIP_DONE_IVB |
+               DE_AUX_CHANNEL_A_IVB;
        u32 render_irqs;
-       u32 hotplug_mask;
 
        dev_priv->irq_mask = ~display_mask;
 
@@ -1961,18 +2040,7 @@ static int ivybridge_irq_postinstall(struct drm_device *dev)
        I915_WRITE(GTIER, render_irqs);
        POSTING_READ(GTIER);
 
-       hotplug_mask = (SDE_CRT_HOTPLUG_CPT |
-                       SDE_PORTB_HOTPLUG_CPT |
-                       SDE_PORTC_HOTPLUG_CPT |
-                       SDE_PORTD_HOTPLUG_CPT);
-       dev_priv->pch_irq_mask = ~hotplug_mask;
-
-       I915_WRITE(SDEIIR, I915_READ(SDEIIR));
-       I915_WRITE(SDEIMR, dev_priv->pch_irq_mask);
-       I915_WRITE(SDEIER, hotplug_mask);
-       POSTING_READ(SDEIER);
-
-       ironlake_enable_pch_hotplug(dev);
+       ibx_irq_postinstall(dev);
 
        return 0;
 }
@@ -1981,7 +2049,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        u32 enable_mask;
-       u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN);
        u32 pipestat_enable = PLANE_FLIP_DONE_INT_EN_VLV;
        u32 render_irqs;
        u16 msid;
@@ -2010,6 +2077,9 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
        msid |= (1<<14);
        pci_write_config_word(dev_priv->dev->pdev, 0x98, msid);
 
+       I915_WRITE(PORT_HOTPLUG_EN, 0);
+       POSTING_READ(PORT_HOTPLUG_EN);
+
        I915_WRITE(VLV_IMR, dev_priv->irq_mask);
        I915_WRITE(VLV_IER, enable_mask);
        I915_WRITE(VLV_IIR, 0xffffffff);
@@ -2018,6 +2088,7 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
        POSTING_READ(VLV_IER);
 
        i915_enable_pipestat(dev_priv, 0, pipestat_enable);
+       i915_enable_pipestat(dev_priv, 0, PIPE_GMBUS_EVENT_ENABLE);
        i915_enable_pipestat(dev_priv, 1, pipestat_enable);
 
        I915_WRITE(VLV_IIR, 0xffffffff);
@@ -2038,13 +2109,22 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
 #endif
 
        I915_WRITE(VLV_MASTER_IER, MASTER_INTERRUPT_ENABLE);
+
+       return 0;
+}
+
+static void valleyview_hpd_irq_setup(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN);
+
        /* Note HDMI and DP share bits */
-       if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS)
-               hotplug_en |= HDMIB_HOTPLUG_INT_EN;
-       if (dev_priv->hotplug_supported_mask & HDMIC_HOTPLUG_INT_STATUS)
-               hotplug_en |= HDMIC_HOTPLUG_INT_EN;
-       if (dev_priv->hotplug_supported_mask & HDMID_HOTPLUG_INT_STATUS)
-               hotplug_en |= HDMID_HOTPLUG_INT_EN;
+       if (dev_priv->hotplug_supported_mask & PORTB_HOTPLUG_INT_STATUS)
+               hotplug_en |= PORTB_HOTPLUG_INT_EN;
+       if (dev_priv->hotplug_supported_mask & PORTC_HOTPLUG_INT_STATUS)
+               hotplug_en |= PORTC_HOTPLUG_INT_EN;
+       if (dev_priv->hotplug_supported_mask & PORTD_HOTPLUG_INT_STATUS)
+               hotplug_en |= PORTD_HOTPLUG_INT_EN;
        if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_I915)
                hotplug_en |= SDVOC_HOTPLUG_INT_EN;
        if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS_I915)
@@ -2055,8 +2135,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
        }
 
        I915_WRITE(PORT_HOTPLUG_EN, hotplug_en);
-
-       return 0;
 }
 
 static void valleyview_irq_uninstall(struct drm_device *dev)
@@ -2286,6 +2364,9 @@ static int i915_irq_postinstall(struct drm_device *dev)
                I915_USER_INTERRUPT;
 
        if (I915_HAS_HOTPLUG(dev)) {
+               I915_WRITE(PORT_HOTPLUG_EN, 0);
+               POSTING_READ(PORT_HOTPLUG_EN);
+
                /* Enable in IER... */
                enable_mask |= I915_DISPLAY_PORT_INTERRUPT;
                /* and unmask in IMR */
@@ -2296,15 +2377,25 @@ static int i915_irq_postinstall(struct drm_device *dev)
        I915_WRITE(IER, enable_mask);
        POSTING_READ(IER);
 
+       intel_opregion_enable_asle(dev);
+
+       return 0;
+}
+
+static void i915_hpd_irq_setup(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       u32 hotplug_en;
+
        if (I915_HAS_HOTPLUG(dev)) {
-               u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN);
-
-               if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS)
-                       hotplug_en |= HDMIB_HOTPLUG_INT_EN;
-               if (dev_priv->hotplug_supported_mask & HDMIC_HOTPLUG_INT_STATUS)
-                       hotplug_en |= HDMIC_HOTPLUG_INT_EN;
-               if (dev_priv->hotplug_supported_mask & HDMID_HOTPLUG_INT_STATUS)
-                       hotplug_en |= HDMID_HOTPLUG_INT_EN;
+               hotplug_en = I915_READ(PORT_HOTPLUG_EN);
+
+               if (dev_priv->hotplug_supported_mask & PORTB_HOTPLUG_INT_STATUS)
+                       hotplug_en |= PORTB_HOTPLUG_INT_EN;
+               if (dev_priv->hotplug_supported_mask & PORTC_HOTPLUG_INT_STATUS)
+                       hotplug_en |= PORTC_HOTPLUG_INT_EN;
+               if (dev_priv->hotplug_supported_mask & PORTD_HOTPLUG_INT_STATUS)
+                       hotplug_en |= PORTD_HOTPLUG_INT_EN;
                if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_I915)
                        hotplug_en |= SDVOC_HOTPLUG_INT_EN;
                if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS_I915)
@@ -2318,10 +2409,6 @@ static int i915_irq_postinstall(struct drm_device *dev)
 
                I915_WRITE(PORT_HOTPLUG_EN, hotplug_en);
        }
-
-       intel_opregion_enable_asle(dev);
-
-       return 0;
 }
 
 static irqreturn_t i915_irq_handler(int irq, void *arg)
@@ -2481,7 +2568,6 @@ static void i965_irq_preinstall(struct drm_device * dev)
 static int i965_irq_postinstall(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-       u32 hotplug_en;
        u32 enable_mask;
        u32 error_mask;
 
@@ -2502,6 +2588,7 @@ static int i965_irq_postinstall(struct drm_device *dev)
 
        dev_priv->pipestat[0] = 0;
        dev_priv->pipestat[1] = 0;
+       i915_enable_pipestat(dev_priv, 0, PIPE_GMBUS_EVENT_ENABLE);
 
        /*
         * Enable some error detection, note the instruction error mask
@@ -2522,14 +2609,27 @@ static int i965_irq_postinstall(struct drm_device *dev)
        I915_WRITE(IER, enable_mask);
        POSTING_READ(IER);
 
+       I915_WRITE(PORT_HOTPLUG_EN, 0);
+       POSTING_READ(PORT_HOTPLUG_EN);
+
+       intel_opregion_enable_asle(dev);
+
+       return 0;
+}
+
+static void i965_hpd_irq_setup(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       u32 hotplug_en;
+
        /* Note HDMI and DP share hotplug bits */
        hotplug_en = 0;
-       if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS)
-               hotplug_en |= HDMIB_HOTPLUG_INT_EN;
-       if (dev_priv->hotplug_supported_mask & HDMIC_HOTPLUG_INT_STATUS)
-               hotplug_en |= HDMIC_HOTPLUG_INT_EN;
-       if (dev_priv->hotplug_supported_mask & HDMID_HOTPLUG_INT_STATUS)
-               hotplug_en |= HDMID_HOTPLUG_INT_EN;
+       if (dev_priv->hotplug_supported_mask & PORTB_HOTPLUG_INT_STATUS)
+               hotplug_en |= PORTB_HOTPLUG_INT_EN;
+       if (dev_priv->hotplug_supported_mask & PORTC_HOTPLUG_INT_STATUS)
+               hotplug_en |= PORTC_HOTPLUG_INT_EN;
+       if (dev_priv->hotplug_supported_mask & PORTD_HOTPLUG_INT_STATUS)
+               hotplug_en |= PORTD_HOTPLUG_INT_EN;
        if (IS_G4X(dev)) {
                if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_G4X)
                        hotplug_en |= SDVOC_HOTPLUG_INT_EN;
@@ -2556,10 +2656,6 @@ static int i965_irq_postinstall(struct drm_device *dev)
        /* Ignore TV since it's buggy */
 
        I915_WRITE(PORT_HOTPLUG_EN, hotplug_en);
-
-       intel_opregion_enable_asle(dev);
-
-       return 0;
 }
 
 static irqreturn_t i965_irq_handler(int irq, void *arg)
@@ -2655,6 +2751,9 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
                if (blc_event || (iir & I915_ASLE_INTERRUPT))
                        intel_opregion_asle_intr(dev);
 
+               if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS)
+                       gmbus_irq_handler(dev);
+
                /* With MSI, interrupts are only generated when iir
                 * transitions from zero to nonzero.  If another bit got
                 * set while we were handling the existing iir bits, then
@@ -2706,10 +2805,16 @@ void intel_irq_init(struct drm_device *dev)
        struct drm_i915_private *dev_priv = dev->dev_private;
 
        INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
-       INIT_WORK(&dev_priv->error_work, i915_error_work_func);
+       INIT_WORK(&dev_priv->gpu_error.work, i915_error_work_func);
        INIT_WORK(&dev_priv->rps.work, gen6_pm_rps_work);
        INIT_WORK(&dev_priv->l3_parity.error_work, ivybridge_parity_work);
 
+       setup_timer(&dev_priv->gpu_error.hangcheck_timer,
+                   i915_hangcheck_elapsed,
+                   (unsigned long) dev);
+
+       pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
+
        dev->driver->get_vblank_counter = i915_get_vblank_counter;
        dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
        if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) {
@@ -2730,7 +2835,8 @@ void intel_irq_init(struct drm_device *dev)
                dev->driver->irq_uninstall = valleyview_irq_uninstall;
                dev->driver->enable_vblank = valleyview_enable_vblank;
                dev->driver->disable_vblank = valleyview_disable_vblank;
-       } else if (IS_IVYBRIDGE(dev)) {
+               dev_priv->display.hpd_irq_setup = valleyview_hpd_irq_setup;
+       } else if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) {
                /* Share pre & uninstall handlers with ILK/SNB */
                dev->driver->irq_handler = ivybridge_irq_handler;
                dev->driver->irq_preinstall = ironlake_irq_preinstall;
@@ -2738,14 +2844,6 @@ void intel_irq_init(struct drm_device *dev)
                dev->driver->irq_uninstall = ironlake_irq_uninstall;
                dev->driver->enable_vblank = ivybridge_enable_vblank;
                dev->driver->disable_vblank = ivybridge_disable_vblank;
-       } else if (IS_HASWELL(dev)) {
-               /* Share interrupts handling with IVB */
-               dev->driver->irq_handler = ivybridge_irq_handler;
-               dev->driver->irq_preinstall = ironlake_irq_preinstall;
-               dev->driver->irq_postinstall = ivybridge_irq_postinstall;
-               dev->driver->irq_uninstall = ironlake_irq_uninstall;
-               dev->driver->enable_vblank = ivybridge_enable_vblank;
-               dev->driver->disable_vblank = ivybridge_disable_vblank;
        } else if (HAS_PCH_SPLIT(dev)) {
                dev->driver->irq_handler = ironlake_irq_handler;
                dev->driver->irq_preinstall = ironlake_irq_preinstall;
@@ -2764,13 +2862,23 @@ void intel_irq_init(struct drm_device *dev)
                        dev->driver->irq_postinstall = i915_irq_postinstall;
                        dev->driver->irq_uninstall = i915_irq_uninstall;
                        dev->driver->irq_handler = i915_irq_handler;
+                       dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup;
                } else {
                        dev->driver->irq_preinstall = i965_irq_preinstall;
                        dev->driver->irq_postinstall = i965_irq_postinstall;
                        dev->driver->irq_uninstall = i965_irq_uninstall;
                        dev->driver->irq_handler = i965_irq_handler;
+                       dev_priv->display.hpd_irq_setup = i965_hpd_irq_setup;
                }
                dev->driver->enable_vblank = i915_enable_vblank;
                dev->driver->disable_vblank = i915_disable_vblank;
        }
 }
+
+void intel_hpd_init(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (dev_priv->display.hpd_irq_setup)
+               dev_priv->display.hpd_irq_setup(dev);
+}
index 59afb7eb6db635cf2f9bbe5477ffc8bd75fddd30..527b664d343490238aded9ce08d42e2f4e4549a6 100644 (file)
 #define   VGA_MSR_MEM_EN (1<<1)
 #define   VGA_MSR_CGA_MODE (1<<0)
 
-#define VGA_SR_INDEX 0x3c4
-#define VGA_SR_DATA 0x3c5
+/*
+ * SR01 is the only VGA register touched on non-UMS setups.
+ * VLV doesn't do UMS, so the sequencer index/data registers
+ * are the only VGA registers which need to include
+ * display_mmio_offset.
+ */
+#define VGA_SR_INDEX (dev_priv->info->display_mmio_offset + 0x3c4)
+#define SR01                   1
+#define VGA_SR_DATA (dev_priv->info->display_mmio_offset + 0x3c5)
 
 #define VGA_AR_INDEX 0x3c0
 #define   VGA_AR_VID_EN (1<<5)
 #define   DISPLAY_PLANE_A           (0<<20)
 #define   DISPLAY_PLANE_B           (1<<20)
 #define GFX_OP_PIPE_CONTROL(len)       ((0x3<<29)|(0x3<<27)|(0x2<<24)|(len-2))
+#define   PIPE_CONTROL_GLOBAL_GTT_IVB                  (1<<24) /* gen7+ */
 #define   PIPE_CONTROL_CS_STALL                                (1<<20)
 #define   PIPE_CONTROL_TLB_INVALIDATE                  (1<<18)
 #define   PIPE_CONTROL_QW_WRITE                                (1<<14)
  *  0x801c/3c: core clock bits
  *  0x8048/68: low pass filter coefficients
  *  0x8100: fast clock controls
+ *
+ * DPIO is VLV only.
  */
-#define DPIO_PKT                       0x2100
+#define DPIO_PKT                       (VLV_DISPLAY_BASE + 0x2100)
 #define  DPIO_RID                      (0<<24)
 #define  DPIO_OP_WRITE                 (1<<16)
 #define  DPIO_OP_READ                  (0<<16)
 #define  DPIO_PORTID                   (0x12<<8)
 #define  DPIO_BYTE                     (0xf<<4)
 #define  DPIO_BUSY                     (1<<0) /* status only */
-#define DPIO_DATA                      0x2104
-#define DPIO_REG                       0x2108
-#define DPIO_CTL                       0x2110
+#define DPIO_DATA                      (VLV_DISPLAY_BASE + 0x2104)
+#define DPIO_REG                       (VLV_DISPLAY_BASE + 0x2108)
+#define DPIO_CTL                       (VLV_DISPLAY_BASE + 0x2110)
 #define  DPIO_MODSEL1                  (1<<3) /* if ref clk b == 27 */
 #define  DPIO_MODSEL0                  (1<<2) /* if ref clk a == 27 */
 #define  DPIO_SFR_BYPASS               (1<<1)
 #define IIR            0x020a4
 #define IMR            0x020a8
 #define ISR            0x020ac
-#define VLV_GUNIT_CLOCK_GATE   0x182060
+#define VLV_GUNIT_CLOCK_GATE   (VLV_DISPLAY_BASE + 0x2060)
 #define   GCFG_DIS             (1<<8)
-#define VLV_IIR_RW     0x182084
-#define VLV_IER                0x1820a0
-#define VLV_IIR                0x1820a4
-#define VLV_IMR                0x1820a8
-#define VLV_ISR                0x1820ac
+#define VLV_IIR_RW     (VLV_DISPLAY_BASE + 0x2084)
+#define VLV_IER                (VLV_DISPLAY_BASE + 0x20a0)
+#define VLV_IIR                (VLV_DISPLAY_BASE + 0x20a4)
+#define VLV_IMR                (VLV_DISPLAY_BASE + 0x20a8)
+#define VLV_ISR                (VLV_DISPLAY_BASE + 0x20ac)
 #define   I915_PIPE_CONTROL_NOTIFY_INTERRUPT           (1<<18)
 #define   I915_DISPLAY_PORT_INTERRUPT                  (1<<17)
 #define   I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT   (1<<15)
 #define   GEN7_FF_TS_SCHED_HS0         (0x3<<16)
 #define   GEN7_FF_TS_SCHED_LOAD_BALANCE        (0x1<<16)
 #define   GEN7_FF_TS_SCHED_HW          (0x0<<16) /* Default */
+#define   GEN7_FF_VS_REF_CNT_FFME      (1 << 15)
 #define   GEN7_FF_VS_SCHED_HS1         (0x5<<12)
 #define   GEN7_FF_VS_SCHED_HS0         (0x3<<12)
 #define   GEN7_FF_VS_SCHED_LOAD_BALANCE        (0x1<<12) /* Default */
 #define   VGA1_PD_P1_DIV_2     (1 << 13)
 #define   VGA1_PD_P1_SHIFT     8
 #define   VGA1_PD_P1_MASK      (0x1f << 8)
-#define _DPLL_A        0x06014
-#define _DPLL_B        0x06018
+#define _DPLL_A        (dev_priv->info->display_mmio_offset + 0x6014)
+#define _DPLL_B        (dev_priv->info->display_mmio_offset + 0x6018)
 #define DPLL(pipe) _PIPE(pipe, _DPLL_A, _DPLL_B)
 #define   DPLL_VCO_ENABLE              (1 << 31)
 #define   DPLL_DVO_HIGH_SPEED          (1 << 30)
 #define   DPLL_LOCK_VLV                        (1<<15)
 #define   DPLL_INTEGRATED_CLOCK_VLV    (1<<13)
 
-#define SRX_INDEX              0x3c4
-#define SRX_DATA               0x3c5
-#define SR01                   1
-#define SR01_SCREEN_OFF                (1<<5)
-
-#define PPCR                   0x61204
-#define PPCR_ON                        (1<<0)
-
-#define DVOB                   0x61140
-#define DVOB_ON                        (1<<31)
-#define DVOC                   0x61160
-#define DVOC_ON                        (1<<31)
-#define LVDS                   0x61180
-#define LVDS_ON                        (1<<31)
-
-/* Scratch pad debug 0 reg:
- */
 #define   DPLL_FPA01_P1_POST_DIV_MASK_I830     0x001f0000
 /*
  * The i830 generation, in LVDS mode, defines P1 as the bit number set within
 #define   SDVO_MULTIPLIER_MASK                 0x000000ff
 #define   SDVO_MULTIPLIER_SHIFT_HIRES          4
 #define   SDVO_MULTIPLIER_SHIFT_VGA            0
-#define _DPLL_A_MD 0x0601c /* 965+ only */
+#define _DPLL_A_MD (dev_priv->info->display_mmio_offset + 0x601c) /* 965+ only */
 /*
  * UDI pixel divider, controlling how many pixels are stuffed into a packet.
  *
  */
 #define   DPLL_MD_VGA_UDI_MULTIPLIER_MASK      0x0000003f
 #define   DPLL_MD_VGA_UDI_MULTIPLIER_SHIFT     0
-#define _DPLL_B_MD 0x06020 /* 965+ only */
+#define _DPLL_B_MD (dev_priv->info->display_mmio_offset + 0x6020) /* 965+ only */
 #define DPLL_MD(pipe) _PIPE(pipe, _DPLL_A_MD, _DPLL_B_MD)
 
 #define _FPA0  0x06040
 #define RAMCLK_GATE_D          0x6210          /* CRL only */
 #define DEUC                   0x6214          /* CRL only */
 
-#define FW_BLC_SELF_VLV                0x6500
+#define FW_BLC_SELF_VLV                (VLV_DISPLAY_BASE + 0x6500)
 #define  FW_CSPWRDWNEN         (1<<15)
 
 /*
  * Palette regs
  */
 
-#define _PALETTE_A             0x0a000
-#define _PALETTE_B             0x0a800
+#define _PALETTE_A             (dev_priv->info->display_mmio_offset + 0xa000)
+#define _PALETTE_B             (dev_priv->info->display_mmio_offset + 0xa800)
 #define PALETTE(pipe) _PIPE(pipe, _PALETTE_A, _PALETTE_B)
 
 /* MCH MMIO space */
 #define   MAD_DIMM_A_SIZE_SHIFT                0
 #define   MAD_DIMM_A_SIZE_MASK         (0xff << MAD_DIMM_A_SIZE_SHIFT)
 
+/** snb MCH registers for priority tuning */
+#define MCH_SSKPD                      (MCHBAR_MIRROR_BASE_SNB + 0x5d10)
+#define   MCH_SSKPD_WM0_MASK           0x3f
+#define   MCH_SSKPD_WM0_VAL            0xc
 
 /* Clocking configuration register */
 #define CLKCFG                 0x10c00
  */
 
 /* Pipe A timing regs */
-#define _HTOTAL_A      0x60000
-#define _HBLANK_A      0x60004
-#define _HSYNC_A               0x60008
-#define _VTOTAL_A      0x6000c
-#define _VBLANK_A      0x60010
-#define _VSYNC_A               0x60014
-#define _PIPEASRC      0x6001c
-#define _BCLRPAT_A     0x60020
-#define _VSYNCSHIFT_A  0x60028
+#define _HTOTAL_A      (dev_priv->info->display_mmio_offset + 0x60000)
+#define _HBLANK_A      (dev_priv->info->display_mmio_offset + 0x60004)
+#define _HSYNC_A       (dev_priv->info->display_mmio_offset + 0x60008)
+#define _VTOTAL_A      (dev_priv->info->display_mmio_offset + 0x6000c)
+#define _VBLANK_A      (dev_priv->info->display_mmio_offset + 0x60010)
+#define _VSYNC_A       (dev_priv->info->display_mmio_offset + 0x60014)
+#define _PIPEASRC      (dev_priv->info->display_mmio_offset + 0x6001c)
+#define _BCLRPAT_A     (dev_priv->info->display_mmio_offset + 0x60020)
+#define _VSYNCSHIFT_A  (dev_priv->info->display_mmio_offset + 0x60028)
 
 /* Pipe B timing regs */
-#define _HTOTAL_B      0x61000
-#define _HBLANK_B      0x61004
-#define _HSYNC_B               0x61008
-#define _VTOTAL_B      0x6100c
-#define _VBLANK_B      0x61010
-#define _VSYNC_B               0x61014
-#define _PIPEBSRC      0x6101c
-#define _BCLRPAT_B     0x61020
-#define _VSYNCSHIFT_B  0x61028
+#define _HTOTAL_B      (dev_priv->info->display_mmio_offset + 0x61000)
+#define _HBLANK_B      (dev_priv->info->display_mmio_offset + 0x61004)
+#define _HSYNC_B       (dev_priv->info->display_mmio_offset + 0x61008)
+#define _VTOTAL_B      (dev_priv->info->display_mmio_offset + 0x6100c)
+#define _VBLANK_B      (dev_priv->info->display_mmio_offset + 0x61010)
+#define _VSYNC_B       (dev_priv->info->display_mmio_offset + 0x61014)
+#define _PIPEBSRC      (dev_priv->info->display_mmio_offset + 0x6101c)
+#define _BCLRPAT_B     (dev_priv->info->display_mmio_offset + 0x61020)
+#define _VSYNCSHIFT_B  (dev_priv->info->display_mmio_offset + 0x61028)
 
 
 #define HTOTAL(trans) _TRANSCODER(trans, _HTOTAL_A, _HTOTAL_B)
 
 
 /* Hotplug control (945+ only) */
-#define PORT_HOTPLUG_EN                0x61110
-#define   HDMIB_HOTPLUG_INT_EN                 (1 << 29)
-#define   DPB_HOTPLUG_INT_EN                   (1 << 29)
-#define   HDMIC_HOTPLUG_INT_EN                 (1 << 28)
-#define   DPC_HOTPLUG_INT_EN                   (1 << 28)
-#define   HDMID_HOTPLUG_INT_EN                 (1 << 27)
-#define   DPD_HOTPLUG_INT_EN                   (1 << 27)
+#define PORT_HOTPLUG_EN                (dev_priv->info->display_mmio_offset + 0x61110)
+#define   PORTB_HOTPLUG_INT_EN                 (1 << 29)
+#define   PORTC_HOTPLUG_INT_EN                 (1 << 28)
+#define   PORTD_HOTPLUG_INT_EN                 (1 << 27)
 #define   SDVOB_HOTPLUG_INT_EN                 (1 << 26)
 #define   SDVOC_HOTPLUG_INT_EN                 (1 << 25)
 #define   TV_HOTPLUG_INT_EN                    (1 << 18)
 #define CRT_HOTPLUG_DETECT_VOLTAGE_325MV       (0 << 2)
 #define CRT_HOTPLUG_DETECT_VOLTAGE_475MV       (1 << 2)
 
-#define PORT_HOTPLUG_STAT      0x61114
+#define PORT_HOTPLUG_STAT      (dev_priv->info->display_mmio_offset + 0x61114)
 /* HDMI/DP bits are gen4+ */
-#define   DPB_HOTPLUG_LIVE_STATUS               (1 << 29)
-#define   DPC_HOTPLUG_LIVE_STATUS               (1 << 28)
-#define   DPD_HOTPLUG_LIVE_STATUS               (1 << 27)
-#define   DPD_HOTPLUG_INT_STATUS               (3 << 21)
-#define   DPC_HOTPLUG_INT_STATUS               (3 << 19)
-#define   DPB_HOTPLUG_INT_STATUS               (3 << 17)
-/* HDMI bits are shared with the DP bits */
-#define   HDMIB_HOTPLUG_LIVE_STATUS             (1 << 29)
-#define   HDMIC_HOTPLUG_LIVE_STATUS             (1 << 28)
-#define   HDMID_HOTPLUG_LIVE_STATUS             (1 << 27)
-#define   HDMID_HOTPLUG_INT_STATUS             (3 << 21)
-#define   HDMIC_HOTPLUG_INT_STATUS             (3 << 19)
-#define   HDMIB_HOTPLUG_INT_STATUS             (3 << 17)
+#define   PORTB_HOTPLUG_LIVE_STATUS               (1 << 29)
+#define   PORTC_HOTPLUG_LIVE_STATUS               (1 << 28)
+#define   PORTD_HOTPLUG_LIVE_STATUS               (1 << 27)
+#define   PORTD_HOTPLUG_INT_STATUS             (3 << 21)
+#define   PORTC_HOTPLUG_INT_STATUS             (3 << 19)
+#define   PORTB_HOTPLUG_INT_STATUS             (3 << 17)
 /* CRT/TV common between gen3+ */
 #define   CRT_HOTPLUG_INT_STATUS               (1 << 11)
 #define   TV_HOTPLUG_INT_STATUS                        (1 << 10)
 #define PP_DIVISOR     0x61210
 
 /* Panel fitting */
-#define PFIT_CONTROL   0x61230
+#define PFIT_CONTROL   (dev_priv->info->display_mmio_offset + 0x61230)
 #define   PFIT_ENABLE          (1 << 31)
 #define   PFIT_PIPE_MASK       (3 << 29)
 #define   PFIT_PIPE_SHIFT      29
 #define   PFIT_SCALING_PROGRAMMED (1 << 26)
 #define   PFIT_SCALING_PILLAR  (2 << 26)
 #define   PFIT_SCALING_LETTER  (3 << 26)
-#define PFIT_PGM_RATIOS        0x61234
-#define   PFIT_VERT_SCALE_MASK                 0xfff00000
-#define   PFIT_HORIZ_SCALE_MASK                        0x0000fff0
+#define PFIT_PGM_RATIOS        (dev_priv->info->display_mmio_offset + 0x61234)
 /* Pre-965 */
 #define                PFIT_VERT_SCALE_SHIFT           20
 #define                PFIT_VERT_SCALE_MASK            0xfff00000
 #define                PFIT_HORIZ_SCALE_SHIFT_965      0
 #define                PFIT_HORIZ_SCALE_MASK_965       0x00001fff
 
-#define PFIT_AUTO_RATIOS 0x61238
+#define PFIT_AUTO_RATIOS (dev_priv->info->display_mmio_offset + 0x61238)
 
 /* Backlight control */
 #define BLC_PWM_CTL2           0x61250 /* 965+ only */
 /* Display & cursor control */
 
 /* Pipe A */
-#define _PIPEADSL              0x70000
+#define _PIPEADSL              (dev_priv->info->display_mmio_offset + 0x70000)
 #define   DSL_LINEMASK_GEN2    0x00000fff
 #define   DSL_LINEMASK_GEN3    0x00001fff
-#define _PIPEACONF             0x70008
+#define _PIPEACONF             (dev_priv->info->display_mmio_offset + 0x70008)
 #define   PIPECONF_ENABLE      (1<<31)
 #define   PIPECONF_DISABLE     0
 #define   PIPECONF_DOUBLE_WIDE (1<<30)
 #define   PIPECONF_INTERLACED_DBL_ILK          (4 << 21) /* ilk/snb only */
 #define   PIPECONF_PFIT_PF_INTERLACED_DBL_ILK  (5 << 21) /* ilk/snb only */
 #define   PIPECONF_CXSR_DOWNCLOCK      (1<<16)
-#define   PIPECONF_BPP_MASK    (0x000000e0)
-#define   PIPECONF_BPP_8       (0<<5)
-#define   PIPECONF_BPP_10      (1<<5)
-#define   PIPECONF_BPP_6       (2<<5)
-#define   PIPECONF_BPP_12      (3<<5)
+#define   PIPECONF_COLOR_RANGE_SELECT  (1 << 13)
+#define   PIPECONF_BPC_MASK    (0x7 << 5)
+#define   PIPECONF_8BPC                (0<<5)
+#define   PIPECONF_10BPC       (1<<5)
+#define   PIPECONF_6BPC                (2<<5)
+#define   PIPECONF_12BPC       (3<<5)
 #define   PIPECONF_DITHER_EN   (1<<4)
 #define   PIPECONF_DITHER_TYPE_MASK (0x0000000c)
 #define   PIPECONF_DITHER_TYPE_SP (0<<2)
 #define   PIPECONF_DITHER_TYPE_ST1 (1<<2)
 #define   PIPECONF_DITHER_TYPE_ST2 (2<<2)
 #define   PIPECONF_DITHER_TYPE_TEMP (3<<2)
-#define _PIPEASTAT             0x70024
+#define _PIPEASTAT             (dev_priv->info->display_mmio_offset + 0x70024)
 #define   PIPE_FIFO_UNDERRUN_STATUS            (1UL<<31)
 #define   SPRITE1_FLIPDONE_INT_EN_VLV          (1UL<<30)
 #define   PIPE_CRC_ERROR_ENABLE                        (1UL<<29)
 #define   PIPE_VSYNC_INTERRUPT_ENABLE          (1UL<<25)
 #define   PIPE_DISPLAY_LINE_COMPARE_ENABLE     (1UL<<24)
 #define   PIPE_DPST_EVENT_ENABLE               (1UL<<23)
-#define   SPRITE0_FLIP_DONE_INT_EN_VLV         (1UL<<26)
+#define   SPRITE0_FLIP_DONE_INT_EN_VLV         (1UL<<22)
 #define   PIPE_LEGACY_BLC_EVENT_ENABLE         (1UL<<22)
 #define   PIPE_ODD_FIELD_INTERRUPT_ENABLE      (1UL<<21)
 #define   PIPE_EVEN_FIELD_INTERRUPT_ENABLE     (1UL<<20)
 #define   PIPEA_HBLANK_INT_EN_VLV              (1UL<<16)
 #define   PIPE_OVERLAY_UPDATED_ENABLE          (1UL<<16)
 #define   SPRITE1_FLIPDONE_INT_STATUS_VLV      (1UL<<15)
-#define   SPRITE0_FLIPDONE_INT_STATUS_VLV      (1UL<<15)
+#define   SPRITE0_FLIPDONE_INT_STATUS_VLV      (1UL<<14)
 #define   PIPE_CRC_ERROR_INTERRUPT_STATUS      (1UL<<13)
 #define   PIPE_CRC_DONE_INTERRUPT_STATUS       (1UL<<12)
 #define   PIPE_GMBUS_INTERRUPT_STATUS          (1UL<<11)
 #define   PIPE_START_VBLANK_INTERRUPT_STATUS   (1UL<<2) /* 965 or later */
 #define   PIPE_VBLANK_INTERRUPT_STATUS         (1UL<<1)
 #define   PIPE_OVERLAY_UPDATED_STATUS          (1UL<<0)
-#define   PIPE_BPC_MASK                                (7 << 5) /* Ironlake */
-#define   PIPE_8BPC                            (0 << 5)
-#define   PIPE_10BPC                           (1 << 5)
-#define   PIPE_6BPC                            (2 << 5)
-#define   PIPE_12BPC                           (3 << 5)
 
 #define PIPESRC(pipe) _PIPE(pipe, _PIPEASRC, _PIPEBSRC)
 #define PIPECONF(tran) _TRANSCODER(tran, _PIPEACONF, _PIPEBCONF)
 #define PIPEFRAMEPIXEL(pipe)  _PIPE(pipe, _PIPEAFRAMEPIXEL, _PIPEBFRAMEPIXEL)
 #define PIPESTAT(pipe) _PIPE(pipe, _PIPEASTAT, _PIPEBSTAT)
 
-#define VLV_DPFLIPSTAT                         0x70028
+#define VLV_DPFLIPSTAT                         (VLV_DISPLAY_BASE + 0x70028)
 #define   PIPEB_LINE_COMPARE_INT_EN            (1<<29)
 #define   PIPEB_HLINE_INT_EN                   (1<<28)
 #define   PIPEB_VBLANK_INT_EN                  (1<<27)
 #define   SPRITEA_FLIPDONE_INT_EN              (1<<17)
 #define   PLANEA_FLIPDONE_INT_EN               (1<<16)
 
-#define DPINVGTT                               0x7002c /* VLV only */
+#define DPINVGTT                               (VLV_DISPLAY_BASE + 0x7002c) /* VLV only */
 #define   CURSORB_INVALID_GTT_INT_EN           (1<<23)
 #define   CURSORA_INVALID_GTT_INT_EN           (1<<22)
 #define   SPRITED_INVALID_GTT_INT_EN           (1<<21)
 #define   DSPARB_BEND_SHIFT    9 /* on 855 */
 #define   DSPARB_AEND_SHIFT    0
 
-#define DSPFW1                 0x70034
+#define DSPFW1                 (dev_priv->info->display_mmio_offset + 0x70034)
 #define   DSPFW_SR_SHIFT       23
 #define   DSPFW_SR_MASK                (0x1ff<<23)
 #define   DSPFW_CURSORB_SHIFT  16
 #define   DSPFW_PLANEB_SHIFT   8
 #define   DSPFW_PLANEB_MASK    (0x7f<<8)
 #define   DSPFW_PLANEA_MASK    (0x7f)
-#define DSPFW2                 0x70038
+#define DSPFW2                 (dev_priv->info->display_mmio_offset + 0x70038)
 #define   DSPFW_CURSORA_MASK   0x00003f00
 #define   DSPFW_CURSORA_SHIFT  8
 #define   DSPFW_PLANEC_MASK    (0x7f)
-#define DSPFW3                 0x7003c
+#define DSPFW3                 (dev_priv->info->display_mmio_offset + 0x7003c)
 #define   DSPFW_HPLL_SR_EN     (1<<31)
 #define   DSPFW_CURSOR_SR_SHIFT        24
 #define   PINEVIEW_SELF_REFRESH_EN     (1<<30)
 /* drain latency register values*/
 #define DRAIN_LATENCY_PRECISION_32     32
 #define DRAIN_LATENCY_PRECISION_16     16
-#define VLV_DDL1                       0x70050
+#define VLV_DDL1                       (VLV_DISPLAY_BASE + 0x70050)
 #define DDL_CURSORA_PRECISION_32       (1<<31)
 #define DDL_CURSORA_PRECISION_16       (0<<31)
 #define DDL_CURSORA_SHIFT              24
 #define DDL_PLANEA_PRECISION_32                (1<<7)
 #define DDL_PLANEA_PRECISION_16                (0<<7)
-#define VLV_DDL2                       0x70054
+#define VLV_DDL2                       (VLV_DISPLAY_BASE + 0x70054)
 #define DDL_CURSORB_PRECISION_32       (1<<31)
 #define DDL_CURSORB_PRECISION_16       (0<<31)
 #define DDL_CURSORB_SHIFT              24
  *  } while (high1 != high2);
  *  frame = (high1 << 8) | low1;
  */
-#define _PIPEAFRAMEHIGH          0x70040
+#define _PIPEAFRAMEHIGH          (dev_priv->info->display_mmio_offset + 0x70040)
 #define   PIPE_FRAME_HIGH_MASK    0x0000ffff
 #define   PIPE_FRAME_HIGH_SHIFT   0
-#define _PIPEAFRAMEPIXEL         0x70044
+#define _PIPEAFRAMEPIXEL         (dev_priv->info->display_mmio_offset + 0x70044)
 #define   PIPE_FRAME_LOW_MASK     0xff000000
 #define   PIPE_FRAME_LOW_SHIFT    24
 #define   PIPE_PIXEL_MASK         0x00ffffff
 #define PIPE_FRMCOUNT_GM45(pipe) _PIPE(pipe, _PIPEA_FRMCOUNT_GM45, _PIPEB_FRMCOUNT_GM45)
 
 /* Cursor A & B regs */
-#define _CURACNTR              0x70080
+#define _CURACNTR              (dev_priv->info->display_mmio_offset + 0x70080)
 /* Old style CUR*CNTR flags (desktop 8xx) */
 #define   CURSOR_ENABLE                0x80000000
 #define   CURSOR_GAMMA_ENABLE  0x40000000
 #define   CURSOR_STRIDE_MASK   0x30000000
+#define   CURSOR_PIPE_CSC_ENABLE (1<<24)
 #define   CURSOR_FORMAT_SHIFT  24
 #define   CURSOR_FORMAT_MASK   (0x07 << CURSOR_FORMAT_SHIFT)
 #define   CURSOR_FORMAT_2C     (0x00 << CURSOR_FORMAT_SHIFT)
 #define   MCURSOR_PIPE_A       0x00
 #define   MCURSOR_PIPE_B       (1 << 28)
 #define   MCURSOR_GAMMA_ENABLE  (1 << 26)
-#define _CURABASE              0x70084
-#define _CURAPOS                       0x70088
+#define _CURABASE              (dev_priv->info->display_mmio_offset + 0x70084)
+#define _CURAPOS               (dev_priv->info->display_mmio_offset + 0x70088)
 #define   CURSOR_POS_MASK       0x007FF
 #define   CURSOR_POS_SIGN       0x8000
 #define   CURSOR_X_SHIFT        0
 #define   CURSOR_Y_SHIFT        16
 #define CURSIZE                        0x700a0
-#define _CURBCNTR              0x700c0
-#define _CURBBASE              0x700c4
-#define _CURBPOS                       0x700c8
+#define _CURBCNTR              (dev_priv->info->display_mmio_offset + 0x700c0)
+#define _CURBBASE              (dev_priv->info->display_mmio_offset + 0x700c4)
+#define _CURBPOS               (dev_priv->info->display_mmio_offset + 0x700c8)
 
 #define _CURBCNTR_IVB          0x71080
 #define _CURBBASE_IVB          0x71084
 #define CURPOS_IVB(pipe) _PIPE(pipe, _CURAPOS, _CURBPOS_IVB)
 
 /* Display A control */
-#define _DSPACNTR                0x70180
+#define _DSPACNTR                (dev_priv->info->display_mmio_offset + 0x70180)
 #define   DISPLAY_PLANE_ENABLE                 (1<<31)
 #define   DISPLAY_PLANE_DISABLE                        0
 #define   DISPPLANE_GAMMA_ENABLE               (1<<30)
 #define   DISPPLANE_RGBA888                    (0xf<<26)
 #define   DISPPLANE_STEREO_ENABLE              (1<<25)
 #define   DISPPLANE_STEREO_DISABLE             0
+#define   DISPPLANE_PIPE_CSC_ENABLE            (1<<24)
 #define   DISPPLANE_SEL_PIPE_SHIFT             24
 #define   DISPPLANE_SEL_PIPE_MASK              (3<<DISPPLANE_SEL_PIPE_SHIFT)
 #define   DISPPLANE_SEL_PIPE_A                 0
 #define   DISPPLANE_STEREO_POLARITY_SECOND     (1<<18)
 #define   DISPPLANE_TRICKLE_FEED_DISABLE       (1<<14) /* Ironlake */
 #define   DISPPLANE_TILED                      (1<<10)
-#define _DSPAADDR              0x70184
-#define _DSPASTRIDE            0x70188
-#define _DSPAPOS                       0x7018C /* reserved */
-#define _DSPASIZE              0x70190
-#define _DSPASURF              0x7019C /* 965+ only */
-#define _DSPATILEOFF           0x701A4 /* 965+ only */
-#define _DSPAOFFSET            0x701A4 /* HSW */
-#define _DSPASURFLIVE          0x701AC
+#define _DSPAADDR              (dev_priv->info->display_mmio_offset + 0x70184)
+#define _DSPASTRIDE            (dev_priv->info->display_mmio_offset + 0x70188)
+#define _DSPAPOS               (dev_priv->info->display_mmio_offset + 0x7018C) /* reserved */
+#define _DSPASIZE              (dev_priv->info->display_mmio_offset + 0x70190)
+#define _DSPASURF              (dev_priv->info->display_mmio_offset + 0x7019C) /* 965+ only */
+#define _DSPATILEOFF           (dev_priv->info->display_mmio_offset + 0x701A4) /* 965+ only */
+#define _DSPAOFFSET            (dev_priv->info->display_mmio_offset + 0x701A4) /* HSW */
+#define _DSPASURFLIVE          (dev_priv->info->display_mmio_offset + 0x701AC)
 
 #define DSPCNTR(plane) _PIPE(plane, _DSPACNTR, _DSPBCNTR)
 #define DSPADDR(plane) _PIPE(plane, _DSPAADDR, _DSPBADDR)
                (I915_WRITE((reg), (gfx_addr) | I915_LO_DISPBASE(I915_READ(reg))))
 
 /* VBIOS flags */
-#define SWF00                  0x71410
-#define SWF01                  0x71414
-#define SWF02                  0x71418
-#define SWF03                  0x7141c
-#define SWF04                  0x71420
-#define SWF05                  0x71424
-#define SWF06                  0x71428
-#define SWF10                  0x70410
-#define SWF11                  0x70414
-#define SWF14                  0x71420
-#define SWF30                  0x72414
-#define SWF31                  0x72418
-#define SWF32                  0x7241c
+#define SWF00                  (dev_priv->info->display_mmio_offset + 0x71410)
+#define SWF01                  (dev_priv->info->display_mmio_offset + 0x71414)
+#define SWF02                  (dev_priv->info->display_mmio_offset + 0x71418)
+#define SWF03                  (dev_priv->info->display_mmio_offset + 0x7141c)
+#define SWF04                  (dev_priv->info->display_mmio_offset + 0x71420)
+#define SWF05                  (dev_priv->info->display_mmio_offset + 0x71424)
+#define SWF06                  (dev_priv->info->display_mmio_offset + 0x71428)
+#define SWF10                  (dev_priv->info->display_mmio_offset + 0x70410)
+#define SWF11                  (dev_priv->info->display_mmio_offset + 0x70414)
+#define SWF14                  (dev_priv->info->display_mmio_offset + 0x71420)
+#define SWF30                  (dev_priv->info->display_mmio_offset + 0x72414)
+#define SWF31                  (dev_priv->info->display_mmio_offset + 0x72418)
+#define SWF32                  (dev_priv->info->display_mmio_offset + 0x7241c)
 
 /* Pipe B */
-#define _PIPEBDSL              0x71000
-#define _PIPEBCONF             0x71008
-#define _PIPEBSTAT             0x71024
-#define _PIPEBFRAMEHIGH                0x71040
-#define _PIPEBFRAMEPIXEL               0x71044
+#define _PIPEBDSL              (dev_priv->info->display_mmio_offset + 0x71000)
+#define _PIPEBCONF             (dev_priv->info->display_mmio_offset + 0x71008)
+#define _PIPEBSTAT             (dev_priv->info->display_mmio_offset + 0x71024)
+#define _PIPEBFRAMEHIGH                (dev_priv->info->display_mmio_offset + 0x71040)
+#define _PIPEBFRAMEPIXEL       (dev_priv->info->display_mmio_offset + 0x71044)
 #define _PIPEB_FRMCOUNT_GM45   0x71040
 #define _PIPEB_FLIPCOUNT_GM45  0x71044
 
 
 /* Display B control */
-#define _DSPBCNTR              0x71180
+#define _DSPBCNTR              (dev_priv->info->display_mmio_offset + 0x71180)
 #define   DISPPLANE_ALPHA_TRANS_ENABLE         (1<<15)
 #define   DISPPLANE_ALPHA_TRANS_DISABLE                0
 #define   DISPPLANE_SPRITE_ABOVE_DISPLAY       0
 #define   DISPPLANE_SPRITE_ABOVE_OVERLAY       (1)
-#define _DSPBADDR              0x71184
-#define _DSPBSTRIDE            0x71188
-#define _DSPBPOS                       0x7118C
-#define _DSPBSIZE              0x71190
-#define _DSPBSURF              0x7119C
-#define _DSPBTILEOFF           0x711A4
-#define _DSPBOFFSET            0x711A4
-#define _DSPBSURFLIVE          0x711AC
+#define _DSPBADDR              (dev_priv->info->display_mmio_offset + 0x71184)
+#define _DSPBSTRIDE            (dev_priv->info->display_mmio_offset + 0x71188)
+#define _DSPBPOS               (dev_priv->info->display_mmio_offset + 0x7118C)
+#define _DSPBSIZE              (dev_priv->info->display_mmio_offset + 0x71190)
+#define _DSPBSURF              (dev_priv->info->display_mmio_offset + 0x7119C)
+#define _DSPBTILEOFF           (dev_priv->info->display_mmio_offset + 0x711A4)
+#define _DSPBOFFSET            (dev_priv->info->display_mmio_offset + 0x711A4)
+#define _DSPBSURFLIVE          (dev_priv->info->display_mmio_offset + 0x711AC)
 
 /* Sprite A control */
 #define _DVSACNTR              0x72180
 #define   DVS_FORMAT_RGBX101010        (1<<25)
 #define   DVS_FORMAT_RGBX888   (2<<25)
 #define   DVS_FORMAT_RGBX161616        (3<<25)
+#define   DVS_PIPE_CSC_ENABLE   (1<<24)
 #define   DVS_SOURCE_KEY       (1<<22)
 #define   DVS_RGB_ORDER_XBGR   (1<<20)
 #define   DVS_YUV_BYTE_ORDER_MASK (3<<16)
 #define   SPRITE_FORMAT_RGBX161616     (3<<25)
 #define   SPRITE_FORMAT_YUV444         (4<<25)
 #define   SPRITE_FORMAT_XR_BGR101010   (5<<25) /* Extended range */
-#define   SPRITE_CSC_ENABLE            (1<<24)
+#define   SPRITE_PIPE_CSC_ENABLE       (1<<24)
 #define   SPRITE_SOURCE_KEY            (1<<22)
 #define   SPRITE_RGB_ORDER_RGBX                (1<<20) /* only for 888 and 161616 */
 #define   SPRITE_YUV_TO_RGB_CSC_DISABLE        (1<<19)
 # define VGA_2X_MODE                           (1 << 30)
 # define VGA_PIPE_B_SELECT                     (1 << 29)
 
+#define VLV_VGACNTRL           (VLV_DISPLAY_BASE + 0x71400)
+
 /* Ironlake */
 
 #define CPU_VGACNTRL   0x41000
 #define  FDI_PLL_FREQ_DISABLE_COUNT_LIMIT_MASK  0xff
 
 
-#define _PIPEA_DATA_M1           0x60030
+#define _PIPEA_DATA_M1           (dev_priv->info->display_mmio_offset + 0x60030)
 #define  TU_SIZE(x)             (((x)-1) << 25) /* default size 64 */
 #define  TU_SIZE_MASK           0x7e000000
 #define  PIPE_DATA_M1_OFFSET    0
-#define _PIPEA_DATA_N1           0x60034
+#define _PIPEA_DATA_N1           (dev_priv->info->display_mmio_offset + 0x60034)
 #define  PIPE_DATA_N1_OFFSET    0
 
-#define _PIPEA_DATA_M2           0x60038
+#define _PIPEA_DATA_M2           (dev_priv->info->display_mmio_offset + 0x60038)
 #define  PIPE_DATA_M2_OFFSET    0
-#define _PIPEA_DATA_N2           0x6003c
+#define _PIPEA_DATA_N2           (dev_priv->info->display_mmio_offset + 0x6003c)
 #define  PIPE_DATA_N2_OFFSET    0
 
-#define _PIPEA_LINK_M1           0x60040
+#define _PIPEA_LINK_M1           (dev_priv->info->display_mmio_offset + 0x60040)
 #define  PIPE_LINK_M1_OFFSET    0
-#define _PIPEA_LINK_N1           0x60044
+#define _PIPEA_LINK_N1           (dev_priv->info->display_mmio_offset + 0x60044)
 #define  PIPE_LINK_N1_OFFSET    0
 
-#define _PIPEA_LINK_M2           0x60048
+#define _PIPEA_LINK_M2           (dev_priv->info->display_mmio_offset + 0x60048)
 #define  PIPE_LINK_M2_OFFSET    0
-#define _PIPEA_LINK_N2           0x6004c
+#define _PIPEA_LINK_N2           (dev_priv->info->display_mmio_offset + 0x6004c)
 #define  PIPE_LINK_N2_OFFSET    0
 
 /* PIPEB timing regs are same start from 0x61000 */
 
-#define _PIPEB_DATA_M1           0x61030
-#define _PIPEB_DATA_N1           0x61034
+#define _PIPEB_DATA_M1           (dev_priv->info->display_mmio_offset + 0x61030)
+#define _PIPEB_DATA_N1           (dev_priv->info->display_mmio_offset + 0x61034)
 
-#define _PIPEB_DATA_M2           0x61038
-#define _PIPEB_DATA_N2           0x6103c
+#define _PIPEB_DATA_M2           (dev_priv->info->display_mmio_offset + 0x61038)
+#define _PIPEB_DATA_N2           (dev_priv->info->display_mmio_offset + 0x6103c)
 
-#define _PIPEB_LINK_M1           0x61040
-#define _PIPEB_LINK_N1           0x61044
+#define _PIPEB_LINK_M1           (dev_priv->info->display_mmio_offset + 0x61040)
+#define _PIPEB_LINK_N1           (dev_priv->info->display_mmio_offset + 0x61044)
 
-#define _PIPEB_LINK_M2           0x61048
-#define _PIPEB_LINK_N2           0x6104c
+#define _PIPEB_LINK_M2           (dev_priv->info->display_mmio_offset + 0x61048)
+#define _PIPEB_LINK_N2           (dev_priv->info->display_mmio_offset + 0x6104c)
 
 #define PIPE_DATA_M1(tran) _TRANSCODER(tran, _PIPEA_DATA_M1, _PIPEB_DATA_M1)
 #define PIPE_DATA_N1(tran) _TRANSCODER(tran, _PIPEA_DATA_N1, _PIPEB_DATA_N1)
 #define PORTD_PULSE_DURATION_6ms        (2 << 18)
 #define PORTD_PULSE_DURATION_100ms      (3 << 18)
 #define PORTD_PULSE_DURATION_MASK      (3 << 18)
-#define PORTD_HOTPLUG_NO_DETECT         (0)
-#define PORTD_HOTPLUG_SHORT_DETECT      (1 << 16)
-#define PORTD_HOTPLUG_LONG_DETECT       (1 << 17)
+#define PORTD_HOTPLUG_STATUS_MASK      (0x3 << 16)
+#define  PORTD_HOTPLUG_NO_DETECT       (0 << 16)
+#define  PORTD_HOTPLUG_SHORT_DETECT    (1 << 16)
+#define  PORTD_HOTPLUG_LONG_DETECT     (2 << 16)
 #define PORTC_HOTPLUG_ENABLE            (1 << 12)
 #define PORTC_PULSE_DURATION_2ms        (0)
 #define PORTC_PULSE_DURATION_4_5ms      (1 << 10)
 #define PORTC_PULSE_DURATION_6ms        (2 << 10)
 #define PORTC_PULSE_DURATION_100ms      (3 << 10)
 #define PORTC_PULSE_DURATION_MASK      (3 << 10)
-#define PORTC_HOTPLUG_NO_DETECT         (0)
-#define PORTC_HOTPLUG_SHORT_DETECT      (1 << 8)
-#define PORTC_HOTPLUG_LONG_DETECT       (1 << 9)
+#define PORTC_HOTPLUG_STATUS_MASK      (0x3 << 8)
+#define  PORTC_HOTPLUG_NO_DETECT       (0 << 8)
+#define  PORTC_HOTPLUG_SHORT_DETECT    (1 << 8)
+#define  PORTC_HOTPLUG_LONG_DETECT     (2 << 8)
 #define PORTB_HOTPLUG_ENABLE            (1 << 4)
 #define PORTB_PULSE_DURATION_2ms        (0)
 #define PORTB_PULSE_DURATION_4_5ms      (1 << 2)
 #define PORTB_PULSE_DURATION_6ms        (2 << 2)
 #define PORTB_PULSE_DURATION_100ms      (3 << 2)
 #define PORTB_PULSE_DURATION_MASK      (3 << 2)
-#define PORTB_HOTPLUG_NO_DETECT         (0)
-#define PORTB_HOTPLUG_SHORT_DETECT      (1 << 0)
-#define PORTB_HOTPLUG_LONG_DETECT       (1 << 1)
+#define PORTB_HOTPLUG_STATUS_MASK      (0x3 << 0)
+#define  PORTB_HOTPLUG_NO_DETECT       (0 << 0)
+#define  PORTB_HOTPLUG_SHORT_DETECT    (1 << 0)
+#define  PORTB_HOTPLUG_LONG_DETECT     (2 << 0)
 
 #define PCH_GPIOA               0xc5010
 #define PCH_GPIOB               0xc5014
 #define TVIDEO_DIP_DATA(pipe) _PIPE(pipe, _VIDEO_DIP_DATA_A, _VIDEO_DIP_DATA_B)
 #define TVIDEO_DIP_GCP(pipe) _PIPE(pipe, _VIDEO_DIP_GCP_A, _VIDEO_DIP_GCP_B)
 
-#define VLV_VIDEO_DIP_CTL_A            0x60200
-#define VLV_VIDEO_DIP_DATA_A           0x60208
-#define VLV_VIDEO_DIP_GDCP_PAYLOAD_A   0x60210
+#define VLV_VIDEO_DIP_CTL_A            (VLV_DISPLAY_BASE + 0x60200)
+#define VLV_VIDEO_DIP_DATA_A           (VLV_DISPLAY_BASE + 0x60208)
+#define VLV_VIDEO_DIP_GDCP_PAYLOAD_A   (VLV_DISPLAY_BASE + 0x60210)
 
-#define VLV_VIDEO_DIP_CTL_B            0x61170
-#define VLV_VIDEO_DIP_DATA_B           0x61174
-#define VLV_VIDEO_DIP_GDCP_PAYLOAD_B   0x61178
+#define VLV_VIDEO_DIP_CTL_B            (VLV_DISPLAY_BASE + 0x61170)
+#define VLV_VIDEO_DIP_DATA_B           (VLV_DISPLAY_BASE + 0x61174)
+#define VLV_VIDEO_DIP_GDCP_PAYLOAD_B   (VLV_DISPLAY_BASE + 0x61178)
 
 #define VLV_TVIDEO_DIP_CTL(pipe) \
         _PIPE(pipe, VLV_VIDEO_DIP_CTL_A, VLV_VIDEO_DIP_CTL_B)
 #define  TRANS_FSYNC_DELAY_HB2  (1<<27)
 #define  TRANS_FSYNC_DELAY_HB3  (2<<27)
 #define  TRANS_FSYNC_DELAY_HB4  (3<<27)
-#define  TRANS_DP_AUDIO_ONLY    (1<<26)
-#define  TRANS_DP_VIDEO_AUDIO   (0<<26)
 #define  TRANS_INTERLACE_MASK   (7<<21)
 #define  TRANS_PROGRESSIVE      (0<<21)
 #define  TRANS_INTERLACED       (3<<21)
 #define  FDI_10BPC                      (1<<16)
 #define  FDI_6BPC                       (2<<16)
 #define  FDI_12BPC                      (3<<16)
-#define  FDI_LINK_REVERSE_OVERWRITE     (1<<15)
+#define  FDI_RX_LINK_REVERSAL_OVERRIDE  (1<<15)
 #define  FDI_DMI_LINK_REVERSE_MASK      (1<<14)
 #define  FDI_RX_PLL_ENABLE              (1<<13)
 #define  FDI_FS_ERR_CORRECT_ENABLE      (1<<11)
 #define  LVDS_DETECTED (1 << 1)
 
 /* vlv has 2 sets of panel control regs. */
-#define PIPEA_PP_STATUS         0x61200
-#define PIPEA_PP_CONTROL        0x61204
-#define PIPEA_PP_ON_DELAYS      0x61208
-#define PIPEA_PP_OFF_DELAYS     0x6120c
-#define PIPEA_PP_DIVISOR        0x61210
-
-#define PIPEB_PP_STATUS         0x61300
-#define PIPEB_PP_CONTROL        0x61304
-#define PIPEB_PP_ON_DELAYS      0x61308
-#define PIPEB_PP_OFF_DELAYS     0x6130c
-#define PIPEB_PP_DIVISOR        0x61310
+#define PIPEA_PP_STATUS         (VLV_DISPLAY_BASE + 0x61200)
+#define PIPEA_PP_CONTROL        (VLV_DISPLAY_BASE + 0x61204)
+#define PIPEA_PP_ON_DELAYS      (VLV_DISPLAY_BASE + 0x61208)
+#define PIPEA_PP_OFF_DELAYS     (VLV_DISPLAY_BASE + 0x6120c)
+#define PIPEA_PP_DIVISOR        (VLV_DISPLAY_BASE + 0x61210)
+
+#define PIPEB_PP_STATUS         (VLV_DISPLAY_BASE + 0x61300)
+#define PIPEB_PP_CONTROL        (VLV_DISPLAY_BASE + 0x61304)
+#define PIPEB_PP_ON_DELAYS      (VLV_DISPLAY_BASE + 0x61308)
+#define PIPEB_PP_OFF_DELAYS     (VLV_DISPLAY_BASE + 0x6130c)
+#define PIPEB_PP_DIVISOR        (VLV_DISPLAY_BASE + 0x61310)
 
 #define PCH_PP_STATUS          0xc7200
 #define PCH_PP_CONTROL         0xc7204
 #define GEN6_RP_INTERRUPT_LIMITS               0xA014
 #define GEN6_RPSTAT1                           0xA01C
 #define   GEN6_CAGF_SHIFT                      8
+#define   HSW_CAGF_SHIFT                       7
 #define   GEN6_CAGF_MASK                       (0x7f << GEN6_CAGF_SHIFT)
+#define   HSW_CAGF_MASK                                (0x7f << HSW_CAGF_SHIFT)
 #define GEN6_RP_CONTROL                                0xA024
 #define   GEN6_RP_MEDIA_TURBO                  (1<<11)
 #define   GEN6_RP_MEDIA_MODE_MASK              (3<<9)
 #define   GEN6_PCODE_READ_MIN_FREQ_TABLE       0x9
 #define          GEN6_PCODE_WRITE_RC6VIDS              0x4
 #define          GEN6_PCODE_READ_RC6VIDS               0x5
-#define   GEN6_ENCODE_RC6_VID(mv)              (((mv) / 5) - 245) < 0 ?: 0
-#define   GEN6_DECODE_RC6_VID(vids)            (((vids) * 5) > 0 ? ((vids) * 5) + 245 : 0)
+#define   GEN6_ENCODE_RC6_VID(mv)              (((mv) - 245) / 5)
+#define   GEN6_DECODE_RC6_VID(vids)            (((vids) * 5) + 245)
 #define GEN6_PCODE_DATA                                0x138128
 #define   GEN6_PCODE_FREQ_IA_RATIO_SHIFT       8
 
 #define GEN7_ROW_CHICKEN2_GT2          0xf4f4
 #define   DOP_CLOCK_GATING_DISABLE     (1<<0)
 
-#define G4X_AUD_VID_DID                        0x62020
+#define G4X_AUD_VID_DID                        (dev_priv->info->display_mmio_offset + 0x62020)
 #define INTEL_AUDIO_DEVCL              0x808629FB
 #define INTEL_AUDIO_DEVBLC             0x80862801
 #define INTEL_AUDIO_DEVCTG             0x80862802
 #define   AUDIO_CP_READY_C             (1<<9)
 
 /* HSW Power Wells */
-#define HSW_PWR_WELL_CTL1                      0x45400 /* BIOS */
-#define HSW_PWR_WELL_CTL2                      0x45404 /* Driver */
-#define HSW_PWR_WELL_CTL3                      0x45408 /* KVMR */
-#define HSW_PWR_WELL_CTL4                      0x4540C /* Debug */
+#define HSW_PWR_WELL_BIOS                      0x45400 /* CTL1 */
+#define HSW_PWR_WELL_DRIVER                    0x45404 /* CTL2 */
+#define HSW_PWR_WELL_KVMR                      0x45408 /* CTL3 */
+#define HSW_PWR_WELL_DEBUG                     0x4540C /* CTL4 */
 #define   HSW_PWR_WELL_ENABLE                  (1<<31)
 #define   HSW_PWR_WELL_STATE                   (1<<30)
 #define HSW_PWR_WELL_CTL5                      0x45410
 #define  DDI_BUF_EMP_800MV_0DB_HSW             (7<<24)   /* Sel7 */
 #define  DDI_BUF_EMP_800MV_3_5DB_HSW           (8<<24)   /* Sel8 */
 #define  DDI_BUF_EMP_MASK                      (0xf<<24)
+#define  DDI_BUF_PORT_REVERSAL                 (1<<16)
 #define  DDI_BUF_IS_IDLE                       (1<<7)
 #define  DDI_A_4_LANES                         (1<<4)
 #define  DDI_PORT_WIDTH_X1                     (0<<1)
 #define  WM_DBG_DISALLOW_MAXFIFO       (1<<1)
 #define  WM_DBG_DISALLOW_SPRITE                (1<<2)
 
+/* pipe CSC */
+#define _PIPE_A_CSC_COEFF_RY_GY        0x49010
+#define _PIPE_A_CSC_COEFF_BY   0x49014
+#define _PIPE_A_CSC_COEFF_RU_GU        0x49018
+#define _PIPE_A_CSC_COEFF_BU   0x4901c
+#define _PIPE_A_CSC_COEFF_RV_GV        0x49020
+#define _PIPE_A_CSC_COEFF_BV   0x49024
+#define _PIPE_A_CSC_MODE       0x49028
+#define _PIPE_A_CSC_PREOFF_HI  0x49030
+#define _PIPE_A_CSC_PREOFF_ME  0x49034
+#define _PIPE_A_CSC_PREOFF_LO  0x49038
+#define _PIPE_A_CSC_POSTOFF_HI 0x49040
+#define _PIPE_A_CSC_POSTOFF_ME 0x49044
+#define _PIPE_A_CSC_POSTOFF_LO 0x49048
+
+#define _PIPE_B_CSC_COEFF_RY_GY        0x49110
+#define _PIPE_B_CSC_COEFF_BY   0x49114
+#define _PIPE_B_CSC_COEFF_RU_GU        0x49118
+#define _PIPE_B_CSC_COEFF_BU   0x4911c
+#define _PIPE_B_CSC_COEFF_RV_GV        0x49120
+#define _PIPE_B_CSC_COEFF_BV   0x49124
+#define _PIPE_B_CSC_MODE       0x49128
+#define _PIPE_B_CSC_PREOFF_HI  0x49130
+#define _PIPE_B_CSC_PREOFF_ME  0x49134
+#define _PIPE_B_CSC_PREOFF_LO  0x49138
+#define _PIPE_B_CSC_POSTOFF_HI 0x49140
+#define _PIPE_B_CSC_POSTOFF_ME 0x49144
+#define _PIPE_B_CSC_POSTOFF_LO 0x49148
+
+#define CSC_BLACK_SCREEN_OFFSET (1 << 2)
+#define CSC_POSITION_BEFORE_GAMMA (1 << 1)
+#define CSC_MODE_YUV_TO_RGB (1 << 0)
+
+#define PIPE_CSC_COEFF_RY_GY(pipe) _PIPE(pipe, _PIPE_A_CSC_COEFF_RY_GY, _PIPE_B_CSC_COEFF_RY_GY)
+#define PIPE_CSC_COEFF_BY(pipe) _PIPE(pipe, _PIPE_A_CSC_COEFF_BY, _PIPE_B_CSC_COEFF_BY)
+#define PIPE_CSC_COEFF_RU_GU(pipe) _PIPE(pipe, _PIPE_A_CSC_COEFF_RU_GU, _PIPE_B_CSC_COEFF_RU_GU)
+#define PIPE_CSC_COEFF_BU(pipe) _PIPE(pipe, _PIPE_A_CSC_COEFF_BU, _PIPE_B_CSC_COEFF_BU)
+#define PIPE_CSC_COEFF_RV_GV(pipe) _PIPE(pipe, _PIPE_A_CSC_COEFF_RV_GV, _PIPE_B_CSC_COEFF_RV_GV)
+#define PIPE_CSC_COEFF_BV(pipe) _PIPE(pipe, _PIPE_A_CSC_COEFF_BV, _PIPE_B_CSC_COEFF_BV)
+#define PIPE_CSC_MODE(pipe) _PIPE(pipe, _PIPE_A_CSC_MODE, _PIPE_B_CSC_MODE)
+#define PIPE_CSC_PREOFF_HI(pipe) _PIPE(pipe, _PIPE_A_CSC_PREOFF_HI, _PIPE_B_CSC_PREOFF_HI)
+#define PIPE_CSC_PREOFF_ME(pipe) _PIPE(pipe, _PIPE_A_CSC_PREOFF_ME, _PIPE_B_CSC_PREOFF_ME)
+#define PIPE_CSC_PREOFF_LO(pipe) _PIPE(pipe, _PIPE_A_CSC_PREOFF_LO, _PIPE_B_CSC_PREOFF_LO)
+#define PIPE_CSC_POSTOFF_HI(pipe) _PIPE(pipe, _PIPE_A_CSC_POSTOFF_HI, _PIPE_B_CSC_POSTOFF_HI)
+#define PIPE_CSC_POSTOFF_ME(pipe) _PIPE(pipe, _PIPE_A_CSC_POSTOFF_ME, _PIPE_B_CSC_POSTOFF_ME)
+#define PIPE_CSC_POSTOFF_LO(pipe) _PIPE(pipe, _PIPE_A_CSC_POSTOFF_LO, _PIPE_B_CSC_POSTOFF_LO)
+
 #endif /* _I915_REG_H_ */
index 63d4d30c39de3ebff432161965f9b7221db44786..2135f21ea45870aa3475f55bde828b61a5954a20 100644 (file)
 #include "intel_drv.h"
 #include "i915_reg.h"
 
-static bool i915_pipe_enabled(struct drm_device *dev, enum pipe pipe)
-{
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       u32     dpll_reg;
-
-       /* On IVB, 3rd pipe shares PLL with another one */
-       if (pipe > 1)
-               return false;
-
-       if (HAS_PCH_SPLIT(dev))
-               dpll_reg = _PCH_DPLL(pipe);
-       else
-               dpll_reg = (pipe == PIPE_A) ? _DPLL_A : _DPLL_B;
-
-       return (I915_READ(dpll_reg) & DPLL_VCO_ENABLE);
-}
-
-static void i915_save_palette(struct drm_device *dev, enum pipe pipe)
-{
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       unsigned long reg = (pipe == PIPE_A ? _PALETTE_A : _PALETTE_B);
-       u32 *array;
-       int i;
-
-       if (!i915_pipe_enabled(dev, pipe))
-               return;
-
-       if (HAS_PCH_SPLIT(dev))
-               reg = (pipe == PIPE_A) ? _LGC_PALETTE_A : _LGC_PALETTE_B;
-
-       if (pipe == PIPE_A)
-               array = dev_priv->regfile.save_palette_a;
-       else
-               array = dev_priv->regfile.save_palette_b;
-
-       for (i = 0; i < 256; i++)
-               array[i] = I915_READ(reg + (i << 2));
-}
-
-static void i915_restore_palette(struct drm_device *dev, enum pipe pipe)
-{
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       unsigned long reg = (pipe == PIPE_A ? _PALETTE_A : _PALETTE_B);
-       u32 *array;
-       int i;
-
-       if (!i915_pipe_enabled(dev, pipe))
-               return;
-
-       if (HAS_PCH_SPLIT(dev))
-               reg = (pipe == PIPE_A) ? _LGC_PALETTE_A : _LGC_PALETTE_B;
-
-       if (pipe == PIPE_A)
-               array = dev_priv->regfile.save_palette_a;
-       else
-               array = dev_priv->regfile.save_palette_b;
-
-       for (i = 0; i < 256; i++)
-               I915_WRITE(reg + (i << 2), array[i]);
-}
-
 static u8 i915_read_indexed(struct drm_device *dev, u16 index_port, u16 data_port, u8 reg)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -130,6 +69,12 @@ static void i915_save_vga(struct drm_device *dev)
        int i;
        u16 cr_index, cr_data, st01;
 
+       /* VGA state */
+       dev_priv->regfile.saveVGA0 = I915_READ(VGA0);
+       dev_priv->regfile.saveVGA1 = I915_READ(VGA1);
+       dev_priv->regfile.saveVGA_PD = I915_READ(VGA_PD);
+       dev_priv->regfile.saveVGACNTRL = I915_READ(i915_vgacntrl_reg(dev));
+
        /* VGA color palette registers */
        dev_priv->regfile.saveDACMASK = I915_READ8(VGA_DACMASK);
 
@@ -188,6 +133,15 @@ static void i915_restore_vga(struct drm_device *dev)
        int i;
        u16 cr_index, cr_data, st01;
 
+       /* VGA state */
+       I915_WRITE(i915_vgacntrl_reg(dev), dev_priv->regfile.saveVGACNTRL);
+
+       I915_WRITE(VGA0, dev_priv->regfile.saveVGA0);
+       I915_WRITE(VGA1, dev_priv->regfile.saveVGA1);
+       I915_WRITE(VGA_PD, dev_priv->regfile.saveVGA_PD);
+       POSTING_READ(VGA_PD);
+       udelay(150);
+
        /* MSR bits */
        I915_WRITE8(VGA_MSR_WRITE, dev_priv->regfile.saveMSR);
        if (dev_priv->regfile.saveMSR & VGA_MSR_CGA_MODE) {
@@ -235,396 +189,18 @@ static void i915_restore_vga(struct drm_device *dev)
        I915_WRITE8(VGA_DACMASK, dev_priv->regfile.saveDACMASK);
 }
 
-static void i915_save_modeset_reg(struct drm_device *dev)
-{
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       int i;
-
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
-               return;
-
-       /* Cursor state */
-       dev_priv->regfile.saveCURACNTR = I915_READ(_CURACNTR);
-       dev_priv->regfile.saveCURAPOS = I915_READ(_CURAPOS);
-       dev_priv->regfile.saveCURABASE = I915_READ(_CURABASE);
-       dev_priv->regfile.saveCURBCNTR = I915_READ(_CURBCNTR);
-       dev_priv->regfile.saveCURBPOS = I915_READ(_CURBPOS);
-       dev_priv->regfile.saveCURBBASE = I915_READ(_CURBBASE);
-       if (IS_GEN2(dev))
-               dev_priv->regfile.saveCURSIZE = I915_READ(CURSIZE);
-
-       if (HAS_PCH_SPLIT(dev)) {
-               dev_priv->regfile.savePCH_DREF_CONTROL = I915_READ(PCH_DREF_CONTROL);
-               dev_priv->regfile.saveDISP_ARB_CTL = I915_READ(DISP_ARB_CTL);
-       }
-
-       /* Pipe & plane A info */
-       dev_priv->regfile.savePIPEACONF = I915_READ(_PIPEACONF);
-       dev_priv->regfile.savePIPEASRC = I915_READ(_PIPEASRC);
-       if (HAS_PCH_SPLIT(dev)) {
-               dev_priv->regfile.saveFPA0 = I915_READ(_PCH_FPA0);
-               dev_priv->regfile.saveFPA1 = I915_READ(_PCH_FPA1);
-               dev_priv->regfile.saveDPLL_A = I915_READ(_PCH_DPLL_A);
-       } else {
-               dev_priv->regfile.saveFPA0 = I915_READ(_FPA0);
-               dev_priv->regfile.saveFPA1 = I915_READ(_FPA1);
-               dev_priv->regfile.saveDPLL_A = I915_READ(_DPLL_A);
-       }
-       if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev))
-               dev_priv->regfile.saveDPLL_A_MD = I915_READ(_DPLL_A_MD);
-       dev_priv->regfile.saveHTOTAL_A = I915_READ(_HTOTAL_A);
-       dev_priv->regfile.saveHBLANK_A = I915_READ(_HBLANK_A);
-       dev_priv->regfile.saveHSYNC_A = I915_READ(_HSYNC_A);
-       dev_priv->regfile.saveVTOTAL_A = I915_READ(_VTOTAL_A);
-       dev_priv->regfile.saveVBLANK_A = I915_READ(_VBLANK_A);
-       dev_priv->regfile.saveVSYNC_A = I915_READ(_VSYNC_A);
-       if (!HAS_PCH_SPLIT(dev))
-               dev_priv->regfile.saveBCLRPAT_A = I915_READ(_BCLRPAT_A);
-
-       if (HAS_PCH_SPLIT(dev)) {
-               dev_priv->regfile.savePIPEA_DATA_M1 = I915_READ(_PIPEA_DATA_M1);
-               dev_priv->regfile.savePIPEA_DATA_N1 = I915_READ(_PIPEA_DATA_N1);
-               dev_priv->regfile.savePIPEA_LINK_M1 = I915_READ(_PIPEA_LINK_M1);
-               dev_priv->regfile.savePIPEA_LINK_N1 = I915_READ(_PIPEA_LINK_N1);
-
-               dev_priv->regfile.saveFDI_TXA_CTL = I915_READ(_FDI_TXA_CTL);
-               dev_priv->regfile.saveFDI_RXA_CTL = I915_READ(_FDI_RXA_CTL);
-
-               dev_priv->regfile.savePFA_CTL_1 = I915_READ(_PFA_CTL_1);
-               dev_priv->regfile.savePFA_WIN_SZ = I915_READ(_PFA_WIN_SZ);
-               dev_priv->regfile.savePFA_WIN_POS = I915_READ(_PFA_WIN_POS);
-
-               dev_priv->regfile.saveTRANSACONF = I915_READ(_TRANSACONF);
-               dev_priv->regfile.saveTRANS_HTOTAL_A = I915_READ(_TRANS_HTOTAL_A);
-               dev_priv->regfile.saveTRANS_HBLANK_A = I915_READ(_TRANS_HBLANK_A);
-               dev_priv->regfile.saveTRANS_HSYNC_A = I915_READ(_TRANS_HSYNC_A);
-               dev_priv->regfile.saveTRANS_VTOTAL_A = I915_READ(_TRANS_VTOTAL_A);
-               dev_priv->regfile.saveTRANS_VBLANK_A = I915_READ(_TRANS_VBLANK_A);
-               dev_priv->regfile.saveTRANS_VSYNC_A = I915_READ(_TRANS_VSYNC_A);
-       }
-
-       dev_priv->regfile.saveDSPACNTR = I915_READ(_DSPACNTR);
-       dev_priv->regfile.saveDSPASTRIDE = I915_READ(_DSPASTRIDE);
-       dev_priv->regfile.saveDSPASIZE = I915_READ(_DSPASIZE);
-       dev_priv->regfile.saveDSPAPOS = I915_READ(_DSPAPOS);
-       dev_priv->regfile.saveDSPAADDR = I915_READ(_DSPAADDR);
-       if (INTEL_INFO(dev)->gen >= 4) {
-               dev_priv->regfile.saveDSPASURF = I915_READ(_DSPASURF);
-               dev_priv->regfile.saveDSPATILEOFF = I915_READ(_DSPATILEOFF);
-       }
-       i915_save_palette(dev, PIPE_A);
-       dev_priv->regfile.savePIPEASTAT = I915_READ(_PIPEASTAT);
-
-       /* Pipe & plane B info */
-       dev_priv->regfile.savePIPEBCONF = I915_READ(_PIPEBCONF);
-       dev_priv->regfile.savePIPEBSRC = I915_READ(_PIPEBSRC);
-       if (HAS_PCH_SPLIT(dev)) {
-               dev_priv->regfile.saveFPB0 = I915_READ(_PCH_FPB0);
-               dev_priv->regfile.saveFPB1 = I915_READ(_PCH_FPB1);
-               dev_priv->regfile.saveDPLL_B = I915_READ(_PCH_DPLL_B);
-       } else {
-               dev_priv->regfile.saveFPB0 = I915_READ(_FPB0);
-               dev_priv->regfile.saveFPB1 = I915_READ(_FPB1);
-               dev_priv->regfile.saveDPLL_B = I915_READ(_DPLL_B);
-       }
-       if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev))
-               dev_priv->regfile.saveDPLL_B_MD = I915_READ(_DPLL_B_MD);
-       dev_priv->regfile.saveHTOTAL_B = I915_READ(_HTOTAL_B);
-       dev_priv->regfile.saveHBLANK_B = I915_READ(_HBLANK_B);
-       dev_priv->regfile.saveHSYNC_B = I915_READ(_HSYNC_B);
-       dev_priv->regfile.saveVTOTAL_B = I915_READ(_VTOTAL_B);
-       dev_priv->regfile.saveVBLANK_B = I915_READ(_VBLANK_B);
-       dev_priv->regfile.saveVSYNC_B = I915_READ(_VSYNC_B);
-       if (!HAS_PCH_SPLIT(dev))
-               dev_priv->regfile.saveBCLRPAT_B = I915_READ(_BCLRPAT_B);
-
-       if (HAS_PCH_SPLIT(dev)) {
-               dev_priv->regfile.savePIPEB_DATA_M1 = I915_READ(_PIPEB_DATA_M1);
-               dev_priv->regfile.savePIPEB_DATA_N1 = I915_READ(_PIPEB_DATA_N1);
-               dev_priv->regfile.savePIPEB_LINK_M1 = I915_READ(_PIPEB_LINK_M1);
-               dev_priv->regfile.savePIPEB_LINK_N1 = I915_READ(_PIPEB_LINK_N1);
-
-               dev_priv->regfile.saveFDI_TXB_CTL = I915_READ(_FDI_TXB_CTL);
-               dev_priv->regfile.saveFDI_RXB_CTL = I915_READ(_FDI_RXB_CTL);
-
-               dev_priv->regfile.savePFB_CTL_1 = I915_READ(_PFB_CTL_1);
-               dev_priv->regfile.savePFB_WIN_SZ = I915_READ(_PFB_WIN_SZ);
-               dev_priv->regfile.savePFB_WIN_POS = I915_READ(_PFB_WIN_POS);
-
-               dev_priv->regfile.saveTRANSBCONF = I915_READ(_TRANSBCONF);
-               dev_priv->regfile.saveTRANS_HTOTAL_B = I915_READ(_TRANS_HTOTAL_B);
-               dev_priv->regfile.saveTRANS_HBLANK_B = I915_READ(_TRANS_HBLANK_B);
-               dev_priv->regfile.saveTRANS_HSYNC_B = I915_READ(_TRANS_HSYNC_B);
-               dev_priv->regfile.saveTRANS_VTOTAL_B = I915_READ(_TRANS_VTOTAL_B);
-               dev_priv->regfile.saveTRANS_VBLANK_B = I915_READ(_TRANS_VBLANK_B);
-               dev_priv->regfile.saveTRANS_VSYNC_B = I915_READ(_TRANS_VSYNC_B);
-       }
-
-       dev_priv->regfile.saveDSPBCNTR = I915_READ(_DSPBCNTR);
-       dev_priv->regfile.saveDSPBSTRIDE = I915_READ(_DSPBSTRIDE);
-       dev_priv->regfile.saveDSPBSIZE = I915_READ(_DSPBSIZE);
-       dev_priv->regfile.saveDSPBPOS = I915_READ(_DSPBPOS);
-       dev_priv->regfile.saveDSPBADDR = I915_READ(_DSPBADDR);
-       if (INTEL_INFO(dev)->gen >= 4) {
-               dev_priv->regfile.saveDSPBSURF = I915_READ(_DSPBSURF);
-               dev_priv->regfile.saveDSPBTILEOFF = I915_READ(_DSPBTILEOFF);
-       }
-       i915_save_palette(dev, PIPE_B);
-       dev_priv->regfile.savePIPEBSTAT = I915_READ(_PIPEBSTAT);
-
-       /* Fences */
-       switch (INTEL_INFO(dev)->gen) {
-       case 7:
-       case 6:
-               for (i = 0; i < 16; i++)
-                       dev_priv->regfile.saveFENCE[i] = I915_READ64(FENCE_REG_SANDYBRIDGE_0 + (i * 8));
-               break;
-       case 5:
-       case 4:
-               for (i = 0; i < 16; i++)
-                       dev_priv->regfile.saveFENCE[i] = I915_READ64(FENCE_REG_965_0 + (i * 8));
-               break;
-       case 3:
-               if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
-                       for (i = 0; i < 8; i++)
-                               dev_priv->regfile.saveFENCE[i+8] = I915_READ(FENCE_REG_945_8 + (i * 4));
-       case 2:
-               for (i = 0; i < 8; i++)
-                       dev_priv->regfile.saveFENCE[i] = I915_READ(FENCE_REG_830_0 + (i * 4));
-               break;
-       }
-
-       /* CRT state */
-       if (HAS_PCH_SPLIT(dev))
-               dev_priv->regfile.saveADPA = I915_READ(PCH_ADPA);
-       else
-               dev_priv->regfile.saveADPA = I915_READ(ADPA);
-
-       return;
-}
-
-static void i915_restore_modeset_reg(struct drm_device *dev)
-{
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       int dpll_a_reg, fpa0_reg, fpa1_reg;
-       int dpll_b_reg, fpb0_reg, fpb1_reg;
-       int i;
-
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
-               return;
-
-       /* Fences */
-       switch (INTEL_INFO(dev)->gen) {
-       case 7:
-       case 6:
-               for (i = 0; i < 16; i++)
-                       I915_WRITE64(FENCE_REG_SANDYBRIDGE_0 + (i * 8), dev_priv->regfile.saveFENCE[i]);
-               break;
-       case 5:
-       case 4:
-               for (i = 0; i < 16; i++)
-                       I915_WRITE64(FENCE_REG_965_0 + (i * 8), dev_priv->regfile.saveFENCE[i]);
-               break;
-       case 3:
-       case 2:
-               if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
-                       for (i = 0; i < 8; i++)
-                               I915_WRITE(FENCE_REG_945_8 + (i * 4), dev_priv->regfile.saveFENCE[i+8]);
-               for (i = 0; i < 8; i++)
-                       I915_WRITE(FENCE_REG_830_0 + (i * 4), dev_priv->regfile.saveFENCE[i]);
-               break;
-       }
-
-
-       if (HAS_PCH_SPLIT(dev)) {
-               dpll_a_reg = _PCH_DPLL_A;
-               dpll_b_reg = _PCH_DPLL_B;
-               fpa0_reg = _PCH_FPA0;
-               fpb0_reg = _PCH_FPB0;
-               fpa1_reg = _PCH_FPA1;
-               fpb1_reg = _PCH_FPB1;
-       } else {
-               dpll_a_reg = _DPLL_A;
-               dpll_b_reg = _DPLL_B;
-               fpa0_reg = _FPA0;
-               fpb0_reg = _FPB0;
-               fpa1_reg = _FPA1;
-               fpb1_reg = _FPB1;
-       }
-
-       if (HAS_PCH_SPLIT(dev)) {
-               I915_WRITE(PCH_DREF_CONTROL, dev_priv->regfile.savePCH_DREF_CONTROL);
-               I915_WRITE(DISP_ARB_CTL, dev_priv->regfile.saveDISP_ARB_CTL);
-       }
-
-       /* Pipe & plane A info */
-       /* Prime the clock */
-       if (dev_priv->regfile.saveDPLL_A & DPLL_VCO_ENABLE) {
-               I915_WRITE(dpll_a_reg, dev_priv->regfile.saveDPLL_A &
-                          ~DPLL_VCO_ENABLE);
-               POSTING_READ(dpll_a_reg);
-               udelay(150);
-       }
-       I915_WRITE(fpa0_reg, dev_priv->regfile.saveFPA0);
-       I915_WRITE(fpa1_reg, dev_priv->regfile.saveFPA1);
-       /* Actually enable it */
-       I915_WRITE(dpll_a_reg, dev_priv->regfile.saveDPLL_A);
-       POSTING_READ(dpll_a_reg);
-       udelay(150);
-       if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) {
-               I915_WRITE(_DPLL_A_MD, dev_priv->regfile.saveDPLL_A_MD);
-               POSTING_READ(_DPLL_A_MD);
-       }
-       udelay(150);
-
-       /* Restore mode */
-       I915_WRITE(_HTOTAL_A, dev_priv->regfile.saveHTOTAL_A);
-       I915_WRITE(_HBLANK_A, dev_priv->regfile.saveHBLANK_A);
-       I915_WRITE(_HSYNC_A, dev_priv->regfile.saveHSYNC_A);
-       I915_WRITE(_VTOTAL_A, dev_priv->regfile.saveVTOTAL_A);
-       I915_WRITE(_VBLANK_A, dev_priv->regfile.saveVBLANK_A);
-       I915_WRITE(_VSYNC_A, dev_priv->regfile.saveVSYNC_A);
-       if (!HAS_PCH_SPLIT(dev))
-               I915_WRITE(_BCLRPAT_A, dev_priv->regfile.saveBCLRPAT_A);
-
-       if (HAS_PCH_SPLIT(dev)) {
-               I915_WRITE(_PIPEA_DATA_M1, dev_priv->regfile.savePIPEA_DATA_M1);
-               I915_WRITE(_PIPEA_DATA_N1, dev_priv->regfile.savePIPEA_DATA_N1);
-               I915_WRITE(_PIPEA_LINK_M1, dev_priv->regfile.savePIPEA_LINK_M1);
-               I915_WRITE(_PIPEA_LINK_N1, dev_priv->regfile.savePIPEA_LINK_N1);
-
-               I915_WRITE(_FDI_RXA_CTL, dev_priv->regfile.saveFDI_RXA_CTL);
-               I915_WRITE(_FDI_TXA_CTL, dev_priv->regfile.saveFDI_TXA_CTL);
-
-               I915_WRITE(_PFA_CTL_1, dev_priv->regfile.savePFA_CTL_1);
-               I915_WRITE(_PFA_WIN_SZ, dev_priv->regfile.savePFA_WIN_SZ);
-               I915_WRITE(_PFA_WIN_POS, dev_priv->regfile.savePFA_WIN_POS);
-
-               I915_WRITE(_TRANSACONF, dev_priv->regfile.saveTRANSACONF);
-               I915_WRITE(_TRANS_HTOTAL_A, dev_priv->regfile.saveTRANS_HTOTAL_A);
-               I915_WRITE(_TRANS_HBLANK_A, dev_priv->regfile.saveTRANS_HBLANK_A);
-               I915_WRITE(_TRANS_HSYNC_A, dev_priv->regfile.saveTRANS_HSYNC_A);
-               I915_WRITE(_TRANS_VTOTAL_A, dev_priv->regfile.saveTRANS_VTOTAL_A);
-               I915_WRITE(_TRANS_VBLANK_A, dev_priv->regfile.saveTRANS_VBLANK_A);
-               I915_WRITE(_TRANS_VSYNC_A, dev_priv->regfile.saveTRANS_VSYNC_A);
-       }
-
-       /* Restore plane info */
-       I915_WRITE(_DSPASIZE, dev_priv->regfile.saveDSPASIZE);
-       I915_WRITE(_DSPAPOS, dev_priv->regfile.saveDSPAPOS);
-       I915_WRITE(_PIPEASRC, dev_priv->regfile.savePIPEASRC);
-       I915_WRITE(_DSPAADDR, dev_priv->regfile.saveDSPAADDR);
-       I915_WRITE(_DSPASTRIDE, dev_priv->regfile.saveDSPASTRIDE);
-       if (INTEL_INFO(dev)->gen >= 4) {
-               I915_WRITE(_DSPASURF, dev_priv->regfile.saveDSPASURF);
-               I915_WRITE(_DSPATILEOFF, dev_priv->regfile.saveDSPATILEOFF);
-       }
-
-       I915_WRITE(_PIPEACONF, dev_priv->regfile.savePIPEACONF);
-
-       i915_restore_palette(dev, PIPE_A);
-       /* Enable the plane */
-       I915_WRITE(_DSPACNTR, dev_priv->regfile.saveDSPACNTR);
-       I915_WRITE(_DSPAADDR, I915_READ(_DSPAADDR));
-
-       /* Pipe & plane B info */
-       if (dev_priv->regfile.saveDPLL_B & DPLL_VCO_ENABLE) {
-               I915_WRITE(dpll_b_reg, dev_priv->regfile.saveDPLL_B &
-                          ~DPLL_VCO_ENABLE);
-               POSTING_READ(dpll_b_reg);
-               udelay(150);
-       }
-       I915_WRITE(fpb0_reg, dev_priv->regfile.saveFPB0);
-       I915_WRITE(fpb1_reg, dev_priv->regfile.saveFPB1);
-       /* Actually enable it */
-       I915_WRITE(dpll_b_reg, dev_priv->regfile.saveDPLL_B);
-       POSTING_READ(dpll_b_reg);
-       udelay(150);
-       if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) {
-               I915_WRITE(_DPLL_B_MD, dev_priv->regfile.saveDPLL_B_MD);
-               POSTING_READ(_DPLL_B_MD);
-       }
-       udelay(150);
-
-       /* Restore mode */
-       I915_WRITE(_HTOTAL_B, dev_priv->regfile.saveHTOTAL_B);
-       I915_WRITE(_HBLANK_B, dev_priv->regfile.saveHBLANK_B);
-       I915_WRITE(_HSYNC_B, dev_priv->regfile.saveHSYNC_B);
-       I915_WRITE(_VTOTAL_B, dev_priv->regfile.saveVTOTAL_B);
-       I915_WRITE(_VBLANK_B, dev_priv->regfile.saveVBLANK_B);
-       I915_WRITE(_VSYNC_B, dev_priv->regfile.saveVSYNC_B);
-       if (!HAS_PCH_SPLIT(dev))
-               I915_WRITE(_BCLRPAT_B, dev_priv->regfile.saveBCLRPAT_B);
-
-       if (HAS_PCH_SPLIT(dev)) {
-               I915_WRITE(_PIPEB_DATA_M1, dev_priv->regfile.savePIPEB_DATA_M1);
-               I915_WRITE(_PIPEB_DATA_N1, dev_priv->regfile.savePIPEB_DATA_N1);
-               I915_WRITE(_PIPEB_LINK_M1, dev_priv->regfile.savePIPEB_LINK_M1);
-               I915_WRITE(_PIPEB_LINK_N1, dev_priv->regfile.savePIPEB_LINK_N1);
-
-               I915_WRITE(_FDI_RXB_CTL, dev_priv->regfile.saveFDI_RXB_CTL);
-               I915_WRITE(_FDI_TXB_CTL, dev_priv->regfile.saveFDI_TXB_CTL);
-
-               I915_WRITE(_PFB_CTL_1, dev_priv->regfile.savePFB_CTL_1);
-               I915_WRITE(_PFB_WIN_SZ, dev_priv->regfile.savePFB_WIN_SZ);
-               I915_WRITE(_PFB_WIN_POS, dev_priv->regfile.savePFB_WIN_POS);
-
-               I915_WRITE(_TRANSBCONF, dev_priv->regfile.saveTRANSBCONF);
-               I915_WRITE(_TRANS_HTOTAL_B, dev_priv->regfile.saveTRANS_HTOTAL_B);
-               I915_WRITE(_TRANS_HBLANK_B, dev_priv->regfile.saveTRANS_HBLANK_B);
-               I915_WRITE(_TRANS_HSYNC_B, dev_priv->regfile.saveTRANS_HSYNC_B);
-               I915_WRITE(_TRANS_VTOTAL_B, dev_priv->regfile.saveTRANS_VTOTAL_B);
-               I915_WRITE(_TRANS_VBLANK_B, dev_priv->regfile.saveTRANS_VBLANK_B);
-               I915_WRITE(_TRANS_VSYNC_B, dev_priv->regfile.saveTRANS_VSYNC_B);
-       }
-
-       /* Restore plane info */
-       I915_WRITE(_DSPBSIZE, dev_priv->regfile.saveDSPBSIZE);
-       I915_WRITE(_DSPBPOS, dev_priv->regfile.saveDSPBPOS);
-       I915_WRITE(_PIPEBSRC, dev_priv->regfile.savePIPEBSRC);
-       I915_WRITE(_DSPBADDR, dev_priv->regfile.saveDSPBADDR);
-       I915_WRITE(_DSPBSTRIDE, dev_priv->regfile.saveDSPBSTRIDE);
-       if (INTEL_INFO(dev)->gen >= 4) {
-               I915_WRITE(_DSPBSURF, dev_priv->regfile.saveDSPBSURF);
-               I915_WRITE(_DSPBTILEOFF, dev_priv->regfile.saveDSPBTILEOFF);
-       }
-
-       I915_WRITE(_PIPEBCONF, dev_priv->regfile.savePIPEBCONF);
-
-       i915_restore_palette(dev, PIPE_B);
-       /* Enable the plane */
-       I915_WRITE(_DSPBCNTR, dev_priv->regfile.saveDSPBCNTR);
-       I915_WRITE(_DSPBADDR, I915_READ(_DSPBADDR));
-
-       /* Cursor state */
-       I915_WRITE(_CURAPOS, dev_priv->regfile.saveCURAPOS);
-       I915_WRITE(_CURACNTR, dev_priv->regfile.saveCURACNTR);
-       I915_WRITE(_CURABASE, dev_priv->regfile.saveCURABASE);
-       I915_WRITE(_CURBPOS, dev_priv->regfile.saveCURBPOS);
-       I915_WRITE(_CURBCNTR, dev_priv->regfile.saveCURBCNTR);
-       I915_WRITE(_CURBBASE, dev_priv->regfile.saveCURBBASE);
-       if (IS_GEN2(dev))
-               I915_WRITE(CURSIZE, dev_priv->regfile.saveCURSIZE);
-
-       /* CRT state */
-       if (HAS_PCH_SPLIT(dev))
-               I915_WRITE(PCH_ADPA, dev_priv->regfile.saveADPA);
-       else
-               I915_WRITE(ADPA, dev_priv->regfile.saveADPA);
-
-       return;
-}
-
 static void i915_save_display(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
        /* Display arbitration control */
-       dev_priv->regfile.saveDSPARB = I915_READ(DSPARB);
+       if (INTEL_INFO(dev)->gen <= 4)
+               dev_priv->regfile.saveDSPARB = I915_READ(DSPARB);
 
        /* This is only meaningful in non-KMS mode */
        /* Don't regfile.save them in KMS mode */
-       i915_save_modeset_reg(dev);
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               i915_save_display_reg(dev);
 
        /* LVDS state */
        if (HAS_PCH_SPLIT(dev)) {
@@ -658,24 +234,6 @@ static void i915_save_display(struct drm_device *dev)
                dev_priv->regfile.savePP_DIVISOR = I915_READ(PP_DIVISOR);
        }
 
-       if (!drm_core_check_feature(dev, DRIVER_MODESET)) {
-               /* Display Port state */
-               if (SUPPORTS_INTEGRATED_DP(dev)) {
-                       dev_priv->regfile.saveDP_B = I915_READ(DP_B);
-                       dev_priv->regfile.saveDP_C = I915_READ(DP_C);
-                       dev_priv->regfile.saveDP_D = I915_READ(DP_D);
-                       dev_priv->regfile.savePIPEA_GMCH_DATA_M = I915_READ(_PIPEA_GMCH_DATA_M);
-                       dev_priv->regfile.savePIPEB_GMCH_DATA_M = I915_READ(_PIPEB_GMCH_DATA_M);
-                       dev_priv->regfile.savePIPEA_GMCH_DATA_N = I915_READ(_PIPEA_GMCH_DATA_N);
-                       dev_priv->regfile.savePIPEB_GMCH_DATA_N = I915_READ(_PIPEB_GMCH_DATA_N);
-                       dev_priv->regfile.savePIPEA_DP_LINK_M = I915_READ(_PIPEA_DP_LINK_M);
-                       dev_priv->regfile.savePIPEB_DP_LINK_M = I915_READ(_PIPEB_DP_LINK_M);
-                       dev_priv->regfile.savePIPEA_DP_LINK_N = I915_READ(_PIPEA_DP_LINK_N);
-                       dev_priv->regfile.savePIPEB_DP_LINK_N = I915_READ(_PIPEB_DP_LINK_N);
-               }
-               /* FIXME: regfile.save TV & SDVO state */
-       }
-
        /* Only regfile.save FBC state on the platform that supports FBC */
        if (I915_HAS_FBC(dev)) {
                if (HAS_PCH_SPLIT(dev)) {
@@ -690,16 +248,8 @@ static void i915_save_display(struct drm_device *dev)
                }
        }
 
-       /* VGA state */
-       dev_priv->regfile.saveVGA0 = I915_READ(VGA0);
-       dev_priv->regfile.saveVGA1 = I915_READ(VGA1);
-       dev_priv->regfile.saveVGA_PD = I915_READ(VGA_PD);
-       if (HAS_PCH_SPLIT(dev))
-               dev_priv->regfile.saveVGACNTRL = I915_READ(CPU_VGACNTRL);
-       else
-               dev_priv->regfile.saveVGACNTRL = I915_READ(VGACNTRL);
-
-       i915_save_vga(dev);
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               i915_save_vga(dev);
 }
 
 static void i915_restore_display(struct drm_device *dev)
@@ -707,25 +257,11 @@ static void i915_restore_display(struct drm_device *dev)
        struct drm_i915_private *dev_priv = dev->dev_private;
 
        /* Display arbitration */
-       I915_WRITE(DSPARB, dev_priv->regfile.saveDSPARB);
+       if (INTEL_INFO(dev)->gen <= 4)
+               I915_WRITE(DSPARB, dev_priv->regfile.saveDSPARB);
 
-       if (!drm_core_check_feature(dev, DRIVER_MODESET)) {
-               /* Display port ratios (must be done before clock is set) */
-               if (SUPPORTS_INTEGRATED_DP(dev)) {
-                       I915_WRITE(_PIPEA_GMCH_DATA_M, dev_priv->regfile.savePIPEA_GMCH_DATA_M);
-                       I915_WRITE(_PIPEB_GMCH_DATA_M, dev_priv->regfile.savePIPEB_GMCH_DATA_M);
-                       I915_WRITE(_PIPEA_GMCH_DATA_N, dev_priv->regfile.savePIPEA_GMCH_DATA_N);
-                       I915_WRITE(_PIPEB_GMCH_DATA_N, dev_priv->regfile.savePIPEB_GMCH_DATA_N);
-                       I915_WRITE(_PIPEA_DP_LINK_M, dev_priv->regfile.savePIPEA_DP_LINK_M);
-                       I915_WRITE(_PIPEB_DP_LINK_M, dev_priv->regfile.savePIPEB_DP_LINK_M);
-                       I915_WRITE(_PIPEA_DP_LINK_N, dev_priv->regfile.savePIPEA_DP_LINK_N);
-                       I915_WRITE(_PIPEB_DP_LINK_N, dev_priv->regfile.savePIPEB_DP_LINK_N);
-               }
-       }
-
-       /* This is only meaningful in non-KMS mode */
-       /* Don't restore them in KMS mode */
-       i915_restore_modeset_reg(dev);
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               i915_restore_display_reg(dev);
 
        /* LVDS state */
        if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev))
@@ -763,16 +299,6 @@ static void i915_restore_display(struct drm_device *dev)
                I915_WRITE(PP_CONTROL, dev_priv->regfile.savePP_CONTROL);
        }
 
-       if (!drm_core_check_feature(dev, DRIVER_MODESET)) {
-               /* Display Port state */
-               if (SUPPORTS_INTEGRATED_DP(dev)) {
-                       I915_WRITE(DP_B, dev_priv->regfile.saveDP_B);
-                       I915_WRITE(DP_C, dev_priv->regfile.saveDP_C);
-                       I915_WRITE(DP_D, dev_priv->regfile.saveDP_D);
-               }
-               /* FIXME: restore TV & SDVO state */
-       }
-
        /* only restore FBC info on the platform that supports FBC*/
        intel_disable_fbc(dev);
        if (I915_HAS_FBC(dev)) {
@@ -787,19 +313,11 @@ static void i915_restore_display(struct drm_device *dev)
                        I915_WRITE(FBC_CONTROL, dev_priv->regfile.saveFBC_CONTROL);
                }
        }
-       /* VGA state */
-       if (HAS_PCH_SPLIT(dev))
-               I915_WRITE(CPU_VGACNTRL, dev_priv->regfile.saveVGACNTRL);
-       else
-               I915_WRITE(VGACNTRL, dev_priv->regfile.saveVGACNTRL);
 
-       I915_WRITE(VGA0, dev_priv->regfile.saveVGA0);
-       I915_WRITE(VGA1, dev_priv->regfile.saveVGA1);
-       I915_WRITE(VGA_PD, dev_priv->regfile.saveVGA_PD);
-       POSTING_READ(VGA_PD);
-       udelay(150);
-
-       i915_restore_vga(dev);
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               i915_restore_vga(dev);
+       else
+               i915_redisable_vga(dev);
 }
 
 int i915_save_state(struct drm_device *dev)
diff --git a/drivers/gpu/drm/i915/i915_ums.c b/drivers/gpu/drm/i915/i915_ums.c
new file mode 100644 (file)
index 0000000..985a097
--- /dev/null
@@ -0,0 +1,503 @@
+/*
+ *
+ * Copyright 2008 (c) Intel Corporation
+ *   Jesse Barnes <jbarnes@virtuousgeek.org>
+ * Copyright 2013 (c) Intel Corporation
+ *   Daniel Vetter <daniel.vetter@ffwll.ch>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <drm/drmP.h>
+#include <drm/i915_drm.h>
+#include "intel_drv.h"
+#include "i915_reg.h"
+
+static bool i915_pipe_enabled(struct drm_device *dev, enum pipe pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32     dpll_reg;
+
+       /* On IVB, 3rd pipe shares PLL with another one */
+       if (pipe > 1)
+               return false;
+
+       if (HAS_PCH_SPLIT(dev))
+               dpll_reg = _PCH_DPLL(pipe);
+       else
+               dpll_reg = (pipe == PIPE_A) ? _DPLL_A : _DPLL_B;
+
+       return (I915_READ(dpll_reg) & DPLL_VCO_ENABLE);
+}
+
+static void i915_save_palette(struct drm_device *dev, enum pipe pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       unsigned long reg = (pipe == PIPE_A ? _PALETTE_A : _PALETTE_B);
+       u32 *array;
+       int i;
+
+       if (!i915_pipe_enabled(dev, pipe))
+               return;
+
+       if (HAS_PCH_SPLIT(dev))
+               reg = (pipe == PIPE_A) ? _LGC_PALETTE_A : _LGC_PALETTE_B;
+
+       if (pipe == PIPE_A)
+               array = dev_priv->regfile.save_palette_a;
+       else
+               array = dev_priv->regfile.save_palette_b;
+
+       for (i = 0; i < 256; i++)
+               array[i] = I915_READ(reg + (i << 2));
+}
+
+static void i915_restore_palette(struct drm_device *dev, enum pipe pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       unsigned long reg = (pipe == PIPE_A ? _PALETTE_A : _PALETTE_B);
+       u32 *array;
+       int i;
+
+       if (!i915_pipe_enabled(dev, pipe))
+               return;
+
+       if (HAS_PCH_SPLIT(dev))
+               reg = (pipe == PIPE_A) ? _LGC_PALETTE_A : _LGC_PALETTE_B;
+
+       if (pipe == PIPE_A)
+               array = dev_priv->regfile.save_palette_a;
+       else
+               array = dev_priv->regfile.save_palette_b;
+
+       for (i = 0; i < 256; i++)
+               I915_WRITE(reg + (i << 2), array[i]);
+}
+
+void i915_save_display_reg(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int i;
+
+       /* Cursor state */
+       dev_priv->regfile.saveCURACNTR = I915_READ(_CURACNTR);
+       dev_priv->regfile.saveCURAPOS = I915_READ(_CURAPOS);
+       dev_priv->regfile.saveCURABASE = I915_READ(_CURABASE);
+       dev_priv->regfile.saveCURBCNTR = I915_READ(_CURBCNTR);
+       dev_priv->regfile.saveCURBPOS = I915_READ(_CURBPOS);
+       dev_priv->regfile.saveCURBBASE = I915_READ(_CURBBASE);
+       if (IS_GEN2(dev))
+               dev_priv->regfile.saveCURSIZE = I915_READ(CURSIZE);
+
+       if (HAS_PCH_SPLIT(dev)) {
+               dev_priv->regfile.savePCH_DREF_CONTROL = I915_READ(PCH_DREF_CONTROL);
+               dev_priv->regfile.saveDISP_ARB_CTL = I915_READ(DISP_ARB_CTL);
+       }
+
+       /* Pipe & plane A info */
+       dev_priv->regfile.savePIPEACONF = I915_READ(_PIPEACONF);
+       dev_priv->regfile.savePIPEASRC = I915_READ(_PIPEASRC);
+       if (HAS_PCH_SPLIT(dev)) {
+               dev_priv->regfile.saveFPA0 = I915_READ(_PCH_FPA0);
+               dev_priv->regfile.saveFPA1 = I915_READ(_PCH_FPA1);
+               dev_priv->regfile.saveDPLL_A = I915_READ(_PCH_DPLL_A);
+       } else {
+               dev_priv->regfile.saveFPA0 = I915_READ(_FPA0);
+               dev_priv->regfile.saveFPA1 = I915_READ(_FPA1);
+               dev_priv->regfile.saveDPLL_A = I915_READ(_DPLL_A);
+       }
+       if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev))
+               dev_priv->regfile.saveDPLL_A_MD = I915_READ(_DPLL_A_MD);
+       dev_priv->regfile.saveHTOTAL_A = I915_READ(_HTOTAL_A);
+       dev_priv->regfile.saveHBLANK_A = I915_READ(_HBLANK_A);
+       dev_priv->regfile.saveHSYNC_A = I915_READ(_HSYNC_A);
+       dev_priv->regfile.saveVTOTAL_A = I915_READ(_VTOTAL_A);
+       dev_priv->regfile.saveVBLANK_A = I915_READ(_VBLANK_A);
+       dev_priv->regfile.saveVSYNC_A = I915_READ(_VSYNC_A);
+       if (!HAS_PCH_SPLIT(dev))
+               dev_priv->regfile.saveBCLRPAT_A = I915_READ(_BCLRPAT_A);
+
+       if (HAS_PCH_SPLIT(dev)) {
+               dev_priv->regfile.savePIPEA_DATA_M1 = I915_READ(_PIPEA_DATA_M1);
+               dev_priv->regfile.savePIPEA_DATA_N1 = I915_READ(_PIPEA_DATA_N1);
+               dev_priv->regfile.savePIPEA_LINK_M1 = I915_READ(_PIPEA_LINK_M1);
+               dev_priv->regfile.savePIPEA_LINK_N1 = I915_READ(_PIPEA_LINK_N1);
+
+               dev_priv->regfile.saveFDI_TXA_CTL = I915_READ(_FDI_TXA_CTL);
+               dev_priv->regfile.saveFDI_RXA_CTL = I915_READ(_FDI_RXA_CTL);
+
+               dev_priv->regfile.savePFA_CTL_1 = I915_READ(_PFA_CTL_1);
+               dev_priv->regfile.savePFA_WIN_SZ = I915_READ(_PFA_WIN_SZ);
+               dev_priv->regfile.savePFA_WIN_POS = I915_READ(_PFA_WIN_POS);
+
+               dev_priv->regfile.saveTRANSACONF = I915_READ(_TRANSACONF);
+               dev_priv->regfile.saveTRANS_HTOTAL_A = I915_READ(_TRANS_HTOTAL_A);
+               dev_priv->regfile.saveTRANS_HBLANK_A = I915_READ(_TRANS_HBLANK_A);
+               dev_priv->regfile.saveTRANS_HSYNC_A = I915_READ(_TRANS_HSYNC_A);
+               dev_priv->regfile.saveTRANS_VTOTAL_A = I915_READ(_TRANS_VTOTAL_A);
+               dev_priv->regfile.saveTRANS_VBLANK_A = I915_READ(_TRANS_VBLANK_A);
+               dev_priv->regfile.saveTRANS_VSYNC_A = I915_READ(_TRANS_VSYNC_A);
+       }
+
+       dev_priv->regfile.saveDSPACNTR = I915_READ(_DSPACNTR);
+       dev_priv->regfile.saveDSPASTRIDE = I915_READ(_DSPASTRIDE);
+       dev_priv->regfile.saveDSPASIZE = I915_READ(_DSPASIZE);
+       dev_priv->regfile.saveDSPAPOS = I915_READ(_DSPAPOS);
+       dev_priv->regfile.saveDSPAADDR = I915_READ(_DSPAADDR);
+       if (INTEL_INFO(dev)->gen >= 4) {
+               dev_priv->regfile.saveDSPASURF = I915_READ(_DSPASURF);
+               dev_priv->regfile.saveDSPATILEOFF = I915_READ(_DSPATILEOFF);
+       }
+       i915_save_palette(dev, PIPE_A);
+       dev_priv->regfile.savePIPEASTAT = I915_READ(_PIPEASTAT);
+
+       /* Pipe & plane B info */
+       dev_priv->regfile.savePIPEBCONF = I915_READ(_PIPEBCONF);
+       dev_priv->regfile.savePIPEBSRC = I915_READ(_PIPEBSRC);
+       if (HAS_PCH_SPLIT(dev)) {
+               dev_priv->regfile.saveFPB0 = I915_READ(_PCH_FPB0);
+               dev_priv->regfile.saveFPB1 = I915_READ(_PCH_FPB1);
+               dev_priv->regfile.saveDPLL_B = I915_READ(_PCH_DPLL_B);
+       } else {
+               dev_priv->regfile.saveFPB0 = I915_READ(_FPB0);
+               dev_priv->regfile.saveFPB1 = I915_READ(_FPB1);
+               dev_priv->regfile.saveDPLL_B = I915_READ(_DPLL_B);
+       }
+       if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev))
+               dev_priv->regfile.saveDPLL_B_MD = I915_READ(_DPLL_B_MD);
+       dev_priv->regfile.saveHTOTAL_B = I915_READ(_HTOTAL_B);
+       dev_priv->regfile.saveHBLANK_B = I915_READ(_HBLANK_B);
+       dev_priv->regfile.saveHSYNC_B = I915_READ(_HSYNC_B);
+       dev_priv->regfile.saveVTOTAL_B = I915_READ(_VTOTAL_B);
+       dev_priv->regfile.saveVBLANK_B = I915_READ(_VBLANK_B);
+       dev_priv->regfile.saveVSYNC_B = I915_READ(_VSYNC_B);
+       if (!HAS_PCH_SPLIT(dev))
+               dev_priv->regfile.saveBCLRPAT_B = I915_READ(_BCLRPAT_B);
+
+       if (HAS_PCH_SPLIT(dev)) {
+               dev_priv->regfile.savePIPEB_DATA_M1 = I915_READ(_PIPEB_DATA_M1);
+               dev_priv->regfile.savePIPEB_DATA_N1 = I915_READ(_PIPEB_DATA_N1);
+               dev_priv->regfile.savePIPEB_LINK_M1 = I915_READ(_PIPEB_LINK_M1);
+               dev_priv->regfile.savePIPEB_LINK_N1 = I915_READ(_PIPEB_LINK_N1);
+
+               dev_priv->regfile.saveFDI_TXB_CTL = I915_READ(_FDI_TXB_CTL);
+               dev_priv->regfile.saveFDI_RXB_CTL = I915_READ(_FDI_RXB_CTL);
+
+               dev_priv->regfile.savePFB_CTL_1 = I915_READ(_PFB_CTL_1);
+               dev_priv->regfile.savePFB_WIN_SZ = I915_READ(_PFB_WIN_SZ);
+               dev_priv->regfile.savePFB_WIN_POS = I915_READ(_PFB_WIN_POS);
+
+               dev_priv->regfile.saveTRANSBCONF = I915_READ(_TRANSBCONF);
+               dev_priv->regfile.saveTRANS_HTOTAL_B = I915_READ(_TRANS_HTOTAL_B);
+               dev_priv->regfile.saveTRANS_HBLANK_B = I915_READ(_TRANS_HBLANK_B);
+               dev_priv->regfile.saveTRANS_HSYNC_B = I915_READ(_TRANS_HSYNC_B);
+               dev_priv->regfile.saveTRANS_VTOTAL_B = I915_READ(_TRANS_VTOTAL_B);
+               dev_priv->regfile.saveTRANS_VBLANK_B = I915_READ(_TRANS_VBLANK_B);
+               dev_priv->regfile.saveTRANS_VSYNC_B = I915_READ(_TRANS_VSYNC_B);
+       }
+
+       dev_priv->regfile.saveDSPBCNTR = I915_READ(_DSPBCNTR);
+       dev_priv->regfile.saveDSPBSTRIDE = I915_READ(_DSPBSTRIDE);
+       dev_priv->regfile.saveDSPBSIZE = I915_READ(_DSPBSIZE);
+       dev_priv->regfile.saveDSPBPOS = I915_READ(_DSPBPOS);
+       dev_priv->regfile.saveDSPBADDR = I915_READ(_DSPBADDR);
+       if (INTEL_INFO(dev)->gen >= 4) {
+               dev_priv->regfile.saveDSPBSURF = I915_READ(_DSPBSURF);
+               dev_priv->regfile.saveDSPBTILEOFF = I915_READ(_DSPBTILEOFF);
+       }
+       i915_save_palette(dev, PIPE_B);
+       dev_priv->regfile.savePIPEBSTAT = I915_READ(_PIPEBSTAT);
+
+       /* Fences */
+       switch (INTEL_INFO(dev)->gen) {
+       case 7:
+       case 6:
+               for (i = 0; i < 16; i++)
+                       dev_priv->regfile.saveFENCE[i] = I915_READ64(FENCE_REG_SANDYBRIDGE_0 + (i * 8));
+               break;
+       case 5:
+       case 4:
+               for (i = 0; i < 16; i++)
+                       dev_priv->regfile.saveFENCE[i] = I915_READ64(FENCE_REG_965_0 + (i * 8));
+               break;
+       case 3:
+               if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
+                       for (i = 0; i < 8; i++)
+                               dev_priv->regfile.saveFENCE[i+8] = I915_READ(FENCE_REG_945_8 + (i * 4));
+       case 2:
+               for (i = 0; i < 8; i++)
+                       dev_priv->regfile.saveFENCE[i] = I915_READ(FENCE_REG_830_0 + (i * 4));
+               break;
+       }
+
+       /* CRT state */
+       if (HAS_PCH_SPLIT(dev))
+               dev_priv->regfile.saveADPA = I915_READ(PCH_ADPA);
+       else
+               dev_priv->regfile.saveADPA = I915_READ(ADPA);
+
+       /* Display Port state */
+       if (SUPPORTS_INTEGRATED_DP(dev)) {
+               dev_priv->regfile.saveDP_B = I915_READ(DP_B);
+               dev_priv->regfile.saveDP_C = I915_READ(DP_C);
+               dev_priv->regfile.saveDP_D = I915_READ(DP_D);
+               dev_priv->regfile.savePIPEA_GMCH_DATA_M = I915_READ(_PIPEA_GMCH_DATA_M);
+               dev_priv->regfile.savePIPEB_GMCH_DATA_M = I915_READ(_PIPEB_GMCH_DATA_M);
+               dev_priv->regfile.savePIPEA_GMCH_DATA_N = I915_READ(_PIPEA_GMCH_DATA_N);
+               dev_priv->regfile.savePIPEB_GMCH_DATA_N = I915_READ(_PIPEB_GMCH_DATA_N);
+               dev_priv->regfile.savePIPEA_DP_LINK_M = I915_READ(_PIPEA_DP_LINK_M);
+               dev_priv->regfile.savePIPEB_DP_LINK_M = I915_READ(_PIPEB_DP_LINK_M);
+               dev_priv->regfile.savePIPEA_DP_LINK_N = I915_READ(_PIPEA_DP_LINK_N);
+               dev_priv->regfile.savePIPEB_DP_LINK_N = I915_READ(_PIPEB_DP_LINK_N);
+       }
+       /* FIXME: regfile.save TV & SDVO state */
+
+       return;
+}
+
+void i915_restore_display_reg(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int dpll_a_reg, fpa0_reg, fpa1_reg;
+       int dpll_b_reg, fpb0_reg, fpb1_reg;
+       int i;
+
+       /* Display port ratios (must be done before clock is set) */
+       if (SUPPORTS_INTEGRATED_DP(dev)) {
+               I915_WRITE(_PIPEA_GMCH_DATA_M, dev_priv->regfile.savePIPEA_GMCH_DATA_M);
+               I915_WRITE(_PIPEB_GMCH_DATA_M, dev_priv->regfile.savePIPEB_GMCH_DATA_M);
+               I915_WRITE(_PIPEA_GMCH_DATA_N, dev_priv->regfile.savePIPEA_GMCH_DATA_N);
+               I915_WRITE(_PIPEB_GMCH_DATA_N, dev_priv->regfile.savePIPEB_GMCH_DATA_N);
+               I915_WRITE(_PIPEA_DP_LINK_M, dev_priv->regfile.savePIPEA_DP_LINK_M);
+               I915_WRITE(_PIPEB_DP_LINK_M, dev_priv->regfile.savePIPEB_DP_LINK_M);
+               I915_WRITE(_PIPEA_DP_LINK_N, dev_priv->regfile.savePIPEA_DP_LINK_N);
+               I915_WRITE(_PIPEB_DP_LINK_N, dev_priv->regfile.savePIPEB_DP_LINK_N);
+       }
+
+       /* Fences */
+       switch (INTEL_INFO(dev)->gen) {
+       case 7:
+       case 6:
+               for (i = 0; i < 16; i++)
+                       I915_WRITE64(FENCE_REG_SANDYBRIDGE_0 + (i * 8), dev_priv->regfile.saveFENCE[i]);
+               break;
+       case 5:
+       case 4:
+               for (i = 0; i < 16; i++)
+                       I915_WRITE64(FENCE_REG_965_0 + (i * 8), dev_priv->regfile.saveFENCE[i]);
+               break;
+       case 3:
+       case 2:
+               if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
+                       for (i = 0; i < 8; i++)
+                               I915_WRITE(FENCE_REG_945_8 + (i * 4), dev_priv->regfile.saveFENCE[i+8]);
+               for (i = 0; i < 8; i++)
+                       I915_WRITE(FENCE_REG_830_0 + (i * 4), dev_priv->regfile.saveFENCE[i]);
+               break;
+       }
+
+
+       if (HAS_PCH_SPLIT(dev)) {
+               dpll_a_reg = _PCH_DPLL_A;
+               dpll_b_reg = _PCH_DPLL_B;
+               fpa0_reg = _PCH_FPA0;
+               fpb0_reg = _PCH_FPB0;
+               fpa1_reg = _PCH_FPA1;
+               fpb1_reg = _PCH_FPB1;
+       } else {
+               dpll_a_reg = _DPLL_A;
+               dpll_b_reg = _DPLL_B;
+               fpa0_reg = _FPA0;
+               fpb0_reg = _FPB0;
+               fpa1_reg = _FPA1;
+               fpb1_reg = _FPB1;
+       }
+
+       if (HAS_PCH_SPLIT(dev)) {
+               I915_WRITE(PCH_DREF_CONTROL, dev_priv->regfile.savePCH_DREF_CONTROL);
+               I915_WRITE(DISP_ARB_CTL, dev_priv->regfile.saveDISP_ARB_CTL);
+       }
+
+       /* Pipe & plane A info */
+       /* Prime the clock */
+       if (dev_priv->regfile.saveDPLL_A & DPLL_VCO_ENABLE) {
+               I915_WRITE(dpll_a_reg, dev_priv->regfile.saveDPLL_A &
+                          ~DPLL_VCO_ENABLE);
+               POSTING_READ(dpll_a_reg);
+               udelay(150);
+       }
+       I915_WRITE(fpa0_reg, dev_priv->regfile.saveFPA0);
+       I915_WRITE(fpa1_reg, dev_priv->regfile.saveFPA1);
+       /* Actually enable it */
+       I915_WRITE(dpll_a_reg, dev_priv->regfile.saveDPLL_A);
+       POSTING_READ(dpll_a_reg);
+       udelay(150);
+       if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) {
+               I915_WRITE(_DPLL_A_MD, dev_priv->regfile.saveDPLL_A_MD);
+               POSTING_READ(_DPLL_A_MD);
+       }
+       udelay(150);
+
+       /* Restore mode */
+       I915_WRITE(_HTOTAL_A, dev_priv->regfile.saveHTOTAL_A);
+       I915_WRITE(_HBLANK_A, dev_priv->regfile.saveHBLANK_A);
+       I915_WRITE(_HSYNC_A, dev_priv->regfile.saveHSYNC_A);
+       I915_WRITE(_VTOTAL_A, dev_priv->regfile.saveVTOTAL_A);
+       I915_WRITE(_VBLANK_A, dev_priv->regfile.saveVBLANK_A);
+       I915_WRITE(_VSYNC_A, dev_priv->regfile.saveVSYNC_A);
+       if (!HAS_PCH_SPLIT(dev))
+               I915_WRITE(_BCLRPAT_A, dev_priv->regfile.saveBCLRPAT_A);
+
+       if (HAS_PCH_SPLIT(dev)) {
+               I915_WRITE(_PIPEA_DATA_M1, dev_priv->regfile.savePIPEA_DATA_M1);
+               I915_WRITE(_PIPEA_DATA_N1, dev_priv->regfile.savePIPEA_DATA_N1);
+               I915_WRITE(_PIPEA_LINK_M1, dev_priv->regfile.savePIPEA_LINK_M1);
+               I915_WRITE(_PIPEA_LINK_N1, dev_priv->regfile.savePIPEA_LINK_N1);
+
+               I915_WRITE(_FDI_RXA_CTL, dev_priv->regfile.saveFDI_RXA_CTL);
+               I915_WRITE(_FDI_TXA_CTL, dev_priv->regfile.saveFDI_TXA_CTL);
+
+               I915_WRITE(_PFA_CTL_1, dev_priv->regfile.savePFA_CTL_1);
+               I915_WRITE(_PFA_WIN_SZ, dev_priv->regfile.savePFA_WIN_SZ);
+               I915_WRITE(_PFA_WIN_POS, dev_priv->regfile.savePFA_WIN_POS);
+
+               I915_WRITE(_TRANSACONF, dev_priv->regfile.saveTRANSACONF);
+               I915_WRITE(_TRANS_HTOTAL_A, dev_priv->regfile.saveTRANS_HTOTAL_A);
+               I915_WRITE(_TRANS_HBLANK_A, dev_priv->regfile.saveTRANS_HBLANK_A);
+               I915_WRITE(_TRANS_HSYNC_A, dev_priv->regfile.saveTRANS_HSYNC_A);
+               I915_WRITE(_TRANS_VTOTAL_A, dev_priv->regfile.saveTRANS_VTOTAL_A);
+               I915_WRITE(_TRANS_VBLANK_A, dev_priv->regfile.saveTRANS_VBLANK_A);
+               I915_WRITE(_TRANS_VSYNC_A, dev_priv->regfile.saveTRANS_VSYNC_A);
+       }
+
+       /* Restore plane info */
+       I915_WRITE(_DSPASIZE, dev_priv->regfile.saveDSPASIZE);
+       I915_WRITE(_DSPAPOS, dev_priv->regfile.saveDSPAPOS);
+       I915_WRITE(_PIPEASRC, dev_priv->regfile.savePIPEASRC);
+       I915_WRITE(_DSPAADDR, dev_priv->regfile.saveDSPAADDR);
+       I915_WRITE(_DSPASTRIDE, dev_priv->regfile.saveDSPASTRIDE);
+       if (INTEL_INFO(dev)->gen >= 4) {
+               I915_WRITE(_DSPASURF, dev_priv->regfile.saveDSPASURF);
+               I915_WRITE(_DSPATILEOFF, dev_priv->regfile.saveDSPATILEOFF);
+       }
+
+       I915_WRITE(_PIPEACONF, dev_priv->regfile.savePIPEACONF);
+
+       i915_restore_palette(dev, PIPE_A);
+       /* Enable the plane */
+       I915_WRITE(_DSPACNTR, dev_priv->regfile.saveDSPACNTR);
+       I915_WRITE(_DSPAADDR, I915_READ(_DSPAADDR));
+
+       /* Pipe & plane B info */
+       if (dev_priv->regfile.saveDPLL_B & DPLL_VCO_ENABLE) {
+               I915_WRITE(dpll_b_reg, dev_priv->regfile.saveDPLL_B &
+                          ~DPLL_VCO_ENABLE);
+               POSTING_READ(dpll_b_reg);
+               udelay(150);
+       }
+       I915_WRITE(fpb0_reg, dev_priv->regfile.saveFPB0);
+       I915_WRITE(fpb1_reg, dev_priv->regfile.saveFPB1);
+       /* Actually enable it */
+       I915_WRITE(dpll_b_reg, dev_priv->regfile.saveDPLL_B);
+       POSTING_READ(dpll_b_reg);
+       udelay(150);
+       if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) {
+               I915_WRITE(_DPLL_B_MD, dev_priv->regfile.saveDPLL_B_MD);
+               POSTING_READ(_DPLL_B_MD);
+       }
+       udelay(150);
+
+       /* Restore mode */
+       I915_WRITE(_HTOTAL_B, dev_priv->regfile.saveHTOTAL_B);
+       I915_WRITE(_HBLANK_B, dev_priv->regfile.saveHBLANK_B);
+       I915_WRITE(_HSYNC_B, dev_priv->regfile.saveHSYNC_B);
+       I915_WRITE(_VTOTAL_B, dev_priv->regfile.saveVTOTAL_B);
+       I915_WRITE(_VBLANK_B, dev_priv->regfile.saveVBLANK_B);
+       I915_WRITE(_VSYNC_B, dev_priv->regfile.saveVSYNC_B);
+       if (!HAS_PCH_SPLIT(dev))
+               I915_WRITE(_BCLRPAT_B, dev_priv->regfile.saveBCLRPAT_B);
+
+       if (HAS_PCH_SPLIT(dev)) {
+               I915_WRITE(_PIPEB_DATA_M1, dev_priv->regfile.savePIPEB_DATA_M1);
+               I915_WRITE(_PIPEB_DATA_N1, dev_priv->regfile.savePIPEB_DATA_N1);
+               I915_WRITE(_PIPEB_LINK_M1, dev_priv->regfile.savePIPEB_LINK_M1);
+               I915_WRITE(_PIPEB_LINK_N1, dev_priv->regfile.savePIPEB_LINK_N1);
+
+               I915_WRITE(_FDI_RXB_CTL, dev_priv->regfile.saveFDI_RXB_CTL);
+               I915_WRITE(_FDI_TXB_CTL, dev_priv->regfile.saveFDI_TXB_CTL);
+
+               I915_WRITE(_PFB_CTL_1, dev_priv->regfile.savePFB_CTL_1);
+               I915_WRITE(_PFB_WIN_SZ, dev_priv->regfile.savePFB_WIN_SZ);
+               I915_WRITE(_PFB_WIN_POS, dev_priv->regfile.savePFB_WIN_POS);
+
+               I915_WRITE(_TRANSBCONF, dev_priv->regfile.saveTRANSBCONF);
+               I915_WRITE(_TRANS_HTOTAL_B, dev_priv->regfile.saveTRANS_HTOTAL_B);
+               I915_WRITE(_TRANS_HBLANK_B, dev_priv->regfile.saveTRANS_HBLANK_B);
+               I915_WRITE(_TRANS_HSYNC_B, dev_priv->regfile.saveTRANS_HSYNC_B);
+               I915_WRITE(_TRANS_VTOTAL_B, dev_priv->regfile.saveTRANS_VTOTAL_B);
+               I915_WRITE(_TRANS_VBLANK_B, dev_priv->regfile.saveTRANS_VBLANK_B);
+               I915_WRITE(_TRANS_VSYNC_B, dev_priv->regfile.saveTRANS_VSYNC_B);
+       }
+
+       /* Restore plane info */
+       I915_WRITE(_DSPBSIZE, dev_priv->regfile.saveDSPBSIZE);
+       I915_WRITE(_DSPBPOS, dev_priv->regfile.saveDSPBPOS);
+       I915_WRITE(_PIPEBSRC, dev_priv->regfile.savePIPEBSRC);
+       I915_WRITE(_DSPBADDR, dev_priv->regfile.saveDSPBADDR);
+       I915_WRITE(_DSPBSTRIDE, dev_priv->regfile.saveDSPBSTRIDE);
+       if (INTEL_INFO(dev)->gen >= 4) {
+               I915_WRITE(_DSPBSURF, dev_priv->regfile.saveDSPBSURF);
+               I915_WRITE(_DSPBTILEOFF, dev_priv->regfile.saveDSPBTILEOFF);
+       }
+
+       I915_WRITE(_PIPEBCONF, dev_priv->regfile.savePIPEBCONF);
+
+       i915_restore_palette(dev, PIPE_B);
+       /* Enable the plane */
+       I915_WRITE(_DSPBCNTR, dev_priv->regfile.saveDSPBCNTR);
+       I915_WRITE(_DSPBADDR, I915_READ(_DSPBADDR));
+
+       /* Cursor state */
+       I915_WRITE(_CURAPOS, dev_priv->regfile.saveCURAPOS);
+       I915_WRITE(_CURACNTR, dev_priv->regfile.saveCURACNTR);
+       I915_WRITE(_CURABASE, dev_priv->regfile.saveCURABASE);
+       I915_WRITE(_CURBPOS, dev_priv->regfile.saveCURBPOS);
+       I915_WRITE(_CURBCNTR, dev_priv->regfile.saveCURBCNTR);
+       I915_WRITE(_CURBBASE, dev_priv->regfile.saveCURBBASE);
+       if (IS_GEN2(dev))
+               I915_WRITE(CURSIZE, dev_priv->regfile.saveCURSIZE);
+
+       /* CRT state */
+       if (HAS_PCH_SPLIT(dev))
+               I915_WRITE(PCH_ADPA, dev_priv->regfile.saveADPA);
+       else
+               I915_WRITE(ADPA, dev_priv->regfile.saveADPA);
+
+       /* Display Port state */
+       if (SUPPORTS_INTEGRATED_DP(dev)) {
+               I915_WRITE(DP_B, dev_priv->regfile.saveDP_B);
+               I915_WRITE(DP_C, dev_priv->regfile.saveDP_C);
+               I915_WRITE(DP_D, dev_priv->regfile.saveDP_D);
+       }
+       /* FIXME: restore TV & SDVO state */
+
+       return;
+}
index 9293878ec7eb53165e259e1607a57e23519dbaa7..969d08c72d1023c89116234fcf1844622d0f1081 100644 (file)
@@ -267,27 +267,27 @@ static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector)
 
                crt->force_hotplug_required = 0;
 
-               save_adpa = adpa = I915_READ(PCH_ADPA);
+               save_adpa = adpa = I915_READ(crt->adpa_reg);
                DRM_DEBUG_KMS("trigger hotplug detect cycle: adpa=0x%x\n", adpa);
 
                adpa |= ADPA_CRT_HOTPLUG_FORCE_TRIGGER;
                if (turn_off_dac)
                        adpa &= ~ADPA_DAC_ENABLE;
 
-               I915_WRITE(PCH_ADPA, adpa);
+               I915_WRITE(crt->adpa_reg, adpa);
 
-               if (wait_for((I915_READ(PCH_ADPA) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) == 0,
+               if (wait_for((I915_READ(crt->adpa_reg) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) == 0,
                             1000))
                        DRM_DEBUG_KMS("timed out waiting for FORCE_TRIGGER");
 
                if (turn_off_dac) {
-                       I915_WRITE(PCH_ADPA, save_adpa);
-                       POSTING_READ(PCH_ADPA);
+                       I915_WRITE(crt->adpa_reg, save_adpa);
+                       POSTING_READ(crt->adpa_reg);
                }
        }
 
        /* Check the status to see if both blue and green are on now */
-       adpa = I915_READ(PCH_ADPA);
+       adpa = I915_READ(crt->adpa_reg);
        if ((adpa & ADPA_CRT_HOTPLUG_MONITOR_MASK) != 0)
                ret = true;
        else
@@ -300,26 +300,27 @@ static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector)
 static bool valleyview_crt_detect_hotplug(struct drm_connector *connector)
 {
        struct drm_device *dev = connector->dev;
+       struct intel_crt *crt = intel_attached_crt(connector);
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 adpa;
        bool ret;
        u32 save_adpa;
 
-       save_adpa = adpa = I915_READ(ADPA);
+       save_adpa = adpa = I915_READ(crt->adpa_reg);
        DRM_DEBUG_KMS("trigger hotplug detect cycle: adpa=0x%x\n", adpa);
 
        adpa |= ADPA_CRT_HOTPLUG_FORCE_TRIGGER;
 
-       I915_WRITE(ADPA, adpa);
+       I915_WRITE(crt->adpa_reg, adpa);
 
-       if (wait_for((I915_READ(ADPA) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) == 0,
+       if (wait_for((I915_READ(crt->adpa_reg) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) == 0,
                     1000)) {
                DRM_DEBUG_KMS("timed out waiting for FORCE_TRIGGER");
-               I915_WRITE(ADPA, save_adpa);
+               I915_WRITE(crt->adpa_reg, save_adpa);
        }
 
        /* Check the status to see if both blue and green are on now */
-       adpa = I915_READ(ADPA);
+       adpa = I915_READ(crt->adpa_reg);
        if ((adpa & ADPA_CRT_HOTPLUG_MONITOR_MASK) != 0)
                ret = true;
        else
@@ -665,11 +666,11 @@ static void intel_crt_reset(struct drm_connector *connector)
        if (HAS_PCH_SPLIT(dev)) {
                u32 adpa;
 
-               adpa = I915_READ(PCH_ADPA);
+               adpa = I915_READ(crt->adpa_reg);
                adpa &= ~ADPA_CRT_HOTPLUG_MASK;
                adpa |= ADPA_HOTPLUG_BITS;
-               I915_WRITE(PCH_ADPA, adpa);
-               POSTING_READ(PCH_ADPA);
+               I915_WRITE(crt->adpa_reg, adpa);
+               POSTING_READ(crt->adpa_reg);
 
                DRM_DEBUG_KMS("pch crt adpa set to 0x%x\n", adpa);
                crt->force_hotplug_required = 1;
@@ -684,7 +685,6 @@ static void intel_crt_reset(struct drm_connector *connector)
 static const struct drm_encoder_helper_funcs crt_encoder_funcs = {
        .mode_fixup = intel_crt_mode_fixup,
        .mode_set = intel_crt_mode_set,
-       .disable = intel_encoder_noop,
 };
 
 static const struct drm_connector_funcs intel_crt_connector_funcs = {
@@ -776,7 +776,7 @@ void intel_crt_init(struct drm_device *dev)
 
        crt->base.disable = intel_disable_crt;
        crt->base.enable = intel_enable_crt;
-       if (IS_HASWELL(dev))
+       if (HAS_DDI(dev))
                crt->base.get_hw_state = intel_ddi_get_hw_state;
        else
                crt->base.get_hw_state = intel_crt_get_hw_state;
@@ -800,10 +800,14 @@ void intel_crt_init(struct drm_device *dev)
        dev_priv->hotplug_supported_mask |= CRT_HOTPLUG_INT_STATUS;
 
        /*
-        * TODO: find a proper way to discover whether we need to set the
-        * polarity reversal bit or not, instead of relying on the BIOS.
+        * TODO: find a proper way to discover whether we need to set the the
+        * polarity and link reversal bits or not, instead of relying on the
+        * BIOS.
         */
-       if (HAS_PCH_LPT(dev))
-               dev_priv->fdi_rx_polarity_reversed =
-                    !!(I915_READ(_FDI_RXA_CTL) & FDI_RX_POLARITY_REVERSED_LPT);
+       if (HAS_PCH_LPT(dev)) {
+               u32 fdi_config = FDI_RX_POLARITY_REVERSED_LPT |
+                                FDI_RX_LINK_REVERSAL_OVERRIDE;
+
+               dev_priv->fdi_rx_config = I915_READ(_FDI_RXA_CTL) & fdi_config;
+       }
 }
index 4bad0f724019561c1895517d81adf1067ef617b7..d64af5aa4a1caeccc1b5f5f6d85e68cc4e5c7fff 100644 (file)
@@ -84,7 +84,8 @@ static enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder)
  * in either FDI or DP modes only, as HDMI connections will work with both
  * of those
  */
-void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port, bool use_fdi_mode)
+static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port,
+                                     bool use_fdi_mode)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 reg;
@@ -114,16 +115,17 @@ void intel_prepare_ddi(struct drm_device *dev)
 {
        int port;
 
-       if (IS_HASWELL(dev)) {
-               for (port = PORT_A; port < PORT_E; port++)
-                       intel_prepare_ddi_buffers(dev, port, false);
+       if (!HAS_DDI(dev))
+               return;
 
-               /* DDI E is the suggested one to work in FDI mode, so program is as such by
-                * default. It will have to be re-programmed in case a digital DP output
-                * will be detected on it
-                */
-               intel_prepare_ddi_buffers(dev, PORT_E, true);
-       }
+       for (port = PORT_A; port < PORT_E; port++)
+               intel_prepare_ddi_buffers(dev, port, false);
+
+       /* DDI E is the suggested one to work in FDI mode, so program is as such
+        * by default. It will have to be re-programmed in case a digital DP
+        * output will be detected on it
+        */
+       intel_prepare_ddi_buffers(dev, PORT_E, true);
 }
 
 static const long hsw_ddi_buf_ctl_values[] = {
@@ -178,10 +180,8 @@ void hsw_fdi_link_train(struct drm_crtc *crtc)
                                  FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90);
 
        /* Enable the PCH Receiver FDI PLL */
-       rx_ctl_val = FDI_RX_PLL_ENABLE | FDI_RX_ENHANCE_FRAME_ENABLE |
-                    ((intel_crtc->fdi_lanes - 1) << 19);
-       if (dev_priv->fdi_rx_polarity_reversed)
-               rx_ctl_val |= FDI_RX_POLARITY_REVERSED_LPT;
+       rx_ctl_val = dev_priv->fdi_rx_config | FDI_RX_ENHANCE_FRAME_ENABLE |
+                    FDI_RX_PLL_ENABLE | ((intel_crtc->fdi_lanes - 1) << 19);
        I915_WRITE(_FDI_RXA_CTL, rx_ctl_val);
        POSTING_READ(_FDI_RXA_CTL);
        udelay(220);
@@ -203,7 +203,10 @@ void hsw_fdi_link_train(struct drm_crtc *crtc)
                                        DP_TP_CTL_LINK_TRAIN_PAT1 |
                                        DP_TP_CTL_ENABLE);
 
-               /* Configure and enable DDI_BUF_CTL for DDI E with next voltage */
+               /* Configure and enable DDI_BUF_CTL for DDI E with next voltage.
+                * DDI E does not support port reversal, the functionality is
+                * achieved on the PCH side in FDI_RX_CTL, so no need to set the
+                * port reversal bit */
                I915_WRITE(DDI_BUF_CTL(PORT_E),
                           DDI_BUF_CTL_ENABLE |
                           ((intel_crtc->fdi_lanes - 1) << 1) |
@@ -675,10 +678,14 @@ static void intel_ddi_mode_set(struct drm_encoder *encoder,
        DRM_DEBUG_KMS("Preparing DDI mode for Haswell on port %c, pipe %c\n",
                      port_name(port), pipe_name(pipe));
 
+       intel_crtc->eld_vld = false;
        if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
                struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+               struct intel_digital_port *intel_dig_port =
+                       enc_to_dig_port(encoder);
 
-               intel_dp->DP = DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW;
+               intel_dp->DP = intel_dig_port->port_reversal |
+                              DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW;
                switch (intel_dp->lane_count) {
                case 1:
                        intel_dp->DP |= DDI_PORT_WIDTH_X1;
@@ -985,7 +992,13 @@ void intel_ddi_enable_pipe_func(struct drm_crtc *crtc)
        if (cpu_transcoder == TRANSCODER_EDP) {
                switch (pipe) {
                case PIPE_A:
-                       temp |= TRANS_DDI_EDP_INPUT_A_ONOFF;
+                       /* Can only use the always-on power well for eDP when
+                        * not using the panel fitter, and when not using motion
+                         * blur mitigation (which we don't support). */
+                       if (dev_priv->pch_pf_size)
+                               temp |= TRANS_DDI_EDP_INPUT_A_ONOFF;
+                       else
+                               temp |= TRANS_DDI_EDP_INPUT_A_ON;
                        break;
                case PIPE_B:
                        temp |= TRANS_DDI_EDP_INPUT_B_ONOFF;
@@ -1069,7 +1082,7 @@ bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector)
        if (port == PORT_A)
                cpu_transcoder = TRANSCODER_EDP;
        else
-               cpu_transcoder = pipe;
+               cpu_transcoder = (enum transcoder) pipe;
 
        tmp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder));
 
@@ -1285,34 +1298,58 @@ static void intel_ddi_post_disable(struct intel_encoder *intel_encoder)
 static void intel_enable_ddi(struct intel_encoder *intel_encoder)
 {
        struct drm_encoder *encoder = &intel_encoder->base;
+       struct drm_crtc *crtc = encoder->crtc;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int pipe = intel_crtc->pipe;
        struct drm_device *dev = encoder->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        enum port port = intel_ddi_get_encoder_port(intel_encoder);
        int type = intel_encoder->type;
+       uint32_t tmp;
 
        if (type == INTEL_OUTPUT_HDMI) {
+               struct intel_digital_port *intel_dig_port =
+                       enc_to_dig_port(encoder);
+
                /* In HDMI/DVI mode, the port width, and swing/emphasis values
                 * are ignored so nothing special needs to be done besides
                 * enabling the port.
                 */
-               I915_WRITE(DDI_BUF_CTL(port), DDI_BUF_CTL_ENABLE);
+               I915_WRITE(DDI_BUF_CTL(port),
+                          intel_dig_port->port_reversal | DDI_BUF_CTL_ENABLE);
        } else if (type == INTEL_OUTPUT_EDP) {
                struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
 
                ironlake_edp_backlight_on(intel_dp);
        }
+
+       if (intel_crtc->eld_vld) {
+               tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD);
+               tmp |= ((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) << (pipe * 4));
+               I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp);
+       }
 }
 
 static void intel_disable_ddi(struct intel_encoder *intel_encoder)
 {
        struct drm_encoder *encoder = &intel_encoder->base;
+       struct drm_crtc *crtc = encoder->crtc;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int pipe = intel_crtc->pipe;
        int type = intel_encoder->type;
+       struct drm_device *dev = encoder->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t tmp;
 
        if (type == INTEL_OUTPUT_EDP) {
                struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
 
                ironlake_edp_backlight_off(intel_dp);
        }
+
+       tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD);
+       tmp &= ~((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) << (pipe * 4));
+       I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp);
 }
 
 int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv)
@@ -1452,11 +1489,11 @@ static const struct drm_encoder_funcs intel_ddi_funcs = {
 static const struct drm_encoder_helper_funcs intel_ddi_helper_funcs = {
        .mode_fixup = intel_ddi_mode_fixup,
        .mode_set = intel_ddi_mode_set,
-       .disable = intel_encoder_noop,
 };
 
 void intel_ddi_init(struct drm_device *dev, enum port port)
 {
+       struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_digital_port *intel_dig_port;
        struct intel_encoder *intel_encoder;
        struct drm_encoder *encoder;
@@ -1497,6 +1534,8 @@ void intel_ddi_init(struct drm_device *dev, enum port port)
        intel_encoder->get_hw_state = intel_ddi_get_hw_state;
 
        intel_dig_port->port = port;
+       intel_dig_port->port_reversal = I915_READ(DDI_BUF_CTL(port)) &
+                                       DDI_BUF_PORT_REVERSAL;
        if (hdmi_connector)
                intel_dig_port->hdmi.sdvox_reg = DDI_BUF_CTL(port);
        else
index da1ad9c80bb53722e7afd423b27a819f31ff6927..6337196b793116681f7f481640d739995760655e 100644 (file)
@@ -154,8 +154,8 @@ static const intel_limit_t intel_limits_i9xx_sdvo = {
        .vco = { .min = 1400000, .max = 2800000 },
        .n = { .min = 1, .max = 6 },
        .m = { .min = 70, .max = 120 },
-       .m1 = { .min = 10, .max = 22 },
-       .m2 = { .min = 5, .max = 9 },
+       .m1 = { .min = 8, .max = 18 },
+       .m2 = { .min = 3, .max = 7 },
        .p = { .min = 5, .max = 80 },
        .p1 = { .min = 1, .max = 8 },
        .p2 = { .dot_limit = 200000,
@@ -168,8 +168,8 @@ static const intel_limit_t intel_limits_i9xx_lvds = {
        .vco = { .min = 1400000, .max = 2800000 },
        .n = { .min = 1, .max = 6 },
        .m = { .min = 70, .max = 120 },
-       .m1 = { .min = 10, .max = 22 },
-       .m2 = { .min = 5, .max = 9 },
+       .m1 = { .min = 8, .max = 18 },
+       .m2 = { .min = 3, .max = 7 },
        .p = { .min = 7, .max = 98 },
        .p1 = { .min = 1, .max = 8 },
        .p2 = { .dot_limit = 112000,
@@ -416,13 +416,11 @@ static const intel_limit_t intel_limits_vlv_dp = {
 
 u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg)
 {
-       unsigned long flags;
-       u32 val = 0;
+       WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock));
 
-       spin_lock_irqsave(&dev_priv->dpio_lock, flags);
        if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) {
                DRM_ERROR("DPIO idle wait timed out\n");
-               goto out_unlock;
+               return 0;
        }
 
        I915_WRITE(DPIO_REG, reg);
@@ -430,24 +428,20 @@ u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg)
                   DPIO_BYTE);
        if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) {
                DRM_ERROR("DPIO read wait timed out\n");
-               goto out_unlock;
+               return 0;
        }
-       val = I915_READ(DPIO_DATA);
 
-out_unlock:
-       spin_unlock_irqrestore(&dev_priv->dpio_lock, flags);
-       return val;
+       return I915_READ(DPIO_DATA);
 }
 
 static void intel_dpio_write(struct drm_i915_private *dev_priv, int reg,
                             u32 val)
 {
-       unsigned long flags;
+       WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock));
 
-       spin_lock_irqsave(&dev_priv->dpio_lock, flags);
        if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) {
                DRM_ERROR("DPIO idle wait timed out\n");
-               goto out_unlock;
+               return;
        }
 
        I915_WRITE(DPIO_DATA, val);
@@ -456,9 +450,6 @@ static void intel_dpio_write(struct drm_i915_private *dev_priv, int reg,
                   DPIO_BYTE);
        if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100))
                DRM_ERROR("DPIO write wait timed out\n");
-
-out_unlock:
-       spin_unlock_irqrestore(&dev_priv->dpio_lock, flags);
 }
 
 static void vlv_init_dpio(struct drm_device *dev)
@@ -472,61 +463,14 @@ static void vlv_init_dpio(struct drm_device *dev)
        POSTING_READ(DPIO_CTL);
 }
 
-static int intel_dual_link_lvds_callback(const struct dmi_system_id *id)
-{
-       DRM_INFO("Forcing lvds to dual link mode on %s\n", id->ident);
-       return 1;
-}
-
-static const struct dmi_system_id intel_dual_link_lvds[] = {
-       {
-               .callback = intel_dual_link_lvds_callback,
-               .ident = "Apple MacBook Pro (Core i5/i7 Series)",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro8,2"),
-               },
-       },
-       { }     /* terminating entry */
-};
-
-static bool is_dual_link_lvds(struct drm_i915_private *dev_priv,
-                             unsigned int reg)
-{
-       unsigned int val;
-
-       /* use the module option value if specified */
-       if (i915_lvds_channel_mode > 0)
-               return i915_lvds_channel_mode == 2;
-
-       if (dmi_check_system(intel_dual_link_lvds))
-               return true;
-
-       if (dev_priv->lvds_val)
-               val = dev_priv->lvds_val;
-       else {
-               /* BIOS should set the proper LVDS register value at boot, but
-                * in reality, it doesn't set the value when the lid is closed;
-                * we need to check "the value to be set" in VBT when LVDS
-                * register is uninitialized.
-                */
-               val = I915_READ(reg);
-               if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED)))
-                       val = dev_priv->bios_lvds_val;
-               dev_priv->lvds_val = val;
-       }
-       return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP;
-}
-
 static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc,
                                                int refclk)
 {
        struct drm_device *dev = crtc->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
        const intel_limit_t *limit;
 
        if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
-               if (is_dual_link_lvds(dev_priv, PCH_LVDS)) {
+               if (intel_is_dual_link_lvds(dev)) {
                        /* LVDS dual channel */
                        if (refclk == 100000)
                                limit = &intel_limits_ironlake_dual_lvds_100m;
@@ -550,11 +494,10 @@ static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc,
 static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
        const intel_limit_t *limit;
 
        if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
-               if (is_dual_link_lvds(dev_priv, LVDS))
+               if (intel_is_dual_link_lvds(dev))
                        /* LVDS with dual channel */
                        limit = &intel_limits_g4x_dual_channel_lvds;
                else
@@ -686,19 +629,16 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
 
 {
        struct drm_device *dev = crtc->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
        intel_clock_t clock;
        int err = target;
 
-       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) &&
-           (I915_READ(LVDS)) != 0) {
+       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
                /*
-                * For LVDS, if the panel is on, just rely on its current
-                * settings for dual-channel.  We haven't figured out how to
-                * reliably set up different single/dual channel state, if we
-                * even can.
+                * For LVDS just rely on its current settings for dual-channel.
+                * We haven't figured out how to reliably set up different
+                * single/dual channel state, if we even can.
                 */
-               if (is_dual_link_lvds(dev_priv, LVDS))
+               if (intel_is_dual_link_lvds(dev))
                        clock.p2 = limit->p2.p2_fast;
                else
                        clock.p2 = limit->p2.p2_slow;
@@ -751,7 +691,6 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
                        intel_clock_t *best_clock)
 {
        struct drm_device *dev = crtc->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
        intel_clock_t clock;
        int max_n;
        bool found;
@@ -766,8 +705,7 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
                        lvds_reg = PCH_LVDS;
                else
                        lvds_reg = LVDS;
-               if ((I915_READ(lvds_reg) & LVDS_CLKB_POWER_MASK) ==
-                   LVDS_CLKB_POWER_UP)
+               if (intel_is_dual_link_lvds(dev))
                        clock.p2 = limit->p2.p2_fast;
                else
                        clock.p2 = limit->p2.p2_slow;
@@ -1047,6 +985,51 @@ void intel_wait_for_pipe_off(struct drm_device *dev, int pipe)
        }
 }
 
+/*
+ * ibx_digital_port_connected - is the specified port connected?
+ * @dev_priv: i915 private structure
+ * @port: the port to test
+ *
+ * Returns true if @port is connected, false otherwise.
+ */
+bool ibx_digital_port_connected(struct drm_i915_private *dev_priv,
+                               struct intel_digital_port *port)
+{
+       u32 bit;
+
+       if (HAS_PCH_IBX(dev_priv->dev)) {
+               switch(port->port) {
+               case PORT_B:
+                       bit = SDE_PORTB_HOTPLUG;
+                       break;
+               case PORT_C:
+                       bit = SDE_PORTC_HOTPLUG;
+                       break;
+               case PORT_D:
+                       bit = SDE_PORTD_HOTPLUG;
+                       break;
+               default:
+                       return true;
+               }
+       } else {
+               switch(port->port) {
+               case PORT_B:
+                       bit = SDE_PORTB_HOTPLUG_CPT;
+                       break;
+               case PORT_C:
+                       bit = SDE_PORTC_HOTPLUG_CPT;
+                       break;
+               case PORT_D:
+                       bit = SDE_PORTD_HOTPLUG_CPT;
+                       break;
+               default:
+                       return true;
+               }
+       }
+
+       return I915_READ(SDEISR) & bit;
+}
+
 static const char *state_string(bool enabled)
 {
        return enabled ? "on" : "off";
@@ -1125,8 +1108,8 @@ static void assert_fdi_tx(struct drm_i915_private *dev_priv,
        enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv,
                                                                      pipe);
 
-       if (IS_HASWELL(dev_priv->dev)) {
-               /* On Haswell, DDI is used instead of FDI_TX_CTL */
+       if (HAS_DDI(dev_priv->dev)) {
+               /* DDI does not have a specific FDI_TX register */
                reg = TRANS_DDI_FUNC_CTL(cpu_transcoder);
                val = I915_READ(reg);
                cur_state = !!(val & TRANS_DDI_FUNC_ENABLE);
@@ -1170,7 +1153,7 @@ static void assert_fdi_tx_pll_enabled(struct drm_i915_private *dev_priv,
                return;
 
        /* On Haswell, DDI ports are responsible for the FDI PLL setup */
-       if (IS_HASWELL(dev_priv->dev))
+       if (HAS_DDI(dev_priv->dev))
                return;
 
        reg = FDI_TX_CTL(pipe);
@@ -1231,9 +1214,15 @@ void assert_pipe(struct drm_i915_private *dev_priv,
        if (pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE)
                state = true;
 
-       reg = PIPECONF(cpu_transcoder);
-       val = I915_READ(reg);
-       cur_state = !!(val & PIPECONF_ENABLE);
+       if (IS_HASWELL(dev_priv->dev) && cpu_transcoder != TRANSCODER_EDP &&
+           !(I915_READ(HSW_PWR_WELL_DRIVER) & HSW_PWR_WELL_ENABLE)) {
+               cur_state = false;
+       } else {
+               reg = PIPECONF(cpu_transcoder);
+               val = I915_READ(reg);
+               cur_state = !!(val & PIPECONF_ENABLE);
+       }
+
        WARN(cur_state != state,
             "pipe %c assertion failure (expected %s, current %s)\n",
             pipe_name(pipe), state_string(state), state_string(cur_state));
@@ -1509,13 +1498,14 @@ static void
 intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value,
                enum intel_sbi_destination destination)
 {
-       unsigned long flags;
        u32 tmp;
 
-       spin_lock_irqsave(&dev_priv->dpio_lock, flags);
-       if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_BUSY) == 0, 100)) {
+       WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock));
+
+       if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_BUSY) == 0,
+                               100)) {
                DRM_ERROR("timeout waiting for SBI to become ready\n");
-               goto out_unlock;
+               return;
        }
 
        I915_WRITE(SBI_ADDR, (reg << 16));
@@ -1530,24 +1520,21 @@ intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value,
        if (wait_for((I915_READ(SBI_CTL_STAT) & (SBI_BUSY | SBI_RESPONSE_FAIL)) == 0,
                                100)) {
                DRM_ERROR("timeout waiting for SBI to complete write transaction\n");
-               goto out_unlock;
+               return;
        }
-
-out_unlock:
-       spin_unlock_irqrestore(&dev_priv->dpio_lock, flags);
 }
 
 static u32
 intel_sbi_read(struct drm_i915_private *dev_priv, u16 reg,
               enum intel_sbi_destination destination)
 {
-       unsigned long flags;
        u32 value = 0;
+       WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock));
 
-       spin_lock_irqsave(&dev_priv->dpio_lock, flags);
-       if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_BUSY) == 0, 100)) {
+       if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_BUSY) == 0,
+                               100)) {
                DRM_ERROR("timeout waiting for SBI to become ready\n");
-               goto out_unlock;
+               return 0;
        }
 
        I915_WRITE(SBI_ADDR, (reg << 16));
@@ -1561,14 +1548,10 @@ intel_sbi_read(struct drm_i915_private *dev_priv, u16 reg,
        if (wait_for((I915_READ(SBI_CTL_STAT) & (SBI_BUSY | SBI_RESPONSE_FAIL)) == 0,
                                100)) {
                DRM_ERROR("timeout waiting for SBI to complete read transaction\n");
-               goto out_unlock;
+               return 0;
        }
 
-       value = I915_READ(SBI_DATA);
-
-out_unlock:
-       spin_unlock_irqrestore(&dev_priv->dpio_lock, flags);
-       return value;
+       return I915_READ(SBI_DATA);
 }
 
 /**
@@ -1700,8 +1683,8 @@ static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv,
                 * make the BPC in transcoder be consistent with
                 * that in pipeconf reg.
                 */
-               val &= ~PIPE_BPC_MASK;
-               val |= pipeconf_val & PIPE_BPC_MASK;
+               val &= ~PIPECONF_BPC_MASK;
+               val |= pipeconf_val & PIPECONF_BPC_MASK;
        }
 
        val &= ~TRANS_INTERLACE_MASK;
@@ -1728,7 +1711,7 @@ static void lpt_enable_pch_transcoder(struct drm_i915_private *dev_priv,
        BUG_ON(dev_priv->info->gen < 5);
 
        /* FDI must be feeding us bits for PCH ports */
-       assert_fdi_tx_enabled(dev_priv, cpu_transcoder);
+       assert_fdi_tx_enabled(dev_priv, (enum pipe) cpu_transcoder);
        assert_fdi_rx_enabled(dev_priv, TRANSCODER_A);
 
        /* Workaround: set timing override bit. */
@@ -1816,11 +1799,11 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe,
 {
        enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv,
                                                                      pipe);
-       enum transcoder pch_transcoder;
+       enum pipe pch_transcoder;
        int reg;
        u32 val;
 
-       if (IS_HASWELL(dev_priv->dev))
+       if (HAS_PCH_LPT(dev_priv->dev))
                pch_transcoder = TRANSCODER_A;
        else
                pch_transcoder = pipe;
@@ -1836,7 +1819,8 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe,
                if (pch_port) {
                        /* if driving the PCH, we need FDI enabled */
                        assert_fdi_rx_pll_enabled(dev_priv, pch_transcoder);
-                       assert_fdi_tx_pll_enabled(dev_priv, cpu_transcoder);
+                       assert_fdi_tx_pll_enabled(dev_priv,
+                                                 (enum pipe) cpu_transcoder);
                }
                /* FIXME: assert CPU port conditions for SNB+ */
        }
@@ -2242,10 +2226,6 @@ intel_finish_fb(struct drm_framebuffer *old_fb)
        bool was_interruptible = dev_priv->mm.interruptible;
        int ret;
 
-       wait_event(dev_priv->pending_flip_queue,
-                  atomic_read(&dev_priv->mm.wedged) ||
-                  atomic_read(&obj->pending_flip) == 0);
-
        /* Big Hammer, we also need to ensure that any pending
         * MI_WAIT_FOR_EVENT inside a user batch buffer on the
         * current scanout is retired before unpinning the old
@@ -2350,43 +2330,6 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
        return 0;
 }
 
-static void ironlake_set_pll_edp(struct drm_crtc *crtc, int clock)
-{
-       struct drm_device *dev = crtc->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 dpa_ctl;
-
-       DRM_DEBUG_KMS("eDP PLL enable for clock %d\n", clock);
-       dpa_ctl = I915_READ(DP_A);
-       dpa_ctl &= ~DP_PLL_FREQ_MASK;
-
-       if (clock < 200000) {
-               u32 temp;
-               dpa_ctl |= DP_PLL_FREQ_160MHZ;
-               /* workaround for 160Mhz:
-                  1) program 0x4600c bits 15:0 = 0x8124
-                  2) program 0x46010 bit 0 = 1
-                  3) program 0x46034 bit 24 = 1
-                  4) program 0x64000 bit 14 = 1
-                  */
-               temp = I915_READ(0x4600c);
-               temp &= 0xffff0000;
-               I915_WRITE(0x4600c, temp | 0x8124);
-
-               temp = I915_READ(0x46010);
-               I915_WRITE(0x46010, temp | 1);
-
-               temp = I915_READ(0x46034);
-               I915_WRITE(0x46034, temp | (1 << 24));
-       } else {
-               dpa_ctl |= DP_PLL_FREQ_270MHZ;
-       }
-       I915_WRITE(DP_A, dpa_ctl);
-
-       POSTING_READ(DP_A);
-       udelay(500);
-}
-
 static void intel_fdi_normal_train(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
@@ -2815,7 +2758,7 @@ static void ironlake_fdi_pll_enable(struct intel_crtc *intel_crtc)
        temp = I915_READ(reg);
        temp &= ~((0x7 << 19) | (0x7 << 16));
        temp |= (intel_crtc->fdi_lanes - 1) << 19;
-       temp |= (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) << 11;
+       temp |= (I915_READ(PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11;
        I915_WRITE(reg, temp | FDI_RX_PLL_ENABLE);
 
        POSTING_READ(reg);
@@ -2828,18 +2771,14 @@ static void ironlake_fdi_pll_enable(struct intel_crtc *intel_crtc)
        POSTING_READ(reg);
        udelay(200);
 
-       /* On Haswell, the PLL configuration for ports and pipes is handled
-        * separately, as part of DDI setup */
-       if (!IS_HASWELL(dev)) {
-               /* Enable CPU FDI TX PLL, always on for Ironlake */
-               reg = FDI_TX_CTL(pipe);
-               temp = I915_READ(reg);
-               if ((temp & FDI_TX_PLL_ENABLE) == 0) {
-                       I915_WRITE(reg, temp | FDI_TX_PLL_ENABLE);
+       /* Enable CPU FDI TX PLL, always on for Ironlake */
+       reg = FDI_TX_CTL(pipe);
+       temp = I915_READ(reg);
+       if ((temp & FDI_TX_PLL_ENABLE) == 0) {
+               I915_WRITE(reg, temp | FDI_TX_PLL_ENABLE);
 
-                       POSTING_READ(reg);
-                       udelay(100);
-               }
+               POSTING_READ(reg);
+               udelay(100);
        }
 }
 
@@ -2889,7 +2828,7 @@ static void ironlake_fdi_disable(struct drm_crtc *crtc)
        reg = FDI_RX_CTL(pipe);
        temp = I915_READ(reg);
        temp &= ~(0x7 << 16);
-       temp |= (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) << 11;
+       temp |= (I915_READ(PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11;
        I915_WRITE(reg, temp & ~FDI_RX_ENABLE);
 
        POSTING_READ(reg);
@@ -2918,7 +2857,7 @@ static void ironlake_fdi_disable(struct drm_crtc *crtc)
        }
        /* BPC in FDI rx is consistent with that in PIPECONF */
        temp &= ~(0x07 << 16);
-       temp |= (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) << 11;
+       temp |= (I915_READ(PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11;
        I915_WRITE(reg, temp);
 
        POSTING_READ(reg);
@@ -2929,10 +2868,12 @@ static bool intel_crtc_has_pending_flip(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        unsigned long flags;
        bool pending;
 
-       if (atomic_read(&dev_priv->mm.wedged))
+       if (i915_reset_in_progress(&dev_priv->gpu_error) ||
+           intel_crtc->reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter))
                return false;
 
        spin_lock_irqsave(&dev->event_lock, flags);
@@ -2950,6 +2891,8 @@ static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc)
        if (crtc->fb == NULL)
                return;
 
+       WARN_ON(waitqueue_active(&dev_priv->pending_flip_queue));
+
        wait_event(dev_priv->pending_flip_queue,
                   !intel_crtc_has_pending_flip(crtc));
 
@@ -2992,6 +2935,8 @@ static void lpt_program_iclkip(struct drm_crtc *crtc)
        u32 divsel, phaseinc, auxdiv, phasedir = 0;
        u32 temp;
 
+       mutex_lock(&dev_priv->dpio_lock);
+
        /* It is necessary to ungate the pixclk gate prior to programming
         * the divisors, and gate it back when it is done.
         */
@@ -3066,6 +3011,8 @@ static void lpt_program_iclkip(struct drm_crtc *crtc)
        udelay(24);
 
        I915_WRITE(PIXCLK_GATE, PIXCLK_GATE_UNGATE);
+
+       mutex_unlock(&dev_priv->dpio_lock);
 }
 
 /*
@@ -3146,7 +3093,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
        if (HAS_PCH_CPT(dev) &&
            (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) ||
             intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))) {
-               u32 bpc = (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) >> 5;
+               u32 bpc = (I915_READ(PIPECONF(pipe)) & PIPECONF_BPC_MASK) >> 5;
                reg = TRANS_DP_CTL(pipe);
                temp = I915_READ(reg);
                temp &= ~(TRANS_DP_PORT_SEL_MASK |
@@ -3623,7 +3570,7 @@ static void haswell_crtc_off(struct drm_crtc *crtc)
 
        /* Stop saying we're using TRANSCODER_EDP because some other CRTC might
         * start using it. */
-       intel_crtc->cpu_transcoder = intel_crtc->pipe;
+       intel_crtc->cpu_transcoder = (enum transcoder) intel_crtc->pipe;
 
        intel_ddi_put_crtc_pll(crtc);
 }
@@ -3664,6 +3611,11 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
        intel_update_watermarks(dev);
 
        intel_enable_pll(dev_priv, pipe);
+
+       for_each_encoder_on_crtc(dev, crtc, encoder)
+               if (encoder->pre_enable)
+                       encoder->pre_enable(encoder);
+
        intel_enable_pipe(dev_priv, pipe, false);
        intel_enable_plane(dev_priv, plane, pipe);
 
@@ -3686,6 +3638,7 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
        struct intel_encoder *encoder;
        int pipe = intel_crtc->pipe;
        int plane = intel_crtc->plane;
+       u32 pctl;
 
 
        if (!intel_crtc->active)
@@ -3705,6 +3658,13 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
 
        intel_disable_plane(dev_priv, plane, pipe);
        intel_disable_pipe(dev_priv, pipe);
+
+       /* Disable pannel fitter if it is on this pipe. */
+       pctl = I915_READ(PFIT_CONTROL);
+       if ((pctl & PFIT_ENABLE) &&
+           ((pctl & PFIT_PIPE_MASK) >> PFIT_PIPE_SHIFT) == pipe)
+               I915_WRITE(PFIT_CONTROL, 0);
+
        intel_disable_pll(dev_priv, pipe);
 
        intel_crtc->active = false;
@@ -3767,19 +3727,17 @@ void intel_crtc_update_dpms(struct drm_crtc *crtc)
        intel_crtc_update_sarea(crtc, enable);
 }
 
-static void intel_crtc_noop(struct drm_crtc *crtc)
-{
-}
-
 static void intel_crtc_disable(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_connector *connector;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 
        /* crtc should still be enabled when we disable it. */
        WARN_ON(!crtc->enabled);
 
+       intel_crtc->eld_vld = false;
        dev_priv->display.crtc_disable(crtc);
        intel_crtc_update_sarea(crtc, false);
        dev_priv->display.off(crtc);
@@ -3817,10 +3775,6 @@ void intel_modeset_disable(struct drm_device *dev)
        }
 }
 
-void intel_encoder_noop(struct drm_encoder *encoder)
-{
-}
-
 void intel_encoder_destroy(struct drm_encoder *encoder)
 {
        struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
@@ -4012,16 +3966,8 @@ static int i830_get_display_clock_speed(struct drm_device *dev)
        return 133000;
 }
 
-struct fdi_m_n {
-       u32        tu;
-       u32        gmch_m;
-       u32        gmch_n;
-       u32        link_m;
-       u32        link_n;
-};
-
 static void
-fdi_reduce_ratio(u32 *num, u32 *den)
+intel_reduce_ratio(uint32_t *num, uint32_t *den)
 {
        while (*num > 0xffffff || *den > 0xffffff) {
                *num >>= 1;
@@ -4029,20 +3975,18 @@ fdi_reduce_ratio(u32 *num, u32 *den)
        }
 }
 
-static void
-ironlake_compute_m_n(int bits_per_pixel, int nlanes, int pixel_clock,
-                    int link_clock, struct fdi_m_n *m_n)
+void
+intel_link_compute_m_n(int bits_per_pixel, int nlanes,
+                      int pixel_clock, int link_clock,
+                      struct intel_link_m_n *m_n)
 {
-       m_n->tu = 64; /* default size */
-
-       /* BUG_ON(pixel_clock > INT_MAX / 36); */
+       m_n->tu = 64;
        m_n->gmch_m = bits_per_pixel * pixel_clock;
        m_n->gmch_n = link_clock * nlanes * 8;
-       fdi_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n);
-
+       intel_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n);
        m_n->link_m = pixel_clock;
        m_n->link_n = link_clock;
-       fdi_reduce_ratio(&m_n->link_m, &m_n->link_n);
+       intel_reduce_ratio(&m_n->link_m, &m_n->link_n);
 }
 
 static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv)
@@ -4289,51 +4233,6 @@ static void i9xx_update_pll_dividers(struct drm_crtc *crtc,
        }
 }
 
-static void intel_update_lvds(struct drm_crtc *crtc, intel_clock_t *clock,
-                             struct drm_display_mode *adjusted_mode)
-{
-       struct drm_device *dev = crtc->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       int pipe = intel_crtc->pipe;
-       u32 temp;
-
-       temp = I915_READ(LVDS);
-       temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP;
-       if (pipe == 1) {
-               temp |= LVDS_PIPEB_SELECT;
-       } else {
-               temp &= ~LVDS_PIPEB_SELECT;
-       }
-       /* set the corresponsding LVDS_BORDER bit */
-       temp |= dev_priv->lvds_border_bits;
-       /* Set the B0-B3 data pairs corresponding to whether we're going to
-        * set the DPLLs for dual-channel mode or not.
-        */
-       if (clock->p2 == 7)
-               temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP;
-       else
-               temp &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP);
-
-       /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP)
-        * appropriately here, but we need to look more thoroughly into how
-        * panels behave in the two modes.
-        */
-       /* set the dithering flag on LVDS as needed */
-       if (INTEL_INFO(dev)->gen >= 4) {
-               if (dev_priv->lvds_dither)
-                       temp |= LVDS_ENABLE_DITHER;
-               else
-                       temp &= ~LVDS_ENABLE_DITHER;
-       }
-       temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY);
-       if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
-               temp |= LVDS_HSYNC_POLARITY;
-       if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
-               temp |= LVDS_VSYNC_POLARITY;
-       I915_WRITE(LVDS, temp);
-}
-
 static void vlv_update_pll(struct drm_crtc *crtc,
                           struct drm_display_mode *mode,
                           struct drm_display_mode *adjusted_mode,
@@ -4349,6 +4248,8 @@ static void vlv_update_pll(struct drm_crtc *crtc,
        bool is_sdvo;
        u32 temp;
 
+       mutex_lock(&dev_priv->dpio_lock);
+
        is_sdvo = intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO) ||
                intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI);
 
@@ -4432,6 +4333,8 @@ static void vlv_update_pll(struct drm_crtc *crtc,
                        temp |= (1 << 21);
                intel_dpio_write(dev_priv, DPIO_DATA_CHANNEL2, temp);
        }
+
+       mutex_unlock(&dev_priv->dpio_lock);
 }
 
 static void i9xx_update_pll(struct drm_crtc *crtc,
@@ -4443,6 +4346,7 @@ static void i9xx_update_pll(struct drm_crtc *crtc,
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       struct intel_encoder *encoder;
        int pipe = intel_crtc->pipe;
        u32 dpll;
        bool is_sdvo;
@@ -4511,12 +4415,9 @@ static void i9xx_update_pll(struct drm_crtc *crtc,
        POSTING_READ(DPLL(pipe));
        udelay(150);
 
-       /* The LVDS pin pair needs to be on before the DPLLs are enabled.
-        * This is an exception to the general rule that mode_set doesn't turn
-        * things on.
-        */
-       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
-               intel_update_lvds(crtc, clock, adjusted_mode);
+       for_each_encoder_on_crtc(dev, crtc, encoder)
+               if (encoder->pre_pll_enable)
+                       encoder->pre_pll_enable(encoder);
 
        if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT))
                intel_dp_set_m_n(crtc, mode, adjusted_mode);
@@ -4555,6 +4456,7 @@ static void i8xx_update_pll(struct drm_crtc *crtc,
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       struct intel_encoder *encoder;
        int pipe = intel_crtc->pipe;
        u32 dpll;
 
@@ -4588,12 +4490,9 @@ static void i8xx_update_pll(struct drm_crtc *crtc,
        POSTING_READ(DPLL(pipe));
        udelay(150);
 
-       /* The LVDS pin pair needs to be on before the DPLLs are enabled.
-        * This is an exception to the general rule that mode_set doesn't turn
-        * things on.
-        */
-       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
-               intel_update_lvds(crtc, clock, adjusted_mode);
+       for_each_encoder_on_crtc(dev, crtc, encoder)
+               if (encoder->pre_pll_enable)
+                       encoder->pre_pll_enable(encoder);
 
        I915_WRITE(DPLL(pipe), dpll);
 
@@ -4783,10 +4682,10 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
        }
 
        /* default to 8bpc */
-       pipeconf &= ~(PIPECONF_BPP_MASK | PIPECONF_DITHER_EN);
+       pipeconf &= ~(PIPECONF_BPC_MASK | PIPECONF_DITHER_EN);
        if (is_dp) {
                if (adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC) {
-                       pipeconf |= PIPECONF_BPP_6 |
+                       pipeconf |= PIPECONF_6BPC |
                                    PIPECONF_DITHER_EN |
                                    PIPECONF_DITHER_TYPE_SP;
                }
@@ -4794,7 +4693,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
 
        if (IS_VALLEYVIEW(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) {
                if (adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC) {
-                       pipeconf |= PIPECONF_BPP_6 |
+                       pipeconf |= PIPECONF_6BPC |
                                        PIPECONF_ENABLE |
                                        I965_PIPECONF_ACTIVE;
                }
@@ -4981,6 +4880,8 @@ static void lpt_init_pch_refclk(struct drm_device *dev)
        if (!has_vga)
                return;
 
+       mutex_lock(&dev_priv->dpio_lock);
+
        /* XXX: Rip out SDV support once Haswell ships for real. */
        if (IS_HASWELL(dev) && (dev->pci_device & 0xFF00) == 0x0C00)
                is_sdv = true;
@@ -5123,6 +5024,8 @@ static void lpt_init_pch_refclk(struct drm_device *dev)
        tmp = intel_sbi_read(dev_priv, SBI_DBUFF0, SBI_ICLK);
        tmp |= SBI_DBUFF0_ENABLE;
        intel_sbi_write(dev_priv, SBI_DBUFF0, tmp, SBI_ICLK);
+
+       mutex_unlock(&dev_priv->dpio_lock);
 }
 
 /*
@@ -5177,19 +5080,19 @@ static void ironlake_set_pipeconf(struct drm_crtc *crtc,
 
        val = I915_READ(PIPECONF(pipe));
 
-       val &= ~PIPE_BPC_MASK;
+       val &= ~PIPECONF_BPC_MASK;
        switch (intel_crtc->bpp) {
        case 18:
-               val |= PIPE_6BPC;
+               val |= PIPECONF_6BPC;
                break;
        case 24:
-               val |= PIPE_8BPC;
+               val |= PIPECONF_8BPC;
                break;
        case 30:
-               val |= PIPE_10BPC;
+               val |= PIPECONF_10BPC;
                break;
        case 36:
-               val |= PIPE_12BPC;
+               val |= PIPECONF_12BPC;
                break;
        default:
                /* Case prevented by intel_choose_pipe_bpp_dither. */
@@ -5206,10 +5109,80 @@ static void ironlake_set_pipeconf(struct drm_crtc *crtc,
        else
                val |= PIPECONF_PROGRESSIVE;
 
+       if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE)
+               val |= PIPECONF_COLOR_RANGE_SELECT;
+       else
+               val &= ~PIPECONF_COLOR_RANGE_SELECT;
+
        I915_WRITE(PIPECONF(pipe), val);
        POSTING_READ(PIPECONF(pipe));
 }
 
+/*
+ * Set up the pipe CSC unit.
+ *
+ * Currently only full range RGB to limited range RGB conversion
+ * is supported, but eventually this should handle various
+ * RGB<->YCbCr scenarios as well.
+ */
+static void intel_set_pipe_csc(struct drm_crtc *crtc,
+                              const struct drm_display_mode *adjusted_mode)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int pipe = intel_crtc->pipe;
+       uint16_t coeff = 0x7800; /* 1.0 */
+
+       /*
+        * TODO: Check what kind of values actually come out of the pipe
+        * with these coeff/postoff values and adjust to get the best
+        * accuracy. Perhaps we even need to take the bpc value into
+        * consideration.
+        */
+
+       if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE)
+               coeff = ((235 - 16) * (1 << 12) / 255) & 0xff8; /* 0.xxx... */
+
+       /*
+        * GY/GU and RY/RU should be the other way around according
+        * to BSpec, but reality doesn't agree. Just set them up in
+        * a way that results in the correct picture.
+        */
+       I915_WRITE(PIPE_CSC_COEFF_RY_GY(pipe), coeff << 16);
+       I915_WRITE(PIPE_CSC_COEFF_BY(pipe), 0);
+
+       I915_WRITE(PIPE_CSC_COEFF_RU_GU(pipe), coeff);
+       I915_WRITE(PIPE_CSC_COEFF_BU(pipe), 0);
+
+       I915_WRITE(PIPE_CSC_COEFF_RV_GV(pipe), 0);
+       I915_WRITE(PIPE_CSC_COEFF_BV(pipe), coeff << 16);
+
+       I915_WRITE(PIPE_CSC_PREOFF_HI(pipe), 0);
+       I915_WRITE(PIPE_CSC_PREOFF_ME(pipe), 0);
+       I915_WRITE(PIPE_CSC_PREOFF_LO(pipe), 0);
+
+       if (INTEL_INFO(dev)->gen > 6) {
+               uint16_t postoff = 0;
+
+               if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE)
+                       postoff = (16 * (1 << 13) / 255) & 0x1fff;
+
+               I915_WRITE(PIPE_CSC_POSTOFF_HI(pipe), postoff);
+               I915_WRITE(PIPE_CSC_POSTOFF_ME(pipe), postoff);
+               I915_WRITE(PIPE_CSC_POSTOFF_LO(pipe), postoff);
+
+               I915_WRITE(PIPE_CSC_MODE(pipe), 0);
+       } else {
+               uint32_t mode = CSC_MODE_YUV_TO_RGB;
+
+               if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE)
+                       mode |= CSC_BLACK_SCREEN_OFFSET;
+
+               I915_WRITE(PIPE_CSC_MODE(pipe), mode);
+       }
+}
+
 static void haswell_set_pipeconf(struct drm_crtc *crtc,
                                 struct drm_display_mode *adjusted_mode,
                                 bool dither)
@@ -5400,7 +5373,7 @@ static void ironlake_set_m_n(struct drm_crtc *crtc,
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        enum transcoder cpu_transcoder = intel_crtc->cpu_transcoder;
        struct intel_encoder *intel_encoder, *edp_encoder = NULL;
-       struct fdi_m_n m_n = {0};
+       struct intel_link_m_n m_n = {0};
        int target_clock, pixel_multiplier, lane, link_bw;
        bool is_dp = false, is_cpu_edp = false;
 
@@ -5452,8 +5425,7 @@ static void ironlake_set_m_n(struct drm_crtc *crtc,
 
        if (pixel_multiplier > 1)
                link_bw *= pixel_multiplier;
-       ironlake_compute_m_n(intel_crtc->bpp, lane, target_clock, link_bw,
-                            &m_n);
+       intel_link_compute_m_n(intel_crtc->bpp, lane, target_clock, link_bw, &m_n);
 
        I915_WRITE(PIPE_DATA_M1(cpu_transcoder), TU_SIZE(m_n.tu) | m_n.gmch_m);
        I915_WRITE(PIPE_DATA_N1(cpu_transcoder), m_n.gmch_n);
@@ -5506,7 +5478,7 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc,
        if (is_lvds) {
                if ((intel_panel_use_ssc(dev_priv) &&
                     dev_priv->lvds_ssc_freq == 100) ||
-                   (I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP)
+                   intel_is_dual_link_lvds(dev))
                        factor = 25;
        } else if (is_sdvo && is_tv)
                factor = 20;
@@ -5581,7 +5553,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
        bool ok, has_reduced_clock = false;
        bool is_lvds = false, is_dp = false, is_cpu_edp = false;
        struct intel_encoder *encoder;
-       u32 temp;
        int ret;
        bool dither, fdi_config_ok;
 
@@ -5645,54 +5616,12 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
        } else
                intel_put_pch_pll(intel_crtc);
 
-       /* The LVDS pin pair needs to be on before the DPLLs are enabled.
-        * This is an exception to the general rule that mode_set doesn't turn
-        * things on.
-        */
-       if (is_lvds) {
-               temp = I915_READ(PCH_LVDS);
-               temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP;
-               if (HAS_PCH_CPT(dev)) {
-                       temp &= ~PORT_TRANS_SEL_MASK;
-                       temp |= PORT_TRANS_SEL_CPT(pipe);
-               } else {
-                       if (pipe == 1)
-                               temp |= LVDS_PIPEB_SELECT;
-                       else
-                               temp &= ~LVDS_PIPEB_SELECT;
-               }
-
-               /* set the corresponsding LVDS_BORDER bit */
-               temp |= dev_priv->lvds_border_bits;
-               /* Set the B0-B3 data pairs corresponding to whether we're going to
-                * set the DPLLs for dual-channel mode or not.
-                */
-               if (clock.p2 == 7)
-                       temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP;
-               else
-                       temp &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP);
-
-               /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP)
-                * appropriately here, but we need to look more thoroughly into how
-                * panels behave in the two modes.
-                */
-               temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY);
-               if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
-                       temp |= LVDS_HSYNC_POLARITY;
-               if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
-                       temp |= LVDS_VSYNC_POLARITY;
-               I915_WRITE(PCH_LVDS, temp);
-       }
-
-       if (is_dp && !is_cpu_edp) {
+       if (is_dp && !is_cpu_edp)
                intel_dp_set_m_n(crtc, mode, adjusted_mode);
-       } else {
-               /* For non-DP output, clear any trans DP clock recovery setting.*/
-               I915_WRITE(TRANSDATA_M1(pipe), 0);
-               I915_WRITE(TRANSDATA_N1(pipe), 0);
-               I915_WRITE(TRANSDPLINK_M1(pipe), 0);
-               I915_WRITE(TRANSDPLINK_N1(pipe), 0);
-       }
+
+       for_each_encoder_on_crtc(dev, crtc, encoder)
+               if (encoder->pre_pll_enable)
+                       encoder->pre_pll_enable(encoder);
 
        if (intel_crtc->pch_pll) {
                I915_WRITE(intel_crtc->pch_pll->pll_reg, dpll);
@@ -5727,9 +5656,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
 
        fdi_config_ok = ironlake_check_fdi_lanes(intel_crtc);
 
-       if (is_cpu_edp)
-               ironlake_set_pll_edp(crtc, adjusted_mode->clock);
-
        ironlake_set_pipeconf(crtc, adjusted_mode, dither);
 
        intel_wait_for_vblank(dev, pipe);
@@ -5747,6 +5673,35 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
        return fdi_config_ok ? ret : -EINVAL;
 }
 
+static void haswell_modeset_global_resources(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       bool enable = false;
+       struct intel_crtc *crtc;
+       struct intel_encoder *encoder;
+
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
+               if (crtc->pipe != PIPE_A && crtc->base.enabled)
+                       enable = true;
+               /* XXX: Should check for edp transcoder here, but thanks to init
+                * sequence that's not yet available. Just in case desktop eDP
+                * on PORT D is possible on haswell, too. */
+       }
+
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list,
+                           base.head) {
+               if (encoder->type != INTEL_OUTPUT_EDP &&
+                   encoder->connectors_active)
+                       enable = true;
+       }
+
+       /* Even the eDP panel fitter is outside the always-on well. */
+       if (dev_priv->pch_pf_size)
+               enable = true;
+
+       intel_set_power_well(dev, enable);
+}
+
 static int haswell_crtc_mode_set(struct drm_crtc *crtc,
                                 struct drm_display_mode *mode,
                                 struct drm_display_mode *adjusted_mode,
@@ -5759,20 +5714,13 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc,
        int pipe = intel_crtc->pipe;
        int plane = intel_crtc->plane;
        int num_connectors = 0;
-       intel_clock_t clock, reduced_clock;
-       u32 dpll = 0, fp = 0, fp2 = 0;
-       bool ok, has_reduced_clock = false;
-       bool is_lvds = false, is_dp = false, is_cpu_edp = false;
+       bool is_dp = false, is_cpu_edp = false;
        struct intel_encoder *encoder;
-       u32 temp;
        int ret;
        bool dither;
 
        for_each_encoder_on_crtc(dev, crtc, encoder) {
                switch (encoder->type) {
-               case INTEL_OUTPUT_LVDS:
-                       is_lvds = true;
-                       break;
                case INTEL_OUTPUT_DISPLAYPORT:
                        is_dp = true;
                        break;
@@ -5786,11 +5734,6 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc,
                num_connectors++;
        }
 
-       if (is_cpu_edp)
-               intel_crtc->cpu_transcoder = TRANSCODER_EDP;
-       else
-               intel_crtc->cpu_transcoder = pipe;
-
        /* We are not sure yet this won't happen. */
        WARN(!HAS_PCH_LPT(dev), "Unexpected PCH type %d\n",
             INTEL_PCH_TYPE(dev));
@@ -5806,147 +5749,32 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc,
        if (!intel_ddi_pll_mode_set(crtc, adjusted_mode->clock))
                return -EINVAL;
 
-       if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) {
-               ok = ironlake_compute_clocks(crtc, adjusted_mode, &clock,
-                                            &has_reduced_clock,
-                                            &reduced_clock);
-               if (!ok) {
-                       DRM_ERROR("Couldn't find PLL settings for mode!\n");
-                       return -EINVAL;
-               }
-       }
-
        /* Ensure that the cursor is valid for the new mode before changing... */
        intel_crtc_update_cursor(crtc, true);
 
        /* determine panel color depth */
        dither = intel_choose_pipe_bpp_dither(crtc, fb, &intel_crtc->bpp,
                                              adjusted_mode);
-       if (is_lvds && dev_priv->lvds_dither)
-               dither = true;
 
        DRM_DEBUG_KMS("Mode for pipe %d:\n", pipe);
        drm_mode_debug_printmodeline(mode);
 
-       if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) {
-               fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
-               if (has_reduced_clock)
-                       fp2 = reduced_clock.n << 16 | reduced_clock.m1 << 8 |
-                             reduced_clock.m2;
-
-               dpll = ironlake_compute_dpll(intel_crtc, adjusted_mode, &clock,
-                                            fp);
-
-               /* CPU eDP is the only output that doesn't need a PCH PLL of its
-                * own on pre-Haswell/LPT generation */
-               if (!is_cpu_edp) {
-                       struct intel_pch_pll *pll;
-
-                       pll = intel_get_pch_pll(intel_crtc, dpll, fp);
-                       if (pll == NULL) {
-                               DRM_DEBUG_DRIVER("failed to find PLL for pipe %d\n",
-                                                pipe);
-                               return -EINVAL;
-                       }
-               } else
-                       intel_put_pch_pll(intel_crtc);
-
-               /* The LVDS pin pair needs to be on before the DPLLs are
-                * enabled.  This is an exception to the general rule that
-                * mode_set doesn't turn things on.
-                */
-               if (is_lvds) {
-                       temp = I915_READ(PCH_LVDS);
-                       temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP;
-                       if (HAS_PCH_CPT(dev)) {
-                               temp &= ~PORT_TRANS_SEL_MASK;
-                               temp |= PORT_TRANS_SEL_CPT(pipe);
-                       } else {
-                               if (pipe == 1)
-                                       temp |= LVDS_PIPEB_SELECT;
-                               else
-                                       temp &= ~LVDS_PIPEB_SELECT;
-                       }
-
-                       /* set the corresponsding LVDS_BORDER bit */
-                       temp |= dev_priv->lvds_border_bits;
-                       /* Set the B0-B3 data pairs corresponding to whether
-                        * we're going to set the DPLLs for dual-channel mode or
-                        * not.
-                        */
-                       if (clock.p2 == 7)
-                               temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP;
-                       else
-                               temp &= ~(LVDS_B0B3_POWER_UP |
-                                         LVDS_CLKB_POWER_UP);
-
-                       /* It would be nice to set 24 vs 18-bit mode
-                        * (LVDS_A3_POWER_UP) appropriately here, but we need to
-                        * look more thoroughly into how panels behave in the
-                        * two modes.
-                        */
-                       temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY);
-                       if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
-                               temp |= LVDS_HSYNC_POLARITY;
-                       if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
-                               temp |= LVDS_VSYNC_POLARITY;
-                       I915_WRITE(PCH_LVDS, temp);
-               }
-       }
-
-       if (is_dp && !is_cpu_edp) {
+       if (is_dp && !is_cpu_edp)
                intel_dp_set_m_n(crtc, mode, adjusted_mode);
-       } else {
-               if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) {
-                       /* For non-DP output, clear any trans DP clock recovery
-                        * setting.*/
-                       I915_WRITE(TRANSDATA_M1(pipe), 0);
-                       I915_WRITE(TRANSDATA_N1(pipe), 0);
-                       I915_WRITE(TRANSDPLINK_M1(pipe), 0);
-                       I915_WRITE(TRANSDPLINK_N1(pipe), 0);
-               }
-       }
 
        intel_crtc->lowfreq_avail = false;
-       if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) {
-               if (intel_crtc->pch_pll) {
-                       I915_WRITE(intel_crtc->pch_pll->pll_reg, dpll);
-
-                       /* Wait for the clocks to stabilize. */
-                       POSTING_READ(intel_crtc->pch_pll->pll_reg);
-                       udelay(150);
-
-                       /* The pixel multiplier can only be updated once the
-                        * DPLL is enabled and the clocks are stable.
-                        *
-                        * So write it again.
-                        */
-                       I915_WRITE(intel_crtc->pch_pll->pll_reg, dpll);
-               }
-
-               if (intel_crtc->pch_pll) {
-                       if (is_lvds && has_reduced_clock && i915_powersave) {
-                               I915_WRITE(intel_crtc->pch_pll->fp1_reg, fp2);
-                               intel_crtc->lowfreq_avail = true;
-                       } else {
-                               I915_WRITE(intel_crtc->pch_pll->fp1_reg, fp);
-                       }
-               }
-       }
 
        intel_set_pipe_timings(intel_crtc, mode, adjusted_mode);
 
        if (!is_dp || is_cpu_edp)
                ironlake_set_m_n(crtc, mode, adjusted_mode);
 
-       if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev))
-               if (is_cpu_edp)
-                       ironlake_set_pll_edp(crtc, adjusted_mode->clock);
-
        haswell_set_pipeconf(crtc, adjusted_mode, dither);
 
+       intel_set_pipe_csc(crtc, adjusted_mode);
+
        /* Set up the display plane register */
-       I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE);
+       I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE | DISPPLANE_PIPE_CSC_ENABLE);
        POSTING_READ(DSPCNTR(plane));
 
        ret = intel_pipe_set_base(crtc, x, y, fb);
@@ -5972,6 +5800,11 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        int pipe = intel_crtc->pipe;
        int ret;
 
+       if (IS_HASWELL(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))
+               intel_crtc->cpu_transcoder = TRANSCODER_EDP;
+       else
+               intel_crtc->cpu_transcoder = pipe;
+
        drm_vblank_pre_modeset(dev, pipe);
 
        ret = dev_priv->display.crtc_mode_set(crtc, mode, adjusted_mode,
@@ -6068,6 +5901,7 @@ static void haswell_write_eld(struct drm_connector *connector,
        struct drm_i915_private *dev_priv = connector->dev->dev_private;
        uint8_t *eld = connector->eld;
        struct drm_device *dev = crtc->dev;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        uint32_t eldv;
        uint32_t i;
        int len;
@@ -6109,6 +5943,7 @@ static void haswell_write_eld(struct drm_connector *connector,
        DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(pipe));
 
        eldv = AUDIO_ELD_VALID_A << (pipe * 4);
+       intel_crtc->eld_vld = true;
 
        if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
                DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n");
@@ -6344,6 +6179,8 @@ static void ivb_update_cursor(struct drm_crtc *crtc, u32 base)
                        cntl &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE);
                        cntl |= CURSOR_MODE_DISABLE;
                }
+               if (IS_HASWELL(dev))
+                       cntl |= CURSOR_PIPE_CSC_ENABLE;
                I915_WRITE(CURCNTR_IVB(pipe), cntl);
 
                intel_crtc->cursor_visible = visible;
@@ -6700,6 +6537,8 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
        if (encoder->crtc) {
                crtc = encoder->crtc;
 
+               mutex_lock(&crtc->mutex);
+
                old->dpms_mode = connector->dpms;
                old->load_detect_temp = false;
 
@@ -6729,6 +6568,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
                return false;
        }
 
+       mutex_lock(&crtc->mutex);
        intel_encoder->new_crtc = to_intel_crtc(crtc);
        to_intel_connector(connector)->new_encoder = intel_encoder;
 
@@ -6756,13 +6596,15 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
                DRM_DEBUG_KMS("reusing fbdev for load-detection framebuffer\n");
        if (IS_ERR(fb)) {
                DRM_DEBUG_KMS("failed to allocate framebuffer for load-detection\n");
+               mutex_unlock(&crtc->mutex);
                return false;
        }
 
-       if (!intel_set_mode(crtc, mode, 0, 0, fb)) {
+       if (intel_set_mode(crtc, mode, 0, 0, fb)) {
                DRM_DEBUG_KMS("failed to set mode on load-detect pipe\n");
                if (old->release_fb)
                        old->release_fb->funcs->destroy(old->release_fb);
+               mutex_unlock(&crtc->mutex);
                return false;
        }
 
@@ -6777,27 +6619,31 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
        struct intel_encoder *intel_encoder =
                intel_attached_encoder(connector);
        struct drm_encoder *encoder = &intel_encoder->base;
+       struct drm_crtc *crtc = encoder->crtc;
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
                      connector->base.id, drm_get_connector_name(connector),
                      encoder->base.id, drm_get_encoder_name(encoder));
 
        if (old->load_detect_temp) {
-               struct drm_crtc *crtc = encoder->crtc;
-
                to_intel_connector(connector)->new_encoder = NULL;
                intel_encoder->new_crtc = NULL;
                intel_set_mode(crtc, NULL, 0, 0, NULL);
 
-               if (old->release_fb)
-                       old->release_fb->funcs->destroy(old->release_fb);
+               if (old->release_fb) {
+                       drm_framebuffer_unregister_private(old->release_fb);
+                       drm_framebuffer_unreference(old->release_fb);
+               }
 
+               mutex_unlock(&crtc->mutex);
                return;
        }
 
        /* Switch crtc and encoder back off if necessary */
        if (old->dpms_mode != DRM_MODE_DPMS_ON)
                connector->funcs->dpms(connector, old->dpms_mode);
+
+       mutex_unlock(&crtc->mutex);
 }
 
 /* Returns the clock of the currently programmed mode of the given pipe. */
@@ -6993,11 +6839,6 @@ void intel_mark_busy(struct drm_device *dev)
 
 void intel_mark_idle(struct drm_device *dev)
 {
-}
-
-void intel_mark_fb_busy(struct drm_i915_gem_object *obj)
-{
-       struct drm_device *dev = obj->base.dev;
        struct drm_crtc *crtc;
 
        if (!i915_powersave)
@@ -7007,12 +6848,11 @@ void intel_mark_fb_busy(struct drm_i915_gem_object *obj)
                if (!crtc->fb)
                        continue;
 
-               if (to_intel_framebuffer(crtc->fb)->obj == obj)
-                       intel_increase_pllclock(crtc);
+               intel_decrease_pllclock(crtc);
        }
 }
 
-void intel_mark_fb_idle(struct drm_i915_gem_object *obj)
+void intel_mark_fb_busy(struct drm_i915_gem_object *obj)
 {
        struct drm_device *dev = obj->base.dev;
        struct drm_crtc *crtc;
@@ -7025,7 +6865,7 @@ void intel_mark_fb_idle(struct drm_i915_gem_object *obj)
                        continue;
 
                if (to_intel_framebuffer(crtc->fb)->obj == obj)
-                       intel_decrease_pllclock(crtc);
+                       intel_increase_pllclock(crtc);
        }
 }
 
@@ -7109,9 +6949,7 @@ static void do_intel_finish_page_flip(struct drm_device *dev,
 
        obj = work->old_fb_obj;
 
-       atomic_clear_mask(1 << intel_crtc->plane,
-                         &obj->pending_flip.counter);
-       wake_up(&dev_priv->pending_flip_queue);
+       wake_up_all(&dev_priv->pending_flip_queue);
 
        queue_work(dev_priv->wq, &work->work);
 
@@ -7474,11 +7312,8 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
 
        work->enable_stall_check = true;
 
-       /* Block clients from rendering to the new back buffer until
-        * the flip occurs and the object is no longer visible.
-        */
-       atomic_add(1 << intel_crtc->plane, &work->old_fb_obj->pending_flip);
        atomic_inc(&intel_crtc->unpin_work_count);
+       intel_crtc->reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter);
 
        ret = dev_priv->display.queue_flip(dev, crtc, fb, obj);
        if (ret)
@@ -7494,7 +7329,6 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
 
 cleanup_pending:
        atomic_dec(&intel_crtc->unpin_work_count);
-       atomic_sub(1 << intel_crtc->plane, &work->old_fb_obj->pending_flip);
        drm_gem_object_unreference(&work->old_fb_obj->base);
        drm_gem_object_unreference(&obj->base);
        mutex_unlock(&dev->struct_mutex);
@@ -7514,7 +7348,6 @@ free_work:
 static struct drm_crtc_helper_funcs intel_helper_funcs = {
        .mode_set_base_atomic = intel_pipe_set_base_atomic,
        .load_lut = intel_crtc_load_lut,
-       .disable = intel_crtc_noop,
 };
 
 bool intel_encoder_check_is_cloned(struct intel_encoder *encoder)
@@ -7904,16 +7737,21 @@ intel_modeset_check_state(struct drm_device *dev)
        }
 }
 
-bool intel_set_mode(struct drm_crtc *crtc,
-                   struct drm_display_mode *mode,
-                   int x, int y, struct drm_framebuffer *fb)
+int intel_set_mode(struct drm_crtc *crtc,
+                  struct drm_display_mode *mode,
+                  int x, int y, struct drm_framebuffer *fb)
 {
        struct drm_device *dev = crtc->dev;
        drm_i915_private_t *dev_priv = dev->dev_private;
-       struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode;
+       struct drm_display_mode *adjusted_mode, *saved_mode, *saved_hwmode;
        struct intel_crtc *intel_crtc;
        unsigned disable_pipes, prepare_pipes, modeset_pipes;
-       bool ret = true;
+       int ret = 0;
+
+       saved_mode = kmalloc(2 * sizeof(*saved_mode), GFP_KERNEL);
+       if (!saved_mode)
+               return -ENOMEM;
+       saved_hwmode = saved_mode + 1;
 
        intel_modeset_affected_pipes(crtc, &modeset_pipes,
                                     &prepare_pipes, &disable_pipes);
@@ -7924,8 +7762,8 @@ bool intel_set_mode(struct drm_crtc *crtc,
        for_each_intel_crtc_masked(dev, disable_pipes, intel_crtc)
                intel_crtc_disable(&intel_crtc->base);
 
-       saved_hwmode = crtc->hwmode;
-       saved_mode = crtc->mode;
+       *saved_hwmode = crtc->hwmode;
+       *saved_mode = crtc->mode;
 
        /* Hack: Because we don't (yet) support global modeset on multiple
         * crtcs, we don't keep track of the new mode for more than one crtc.
@@ -7936,7 +7774,8 @@ bool intel_set_mode(struct drm_crtc *crtc,
        if (modeset_pipes) {
                adjusted_mode = intel_modeset_adjusted_mode(crtc, mode);
                if (IS_ERR(adjusted_mode)) {
-                       return false;
+                       ret = PTR_ERR(adjusted_mode);
+                       goto out;
                }
        }
 
@@ -7962,11 +7801,11 @@ bool intel_set_mode(struct drm_crtc *crtc,
         * on the DPLL.
         */
        for_each_intel_crtc_masked(dev, modeset_pipes, intel_crtc) {
-               ret = !intel_crtc_mode_set(&intel_crtc->base,
-                                          mode, adjusted_mode,
-                                          x, y, fb);
-               if (!ret)
-                   goto done;
+               ret = intel_crtc_mode_set(&intel_crtc->base,
+                                         mode, adjusted_mode,
+                                         x, y, fb);
+               if (ret)
+                       goto done;
        }
 
        /* Now enable the clocks, plane, pipe, and connectors that we set up. */
@@ -7987,16 +7826,23 @@ bool intel_set_mode(struct drm_crtc *crtc,
        /* FIXME: add subpixel order */
 done:
        drm_mode_destroy(dev, adjusted_mode);
-       if (!ret && crtc->enabled) {
-               crtc->hwmode = saved_hwmode;
-               crtc->mode = saved_mode;
+       if (ret && crtc->enabled) {
+               crtc->hwmode = *saved_hwmode;
+               crtc->mode = *saved_mode;
        } else {
                intel_modeset_check_state(dev);
        }
 
+out:
+       kfree(saved_mode);
        return ret;
 }
 
+void intel_crtc_restore_mode(struct drm_crtc *crtc)
+{
+       intel_set_mode(crtc, &crtc->mode, crtc->x, crtc->y, crtc->fb);
+}
+
 #undef for_each_intel_crtc_masked
 
 static void intel_set_config_free(struct intel_set_config *config)
@@ -8109,7 +7955,7 @@ intel_modeset_stage_output_state(struct drm_device *dev,
        struct intel_encoder *encoder;
        int count, ro;
 
-       /* The upper layers ensure that we either disabl a crtc or have a list
+       /* The upper layers ensure that we either disable a crtc or have a list
         * of connectors. For paranoia, double-check this. */
        WARN_ON(!set->fb && (set->num_connectors != 0));
        WARN_ON(set->fb && (set->num_connectors == 0));
@@ -8211,14 +8057,9 @@ static int intel_crtc_set_config(struct drm_mode_set *set)
        BUG_ON(!set->crtc);
        BUG_ON(!set->crtc->helper_private);
 
-       if (!set->mode)
-               set->fb = NULL;
-
-       /* The fb helper likes to play gross jokes with ->mode_set_config.
-        * Unfortunately the crtc helper doesn't do much at all for this case,
-        * so we have to cope with this madness until the fb helper is fixed up. */
-       if (set->fb && set->num_connectors == 0)
-               return 0;
+       /* Enforce sane interface api - has been abused by the fb helper. */
+       BUG_ON(!set->mode && set->fb);
+       BUG_ON(set->fb && set->num_connectors == 0);
 
        if (set->fb) {
                DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n",
@@ -8262,11 +8103,11 @@ static int intel_crtc_set_config(struct drm_mode_set *set)
                        drm_mode_debug_printmodeline(set->mode);
                }
 
-               if (!intel_set_mode(set->crtc, set->mode,
-                                   set->x, set->y, set->fb)) {
-                       DRM_ERROR("failed to set mode on [CRTC:%d]\n",
-                                 set->crtc->base.id);
-                       ret = -EINVAL;
+               ret = intel_set_mode(set->crtc, set->mode,
+                                    set->x, set->y, set->fb);
+               if (ret) {
+                       DRM_ERROR("failed to set mode on [CRTC:%d], err = %d\n",
+                                 set->crtc->base.id, ret);
                        goto fail;
                }
        } else if (config->fb_changed) {
@@ -8283,8 +8124,8 @@ fail:
 
        /* Try to restore the config */
        if (config->mode_changed &&
-           !intel_set_mode(save_set.crtc, save_set.mode,
-                           save_set.x, save_set.y, save_set.fb))
+           intel_set_mode(save_set.crtc, save_set.mode,
+                          save_set.x, save_set.y, save_set.fb))
                DRM_ERROR("failed to restore config after modeset failure\n");
 
 out_config:
@@ -8303,7 +8144,7 @@ static const struct drm_crtc_funcs intel_crtc_funcs = {
 
 static void intel_cpu_pll_init(struct drm_device *dev)
 {
-       if (IS_HASWELL(dev))
+       if (HAS_DDI(dev))
                intel_ddi_pll_init(dev);
 }
 
@@ -8439,11 +8280,10 @@ static void intel_setup_outputs(struct drm_device *dev)
                I915_WRITE(PFIT_CONTROL, 0);
        }
 
-       if (!(IS_HASWELL(dev) &&
-             (I915_READ(DDI_BUF_CTL(PORT_A)) & DDI_A_4_LANES)))
+       if (!(HAS_DDI(dev) && (I915_READ(DDI_BUF_CTL(PORT_A)) & DDI_A_4_LANES)))
                intel_crt_init(dev);
 
-       if (IS_HASWELL(dev)) {
+       if (HAS_DDI(dev)) {
                int found;
 
                /* Haswell uses DDI functions to detect digital outputs */
@@ -8490,23 +8330,18 @@ static void intel_setup_outputs(struct drm_device *dev)
                if (I915_READ(PCH_DP_D) & DP_DETECTED)
                        intel_dp_init(dev, PCH_DP_D, PORT_D);
        } else if (IS_VALLEYVIEW(dev)) {
-               int found;
-
                /* Check for built-in panel first. Shares lanes with HDMI on SDVOC */
-               if (I915_READ(DP_C) & DP_DETECTED)
-                       intel_dp_init(dev, DP_C, PORT_C);
+               if (I915_READ(VLV_DISPLAY_BASE + DP_C) & DP_DETECTED)
+                       intel_dp_init(dev, VLV_DISPLAY_BASE + DP_C, PORT_C);
 
-               if (I915_READ(SDVOB) & PORT_DETECTED) {
-                       /* SDVOB multiplex with HDMIB */
-                       found = intel_sdvo_init(dev, SDVOB, true);
-                       if (!found)
-                               intel_hdmi_init(dev, SDVOB, PORT_B);
-                       if (!found && (I915_READ(DP_B) & DP_DETECTED))
-                               intel_dp_init(dev, DP_B, PORT_B);
+               if (I915_READ(VLV_DISPLAY_BASE + SDVOB) & PORT_DETECTED) {
+                       intel_hdmi_init(dev, VLV_DISPLAY_BASE + SDVOB, PORT_B);
+                       if (I915_READ(VLV_DISPLAY_BASE + DP_B) & DP_DETECTED)
+                               intel_dp_init(dev, VLV_DISPLAY_BASE + DP_B, PORT_B);
                }
 
-               if (I915_READ(SDVOC) & PORT_DETECTED)
-                       intel_hdmi_init(dev, SDVOC, PORT_C);
+               if (I915_READ(VLV_DISPLAY_BASE + SDVOC) & PORT_DETECTED)
+                       intel_hdmi_init(dev, VLV_DISPLAY_BASE + SDVOC, PORT_C);
 
        } else if (SUPPORTS_DIGITAL_OUTPUTS(dev)) {
                bool found = false;
@@ -8666,14 +8501,15 @@ int intel_framebuffer_init(struct drm_device *dev,
        if (mode_cmd->offsets[0] != 0)
                return -EINVAL;
 
+       drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd);
+       intel_fb->obj = obj;
+
        ret = drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs);
        if (ret) {
                DRM_ERROR("framebuffer init failed %d\n", ret);
                return ret;
        }
 
-       drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd);
-       intel_fb->obj = obj;
        return 0;
 }
 
@@ -8703,7 +8539,7 @@ static void intel_init_display(struct drm_device *dev)
        struct drm_i915_private *dev_priv = dev->dev_private;
 
        /* We always want a DPMS function */
-       if (IS_HASWELL(dev)) {
+       if (HAS_DDI(dev)) {
                dev_priv->display.crtc_mode_set = haswell_crtc_mode_set;
                dev_priv->display.crtc_enable = haswell_crtc_enable;
                dev_priv->display.crtc_disable = haswell_crtc_disable;
@@ -8765,8 +8601,9 @@ static void intel_init_display(struct drm_device *dev)
                } else if (IS_HASWELL(dev)) {
                        dev_priv->display.fdi_link_train = hsw_fdi_link_train;
                        dev_priv->display.write_eld = haswell_write_eld;
-               } else
-                       dev_priv->display.update_wm = NULL;
+                       dev_priv->display.modeset_global_resources =
+                               haswell_modeset_global_resources;
+               }
        } else if (IS_G4X(dev)) {
                dev_priv->display.write_eld = g4x_write_eld;
        }
@@ -8888,6 +8725,18 @@ static struct intel_quirk intel_quirks[] = {
 
        /* Acer Aspire 5734Z must invert backlight brightness */
        { 0x2a42, 0x1025, 0x0459, quirk_invert_brightness },
+
+       /* Acer/eMachines G725 */
+       { 0x2a42, 0x1025, 0x0210, quirk_invert_brightness },
+
+       /* Acer/eMachines e725 */
+       { 0x2a42, 0x1025, 0x0212, quirk_invert_brightness },
+
+       /* Acer/Packard Bell NCL20 */
+       { 0x2a42, 0x1025, 0x034b, quirk_invert_brightness },
+
+       /* Acer Aspire 4736Z */
+       { 0x2a42, 0x1025, 0x0260, quirk_invert_brightness },
 };
 
 static void intel_init_quirks(struct drm_device *dev)
@@ -8916,12 +8765,7 @@ static void i915_disable_vga(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u8 sr1;
-       u32 vga_reg;
-
-       if (HAS_PCH_SPLIT(dev))
-               vga_reg = CPU_VGACNTRL;
-       else
-               vga_reg = VGACNTRL;
+       u32 vga_reg = i915_vgacntrl_reg(dev);
 
        vga_get_uninterruptible(dev->pdev, VGA_RSRC_LEGACY_IO);
        outb(SR01, VGA_SR_INDEX);
@@ -8936,10 +8780,7 @@ static void i915_disable_vga(struct drm_device *dev)
 
 void intel_modeset_init_hw(struct drm_device *dev)
 {
-       /* We attempt to init the necessary power wells early in the initialization
-        * time, so the subsystems that expect power to be enabled can work.
-        */
-       intel_init_power_wells(dev);
+       intel_init_power_well(dev);
 
        intel_prepare_ddi(dev);
 
@@ -8981,7 +8822,7 @@ void intel_modeset_init(struct drm_device *dev)
                dev->mode_config.max_width = 8192;
                dev->mode_config.max_height = 8192;
        }
-       dev->mode_config.fb_base = dev_priv->mm.gtt_base_addr;
+       dev->mode_config.fb_base = dev_priv->gtt.mappable_base;
 
        DRM_DEBUG_KMS("%d display pipe%s available.\n",
                      dev_priv->num_pipe, dev_priv->num_pipe > 1 ? "s" : "");
@@ -8999,6 +8840,9 @@ void intel_modeset_init(struct drm_device *dev)
        /* Just disable it once at startup */
        i915_disable_vga(dev);
        intel_setup_outputs(dev);
+
+       /* Just in case the BIOS is doing something questionable. */
+       intel_disable_fbc(dev);
 }
 
 static void
@@ -9180,20 +9024,14 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder)
         * the crtc fixup. */
 }
 
-static void i915_redisable_vga(struct drm_device *dev)
+void i915_redisable_vga(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 vga_reg;
-
-       if (HAS_PCH_SPLIT(dev))
-               vga_reg = CPU_VGACNTRL;
-       else
-               vga_reg = VGACNTRL;
+       u32 vga_reg = i915_vgacntrl_reg(dev);
 
        if (I915_READ(vga_reg) != VGA_DISP_DISABLE) {
                DRM_DEBUG_KMS("Something enabled VGA plane, disabling it\n");
-               I915_WRITE(vga_reg, VGA_DISP_DISABLE);
-               POSTING_READ(vga_reg);
+               i915_disable_vga(dev);
        }
 }
 
@@ -9209,7 +9047,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev,
        struct intel_encoder *encoder;
        struct intel_connector *connector;
 
-       if (IS_HASWELL(dev)) {
+       if (HAS_DDI(dev)) {
                tmp = I915_READ(TRANS_DDI_FUNC_CTL(TRANSCODER_EDP));
 
                if (tmp & TRANS_DDI_FUNC_ENABLE) {
@@ -9250,7 +9088,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev,
                              crtc->active ? "enabled" : "disabled");
        }
 
-       if (IS_HASWELL(dev))
+       if (HAS_DDI(dev))
                intel_ddi_setup_hw_pll_state(dev);
 
        list_for_each_entry(encoder, &dev->mode_config.encoder_list,
@@ -9301,9 +9139,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev,
 
        if (force_restore) {
                for_each_pipe(pipe) {
-                       crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
-                       intel_set_mode(&crtc->base, &crtc->base.mode,
-                                      crtc->base.x, crtc->base.y, crtc->base.fb);
+                       intel_crtc_restore_mode(dev_priv->pipe_to_crtc_mapping[pipe]);
                }
 
                i915_redisable_vga(dev);
@@ -9367,6 +9203,8 @@ void intel_modeset_cleanup(struct drm_device *dev)
        flush_scheduled_work();
 
        drm_mode_config_cleanup(dev);
+
+       intel_cleanup_overlay(dev);
 }
 
 /*
index fb3715b4b09d26d1357fdfe16320d6680f5ee92a..31c0205685abb1e575db6a3b00198e1637013dfb 100644 (file)
@@ -148,15 +148,6 @@ intel_dp_max_link_bw(struct intel_dp *intel_dp)
        return max_link_bw;
 }
 
-static int
-intel_dp_link_clock(uint8_t link_bw)
-{
-       if (link_bw == DP_LINK_BW_2_7)
-               return 270000;
-       else
-               return 162000;
-}
-
 /*
  * The units on the numbers in the next two are... bizarre.  Examples will
  * make it clearer; this one parallels an example in the eDP spec.
@@ -191,7 +182,8 @@ intel_dp_adjust_dithering(struct intel_dp *intel_dp,
                          struct drm_display_mode *mode,
                          bool adjust_mode)
 {
-       int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(intel_dp));
+       int max_link_clock =
+               drm_dp_bw_code_to_link_rate(intel_dp_max_link_bw(intel_dp));
        int max_lanes = drm_dp_max_lane_count(intel_dp->dpcd);
        int max_rate, mode_rate;
 
@@ -330,6 +322,48 @@ intel_dp_check_edp(struct intel_dp *intel_dp)
        }
 }
 
+static uint32_t
+intel_dp_aux_wait_done(struct intel_dp *intel_dp, bool has_aux_irq)
+{
+       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+       struct drm_device *dev = intel_dig_port->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t ch_ctl = intel_dp->output_reg + 0x10;
+       uint32_t status;
+       bool done;
+
+       if (IS_HASWELL(dev)) {
+               switch (intel_dig_port->port) {
+               case PORT_A:
+                       ch_ctl = DPA_AUX_CH_CTL;
+                       break;
+               case PORT_B:
+                       ch_ctl = PCH_DPB_AUX_CH_CTL;
+                       break;
+               case PORT_C:
+                       ch_ctl = PCH_DPC_AUX_CH_CTL;
+                       break;
+               case PORT_D:
+                       ch_ctl = PCH_DPD_AUX_CH_CTL;
+                       break;
+               default:
+                       BUG();
+               }
+       }
+
+#define C (((status = I915_READ_NOTRACE(ch_ctl)) & DP_AUX_CH_CTL_SEND_BUSY) == 0)
+       if (has_aux_irq)
+               done = wait_event_timeout(dev_priv->gmbus_wait_queue, C, 10);
+       else
+               done = wait_for_atomic(C, 10) == 0;
+       if (!done)
+               DRM_ERROR("dp aux hw did not signal timeout (has irq: %i)!\n",
+                         has_aux_irq);
+#undef C
+
+       return status;
+}
+
 static int
 intel_dp_aux_ch(struct intel_dp *intel_dp,
                uint8_t *send, int send_bytes,
@@ -341,11 +375,17 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
        struct drm_i915_private *dev_priv = dev->dev_private;
        uint32_t ch_ctl = output_reg + 0x10;
        uint32_t ch_data = ch_ctl + 4;
-       int i;
-       int recv_bytes;
+       int i, ret, recv_bytes;
        uint32_t status;
        uint32_t aux_clock_divider;
        int try, precharge;
+       bool has_aux_irq = INTEL_INFO(dev)->gen >= 5 && !IS_VALLEYVIEW(dev);
+
+       /* dp aux is extremely sensitive to irq latency, hence request the
+        * lowest possible wakeup latency and so prevent the cpu from going into
+        * deep sleep states.
+        */
+       pm_qos_update_request(&dev_priv->pm_qos, 0);
 
        if (IS_HASWELL(dev)) {
                switch (intel_dig_port->port) {
@@ -379,7 +419,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
         * clock divider.
         */
        if (is_cpu_edp(intel_dp)) {
-               if (IS_HASWELL(dev))
+               if (HAS_DDI(dev))
                        aux_clock_divider = intel_ddi_get_cdclk_freq(dev_priv) >> 1;
                else if (IS_VALLEYVIEW(dev))
                        aux_clock_divider = 100;
@@ -399,7 +439,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
 
        /* Try to wait for any previous AUX channel activity */
        for (try = 0; try < 3; try++) {
-               status = I915_READ(ch_ctl);
+               status = I915_READ_NOTRACE(ch_ctl);
                if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0)
                        break;
                msleep(1);
@@ -408,7 +448,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
        if (try == 3) {
                WARN(1, "dp_aux_ch not started status 0x%08x\n",
                     I915_READ(ch_ctl));
-               return -EBUSY;
+               ret = -EBUSY;
+               goto out;
        }
 
        /* Must try at least 3 times according to DP spec */
@@ -421,6 +462,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
                /* Send the command and wait for it to complete */
                I915_WRITE(ch_ctl,
                           DP_AUX_CH_CTL_SEND_BUSY |
+                          (has_aux_irq ? DP_AUX_CH_CTL_INTERRUPT : 0) |
                           DP_AUX_CH_CTL_TIME_OUT_400us |
                           (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
                           (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
@@ -428,12 +470,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
                           DP_AUX_CH_CTL_DONE |
                           DP_AUX_CH_CTL_TIME_OUT_ERROR |
                           DP_AUX_CH_CTL_RECEIVE_ERROR);
-               for (;;) {
-                       status = I915_READ(ch_ctl);
-                       if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0)
-                               break;
-                       udelay(100);
-               }
+
+               status = intel_dp_aux_wait_done(intel_dp, has_aux_irq);
 
                /* Clear done status and any errors */
                I915_WRITE(ch_ctl,
@@ -451,7 +489,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
 
        if ((status & DP_AUX_CH_CTL_DONE) == 0) {
                DRM_ERROR("dp_aux_ch not done status 0x%08x\n", status);
-               return -EBUSY;
+               ret = -EBUSY;
+               goto out;
        }
 
        /* Check for timeout or receive error.
@@ -459,14 +498,16 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
         */
        if (status & DP_AUX_CH_CTL_RECEIVE_ERROR) {
                DRM_ERROR("dp_aux_ch receive error status 0x%08x\n", status);
-               return -EIO;
+               ret = -EIO;
+               goto out;
        }
 
        /* Timeouts occur when the device isn't connected, so they're
         * "normal" -- don't fill the kernel log with these */
        if (status & DP_AUX_CH_CTL_TIME_OUT_ERROR) {
                DRM_DEBUG_KMS("dp_aux_ch timeout status 0x%08x\n", status);
-               return -ETIMEDOUT;
+               ret = -ETIMEDOUT;
+               goto out;
        }
 
        /* Unload any bytes sent back from the other side */
@@ -479,7 +520,11 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
                unpack_aux(I915_READ(ch_data + i),
                           recv + i, recv_bytes - i);
 
-       return recv_bytes;
+       ret = recv_bytes;
+out:
+       pm_qos_update_request(&dev_priv->pm_qos, PM_QOS_DEFAULT_VALUE);
+
+       return ret;
 }
 
 /* Write data to the aux channel in native mode */
@@ -718,16 +763,35 @@ intel_dp_mode_fixup(struct drm_encoder *encoder,
                return false;
 
        bpp = adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC ? 18 : 24;
+
+       if (intel_dp->color_range_auto) {
+               /*
+                * See:
+                * CEA-861-E - 5.1 Default Encoding Parameters
+                * VESA DisplayPort Ver.1.2a - 5.1.1.1 Video Colorimetry
+                */
+               if (bpp != 18 && drm_mode_cea_vic(adjusted_mode) > 1)
+                       intel_dp->color_range = DP_COLOR_RANGE_16_235;
+               else
+                       intel_dp->color_range = 0;
+       }
+
+       if (intel_dp->color_range)
+               adjusted_mode->private_flags |= INTEL_MODE_LIMITED_COLOR_RANGE;
+
        mode_rate = intel_dp_link_required(adjusted_mode->clock, bpp);
 
        for (clock = 0; clock <= max_clock; clock++) {
                for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) {
-                       int link_avail = intel_dp_max_data_rate(intel_dp_link_clock(bws[clock]), lane_count);
+                       int link_bw_clock =
+                               drm_dp_bw_code_to_link_rate(bws[clock]);
+                       int link_avail = intel_dp_max_data_rate(link_bw_clock,
+                                                               lane_count);
 
                        if (mode_rate <= link_avail) {
                                intel_dp->link_bw = bws[clock];
                                intel_dp->lane_count = lane_count;
-                               adjusted_mode->clock = intel_dp_link_clock(intel_dp->link_bw);
+                               adjusted_mode->clock = link_bw_clock;
                                DRM_DEBUG_KMS("DP link bw %02x lane "
                                                "count %d clock %d bpp %d\n",
                                       intel_dp->link_bw, intel_dp->lane_count,
@@ -742,39 +806,6 @@ intel_dp_mode_fixup(struct drm_encoder *encoder,
        return false;
 }
 
-struct intel_dp_m_n {
-       uint32_t        tu;
-       uint32_t        gmch_m;
-       uint32_t        gmch_n;
-       uint32_t        link_m;
-       uint32_t        link_n;
-};
-
-static void
-intel_reduce_ratio(uint32_t *num, uint32_t *den)
-{
-       while (*num > 0xffffff || *den > 0xffffff) {
-               *num >>= 1;
-               *den >>= 1;
-       }
-}
-
-static void
-intel_dp_compute_m_n(int bpp,
-                    int nlanes,
-                    int pixel_clock,
-                    int link_clock,
-                    struct intel_dp_m_n *m_n)
-{
-       m_n->tu = 64;
-       m_n->gmch_m = (pixel_clock * bpp) >> 3;
-       m_n->gmch_n = link_clock * nlanes;
-       intel_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n);
-       m_n->link_m = pixel_clock;
-       m_n->link_n = link_clock;
-       intel_reduce_ratio(&m_n->link_m, &m_n->link_n);
-}
-
 void
 intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
                 struct drm_display_mode *adjusted_mode)
@@ -785,7 +816,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int lane_count = 4;
-       struct intel_dp_m_n m_n;
+       struct intel_link_m_n m_n;
        int pipe = intel_crtc->pipe;
        enum transcoder cpu_transcoder = intel_crtc->cpu_transcoder;
 
@@ -808,8 +839,8 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
         * the number of bytes_per_pixel post-LUT, which we always
         * set up for 8-bits of R/G/B, or 3 bytes total.
         */
-       intel_dp_compute_m_n(intel_crtc->bpp, lane_count,
-                            mode->clock, adjusted_mode->clock, &m_n);
+       intel_link_compute_m_n(intel_crtc->bpp, lane_count,
+                              mode->clock, adjusted_mode->clock, &m_n);
 
        if (IS_HASWELL(dev)) {
                I915_WRITE(PIPE_DATA_M1(cpu_transcoder),
@@ -851,6 +882,32 @@ void intel_dp_init_link_config(struct intel_dp *intel_dp)
        }
 }
 
+static void ironlake_set_pll_edp(struct drm_crtc *crtc, int clock)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 dpa_ctl;
+
+       DRM_DEBUG_KMS("eDP PLL enable for clock %d\n", clock);
+       dpa_ctl = I915_READ(DP_A);
+       dpa_ctl &= ~DP_PLL_FREQ_MASK;
+
+       if (clock < 200000) {
+               /* For a long time we've carried around a ILK-DevA w/a for the
+                * 160MHz clock. If we're really unlucky, it's still required.
+                */
+               DRM_DEBUG_KMS("160MHz cpu eDP clock, might need ilk devA w/a\n");
+               dpa_ctl |= DP_PLL_FREQ_160MHZ;
+       } else {
+               dpa_ctl |= DP_PLL_FREQ_270MHZ;
+       }
+
+       I915_WRITE(DP_A, dpa_ctl);
+
+       POSTING_READ(DP_A);
+       udelay(500);
+}
+
 static void
 intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
                  struct drm_display_mode *adjusted_mode)
@@ -926,7 +983,8 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
                else
                        intel_dp->DP |= DP_PLL_FREQ_270MHZ;
        } else if (!HAS_PCH_CPT(dev) || is_cpu_edp(intel_dp)) {
-               intel_dp->DP |= intel_dp->color_range;
+               if (!HAS_PCH_SPLIT(dev))
+                       intel_dp->DP |= intel_dp->color_range;
 
                if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
                        intel_dp->DP |= DP_SYNC_HS_HIGH;
@@ -950,6 +1008,9 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
        } else {
                intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT;
        }
+
+       if (is_cpu_edp(intel_dp))
+               ironlake_set_pll_edp(crtc, adjusted_mode->clock);
 }
 
 #define IDLE_ON_MASK           (PP_ON | 0        | PP_SEQUENCE_MASK | 0                     | PP_SEQUENCE_STATE_MASK)
@@ -1057,6 +1118,8 @@ static void ironlake_panel_vdd_off_sync(struct intel_dp *intel_dp)
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 pp;
 
+       WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+
        if (!intel_dp->want_panel_vdd && ironlake_edp_have_panel_vdd(intel_dp)) {
                pp = ironlake_get_pp_control(dev_priv);
                pp &= ~EDP_FORCE_VDD;
@@ -1543,7 +1606,7 @@ intel_get_adjust_train(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_ST
 }
 
 static uint32_t
-intel_dp_signal_levels(uint8_t train_set)
+intel_gen4_signal_levels(uint8_t train_set)
 {
        uint32_t        signal_levels = 0;
 
@@ -1641,7 +1704,7 @@ intel_gen7_edp_signal_levels(uint8_t train_set)
 
 /* Gen7.5's (HSW) DP voltage swing and pre-emphasis control */
 static uint32_t
-intel_dp_signal_levels_hsw(uint8_t train_set)
+intel_hsw_signal_levels(uint8_t train_set)
 {
        int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK |
                                         DP_TRAIN_PRE_EMPHASIS_MASK);
@@ -1673,6 +1736,34 @@ intel_dp_signal_levels_hsw(uint8_t train_set)
        }
 }
 
+/* Properly updates "DP" with the correct signal levels. */
+static void
+intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)
+{
+       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+       struct drm_device *dev = intel_dig_port->base.base.dev;
+       uint32_t signal_levels, mask;
+       uint8_t train_set = intel_dp->train_set[0];
+
+       if (IS_HASWELL(dev)) {
+               signal_levels = intel_hsw_signal_levels(train_set);
+               mask = DDI_BUF_EMP_MASK;
+       } else if (IS_GEN7(dev) && is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) {
+               signal_levels = intel_gen7_edp_signal_levels(train_set);
+               mask = EDP_LINK_TRAIN_VOL_EMP_MASK_IVB;
+       } else if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) {
+               signal_levels = intel_gen6_edp_signal_levels(train_set);
+               mask = EDP_LINK_TRAIN_VOL_EMP_MASK_SNB;
+       } else {
+               signal_levels = intel_gen4_signal_levels(train_set);
+               mask = DP_VOLTAGE_MASK | DP_PRE_EMPHASIS_MASK;
+       }
+
+       DRM_DEBUG_KMS("Using signal levels %08x\n", signal_levels);
+
+       *DP = (*DP & ~mask) | signal_levels;
+}
+
 static bool
 intel_dp_set_link_train(struct intel_dp *intel_dp,
                        uint32_t dp_reg_value,
@@ -1696,14 +1787,18 @@ intel_dp_set_link_train(struct intel_dp *intel_dp,
                temp &= ~DP_TP_CTL_LINK_TRAIN_MASK;
                switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) {
                case DP_TRAINING_PATTERN_DISABLE:
-                       temp |= DP_TP_CTL_LINK_TRAIN_IDLE;
-                       I915_WRITE(DP_TP_CTL(port), temp);
 
-                       if (wait_for((I915_READ(DP_TP_STATUS(port)) &
-                                     DP_TP_STATUS_IDLE_DONE), 1))
-                               DRM_ERROR("Timed out waiting for DP idle patterns\n");
+                       if (port != PORT_A) {
+                               temp |= DP_TP_CTL_LINK_TRAIN_IDLE;
+                               I915_WRITE(DP_TP_CTL(port), temp);
+
+                               if (wait_for((I915_READ(DP_TP_STATUS(port)) &
+                                             DP_TP_STATUS_IDLE_DONE), 1))
+                                       DRM_ERROR("Timed out waiting for DP idle patterns\n");
+
+                               temp &= ~DP_TP_CTL_LINK_TRAIN_MASK;
+                       }
 
-                       temp &= ~DP_TP_CTL_LINK_TRAIN_MASK;
                        temp |= DP_TP_CTL_LINK_TRAIN_NORMAL;
 
                        break;
@@ -1791,7 +1886,7 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)
        int voltage_tries, loop_tries;
        uint32_t DP = intel_dp->DP;
 
-       if (IS_HASWELL(dev))
+       if (HAS_DDI(dev))
                intel_ddi_prepare_link_retrain(encoder);
 
        /* Write the link configuration data */
@@ -1809,24 +1904,8 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)
        for (;;) {
                /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */
                uint8_t     link_status[DP_LINK_STATUS_SIZE];
-               uint32_t    signal_levels;
-
-               if (IS_HASWELL(dev)) {
-                       signal_levels = intel_dp_signal_levels_hsw(
-                                                       intel_dp->train_set[0]);
-                       DP = (DP & ~DDI_BUF_EMP_MASK) | signal_levels;
-               } else if (IS_GEN7(dev) && is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) {
-                       signal_levels = intel_gen7_edp_signal_levels(intel_dp->train_set[0]);
-                       DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_IVB) | signal_levels;
-               } else if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) {
-                       signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]);
-                       DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels;
-               } else {
-                       signal_levels = intel_dp_signal_levels(intel_dp->train_set[0]);
-                       DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels;
-               }
-               DRM_DEBUG_KMS("training pattern 1 signal levels %08x\n",
-                             signal_levels);
+
+               intel_dp_set_signal_levels(intel_dp, &DP);
 
                /* Set training pattern 1 */
                if (!intel_dp_set_link_train(intel_dp, DP,
@@ -1882,7 +1961,6 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)
 void
 intel_dp_complete_link_train(struct intel_dp *intel_dp)
 {
-       struct drm_device *dev = intel_dp_to_dev(intel_dp);
        bool channel_eq = false;
        int tries, cr_tries;
        uint32_t DP = intel_dp->DP;
@@ -1892,8 +1970,6 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
        cr_tries = 0;
        channel_eq = false;
        for (;;) {
-               /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */
-               uint32_t    signal_levels;
                uint8_t     link_status[DP_LINK_STATUS_SIZE];
 
                if (cr_tries > 5) {
@@ -1902,19 +1978,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
                        break;
                }
 
-               if (IS_HASWELL(dev)) {
-                       signal_levels = intel_dp_signal_levels_hsw(intel_dp->train_set[0]);
-                       DP = (DP & ~DDI_BUF_EMP_MASK) | signal_levels;
-               } else if (IS_GEN7(dev) && is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) {
-                       signal_levels = intel_gen7_edp_signal_levels(intel_dp->train_set[0]);
-                       DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_IVB) | signal_levels;
-               } else if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) {
-                       signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]);
-                       DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels;
-               } else {
-                       signal_levels = intel_dp_signal_levels(intel_dp->train_set[0]);
-                       DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels;
-               }
+               intel_dp_set_signal_levels(intel_dp, &DP);
 
                /* channel eq pattern */
                if (!intel_dp_set_link_train(intel_dp, DP,
@@ -1964,6 +2028,8 @@ intel_dp_link_down(struct intel_dp *intel_dp)
        struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
        struct drm_device *dev = intel_dig_port->base.base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc =
+               to_intel_crtc(intel_dig_port->base.base.crtc);
        uint32_t DP = intel_dp->DP;
 
        /*
@@ -1981,7 +2047,7 @@ intel_dp_link_down(struct intel_dp *intel_dp)
         *   intel_ddi_prepare_link_retrain will take care of redoing the link
         *   train.
         */
-       if (IS_HASWELL(dev))
+       if (HAS_DDI(dev))
                return;
 
        if (WARN_ON((I915_READ(intel_dp->output_reg) & DP_PORT_EN) == 0))
@@ -1998,7 +2064,8 @@ intel_dp_link_down(struct intel_dp *intel_dp)
        }
        POSTING_READ(intel_dp->output_reg);
 
-       msleep(17);
+       /* We don't really know why we're doing this */
+       intel_wait_for_vblank(dev, intel_crtc->pipe);
 
        if (HAS_PCH_IBX(dev) &&
            I915_READ(intel_dp->output_reg) & DP_PIPEB_SELECT) {
@@ -2018,19 +2085,14 @@ intel_dp_link_down(struct intel_dp *intel_dp)
                /* Changes to enable or select take place the vblank
                 * after being written.
                 */
-               if (crtc == NULL) {
-                       /* We can arrive here never having been attached
-                        * to a CRTC, for instance, due to inheriting
-                        * random state from the BIOS.
-                        *
-                        * If the pipe is not running, play safe and
-                        * wait for the clocks to stabilise before
-                        * continuing.
-                        */
+               if (WARN_ON(crtc == NULL)) {
+                       /* We should never try to disable a port without a crtc
+                        * attached. For paranoia keep the code around for a
+                        * bit. */
                        POSTING_READ(intel_dp->output_reg);
                        msleep(50);
                } else
-                       intel_wait_for_vblank(dev, to_intel_crtc(crtc)->pipe);
+                       intel_wait_for_vblank(dev, intel_crtc->pipe);
        }
 
        DP &= ~DP_AUDIO_OUTPUT_ENABLE;
@@ -2042,10 +2104,16 @@ intel_dp_link_down(struct intel_dp *intel_dp)
 static bool
 intel_dp_get_dpcd(struct intel_dp *intel_dp)
 {
+       char dpcd_hex_dump[sizeof(intel_dp->dpcd) * 3];
+
        if (intel_dp_aux_native_read_retry(intel_dp, 0x000, intel_dp->dpcd,
                                           sizeof(intel_dp->dpcd)) == 0)
                return false; /* aux transfer failed */
 
+       hex_dump_to_buffer(intel_dp->dpcd, sizeof(intel_dp->dpcd),
+                          32, 1, dpcd_hex_dump, sizeof(dpcd_hex_dump), false);
+       DRM_DEBUG_KMS("DPCD: %s\n", dpcd_hex_dump);
+
        if (intel_dp->dpcd[DP_DPCD_REV] == 0)
                return false; /* DPCD not present */
 
@@ -2206,6 +2274,8 @@ static enum drm_connector_status
 ironlake_dp_detect(struct intel_dp *intel_dp)
 {
        struct drm_device *dev = intel_dp_to_dev(intel_dp);
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
        enum drm_connector_status status;
 
        /* Can't disconnect eDP, but you can close the lid... */
@@ -2216,6 +2286,9 @@ ironlake_dp_detect(struct intel_dp *intel_dp)
                return status;
        }
 
+       if (!ibx_digital_port_connected(dev_priv, intel_dig_port))
+               return connector_status_disconnected;
+
        return intel_dp_detect_dpcd(intel_dp);
 }
 
@@ -2224,17 +2297,18 @@ g4x_dp_detect(struct intel_dp *intel_dp)
 {
        struct drm_device *dev = intel_dp_to_dev(intel_dp);
        struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
        uint32_t bit;
 
-       switch (intel_dp->output_reg) {
-       case DP_B:
-               bit = DPB_HOTPLUG_LIVE_STATUS;
+       switch (intel_dig_port->port) {
+       case PORT_B:
+               bit = PORTB_HOTPLUG_LIVE_STATUS;
                break;
-       case DP_C:
-               bit = DPC_HOTPLUG_LIVE_STATUS;
+       case PORT_C:
+               bit = PORTC_HOTPLUG_LIVE_STATUS;
                break;
-       case DP_D:
-               bit = DPD_HOTPLUG_LIVE_STATUS;
+       case PORT_D:
+               bit = PORTD_HOTPLUG_LIVE_STATUS;
                break;
        default:
                return connector_status_unknown;
@@ -2290,13 +2364,6 @@ intel_dp_get_edid_modes(struct drm_connector *connector, struct i2c_adapter *ada
        return intel_ddc_get_modes(connector, adapter);
 }
 
-
-/**
- * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect DP connection.
- *
- * \return true if DP port is connected.
- * \return false if DP port is disconnected.
- */
 static enum drm_connector_status
 intel_dp_detect(struct drm_connector *connector, bool force)
 {
@@ -2306,7 +2373,6 @@ intel_dp_detect(struct drm_connector *connector, bool force)
        struct drm_device *dev = connector->dev;
        enum drm_connector_status status;
        struct edid *edid = NULL;
-       char dpcd_hex_dump[sizeof(intel_dp->dpcd) * 3];
 
        intel_dp->has_audio = false;
 
@@ -2315,10 +2381,6 @@ intel_dp_detect(struct drm_connector *connector, bool force)
        else
                status = g4x_dp_detect(intel_dp);
 
-       hex_dump_to_buffer(intel_dp->dpcd, sizeof(intel_dp->dpcd),
-                          32, 1, dpcd_hex_dump, sizeof(dpcd_hex_dump), false);
-       DRM_DEBUG_KMS("DPCD: %s\n", dpcd_hex_dump);
-
        if (status != connector_status_connected)
                return status;
 
@@ -2419,10 +2481,21 @@ intel_dp_set_property(struct drm_connector *connector,
        }
 
        if (property == dev_priv->broadcast_rgb_property) {
-               if (val == !!intel_dp->color_range)
-                       return 0;
-
-               intel_dp->color_range = val ? DP_COLOR_RANGE_16_235 : 0;
+               switch (val) {
+               case INTEL_BROADCAST_RGB_AUTO:
+                       intel_dp->color_range_auto = true;
+                       break;
+               case INTEL_BROADCAST_RGB_FULL:
+                       intel_dp->color_range_auto = false;
+                       intel_dp->color_range = 0;
+                       break;
+               case INTEL_BROADCAST_RGB_LIMITED:
+                       intel_dp->color_range_auto = false;
+                       intel_dp->color_range = DP_COLOR_RANGE_16_235;
+                       break;
+               default:
+                       return -EINVAL;
+               }
                goto done;
        }
 
@@ -2445,11 +2518,8 @@ intel_dp_set_property(struct drm_connector *connector,
        return -EINVAL;
 
 done:
-       if (intel_encoder->base.crtc) {
-               struct drm_crtc *crtc = intel_encoder->base.crtc;
-               intel_set_mode(crtc, &crtc->mode,
-                              crtc->x, crtc->y, crtc->fb);
-       }
+       if (intel_encoder->base.crtc)
+               intel_crtc_restore_mode(intel_encoder->base.crtc);
 
        return 0;
 }
@@ -2491,7 +2561,6 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder)
 static const struct drm_encoder_helper_funcs intel_dp_helper_funcs = {
        .mode_fixup = intel_dp_mode_fixup,
        .mode_set = intel_dp_mode_set,
-       .disable = intel_encoder_noop,
 };
 
 static const struct drm_connector_funcs intel_dp_connector_funcs = {
@@ -2566,6 +2635,7 @@ intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connect
 
        intel_attach_force_audio_property(connector);
        intel_attach_broadcast_rgb_property(connector);
+       intel_dp->color_range_auto = true;
 
        if (is_edp(intel_dp)) {
                drm_mode_create_scaling_mode_property(connector->dev);
@@ -2755,7 +2825,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
        intel_connector_attach_encoder(intel_connector, intel_encoder);
        drm_sysfs_connector_add(connector);
 
-       if (IS_HASWELL(dev))
+       if (HAS_DDI(dev))
                intel_connector->get_hw_state = intel_ddi_connector_get_hw_state;
        else
                intel_connector->get_hw_state = intel_connector_get_hw_state;
@@ -2767,15 +2837,15 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
                name = "DPDDC-A";
                break;
        case PORT_B:
-               dev_priv->hotplug_supported_mask |= DPB_HOTPLUG_INT_STATUS;
+               dev_priv->hotplug_supported_mask |= PORTB_HOTPLUG_INT_STATUS;
                name = "DPDDC-B";
                break;
        case PORT_C:
-               dev_priv->hotplug_supported_mask |= DPC_HOTPLUG_INT_STATUS;
+               dev_priv->hotplug_supported_mask |= PORTC_HOTPLUG_INT_STATUS;
                name = "DPDDC-C";
                break;
        case PORT_D:
-               dev_priv->hotplug_supported_mask |= DPD_HOTPLUG_INT_STATUS;
+               dev_priv->hotplug_supported_mask |= PORTD_HOTPLUG_INT_STATUS;
                name = "DPDDC-D";
                break;
        default:
index 8a1bd4a3ad0dc724ba4a166f7b0915d5e1795208..d282052aadd4f53a22dc7ede6992b42218cc863f 100644 (file)
  * timings in the mode to prevent the crtc fixup from overwriting them.
  * Currently only lvds needs that. */
 #define INTEL_MODE_CRTC_TIMINGS_SET (0x20)
+/*
+ * Set when limited 16-235 (as opposed to full 0-255) RGB color range is
+ * to be used.
+ */
+#define INTEL_MODE_LIMITED_COLOR_RANGE (0x40)
 
 static inline void
 intel_mode_set_pixel_multiplier(struct drm_display_mode *mode,
@@ -153,6 +158,7 @@ struct intel_encoder {
        bool cloneable;
        bool connectors_active;
        void (*hot_plug)(struct intel_encoder *);
+       void (*pre_pll_enable)(struct intel_encoder *);
        void (*pre_enable)(struct intel_encoder *);
        void (*enable)(struct intel_encoder *);
        void (*disable)(struct intel_encoder *);
@@ -205,6 +211,7 @@ struct intel_crtc {
         * some outputs connected to this crtc.
         */
        bool active;
+       bool eld_vld;
        bool primary_disabled; /* is the crtc obscured by a plane? */
        bool lowfreq_avail;
        struct intel_overlay *overlay;
@@ -228,6 +235,9 @@ struct intel_crtc {
        /* We can share PLLs across outputs if the timings match */
        struct intel_pch_pll *pch_pll;
        uint32_t ddi_pll_sel;
+
+       /* reset counter value when the last flip was submitted */
+       unsigned int reset_counter;
 };
 
 struct intel_plane {
@@ -283,6 +293,9 @@ struct cxsr_latency {
 #define DIP_LEN_AVI     13
 #define DIP_AVI_PR_1    0
 #define DIP_AVI_PR_2    1
+#define DIP_AVI_RGB_QUANT_RANGE_DEFAULT        (0 << 2)
+#define DIP_AVI_RGB_QUANT_RANGE_LIMITED        (1 << 2)
+#define DIP_AVI_RGB_QUANT_RANGE_FULL   (2 << 2)
 
 #define DIP_TYPE_SPD   0x83
 #define DIP_VERSION_SPD        0x1
@@ -337,9 +350,11 @@ struct intel_hdmi {
        u32 sdvox_reg;
        int ddc_bus;
        uint32_t color_range;
+       bool color_range_auto;
        bool has_hdmi_sink;
        bool has_audio;
        enum hdmi_force_audio force_audio;
+       bool rgb_quant_range_selectable;
        void (*write_infoframe)(struct drm_encoder *encoder,
                                struct dip_infoframe *frame);
        void (*set_infoframes)(struct drm_encoder *encoder,
@@ -356,6 +371,7 @@ struct intel_dp {
        bool has_audio;
        enum hdmi_force_audio force_audio;
        uint32_t color_range;
+       bool color_range_auto;
        uint8_t link_bw;
        uint8_t lane_count;
        uint8_t dpcd[DP_RECEIVER_CAP_SIZE];
@@ -377,6 +393,7 @@ struct intel_dp {
 struct intel_digital_port {
        struct intel_encoder base;
        enum port port;
+       u32 port_reversal;
        struct intel_dp dp;
        struct intel_hdmi hdmi;
 };
@@ -439,10 +456,10 @@ extern bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg,
 extern void intel_dvo_init(struct drm_device *dev);
 extern void intel_tv_init(struct drm_device *dev);
 extern void intel_mark_busy(struct drm_device *dev);
-extern void intel_mark_idle(struct drm_device *dev);
 extern void intel_mark_fb_busy(struct drm_i915_gem_object *obj);
-extern void intel_mark_fb_idle(struct drm_i915_gem_object *obj);
+extern void intel_mark_idle(struct drm_device *dev);
 extern bool intel_lvds_init(struct drm_device *dev);
+extern bool intel_is_dual_link_lvds(struct drm_device *dev);
 extern void intel_dp_init(struct drm_device *dev, int output_reg,
                          enum port port);
 extern void intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
@@ -502,12 +519,12 @@ struct intel_set_config {
        bool mode_changed;
 };
 
-extern bool intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
-                          int x, int y, struct drm_framebuffer *old_fb);
+extern int intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
+                         int x, int y, struct drm_framebuffer *old_fb);
 extern void intel_modeset_disable(struct drm_device *dev);
+extern void intel_crtc_restore_mode(struct drm_crtc *crtc);
 extern void intel_crtc_load_lut(struct drm_crtc *crtc);
 extern void intel_crtc_update_dpms(struct drm_crtc *crtc);
-extern void intel_encoder_noop(struct drm_encoder *encoder);
 extern void intel_encoder_destroy(struct drm_encoder *encoder);
 extern void intel_encoder_dpms(struct intel_encoder *encoder, int mode);
 extern bool intel_encoder_check_is_cloned(struct intel_encoder *encoder);
@@ -546,6 +563,9 @@ hdmi_to_dig_port(struct intel_hdmi *intel_hdmi)
        return container_of(intel_hdmi, struct intel_digital_port, hdmi);
 }
 
+bool ibx_digital_port_connected(struct drm_i915_private *dev_priv,
+                               struct intel_digital_port *port);
+
 extern void intel_connector_attach_encoder(struct intel_connector *connector,
                                           struct intel_encoder *encoder);
 extern struct drm_encoder *intel_best_encoder(struct drm_connector *connector);
@@ -589,6 +609,7 @@ extern int intel_framebuffer_init(struct drm_device *dev,
                                  struct drm_mode_fb_cmd2 *mode_cmd,
                                  struct drm_i915_gem_object *obj);
 extern int intel_fbdev_init(struct drm_device *dev);
+extern void intel_fbdev_initial_config(struct drm_device *dev);
 extern void intel_fbdev_fini(struct drm_device *dev);
 extern void intel_fbdev_set_suspend(struct drm_device *dev, int state);
 extern void intel_prepare_page_flip(struct drm_device *dev, int plane);
@@ -648,7 +669,8 @@ extern void intel_update_fbc(struct drm_device *dev);
 extern void intel_gpu_ips_init(struct drm_i915_private *dev_priv);
 extern void intel_gpu_ips_teardown(void);
 
-extern void intel_init_power_wells(struct drm_device *dev);
+extern void intel_init_power_well(struct drm_device *dev);
+extern void intel_set_power_well(struct drm_device *dev, bool enable);
 extern void intel_enable_gt_powersave(struct drm_device *dev);
 extern void intel_disable_gt_powersave(struct drm_device *dev);
 extern void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv);
index 15da99533e5b627eb493c69745b399a8b2439e36..00e70dbe82da017fbfdf643d62df2c14bfbbe3a5 100644 (file)
@@ -345,7 +345,6 @@ static void intel_dvo_destroy(struct drm_connector *connector)
 static const struct drm_encoder_helper_funcs intel_dvo_helper_funcs = {
        .mode_fixup = intel_dvo_mode_fixup,
        .mode_set = intel_dvo_mode_set,
-       .disable = intel_encoder_noop,
 };
 
 static const struct drm_connector_funcs intel_dvo_connector_funcs = {
index 7b30b5c2c4ee1128796f1e3362a23567e13b3549..981bdce3634ebcff3a8c11c80cd44d1ba838a229 100644 (file)
@@ -57,9 +57,10 @@ static struct fb_ops intelfb_ops = {
        .fb_debug_leave = drm_fb_helper_debug_leave,
 };
 
-static int intelfb_create(struct intel_fbdev *ifbdev,
+static int intelfb_create(struct drm_fb_helper *helper,
                          struct drm_fb_helper_surface_size *sizes)
 {
+       struct intel_fbdev *ifbdev = (struct intel_fbdev *)helper;
        struct drm_device *dev = ifbdev->helper.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct fb_info *info;
@@ -83,7 +84,9 @@ static int intelfb_create(struct intel_fbdev *ifbdev,
 
        size = mode_cmd.pitches[0] * mode_cmd.height;
        size = ALIGN(size, PAGE_SIZE);
-       obj = i915_gem_alloc_object(dev, size);
+       obj = i915_gem_object_create_stolen(dev, size);
+       if (obj == NULL)
+               obj = i915_gem_alloc_object(dev, size);
        if (!obj) {
                DRM_ERROR("failed to allocate framebuffer\n");
                ret = -ENOMEM;
@@ -133,14 +136,13 @@ static int intelfb_create(struct intel_fbdev *ifbdev,
                goto out_unpin;
        }
        info->apertures->ranges[0].base = dev->mode_config.fb_base;
-       info->apertures->ranges[0].size =
-               dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT;
+       info->apertures->ranges[0].size = dev_priv->gtt.mappable_end;
 
        info->fix.smem_start = dev->mode_config.fb_base + obj->gtt_offset;
        info->fix.smem_len = size;
 
        info->screen_base =
-               ioremap_wc(dev_priv->mm.gtt_base_addr + obj->gtt_offset,
+               ioremap_wc(dev_priv->gtt.mappable_base + obj->gtt_offset,
                           size);
        if (!info->screen_base) {
                ret = -ENOSPC;
@@ -153,6 +155,13 @@ static int intelfb_create(struct intel_fbdev *ifbdev,
        drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
        drm_fb_helper_fill_var(info, &ifbdev->helper, sizes->fb_width, sizes->fb_height);
 
+       /* If the object is shmemfs backed, it will have given us zeroed pages.
+        * If the object is stolen however, it will be full of whatever
+        * garbage was left in there.
+        */
+       if (ifbdev->ifb.obj->stolen)
+               memset_io(info->screen_base, 0, info->screen_size);
+
        /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
 
        DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08x, bo %p\n",
@@ -173,26 +182,10 @@ out:
        return ret;
 }
 
-static int intel_fb_find_or_create_single(struct drm_fb_helper *helper,
-                                         struct drm_fb_helper_surface_size *sizes)
-{
-       struct intel_fbdev *ifbdev = (struct intel_fbdev *)helper;
-       int new_fb = 0;
-       int ret;
-
-       if (!helper->fb) {
-               ret = intelfb_create(ifbdev, sizes);
-               if (ret)
-                       return ret;
-               new_fb = 1;
-       }
-       return new_fb;
-}
-
 static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
        .gamma_set = intel_crtc_fb_gamma_set,
        .gamma_get = intel_crtc_fb_gamma_get,
-       .fb_probe = intel_fb_find_or_create_single,
+       .fb_probe = intelfb_create,
 };
 
 static void intel_fbdev_destroy(struct drm_device *dev,
@@ -212,6 +205,7 @@ static void intel_fbdev_destroy(struct drm_device *dev,
 
        drm_fb_helper_fini(&ifbdev->helper);
 
+       drm_framebuffer_unregister_private(&ifb->base);
        drm_framebuffer_cleanup(&ifb->base);
        if (ifb->obj) {
                drm_gem_object_unreference_unlocked(&ifb->obj->base);
@@ -241,10 +235,18 @@ int intel_fbdev_init(struct drm_device *dev)
        }
 
        drm_fb_helper_single_add_all_connectors(&ifbdev->helper);
-       drm_fb_helper_initial_config(&ifbdev->helper, 32);
+
        return 0;
 }
 
+void intel_fbdev_initial_config(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+
+       /* Due to peculiar init order wrt to hpd handling this is separate. */
+       drm_fb_helper_initial_config(&dev_priv->fbdev->helper, 32);
+}
+
 void intel_fbdev_fini(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
@@ -280,7 +282,7 @@ void intel_fb_restore_mode(struct drm_device *dev)
        struct drm_mode_config *config = &dev->mode_config;
        struct drm_plane *plane;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
 
        ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper);
        if (ret)
@@ -288,7 +290,8 @@ void intel_fb_restore_mode(struct drm_device *dev)
 
        /* Be sure to shut off any planes that may be active */
        list_for_each_entry(plane, &config->plane_list, head)
-               plane->funcs->disable_plane(plane);
+               if (plane->enabled)
+                       plane->funcs->disable_plane(plane);
 
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
 }
index 2ee9821b9d9312bb502df6502cc7b8c0cdc63f77..3ea0c8b6a00f41d8b46c4b0f96196567d04133a6 100644 (file)
@@ -48,7 +48,7 @@ assert_hdmi_port_disabled(struct intel_hdmi *intel_hdmi)
        struct drm_i915_private *dev_priv = dev->dev_private;
        uint32_t enabled_bits;
 
-       enabled_bits = IS_HASWELL(dev) ? DDI_BUF_CTL_ENABLE : SDVO_ENABLE;
+       enabled_bits = HAS_DDI(dev) ? DDI_BUF_CTL_ENABLE : SDVO_ENABLE;
 
        WARN(I915_READ(intel_hdmi->sdvox_reg) & enabled_bits,
             "HDMI port enabled, expecting disabled\n");
@@ -331,6 +331,7 @@ static void intel_set_infoframe(struct drm_encoder *encoder,
 static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder,
                                         struct drm_display_mode *adjusted_mode)
 {
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
        struct dip_infoframe avi_if = {
                .type = DIP_TYPE_AVI,
                .ver = DIP_VERSION_AVI,
@@ -340,6 +341,13 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder,
        if (adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK)
                avi_if.body.avi.YQ_CN_PR |= DIP_AVI_PR_2;
 
+       if (intel_hdmi->rgb_quant_range_selectable) {
+               if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE)
+                       avi_if.body.avi.ITC_EC_Q_SC |= DIP_AVI_RGB_QUANT_RANGE_LIMITED;
+               else
+                       avi_if.body.avi.ITC_EC_Q_SC |= DIP_AVI_RGB_QUANT_RANGE_FULL;
+       }
+
        avi_if.body.avi.VIC = drm_mode_cea_vic(adjusted_mode);
 
        intel_set_infoframe(encoder, &avi_if);
@@ -364,7 +372,8 @@ static void g4x_set_infoframes(struct drm_encoder *encoder,
                               struct drm_display_mode *adjusted_mode)
 {
        struct drm_i915_private *dev_priv = encoder->dev->dev_private;
-       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+       struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
+       struct intel_hdmi *intel_hdmi = &intel_dig_port->hdmi;
        u32 reg = VIDEO_DIP_CTL;
        u32 val = I915_READ(reg);
        u32 port;
@@ -391,11 +400,11 @@ static void g4x_set_infoframes(struct drm_encoder *encoder,
                return;
        }
 
-       switch (intel_hdmi->sdvox_reg) {
-       case SDVOB:
+       switch (intel_dig_port->port) {
+       case PORT_B:
                port = VIDEO_DIP_PORT_B;
                break;
-       case SDVOC:
+       case PORT_C:
                port = VIDEO_DIP_PORT_C;
                break;
        default:
@@ -428,7 +437,8 @@ static void ibx_set_infoframes(struct drm_encoder *encoder,
 {
        struct drm_i915_private *dev_priv = encoder->dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
-       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+       struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
+       struct intel_hdmi *intel_hdmi = &intel_dig_port->hdmi;
        u32 reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
        u32 val = I915_READ(reg);
        u32 port;
@@ -447,14 +457,14 @@ static void ibx_set_infoframes(struct drm_encoder *encoder,
                return;
        }
 
-       switch (intel_hdmi->sdvox_reg) {
-       case HDMIB:
+       switch (intel_dig_port->port) {
+       case PORT_B:
                port = VIDEO_DIP_PORT_B;
                break;
-       case HDMIC:
+       case PORT_C:
                port = VIDEO_DIP_PORT_C;
                break;
-       case HDMID:
+       case PORT_D:
                port = VIDEO_DIP_PORT_D;
                break;
        default:
@@ -766,6 +776,20 @@ bool intel_hdmi_mode_fixup(struct drm_encoder *encoder,
                           const struct drm_display_mode *mode,
                           struct drm_display_mode *adjusted_mode)
 {
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+
+       if (intel_hdmi->color_range_auto) {
+               /* See CEA-861-E - 5.1 Default Encoding Parameters */
+               if (intel_hdmi->has_hdmi_sink &&
+                   drm_mode_cea_vic(adjusted_mode) > 1)
+                       intel_hdmi->color_range = SDVO_COLOR_RANGE_16_235;
+               else
+                       intel_hdmi->color_range = 0;
+       }
+
+       if (intel_hdmi->color_range)
+               adjusted_mode->private_flags |= INTEL_MODE_LIMITED_COLOR_RANGE;
+
        return true;
 }
 
@@ -773,14 +797,15 @@ static bool g4x_hdmi_connected(struct intel_hdmi *intel_hdmi)
 {
        struct drm_device *dev = intel_hdmi_to_dev(intel_hdmi);
        struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_digital_port *intel_dig_port = hdmi_to_dig_port(intel_hdmi);
        uint32_t bit;
 
-       switch (intel_hdmi->sdvox_reg) {
-       case SDVOB:
-               bit = HDMIB_HOTPLUG_LIVE_STATUS;
+       switch (intel_dig_port->port) {
+       case PORT_B:
+               bit = PORTB_HOTPLUG_LIVE_STATUS;
                break;
-       case SDVOC:
-               bit = HDMIC_HOTPLUG_LIVE_STATUS;
+       case PORT_C:
+               bit = PORTC_HOTPLUG_LIVE_STATUS;
                break;
        default:
                bit = 0;
@@ -793,19 +818,25 @@ static bool g4x_hdmi_connected(struct intel_hdmi *intel_hdmi)
 static enum drm_connector_status
 intel_hdmi_detect(struct drm_connector *connector, bool force)
 {
+       struct drm_device *dev = connector->dev;
        struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
        struct intel_digital_port *intel_dig_port =
                hdmi_to_dig_port(intel_hdmi);
        struct intel_encoder *intel_encoder = &intel_dig_port->base;
-       struct drm_i915_private *dev_priv = connector->dev->dev_private;
+       struct drm_i915_private *dev_priv = dev->dev_private;
        struct edid *edid;
        enum drm_connector_status status = connector_status_disconnected;
 
-       if (IS_G4X(connector->dev) && !g4x_hdmi_connected(intel_hdmi))
+
+       if (IS_G4X(dev) && !g4x_hdmi_connected(intel_hdmi))
                return status;
+       else if (HAS_PCH_SPLIT(dev) &&
+                !ibx_digital_port_connected(dev_priv, intel_dig_port))
+                return status;
 
        intel_hdmi->has_hdmi_sink = false;
        intel_hdmi->has_audio = false;
+       intel_hdmi->rgb_quant_range_selectable = false;
        edid = drm_get_edid(connector,
                            intel_gmbus_get_adapter(dev_priv,
                                                    intel_hdmi->ddc_bus));
@@ -817,6 +848,8 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
                                intel_hdmi->has_hdmi_sink =
                                                drm_detect_hdmi_monitor(edid);
                        intel_hdmi->has_audio = drm_detect_monitor_audio(edid);
+                       intel_hdmi->rgb_quant_range_selectable =
+                               drm_rgb_quant_range_selectable(edid);
                }
                kfree(edid);
        }
@@ -902,21 +935,29 @@ intel_hdmi_set_property(struct drm_connector *connector,
        }
 
        if (property == dev_priv->broadcast_rgb_property) {
-               if (val == !!intel_hdmi->color_range)
-                       return 0;
-
-               intel_hdmi->color_range = val ? SDVO_COLOR_RANGE_16_235 : 0;
+               switch (val) {
+               case INTEL_BROADCAST_RGB_AUTO:
+                       intel_hdmi->color_range_auto = true;
+                       break;
+               case INTEL_BROADCAST_RGB_FULL:
+                       intel_hdmi->color_range_auto = false;
+                       intel_hdmi->color_range = 0;
+                       break;
+               case INTEL_BROADCAST_RGB_LIMITED:
+                       intel_hdmi->color_range_auto = false;
+                       intel_hdmi->color_range = SDVO_COLOR_RANGE_16_235;
+                       break;
+               default:
+                       return -EINVAL;
+               }
                goto done;
        }
 
        return -EINVAL;
 
 done:
-       if (intel_dig_port->base.base.crtc) {
-               struct drm_crtc *crtc = intel_dig_port->base.base.crtc;
-               intel_set_mode(crtc, &crtc->mode,
-                              crtc->x, crtc->y, crtc->fb);
-       }
+       if (intel_dig_port->base.base.crtc)
+               intel_crtc_restore_mode(intel_dig_port->base.base.crtc);
 
        return 0;
 }
@@ -931,7 +972,6 @@ static void intel_hdmi_destroy(struct drm_connector *connector)
 static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = {
        .mode_fixup = intel_hdmi_mode_fixup,
        .mode_set = intel_hdmi_mode_set,
-       .disable = intel_encoder_noop,
 };
 
 static const struct drm_connector_funcs intel_hdmi_connector_funcs = {
@@ -957,6 +997,7 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *c
 {
        intel_attach_force_audio_property(connector);
        intel_attach_broadcast_rgb_property(connector);
+       intel_hdmi->color_range_auto = true;
 }
 
 void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
@@ -980,15 +1021,15 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
        switch (port) {
        case PORT_B:
                intel_hdmi->ddc_bus = GMBUS_PORT_DPB;
-               dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS;
+               dev_priv->hotplug_supported_mask |= PORTB_HOTPLUG_INT_STATUS;
                break;
        case PORT_C:
                intel_hdmi->ddc_bus = GMBUS_PORT_DPC;
-               dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS;
+               dev_priv->hotplug_supported_mask |= PORTC_HOTPLUG_INT_STATUS;
                break;
        case PORT_D:
                intel_hdmi->ddc_bus = GMBUS_PORT_DPD;
-               dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS;
+               dev_priv->hotplug_supported_mask |= PORTD_HOTPLUG_INT_STATUS;
                break;
        case PORT_A:
                /* Internal port only for eDP. */
@@ -1013,7 +1054,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
                intel_hdmi->set_infoframes = cpt_set_infoframes;
        }
 
-       if (IS_HASWELL(dev))
+       if (HAS_DDI(dev))
                intel_connector->get_hw_state = intel_ddi_connector_get_hw_state;
        else
                intel_connector->get_hw_state = intel_connector_get_hw_state;
index 3ef5af15b8124e0e672b62943b7ec26ed0022261..acf8aec9ada7162543954e4adf7c7b28d1bb4770 100644 (file)
@@ -63,6 +63,7 @@ intel_i2c_reset(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        I915_WRITE(dev_priv->gpio_mmio_base + GMBUS0, 0);
+       I915_WRITE(dev_priv->gpio_mmio_base + GMBUS4, 0);
 }
 
 static void intel_i2c_quirk_set(struct drm_i915_private *dev_priv, bool enable)
@@ -202,6 +203,68 @@ intel_gpio_setup(struct intel_gmbus *bus, u32 pin)
        algo->data = bus;
 }
 
+#define HAS_GMBUS_IRQ(dev) (INTEL_INFO(dev)->gen >= 4)
+static int
+gmbus_wait_hw_status(struct drm_i915_private *dev_priv,
+                    u32 gmbus2_status,
+                    u32 gmbus4_irq_en)
+{
+       int i;
+       int reg_offset = dev_priv->gpio_mmio_base;
+       u32 gmbus2 = 0;
+       DEFINE_WAIT(wait);
+
+       /* Important: The hw handles only the first bit, so set only one! Since
+        * we also need to check for NAKs besides the hw ready/idle signal, we
+        * need to wake up periodically and check that ourselves. */
+       I915_WRITE(GMBUS4 + reg_offset, gmbus4_irq_en);
+
+       for (i = 0; i < msecs_to_jiffies(50) + 1; i++) {
+               prepare_to_wait(&dev_priv->gmbus_wait_queue, &wait,
+                               TASK_UNINTERRUPTIBLE);
+
+               gmbus2 = I915_READ_NOTRACE(GMBUS2 + reg_offset);
+               if (gmbus2 & (GMBUS_SATOER | gmbus2_status))
+                       break;
+
+               schedule_timeout(1);
+       }
+       finish_wait(&dev_priv->gmbus_wait_queue, &wait);
+
+       I915_WRITE(GMBUS4 + reg_offset, 0);
+
+       if (gmbus2 & GMBUS_SATOER)
+               return -ENXIO;
+       if (gmbus2 & gmbus2_status)
+               return 0;
+       return -ETIMEDOUT;
+}
+
+static int
+gmbus_wait_idle(struct drm_i915_private *dev_priv)
+{
+       int ret;
+       int reg_offset = dev_priv->gpio_mmio_base;
+
+#define C ((I915_READ_NOTRACE(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0)
+
+       if (!HAS_GMBUS_IRQ(dev_priv->dev))
+               return wait_for(C, 10);
+
+       /* Important: The hw handles only the first bit, so set only one! */
+       I915_WRITE(GMBUS4 + reg_offset, GMBUS_IDLE_EN);
+
+       ret = wait_event_timeout(dev_priv->gmbus_wait_queue, C, 10);
+
+       I915_WRITE(GMBUS4 + reg_offset, 0);
+
+       if (ret)
+               return 0;
+       else
+               return -ETIMEDOUT;
+#undef C
+}
+
 static int
 gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg,
                u32 gmbus1_index)
@@ -219,15 +282,11 @@ gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg,
        while (len) {
                int ret;
                u32 val, loop = 0;
-               u32 gmbus2;
 
-               ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) &
-                              (GMBUS_SATOER | GMBUS_HW_RDY),
-                              50);
+               ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY,
+                                          GMBUS_HW_RDY_EN);
                if (ret)
-                       return -ETIMEDOUT;
-               if (gmbus2 & GMBUS_SATOER)
-                       return -ENXIO;
+                       return ret;
 
                val = I915_READ(GMBUS3 + reg_offset);
                do {
@@ -261,7 +320,6 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg)
                   GMBUS_SLAVE_WRITE | GMBUS_SW_RDY);
        while (len) {
                int ret;
-               u32 gmbus2;
 
                val = loop = 0;
                do {
@@ -270,13 +328,10 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg)
 
                I915_WRITE(GMBUS3 + reg_offset, val);
 
-               ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) &
-                              (GMBUS_SATOER | GMBUS_HW_RDY),
-                              50);
+               ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY,
+                                          GMBUS_HW_RDY_EN);
                if (ret)
-                       return -ETIMEDOUT;
-               if (gmbus2 & GMBUS_SATOER)
-                       return -ENXIO;
+                       return ret;
        }
        return 0;
 }
@@ -345,8 +400,6 @@ gmbus_xfer(struct i2c_adapter *adapter,
        I915_WRITE(GMBUS0 + reg_offset, bus->reg0);
 
        for (i = 0; i < num; i++) {
-               u32 gmbus2;
-
                if (gmbus_is_index_read(msgs, i, num)) {
                        ret = gmbus_xfer_index_read(dev_priv, &msgs[i]);
                        i += 1;  /* set i to the index of the read xfer */
@@ -361,13 +414,12 @@ gmbus_xfer(struct i2c_adapter *adapter,
                if (ret == -ENXIO)
                        goto clear_err;
 
-               ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) &
-                              (GMBUS_SATOER | GMBUS_HW_WAIT_PHASE),
-                              50);
+               ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_WAIT_PHASE,
+                                          GMBUS_HW_WAIT_EN);
+               if (ret == -ENXIO)
+                       goto clear_err;
                if (ret)
                        goto timeout;
-               if (gmbus2 & GMBUS_SATOER)
-                       goto clear_err;
        }
 
        /* Generate a STOP condition on the bus. Note that gmbus can't generata
@@ -380,8 +432,7 @@ gmbus_xfer(struct i2c_adapter *adapter,
         * We will re-enable it at the start of the next xfer,
         * till then let it sleep.
         */
-       if (wait_for((I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0,
-                    10)) {
+       if (gmbus_wait_idle(dev_priv)) {
                DRM_DEBUG_KMS("GMBUS [%s] timed out waiting for idle\n",
                         adapter->name);
                ret = -ETIMEDOUT;
@@ -405,8 +456,7 @@ clear_err:
         * it's slow responding and only answers on the 2nd retry.
         */
        ret = -ENXIO;
-       if (wait_for((I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0,
-                    10)) {
+       if (gmbus_wait_idle(dev_priv)) {
                DRM_DEBUG_KMS("GMBUS [%s] timed out after NAK\n",
                              adapter->name);
                ret = -ETIMEDOUT;
@@ -465,10 +515,13 @@ int intel_setup_gmbus(struct drm_device *dev)
 
        if (HAS_PCH_SPLIT(dev))
                dev_priv->gpio_mmio_base = PCH_GPIOA - GPIOA;
+       else if (IS_VALLEYVIEW(dev))
+               dev_priv->gpio_mmio_base = VLV_DISPLAY_BASE;
        else
                dev_priv->gpio_mmio_base = 0;
 
        mutex_init(&dev_priv->gmbus_mutex);
+       init_waitqueue_head(&dev_priv->gmbus_wait_queue);
 
        for (i = 0; i < GMBUS_NUM_PORTS; i++) {
                struct intel_gmbus *bus = &dev_priv->gmbus[i];
index 17aee74258ad4dfa43083e431fc3f0b224aa4ec4..3d1d97488cc95527c699dd6eb8b0c2ec78cc6adf 100644 (file)
@@ -51,7 +51,8 @@ struct intel_lvds_encoder {
 
        u32 pfit_control;
        u32 pfit_pgm_ratios;
-       bool pfit_dirty;
+       bool is_dual_link;
+       u32 reg;
 
        struct intel_lvds_connector *attached_connector;
 };
@@ -71,15 +72,10 @@ static bool intel_lvds_get_hw_state(struct intel_encoder *encoder,
 {
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 lvds_reg, tmp;
-
-       if (HAS_PCH_SPLIT(dev)) {
-               lvds_reg = PCH_LVDS;
-       } else {
-               lvds_reg = LVDS;
-       }
+       struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
+       u32 tmp;
 
-       tmp = I915_READ(lvds_reg);
+       tmp = I915_READ(lvds_encoder->reg);
 
        if (!(tmp & LVDS_PORT_EN))
                return false;
@@ -92,6 +88,91 @@ static bool intel_lvds_get_hw_state(struct intel_encoder *encoder,
        return true;
 }
 
+/* The LVDS pin pair needs to be on before the DPLLs are enabled.
+ * This is an exception to the general rule that mode_set doesn't turn
+ * things on.
+ */
+static void intel_pre_pll_enable_lvds(struct intel_encoder *encoder)
+{
+       struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
+       struct drm_display_mode *fixed_mode =
+               lvds_encoder->attached_connector->base.panel.fixed_mode;
+       int pipe = intel_crtc->pipe;
+       u32 temp;
+
+       temp = I915_READ(lvds_encoder->reg);
+       temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP;
+
+       if (HAS_PCH_CPT(dev)) {
+               temp &= ~PORT_TRANS_SEL_MASK;
+               temp |= PORT_TRANS_SEL_CPT(pipe);
+       } else {
+               if (pipe == 1) {
+                       temp |= LVDS_PIPEB_SELECT;
+               } else {
+                       temp &= ~LVDS_PIPEB_SELECT;
+               }
+       }
+
+       /* set the corresponsding LVDS_BORDER bit */
+       temp |= dev_priv->lvds_border_bits;
+       /* Set the B0-B3 data pairs corresponding to whether we're going to
+        * set the DPLLs for dual-channel mode or not.
+        */
+       if (lvds_encoder->is_dual_link)
+               temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP;
+       else
+               temp &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP);
+
+       /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP)
+        * appropriately here, but we need to look more thoroughly into how
+        * panels behave in the two modes.
+        */
+
+       /* Set the dithering flag on LVDS as needed, note that there is no
+        * special lvds dither control bit on pch-split platforms, dithering is
+        * only controlled through the PIPECONF reg. */
+       if (INTEL_INFO(dev)->gen == 4) {
+               if (dev_priv->lvds_dither)
+                       temp |= LVDS_ENABLE_DITHER;
+               else
+                       temp &= ~LVDS_ENABLE_DITHER;
+       }
+       temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY);
+       if (fixed_mode->flags & DRM_MODE_FLAG_NHSYNC)
+               temp |= LVDS_HSYNC_POLARITY;
+       if (fixed_mode->flags & DRM_MODE_FLAG_NVSYNC)
+               temp |= LVDS_VSYNC_POLARITY;
+
+       I915_WRITE(lvds_encoder->reg, temp);
+}
+
+static void intel_pre_enable_lvds(struct intel_encoder *encoder)
+{
+       struct drm_device *dev = encoder->base.dev;
+       struct intel_lvds_encoder *enc = to_lvds_encoder(&encoder->base);
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (HAS_PCH_SPLIT(dev) || !enc->pfit_control)
+               return;
+
+       /*
+        * Enable automatic panel scaling so that non-native modes
+        * fill the screen.  The panel fitter should only be
+        * adjusted whilst the pipe is disabled, according to
+        * register description and PRM.
+        */
+       DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n",
+                     enc->pfit_control,
+                     enc->pfit_pgm_ratios);
+
+       I915_WRITE(PFIT_PGM_RATIOS, enc->pfit_pgm_ratios);
+       I915_WRITE(PFIT_CONTROL, enc->pfit_control);
+}
+
 /**
  * Sets the power state for the panel.
  */
@@ -101,38 +182,20 @@ static void intel_enable_lvds(struct intel_encoder *encoder)
        struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
        struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 ctl_reg, lvds_reg, stat_reg;
+       u32 ctl_reg, stat_reg;
 
        if (HAS_PCH_SPLIT(dev)) {
                ctl_reg = PCH_PP_CONTROL;
-               lvds_reg = PCH_LVDS;
                stat_reg = PCH_PP_STATUS;
        } else {
                ctl_reg = PP_CONTROL;
-               lvds_reg = LVDS;
                stat_reg = PP_STATUS;
        }
 
-       I915_WRITE(lvds_reg, I915_READ(lvds_reg) | LVDS_PORT_EN);
-
-       if (lvds_encoder->pfit_dirty) {
-               /*
-                * Enable automatic panel scaling so that non-native modes
-                * fill the screen.  The panel fitter should only be
-                * adjusted whilst the pipe is disabled, according to
-                * register description and PRM.
-                */
-               DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n",
-                             lvds_encoder->pfit_control,
-                             lvds_encoder->pfit_pgm_ratios);
-
-               I915_WRITE(PFIT_PGM_RATIOS, lvds_encoder->pfit_pgm_ratios);
-               I915_WRITE(PFIT_CONTROL, lvds_encoder->pfit_control);
-               lvds_encoder->pfit_dirty = false;
-       }
+       I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) | LVDS_PORT_EN);
 
        I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON);
-       POSTING_READ(lvds_reg);
+       POSTING_READ(lvds_encoder->reg);
        if (wait_for((I915_READ(stat_reg) & PP_ON) != 0, 1000))
                DRM_ERROR("timed out waiting for panel to power on\n");
 
@@ -144,15 +207,13 @@ static void intel_disable_lvds(struct intel_encoder *encoder)
        struct drm_device *dev = encoder->base.dev;
        struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 ctl_reg, lvds_reg, stat_reg;
+       u32 ctl_reg, stat_reg;
 
        if (HAS_PCH_SPLIT(dev)) {
                ctl_reg = PCH_PP_CONTROL;
-               lvds_reg = PCH_LVDS;
                stat_reg = PCH_PP_STATUS;
        } else {
                ctl_reg = PP_CONTROL;
-               lvds_reg = LVDS;
                stat_reg = PP_STATUS;
        }
 
@@ -162,13 +223,8 @@ static void intel_disable_lvds(struct intel_encoder *encoder)
        if (wait_for((I915_READ(stat_reg) & PP_ON) == 0, 1000))
                DRM_ERROR("timed out waiting for panel to power off\n");
 
-       if (lvds_encoder->pfit_control) {
-               I915_WRITE(PFIT_CONTROL, 0);
-               lvds_encoder->pfit_dirty = true;
-       }
-
-       I915_WRITE(lvds_reg, I915_READ(lvds_reg) & ~LVDS_PORT_EN);
-       POSTING_READ(lvds_reg);
+       I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) & ~LVDS_PORT_EN);
+       POSTING_READ(lvds_encoder->reg);
 }
 
 static int intel_lvds_mode_valid(struct drm_connector *connector,
@@ -406,7 +462,6 @@ out:
            pfit_pgm_ratios != lvds_encoder->pfit_pgm_ratios) {
                lvds_encoder->pfit_control = pfit_control;
                lvds_encoder->pfit_pgm_ratios = pfit_pgm_ratios;
-               lvds_encoder->pfit_dirty = true;
        }
        dev_priv->lvds_border_bits = border;
 
@@ -492,13 +547,14 @@ static const struct dmi_system_id intel_no_modeset_on_lid[] = {
 };
 
 /*
- * Lid events. Note the use of 'modeset_on_lid':
- *  - we set it on lid close, and reset it on open
+ * Lid events. Note the use of 'modeset':
+ *  - we set it to MODESET_ON_LID_OPEN on lid close,
+ *    and set it to MODESET_DONE on open
  *  - we use it as a "only once" bit (ie we ignore
- *    duplicate events where it was already properly
- *    set/reset)
- *  - the suspend/resume paths will also set it to
- *    zero, since they restore the mode ("lid open").
+ *    duplicate events where it was already properly set)
+ *  - the suspend/resume paths will set it to
+ *    MODESET_SUSPENDED and ignore the lid open event,
+ *    because they restore the mode ("lid open").
  */
 static int intel_lid_notify(struct notifier_block *nb, unsigned long val,
                            void *unused)
@@ -512,6 +568,9 @@ static int intel_lid_notify(struct notifier_block *nb, unsigned long val,
        if (dev->switch_power_state != DRM_SWITCH_POWER_ON)
                return NOTIFY_OK;
 
+       mutex_lock(&dev_priv->modeset_restore_lock);
+       if (dev_priv->modeset_restore == MODESET_SUSPENDED)
+               goto exit;
        /*
         * check and update the status of LVDS connector after receiving
         * the LID nofication event.
@@ -520,21 +579,24 @@ static int intel_lid_notify(struct notifier_block *nb, unsigned long val,
 
        /* Don't force modeset on machines where it causes a GPU lockup */
        if (dmi_check_system(intel_no_modeset_on_lid))
-               return NOTIFY_OK;
+               goto exit;
        if (!acpi_lid_open()) {
-               dev_priv->modeset_on_lid = 1;
-               return NOTIFY_OK;
+               /* do modeset on next lid open event */
+               dev_priv->modeset_restore = MODESET_ON_LID_OPEN;
+               goto exit;
        }
 
-       if (!dev_priv->modeset_on_lid)
-               return NOTIFY_OK;
-
-       dev_priv->modeset_on_lid = 0;
+       if (dev_priv->modeset_restore == MODESET_DONE)
+               goto exit;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
        intel_modeset_setup_hw_state(dev, true);
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
+
+       dev_priv->modeset_restore = MODESET_DONE;
 
+exit:
+       mutex_unlock(&dev_priv->modeset_restore_lock);
        return NOTIFY_OK;
 }
 
@@ -591,8 +653,7 @@ static int intel_lvds_set_property(struct drm_connector *connector,
                         * If the CRTC is enabled, the display will be changed
                         * according to the new panel fitting mode.
                         */
-                       intel_set_mode(crtc, &crtc->mode,
-                                      crtc->x, crtc->y, crtc->fb);
+                       intel_crtc_restore_mode(crtc);
                }
        }
 
@@ -602,7 +663,6 @@ static int intel_lvds_set_property(struct drm_connector *connector,
 static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = {
        .mode_fixup = intel_lvds_mode_fixup,
        .mode_set = intel_lvds_mode_set,
-       .disable = intel_encoder_noop,
 };
 
 static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = {
@@ -895,6 +955,66 @@ static bool lvds_is_present_in_vbt(struct drm_device *dev,
        return false;
 }
 
+static int intel_dual_link_lvds_callback(const struct dmi_system_id *id)
+{
+       DRM_INFO("Forcing lvds to dual link mode on %s\n", id->ident);
+       return 1;
+}
+
+static const struct dmi_system_id intel_dual_link_lvds[] = {
+       {
+               .callback = intel_dual_link_lvds_callback,
+               .ident = "Apple MacBook Pro (Core i5/i7 Series)",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro8,2"),
+               },
+       },
+       { }     /* terminating entry */
+};
+
+bool intel_is_dual_link_lvds(struct drm_device *dev)
+{
+       struct intel_encoder *encoder;
+       struct intel_lvds_encoder *lvds_encoder;
+
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list,
+                           base.head) {
+               if (encoder->type == INTEL_OUTPUT_LVDS) {
+                       lvds_encoder = to_lvds_encoder(&encoder->base);
+
+                       return lvds_encoder->is_dual_link;
+               }
+       }
+
+       return false;
+}
+
+static bool compute_is_dual_link_lvds(struct intel_lvds_encoder *lvds_encoder)
+{
+       struct drm_device *dev = lvds_encoder->base.base.dev;
+       unsigned int val;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       /* use the module option value if specified */
+       if (i915_lvds_channel_mode > 0)
+               return i915_lvds_channel_mode == 2;
+
+       if (dmi_check_system(intel_dual_link_lvds))
+               return true;
+
+       /* BIOS should set the proper LVDS register value at boot, but
+        * in reality, it doesn't set the value when the lid is closed;
+        * we need to check "the value to be set" in VBT when LVDS
+        * register is uninitialized.
+        */
+       val = I915_READ(lvds_encoder->reg);
+       if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED)))
+               val = dev_priv->bios_lvds_val;
+
+       return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP;
+}
+
 static bool intel_lvds_supported(struct drm_device *dev)
 {
        /* With the introduction of the PCH we gained a dedicated
@@ -980,6 +1100,8 @@ bool intel_lvds_init(struct drm_device *dev)
                         DRM_MODE_ENCODER_LVDS);
 
        intel_encoder->enable = intel_enable_lvds;
+       intel_encoder->pre_enable = intel_pre_enable_lvds;
+       intel_encoder->pre_pll_enable = intel_pre_pll_enable_lvds;
        intel_encoder->disable = intel_disable_lvds;
        intel_encoder->get_hw_state = intel_lvds_get_hw_state;
        intel_connector->get_hw_state = intel_connector_get_hw_state;
@@ -1001,6 +1123,12 @@ bool intel_lvds_init(struct drm_device *dev)
        connector->interlace_allowed = false;
        connector->doublescan_allowed = false;
 
+       if (HAS_PCH_SPLIT(dev)) {
+               lvds_encoder->reg = PCH_LVDS;
+       } else {
+               lvds_encoder->reg = LVDS;
+       }
+
        /* create the scaling mode property */
        drm_mode_create_scaling_mode_property(dev);
        drm_object_attach_property(&connector->base,
@@ -1101,6 +1229,10 @@ bool intel_lvds_init(struct drm_device *dev)
                goto failed;
 
 out:
+       lvds_encoder->is_dual_link = compute_is_dual_link_lvds(lvds_encoder);
+       DRM_DEBUG_KMS("detected %s-link lvds configuration\n",
+                     lvds_encoder->is_dual_link ? "dual" : "single");
+
        /*
         * Unlock registers and just
         * leave them unlocked
index b00f1c83adce2d908700f068e5dc4200f0bcfffa..0e860f39933d8581cce867d8a3eaa8d955edc4a9 100644 (file)
@@ -28,7 +28,6 @@
 #include <linux/fb.h>
 #include <drm/drm_edid.h>
 #include <drm/drmP.h>
-#include <drm/drm_edid.h>
 #include "intel_drv.h"
 #include "i915_drv.h"
 
@@ -101,8 +100,9 @@ intel_attach_force_audio_property(struct drm_connector *connector)
 }
 
 static const struct drm_prop_enum_list broadcast_rgb_names[] = {
-       { 0, "Full" },
-       { 1, "Limited 16:235" },
+       { INTEL_BROADCAST_RGB_AUTO, "Automatic" },
+       { INTEL_BROADCAST_RGB_FULL, "Full" },
+       { INTEL_BROADCAST_RGB_LIMITED, "Limited 16:235" },
 };
 
 void
index 7741c22c934cc7d38528a7cf738d5885c05289a2..4d338740f2cb8332ec1d86970b9bee26049b1744 100644 (file)
@@ -347,7 +347,7 @@ static void intel_didl_outputs(struct drm_device *dev)
        int i = 0;
 
        handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev);
-       if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev)))
+       if (!handle || acpi_bus_get_device(handle, &acpi_dev))
                return;
 
        if (acpi_is_video_device(acpi_dev))
index d7bc817f51a024b803986e76fa492146632da66d..67a2501d519db97bc59e24f0a93c334e58fc1f74 100644 (file)
@@ -195,7 +195,7 @@ intel_overlay_map_regs(struct intel_overlay *overlay)
        if (OVERLAY_NEEDS_PHYSICAL(overlay->dev))
                regs = (struct overlay_registers __iomem *)overlay->reg_bo->phys_obj->handle->vaddr;
        else
-               regs = io_mapping_map_wc(dev_priv->mm.gtt_mapping,
+               regs = io_mapping_map_wc(dev_priv->gtt.mappable,
                                         overlay->reg_bo->gtt_offset);
 
        return regs;
@@ -1045,13 +1045,13 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
        }
 
        if (!(put_image_rec->flags & I915_OVERLAY_ENABLE)) {
-               mutex_lock(&dev->mode_config.mutex);
+               drm_modeset_lock_all(dev);
                mutex_lock(&dev->struct_mutex);
 
                ret = intel_overlay_switch_off(overlay);
 
                mutex_unlock(&dev->struct_mutex);
-               mutex_unlock(&dev->mode_config.mutex);
+               drm_modeset_unlock_all(dev);
 
                return ret;
        }
@@ -1075,7 +1075,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
                goto out_free;
        }
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
        mutex_lock(&dev->struct_mutex);
 
        if (new_bo->tiling_mode) {
@@ -1157,7 +1157,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
                goto out_unlock;
 
        mutex_unlock(&dev->struct_mutex);
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
 
        kfree(params);
 
@@ -1165,7 +1165,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
 
 out_unlock:
        mutex_unlock(&dev->struct_mutex);
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
        drm_gem_object_unreference_unlocked(&new_bo->base);
 out_free:
        kfree(params);
@@ -1241,7 +1241,7 @@ int intel_overlay_attrs(struct drm_device *dev, void *data,
                return -ENODEV;
        }
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
        mutex_lock(&dev->struct_mutex);
 
        ret = -EINVAL;
@@ -1307,7 +1307,7 @@ int intel_overlay_attrs(struct drm_device *dev, void *data,
        ret = 0;
 out_unlock:
        mutex_unlock(&dev->struct_mutex);
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
 
        return ret;
 }
@@ -1333,8 +1333,10 @@ void intel_setup_overlay(struct drm_device *dev)
 
        overlay->dev = dev;
 
-       reg_bo = i915_gem_alloc_object(dev, PAGE_SIZE);
-       if (!reg_bo)
+       reg_bo = i915_gem_object_create_stolen(dev, PAGE_SIZE);
+       if (reg_bo == NULL)
+               reg_bo = i915_gem_alloc_object(dev, PAGE_SIZE);
+       if (reg_bo == NULL)
                goto out_free;
        overlay->reg_bo = reg_bo;
 
@@ -1432,7 +1434,7 @@ intel_overlay_map_regs_atomic(struct intel_overlay *overlay)
                regs = (struct overlay_registers __iomem *)
                        overlay->reg_bo->phys_obj->handle->vaddr;
        else
-               regs = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping,
+               regs = io_mapping_map_atomic_wc(dev_priv->gtt.mappable,
                                                overlay->reg_bo->gtt_offset);
 
        return regs;
index bee8cb6108a7d44883286ef4b35bb6d7f8713a0f..a3730e0289e5eefa15afb9f04a54c65c817f7886 100644 (file)
@@ -321,6 +321,9 @@ void intel_panel_enable_backlight(struct drm_device *dev,
        if (dev_priv->backlight_level == 0)
                dev_priv->backlight_level = intel_panel_get_max_backlight(dev);
 
+       dev_priv->backlight_enabled = true;
+       intel_panel_actually_set_backlight(dev, dev_priv->backlight_level);
+
        if (INTEL_INFO(dev)->gen >= 4) {
                uint32_t reg, tmp;
 
@@ -356,12 +359,12 @@ void intel_panel_enable_backlight(struct drm_device *dev,
        }
 
 set_level:
-       /* Call below after setting BLC_PWM_CPU_CTL2 and BLC_PWM_PCH_CTL1.
-        * BLC_PWM_CPU_CTL may be cleared to zero automatically when these
-        * registers are set.
+       /* Check the current backlight level and try to set again if it's zero.
+        * On some machines, BLC_PWM_CPU_CTL is cleared to zero automatically
+        * when BLC_PWM_CPU_CTL2 and BLC_PWM_PCH_CTL1 are written.
         */
-       dev_priv->backlight_enabled = true;
-       intel_panel_actually_set_backlight(dev, dev_priv->backlight_level);
+       if (!intel_panel_get_backlight(dev))
+               intel_panel_actually_set_backlight(dev, dev_priv->backlight_level);
 }
 
 static void intel_panel_init_backlight(struct drm_device *dev)
index 3280cffe50f4486a4812d809d51ea904f79f04a9..61fee7fcdc2c1efeb7c7c0fd1d0fd058e2dd3199 100644 (file)
@@ -447,12 +447,6 @@ void intel_update_fbc(struct drm_device *dev)
                dev_priv->no_fbc_reason = FBC_MODULE_PARAM;
                goto out_disable;
        }
-       if (intel_fb->obj->base.size > dev_priv->cfb_size) {
-               DRM_DEBUG_KMS("framebuffer too large, disabling "
-                             "compression\n");
-               dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL;
-               goto out_disable;
-       }
        if ((crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) ||
            (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN)) {
                DRM_DEBUG_KMS("mode incompatible with compression, "
@@ -486,6 +480,14 @@ void intel_update_fbc(struct drm_device *dev)
        if (in_dbg_master())
                goto out_disable;
 
+       if (i915_gem_stolen_setup_compression(dev, intel_fb->obj->base.size)) {
+               DRM_INFO("not enough stolen space for compressed buffer (need %zd bytes), disabling\n", intel_fb->obj->base.size);
+               DRM_INFO("hint: you may be able to increase stolen memory size in the BIOS to avoid this\n");
+               DRM_DEBUG_KMS("framebuffer too large, disabling compression\n");
+               dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL;
+               goto out_disable;
+       }
+
        /* If the scanout has not changed, don't modify the FBC settings.
         * Note that we make the fundamental assumption that the fb->obj
         * cannot be unpinned (and have its GTT offset and fence revoked)
@@ -533,6 +535,7 @@ out_disable:
                DRM_DEBUG_KMS("unsupported config, disabling FBC\n");
                intel_disable_fbc(dev);
        }
+       i915_gem_stolen_cleanup_compression(dev);
 }
 
 static void i915_pineview_get_mem_freq(struct drm_device *dev)
@@ -2286,7 +2289,6 @@ err_unpin:
        i915_gem_object_unpin(ctx);
 err_unref:
        drm_gem_object_unreference(&ctx->base);
-       mutex_unlock(&dev->struct_mutex);
        return NULL;
 }
 
@@ -3581,6 +3583,19 @@ static void cpt_init_clock_gating(struct drm_device *dev)
        }
 }
 
+static void gen6_check_mch_setup(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t tmp;
+
+       tmp = I915_READ(MCH_SSKPD);
+       if ((tmp & MCH_SSKPD_WM0_MASK) != MCH_SSKPD_WM0_VAL) {
+               DRM_INFO("Wrong MCH_SSKPD value: 0x%08x\n", tmp);
+               DRM_INFO("This can cause pipe underruns and display issues.\n");
+               DRM_INFO("Please upgrade your BIOS to fix this.\n");
+       }
+}
+
 static void gen6_init_clock_gating(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -3673,6 +3688,8 @@ static void gen6_init_clock_gating(struct drm_device *dev)
        I915_WRITE(GEN6_GT_MODE, _MASKED_BIT_ENABLE(GEN6_GT_MODE_HI));
 
        cpt_init_clock_gating(dev);
+
+       gen6_check_mch_setup(dev);
 }
 
 static void gen7_setup_fixed_func_scheduler(struct drm_i915_private *dev_priv)
@@ -3684,6 +3701,10 @@ static void gen7_setup_fixed_func_scheduler(struct drm_i915_private *dev_priv)
        reg |= GEN7_FF_VS_SCHED_HW;
        reg |= GEN7_FF_DS_SCHED_HW;
 
+       /* WaVSRefCountFullforceMissDisable */
+       if (IS_HASWELL(dev_priv->dev))
+               reg &= ~GEN7_FF_VS_REF_CNT_FFME;
+
        I915_WRITE(GEN7_FF_THREAD_MODE, reg);
 }
 
@@ -3854,6 +3875,8 @@ static void ivybridge_init_clock_gating(struct drm_device *dev)
        I915_WRITE(GEN6_MBCUNIT_SNPCR, snpcr);
 
        cpt_init_clock_gating(dev);
+
+       gen6_check_mch_setup(dev);
 }
 
 static void valleyview_init_clock_gating(struct drm_device *dev)
@@ -4047,35 +4070,57 @@ void intel_init_clock_gating(struct drm_device *dev)
        dev_priv->display.init_clock_gating(dev);
 }
 
-/* Starting with Haswell, we have different power wells for
- * different parts of the GPU. This attempts to enable them all.
- */
-void intel_init_power_wells(struct drm_device *dev)
+void intel_set_power_well(struct drm_device *dev, bool enable)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       unsigned long power_wells[] = {
-               HSW_PWR_WELL_CTL1,
-               HSW_PWR_WELL_CTL2,
-               HSW_PWR_WELL_CTL4
-       };
-       int i;
+       bool is_enabled, enable_requested;
+       uint32_t tmp;
 
        if (!IS_HASWELL(dev))
                return;
 
-       mutex_lock(&dev->struct_mutex);
+       tmp = I915_READ(HSW_PWR_WELL_DRIVER);
+       is_enabled = tmp & HSW_PWR_WELL_STATE;
+       enable_requested = tmp & HSW_PWR_WELL_ENABLE;
 
-       for (i = 0; i < ARRAY_SIZE(power_wells); i++) {
-               int well = I915_READ(power_wells[i]);
+       if (enable) {
+               if (!enable_requested)
+                       I915_WRITE(HSW_PWR_WELL_DRIVER, HSW_PWR_WELL_ENABLE);
 
-               if ((well & HSW_PWR_WELL_STATE) == 0) {
-                       I915_WRITE(power_wells[i], well & HSW_PWR_WELL_ENABLE);
-                       if (wait_for((I915_READ(power_wells[i]) & HSW_PWR_WELL_STATE), 20))
-                               DRM_ERROR("Error enabling power well %lx\n", power_wells[i]);
+               if (!is_enabled) {
+                       DRM_DEBUG_KMS("Enabling power well\n");
+                       if (wait_for((I915_READ(HSW_PWR_WELL_DRIVER) &
+                                     HSW_PWR_WELL_STATE), 20))
+                               DRM_ERROR("Timeout enabling power well\n");
+               }
+       } else {
+               if (enable_requested) {
+                       I915_WRITE(HSW_PWR_WELL_DRIVER, 0);
+                       DRM_DEBUG_KMS("Requesting to disable the power well\n");
                }
        }
+}
 
-       mutex_unlock(&dev->struct_mutex);
+/*
+ * Starting with Haswell, we have a "Power Down Well" that can be turned off
+ * when not needed anymore. We have 4 registers that can request the power well
+ * to be enabled, and it will only be disabled if none of the registers is
+ * requesting it to be enabled.
+ */
+void intel_init_power_well(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (!IS_HASWELL(dev))
+               return;
+
+       /* For now, we need the power well to be always enabled. */
+       intel_set_power_well(dev, true);
+
+       /* We're taking over the BIOS, so clear any requests made by it since
+        * the driver is in charge now. */
+       if (I915_READ(HSW_PWR_WELL_BIOS) & HSW_PWR_WELL_ENABLE)
+               I915_WRITE(HSW_PWR_WELL_BIOS, 0);
 }
 
 /* Set up chip specific power management-related functions */
index 42ff97d667d2b84ad339b27ab1f59110ba5831e8..1d5d613eb6be4d21affe491af19a3eaba99325ee 100644 (file)
@@ -318,6 +318,7 @@ gen7_render_ring_flush(struct intel_ring_buffer *ring,
                 * TLB invalidate requires a post-sync write.
                 */
                flags |= PIPE_CONTROL_QW_WRITE;
+               flags |= PIPE_CONTROL_GLOBAL_GTT_IVB;
 
                /* Workaround: we must issue a pipe_control with CS-stall bit
                 * set before a pipe_control command that has the state cache
@@ -331,7 +332,7 @@ gen7_render_ring_flush(struct intel_ring_buffer *ring,
 
        intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4));
        intel_ring_emit(ring, flags);
-       intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT);
+       intel_ring_emit(ring, scratch_addr);
        intel_ring_emit(ring, 0);
        intel_ring_advance(ring);
 
@@ -467,6 +468,9 @@ init_pipe_control(struct intel_ring_buffer *ring)
        if (pc->cpu_page == NULL)
                goto err_unpin;
 
+       DRM_DEBUG_DRIVER("%s pipe control offset: 0x%08x\n",
+                        ring->name, pc->gtt_offset);
+
        pc->obj = obj;
        ring->private = pc;
        return 0;
@@ -613,6 +617,13 @@ gen6_add_request(struct intel_ring_buffer *ring)
        return 0;
 }
 
+static inline bool i915_gem_has_seqno_wrapped(struct drm_device *dev,
+                                             u32 seqno)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       return dev_priv->last_seqno < seqno;
+}
+
 /**
  * intel_ring_sync - sync the waiter to the signaller on seqno
  *
@@ -643,11 +654,20 @@ gen6_ring_sync(struct intel_ring_buffer *waiter,
        if (ret)
                return ret;
 
-       intel_ring_emit(waiter,
-                       dw1 | signaller->semaphore_register[waiter->id]);
-       intel_ring_emit(waiter, seqno);
-       intel_ring_emit(waiter, 0);
-       intel_ring_emit(waiter, MI_NOOP);
+       /* If seqno wrap happened, omit the wait with no-ops */
+       if (likely(!i915_gem_has_seqno_wrapped(waiter->dev, seqno))) {
+               intel_ring_emit(waiter,
+                               dw1 |
+                               signaller->semaphore_register[waiter->id]);
+               intel_ring_emit(waiter, seqno);
+               intel_ring_emit(waiter, 0);
+               intel_ring_emit(waiter, MI_NOOP);
+       } else {
+               intel_ring_emit(waiter, MI_NOOP);
+               intel_ring_emit(waiter, MI_NOOP);
+               intel_ring_emit(waiter, MI_NOOP);
+               intel_ring_emit(waiter, MI_NOOP);
+       }
        intel_ring_advance(waiter);
 
        return 0;
@@ -728,6 +748,12 @@ ring_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency)
        return intel_read_status_page(ring, I915_GEM_HWS_INDEX);
 }
 
+static void
+ring_set_seqno(struct intel_ring_buffer *ring, u32 seqno)
+{
+       intel_write_status_page(ring, I915_GEM_HWS_INDEX, seqno);
+}
+
 static u32
 pc_render_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency)
 {
@@ -735,6 +761,13 @@ pc_render_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency)
        return pc->cpu_page[0];
 }
 
+static void
+pc_render_set_seqno(struct intel_ring_buffer *ring, u32 seqno)
+{
+       struct pipe_control *pc = ring->private;
+       pc->cpu_page[0] = seqno;
+}
+
 static bool
 gen5_ring_get_irq(struct intel_ring_buffer *ring)
 {
@@ -1164,7 +1197,11 @@ static int intel_init_ring_buffer(struct drm_device *dev,
                        return ret;
        }
 
-       obj = i915_gem_alloc_object(dev, ring->size);
+       obj = NULL;
+       if (!HAS_LLC(dev))
+               obj = i915_gem_object_create_stolen(dev, ring->size);
+       if (obj == NULL)
+               obj = i915_gem_alloc_object(dev, ring->size);
        if (obj == NULL) {
                DRM_ERROR("Failed to allocate ringbuffer\n");
                ret = -ENOMEM;
@@ -1182,7 +1219,7 @@ static int intel_init_ring_buffer(struct drm_device *dev,
                goto err_unpin;
 
        ring->virtual_start =
-               ioremap_wc(dev_priv->mm.gtt->gma_bus_addr + obj->gtt_offset,
+               ioremap_wc(dev_priv->gtt.mappable_base + obj->gtt_offset,
                           ring->size);
        if (ring->virtual_start == NULL) {
                DRM_ERROR("Failed to map ringbuffer.\n");
@@ -1348,7 +1385,8 @@ static int ring_wait_for_space(struct intel_ring_buffer *ring, int n)
 
                msleep(1);
 
-               ret = i915_gem_check_wedge(dev_priv, dev_priv->mm.interruptible);
+               ret = i915_gem_check_wedge(&dev_priv->gpu_error,
+                                          dev_priv->mm.interruptible);
                if (ret)
                        return ret;
        } while (!time_after(jiffies, end));
@@ -1410,14 +1448,35 @@ intel_ring_alloc_seqno(struct intel_ring_buffer *ring)
        return i915_gem_get_seqno(ring->dev, &ring->outstanding_lazy_request);
 }
 
+static int __intel_ring_begin(struct intel_ring_buffer *ring,
+                             int bytes)
+{
+       int ret;
+
+       if (unlikely(ring->tail + bytes > ring->effective_size)) {
+               ret = intel_wrap_ring_buffer(ring);
+               if (unlikely(ret))
+                       return ret;
+       }
+
+       if (unlikely(ring->space < bytes)) {
+               ret = ring_wait_for_space(ring, bytes);
+               if (unlikely(ret))
+                       return ret;
+       }
+
+       ring->space -= bytes;
+       return 0;
+}
+
 int intel_ring_begin(struct intel_ring_buffer *ring,
                     int num_dwords)
 {
        drm_i915_private_t *dev_priv = ring->dev->dev_private;
-       int n = 4*num_dwords;
        int ret;
 
-       ret = i915_gem_check_wedge(dev_priv, dev_priv->mm.interruptible);
+       ret = i915_gem_check_wedge(&dev_priv->gpu_error,
+                                  dev_priv->mm.interruptible);
        if (ret)
                return ret;
 
@@ -1426,20 +1485,21 @@ int intel_ring_begin(struct intel_ring_buffer *ring,
        if (ret)
                return ret;
 
-       if (unlikely(ring->tail + n > ring->effective_size)) {
-               ret = intel_wrap_ring_buffer(ring);
-               if (unlikely(ret))
-                       return ret;
-       }
+       return __intel_ring_begin(ring, num_dwords * sizeof(uint32_t));
+}
 
-       if (unlikely(ring->space < n)) {
-               ret = ring_wait_for_space(ring, n);
-               if (unlikely(ret))
-                       return ret;
+void intel_ring_init_seqno(struct intel_ring_buffer *ring, u32 seqno)
+{
+       struct drm_i915_private *dev_priv = ring->dev->dev_private;
+
+       BUG_ON(ring->outstanding_lazy_request);
+
+       if (INTEL_INFO(ring->dev)->gen >= 6) {
+               I915_WRITE(RING_SYNC_0(ring->mmio_base), 0);
+               I915_WRITE(RING_SYNC_1(ring->mmio_base), 0);
        }
 
-       ring->space -= n;
-       return 0;
+       ring->set_seqno(ring, seqno);
 }
 
 void intel_ring_advance(struct intel_ring_buffer *ring)
@@ -1447,7 +1507,7 @@ void intel_ring_advance(struct intel_ring_buffer *ring)
        struct drm_i915_private *dev_priv = ring->dev->dev_private;
 
        ring->tail &= ring->size - 1;
-       if (dev_priv->stop_rings & intel_ring_flag(ring))
+       if (dev_priv->gpu_error.stop_rings & intel_ring_flag(ring))
                return;
        ring->write_tail(ring, ring->tail);
 }
@@ -1604,6 +1664,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
                ring->irq_put = gen6_ring_put_irq;
                ring->irq_enable_mask = GT_USER_INTERRUPT;
                ring->get_seqno = gen6_ring_get_seqno;
+               ring->set_seqno = ring_set_seqno;
                ring->sync_to = gen6_ring_sync;
                ring->semaphore_register[0] = MI_SEMAPHORE_SYNC_INVALID;
                ring->semaphore_register[1] = MI_SEMAPHORE_SYNC_RV;
@@ -1614,6 +1675,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
                ring->add_request = pc_render_add_request;
                ring->flush = gen4_render_ring_flush;
                ring->get_seqno = pc_render_get_seqno;
+               ring->set_seqno = pc_render_set_seqno;
                ring->irq_get = gen5_ring_get_irq;
                ring->irq_put = gen5_ring_put_irq;
                ring->irq_enable_mask = GT_USER_INTERRUPT | GT_PIPE_NOTIFY;
@@ -1624,6 +1686,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
                else
                        ring->flush = gen4_render_ring_flush;
                ring->get_seqno = ring_get_seqno;
+               ring->set_seqno = ring_set_seqno;
                if (IS_GEN2(dev)) {
                        ring->irq_get = i8xx_ring_get_irq;
                        ring->irq_put = i8xx_ring_put_irq;
@@ -1695,6 +1758,7 @@ int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size)
        else
                ring->flush = gen4_render_ring_flush;
        ring->get_seqno = ring_get_seqno;
+       ring->set_seqno = ring_set_seqno;
        if (IS_GEN2(dev)) {
                ring->irq_get = i8xx_ring_get_irq;
                ring->irq_put = i8xx_ring_put_irq;
@@ -1755,6 +1819,7 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev)
                ring->flush = gen6_ring_flush;
                ring->add_request = gen6_add_request;
                ring->get_seqno = gen6_ring_get_seqno;
+               ring->set_seqno = ring_set_seqno;
                ring->irq_enable_mask = GEN6_BSD_USER_INTERRUPT;
                ring->irq_get = gen6_ring_get_irq;
                ring->irq_put = gen6_ring_put_irq;
@@ -1770,6 +1835,7 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev)
                ring->flush = bsd_ring_flush;
                ring->add_request = i9xx_add_request;
                ring->get_seqno = ring_get_seqno;
+               ring->set_seqno = ring_set_seqno;
                if (IS_GEN5(dev)) {
                        ring->irq_enable_mask = GT_BSD_USER_INTERRUPT;
                        ring->irq_get = gen5_ring_get_irq;
@@ -1799,6 +1865,7 @@ int intel_init_blt_ring_buffer(struct drm_device *dev)
        ring->flush = blt_ring_flush;
        ring->add_request = gen6_add_request;
        ring->get_seqno = gen6_ring_get_seqno;
+       ring->set_seqno = ring_set_seqno;
        ring->irq_enable_mask = GEN6_BLITTER_USER_INTERRUPT;
        ring->irq_get = gen6_ring_get_irq;
        ring->irq_put = gen6_ring_put_irq;
index 6af87cd0572501fb8621f34732627b37d89a83ac..d66208c2c48b61e350e2dbf98c0d52cfbda27f96 100644 (file)
@@ -90,6 +90,8 @@ struct  intel_ring_buffer {
         */
        u32             (*get_seqno)(struct intel_ring_buffer *ring,
                                     bool lazy_coherency);
+       void            (*set_seqno)(struct intel_ring_buffer *ring,
+                                    u32 seqno);
        int             (*dispatch_execbuffer)(struct intel_ring_buffer *ring,
                                               u32 offset, u32 length,
                                               unsigned flags);
@@ -178,6 +180,13 @@ intel_read_status_page(struct intel_ring_buffer *ring,
        return ring->status_page.page_addr[reg];
 }
 
+static inline void
+intel_write_status_page(struct intel_ring_buffer *ring,
+                       int reg, u32 value)
+{
+       ring->status_page.page_addr[reg] = value;
+}
+
 /**
  * Reads a dword out of the status page, which is written to from the command
  * queue by automatic updates, MI_REPORT_HEAD, MI_STORE_DATA_INDEX, or
@@ -208,7 +217,7 @@ static inline void intel_ring_emit(struct intel_ring_buffer *ring,
 }
 void intel_ring_advance(struct intel_ring_buffer *ring);
 int __must_check intel_ring_idle(struct intel_ring_buffer *ring);
-
+void intel_ring_init_seqno(struct intel_ring_buffer *ring, u32 seqno);
 int intel_ring_flush_all_caches(struct intel_ring_buffer *ring);
 int intel_ring_invalidate_all_caches(struct intel_ring_buffer *ring);
 
index c275bf0fa36db86a09d861a7e17457c2c103dd4e..33b46d9694eafb9a4adb8a819c9b6348103855f2 100644 (file)
@@ -103,6 +103,7 @@ struct intel_sdvo {
         * It is only valid when using TMDS encoding and 8 bit per color mode.
         */
        uint32_t color_range;
+       bool color_range_auto;
 
        /**
         * This is set if we're going to treat the device as TV-out.
@@ -125,6 +126,7 @@ struct intel_sdvo {
        bool is_hdmi;
        bool has_hdmi_monitor;
        bool has_hdmi_audio;
+       bool rgb_quant_range_selectable;
 
        /**
         * This is set if we detect output of sdvo device as LVDS and
@@ -946,7 +948,8 @@ static bool intel_sdvo_write_infoframe(struct intel_sdvo *intel_sdvo,
                                    &tx_rate, 1);
 }
 
-static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo)
+static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo,
+                                        const struct drm_display_mode *adjusted_mode)
 {
        struct dip_infoframe avi_if = {
                .type = DIP_TYPE_AVI,
@@ -955,6 +958,13 @@ static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo)
        };
        uint8_t sdvo_data[4 + sizeof(avi_if.body.avi)];
 
+       if (intel_sdvo->rgb_quant_range_selectable) {
+               if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE)
+                       avi_if.body.avi.ITC_EC_Q_SC |= DIP_AVI_RGB_QUANT_RANGE_LIMITED;
+               else
+                       avi_if.body.avi.ITC_EC_Q_SC |= DIP_AVI_RGB_QUANT_RANGE_FULL;
+       }
+
        intel_dip_infoframe_csum(&avi_if);
 
        /* sdvo spec says that the ecc is handled by the hw, and it looks like
@@ -1064,6 +1074,18 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder,
        multiplier = intel_sdvo_get_pixel_multiplier(adjusted_mode);
        intel_mode_set_pixel_multiplier(adjusted_mode, multiplier);
 
+       if (intel_sdvo->color_range_auto) {
+               /* See CEA-861-E - 5.1 Default Encoding Parameters */
+               if (intel_sdvo->has_hdmi_monitor &&
+                   drm_mode_cea_vic(adjusted_mode) > 1)
+                       intel_sdvo->color_range = SDVO_COLOR_RANGE_16_235;
+               else
+                       intel_sdvo->color_range = 0;
+       }
+
+       if (intel_sdvo->color_range)
+               adjusted_mode->private_flags |= INTEL_MODE_LIMITED_COLOR_RANGE;
+
        return true;
 }
 
@@ -1121,7 +1143,7 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
                intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_HDMI);
                intel_sdvo_set_colorimetry(intel_sdvo,
                                           SDVO_COLORIMETRY_RGB256);
-               intel_sdvo_set_avi_infoframe(intel_sdvo);
+               intel_sdvo_set_avi_infoframe(intel_sdvo, adjusted_mode);
        } else
                intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_DVI);
 
@@ -1153,7 +1175,7 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
                /* The real mode polarity is set by the SDVO commands, using
                 * struct intel_sdvo_dtd. */
                sdvox = SDVO_VSYNC_ACTIVE_HIGH | SDVO_HSYNC_ACTIVE_HIGH;
-               if (intel_sdvo->is_hdmi)
+               if (!HAS_PCH_SPLIT(dev) && intel_sdvo->is_hdmi)
                        sdvox |= intel_sdvo->color_range;
                if (INTEL_INFO(dev)->gen < 5)
                        sdvox |= SDVO_BORDER_ENABLE;
@@ -1513,6 +1535,8 @@ intel_sdvo_tmds_sink_detect(struct drm_connector *connector)
                        if (intel_sdvo->is_hdmi) {
                                intel_sdvo->has_hdmi_monitor = drm_detect_hdmi_monitor(edid);
                                intel_sdvo->has_hdmi_audio = drm_detect_monitor_audio(edid);
+                               intel_sdvo->rgb_quant_range_selectable =
+                                       drm_rgb_quant_range_selectable(edid);
                        }
                } else
                        status = connector_status_disconnected;
@@ -1564,6 +1588,7 @@ intel_sdvo_detect(struct drm_connector *connector, bool force)
 
        intel_sdvo->has_hdmi_monitor = false;
        intel_sdvo->has_hdmi_audio = false;
+       intel_sdvo->rgb_quant_range_selectable = false;
 
        if ((intel_sdvo_connector->output_flag & response) == 0)
                ret = connector_status_disconnected;
@@ -1897,10 +1922,21 @@ intel_sdvo_set_property(struct drm_connector *connector,
        }
 
        if (property == dev_priv->broadcast_rgb_property) {
-               if (val == !!intel_sdvo->color_range)
-                       return 0;
-
-               intel_sdvo->color_range = val ? SDVO_COLOR_RANGE_16_235 : 0;
+               switch (val) {
+               case INTEL_BROADCAST_RGB_AUTO:
+                       intel_sdvo->color_range_auto = true;
+                       break;
+               case INTEL_BROADCAST_RGB_FULL:
+                       intel_sdvo->color_range_auto = false;
+                       intel_sdvo->color_range = 0;
+                       break;
+               case INTEL_BROADCAST_RGB_LIMITED:
+                       intel_sdvo->color_range_auto = false;
+                       intel_sdvo->color_range = SDVO_COLOR_RANGE_16_235;
+                       break;
+               default:
+                       return -EINVAL;
+               }
                goto done;
        }
 
@@ -1997,11 +2033,8 @@ set_value:
 
 
 done:
-       if (intel_sdvo->base.base.crtc) {
-               struct drm_crtc *crtc = intel_sdvo->base.base.crtc;
-               intel_set_mode(crtc, &crtc->mode,
-                              crtc->x, crtc->y, crtc->fb);
-       }
+       if (intel_sdvo->base.base.crtc)
+               intel_crtc_restore_mode(intel_sdvo->base.base.crtc);
 
        return 0;
 #undef CHECK_PROPERTY
@@ -2010,7 +2043,6 @@ done:
 static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = {
        .mode_fixup = intel_sdvo_mode_fixup,
        .mode_set = intel_sdvo_mode_set,
-       .disable = intel_encoder_noop,
 };
 
 static const struct drm_connector_funcs intel_sdvo_connector_funcs = {
@@ -2200,13 +2232,16 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector,
 }
 
 static void
-intel_sdvo_add_hdmi_properties(struct intel_sdvo_connector *connector)
+intel_sdvo_add_hdmi_properties(struct intel_sdvo *intel_sdvo,
+                              struct intel_sdvo_connector *connector)
 {
        struct drm_device *dev = connector->base.base.dev;
 
        intel_attach_force_audio_property(&connector->base.base);
-       if (INTEL_INFO(dev)->gen >= 4 && IS_MOBILE(dev))
+       if (INTEL_INFO(dev)->gen >= 4 && IS_MOBILE(dev)) {
                intel_attach_broadcast_rgb_property(&connector->base.base);
+               intel_sdvo->color_range_auto = true;
+       }
 }
 
 static bool
@@ -2254,7 +2289,7 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device)
 
        intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo);
        if (intel_sdvo->is_hdmi)
-               intel_sdvo_add_hdmi_properties(intel_sdvo_connector);
+               intel_sdvo_add_hdmi_properties(intel_sdvo, intel_sdvo_connector);
 
        return true;
 }
index d7b060e0a23199b064c21d30366dd5ff78566e13..d086e48a831a977bad79616502393908100516d9 100644 (file)
@@ -50,6 +50,7 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb,
        u32 sprctl, sprscale = 0;
        unsigned long sprsurf_offset, linear_offset;
        int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
+       bool scaling_was_enabled = dev_priv->sprite_scaling_enabled;
 
        sprctl = I915_READ(SPRCTL(pipe));
 
@@ -89,6 +90,9 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb,
        sprctl |= SPRITE_TRICKLE_FEED_DISABLE;
        sprctl |= SPRITE_ENABLE;
 
+       if (IS_HASWELL(dev))
+               sprctl |= SPRITE_PIPE_CSC_ENABLE;
+
        /* Sizes are 0 based */
        src_w--;
        src_h--;
@@ -103,19 +107,15 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb,
         * when scaling is disabled.
         */
        if (crtc_w != src_w || crtc_h != src_h) {
-               if (!dev_priv->sprite_scaling_enabled) {
-                       dev_priv->sprite_scaling_enabled = true;
+               dev_priv->sprite_scaling_enabled |= 1 << pipe;
+
+               if (!scaling_was_enabled) {
                        intel_update_watermarks(dev);
                        intel_wait_for_vblank(dev, pipe);
                }
                sprscale = SPRITE_SCALE_ENABLE | (src_w << 16) | src_h;
-       } else {
-               if (dev_priv->sprite_scaling_enabled) {
-                       dev_priv->sprite_scaling_enabled = false;
-                       /* potentially re-enable LP watermarks */
-                       intel_update_watermarks(dev);
-               }
-       }
+       } else
+               dev_priv->sprite_scaling_enabled &= ~(1 << pipe);
 
        I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]);
        I915_WRITE(SPRPOS(pipe), (crtc_y << 16) | crtc_x);
@@ -141,6 +141,10 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb,
        I915_WRITE(SPRCTL(pipe), sprctl);
        I915_MODIFY_DISPBASE(SPRSURF(pipe), obj->gtt_offset + sprsurf_offset);
        POSTING_READ(SPRSURF(pipe));
+
+       /* potentially re-enable LP watermarks */
+       if (scaling_was_enabled && !dev_priv->sprite_scaling_enabled)
+               intel_update_watermarks(dev);
 }
 
 static void
@@ -150,6 +154,7 @@ ivb_disable_plane(struct drm_plane *plane)
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_plane *intel_plane = to_intel_plane(plane);
        int pipe = intel_plane->pipe;
+       bool scaling_was_enabled = dev_priv->sprite_scaling_enabled;
 
        I915_WRITE(SPRCTL(pipe), I915_READ(SPRCTL(pipe)) & ~SPRITE_ENABLE);
        /* Can't leave the scaler enabled... */
@@ -159,8 +164,11 @@ ivb_disable_plane(struct drm_plane *plane)
        I915_MODIFY_DISPBASE(SPRSURF(pipe), 0);
        POSTING_READ(SPRSURF(pipe));
 
-       dev_priv->sprite_scaling_enabled = false;
-       intel_update_watermarks(dev);
+       dev_priv->sprite_scaling_enabled &= ~(1 << pipe);
+
+       /* potentially re-enable LP watermarks */
+       if (scaling_was_enabled && !dev_priv->sprite_scaling_enabled)
+               intel_update_watermarks(dev);
 }
 
 static int
@@ -593,7 +601,7 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data,
        if ((set->flags & (I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE)) == (I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE))
                return -EINVAL;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
 
        obj = drm_mode_object_find(dev, set->plane_id, DRM_MODE_OBJECT_PLANE);
        if (!obj) {
@@ -606,7 +614,7 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data,
        ret = intel_plane->update_colorkey(plane, set);
 
 out_unlock:
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
        return ret;
 }
 
@@ -622,7 +630,7 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data,
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -ENODEV;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
 
        obj = drm_mode_object_find(dev, get->plane_id, DRM_MODE_OBJECT_PLANE);
        if (!obj) {
@@ -635,7 +643,7 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data,
        intel_plane->get_colorkey(plane, get);
 
 out_unlock:
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
        return ret;
 }
 
index ea93520c127820fc701a3dcddd173cc8d6598bf1..d808421c1c808e2b0f49480d73968ebae26d07ed 100644 (file)
@@ -1479,8 +1479,7 @@ intel_tv_set_property(struct drm_connector *connector, struct drm_property *prop
        }
 
        if (changed && crtc)
-               intel_set_mode(crtc, &crtc->mode,
-                              crtc->x, crtc->y, crtc->fb);
+               intel_crtc_restore_mode(crtc);
 out:
        return ret;
 }
@@ -1488,7 +1487,6 @@ out:
 static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = {
        .mode_fixup = intel_tv_mode_fixup,
        .mode_set = intel_tv_mode_set,
-       .disable = intel_encoder_noop,
 };
 
 static const struct drm_connector_funcs intel_tv_connector_funcs = {
index 2f486481d79ad01db9c786a44103d7ef4062ad5c..d2253f6394817dc5a0c1cf22ffb64887d0452846 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <drm/drmP.h>
 #include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
 
 #include <linux/fb.h>
 
@@ -120,9 +121,10 @@ static int mgag200fb_create_object(struct mga_fbdev *afbdev,
        return ret;
 }
 
-static int mgag200fb_create(struct mga_fbdev *mfbdev,
+static int mgag200fb_create(struct drm_fb_helper *helper,
                           struct drm_fb_helper_surface_size *sizes)
 {
+       struct mga_fbdev *mfbdev = (struct mga_fbdev *)helper;
        struct drm_device *dev = mfbdev->helper.dev;
        struct drm_mode_fb_cmd2 mode_cmd;
        struct mga_device *mdev = dev->dev_private;
@@ -209,23 +211,6 @@ out:
        return ret;
 }
 
-static int mga_fb_find_or_create_single(struct drm_fb_helper *helper,
-                                          struct drm_fb_helper_surface_size
-                                          *sizes)
-{
-       struct mga_fbdev *mfbdev = (struct mga_fbdev *)helper;
-       int new_fb = 0;
-       int ret;
-
-       if (!helper->fb) {
-               ret = mgag200fb_create(mfbdev, sizes);
-               if (ret)
-                       return ret;
-               new_fb = 1;
-       }
-       return new_fb;
-}
-
 static int mga_fbdev_destroy(struct drm_device *dev,
                                struct mga_fbdev *mfbdev)
 {
@@ -247,6 +232,7 @@ static int mga_fbdev_destroy(struct drm_device *dev,
        }
        drm_fb_helper_fini(&mfbdev->helper);
        vfree(mfbdev->sysram);
+       drm_framebuffer_unregister_private(&mfb->base);
        drm_framebuffer_cleanup(&mfb->base);
 
        return 0;
@@ -255,7 +241,7 @@ static int mga_fbdev_destroy(struct drm_device *dev,
 static struct drm_fb_helper_funcs mga_fb_helper_funcs = {
        .gamma_set = mga_crtc_fb_gamma_set,
        .gamma_get = mga_crtc_fb_gamma_get,
-       .fb_probe = mga_fb_find_or_create_single,
+       .fb_probe = mgag200fb_create,
 };
 
 int mgag200_fbdev_init(struct mga_device *mdev)
@@ -277,6 +263,10 @@ int mgag200_fbdev_init(struct mga_device *mdev)
                return ret;
        }
        drm_fb_helper_single_add_all_connectors(&mfbdev->helper);
+
+       /* disable all the possible outputs/crtcs before entering KMS mode */
+       drm_helper_disable_unused_functions(mdev->dev);
+
        drm_fb_helper_initial_config(&mfbdev->helper, 32);
 
        return 0;
index 70dd3c5529d400f492b8cd113ce9fd0950036963..64297c72464f996404730df6f3ec2eb67beccc33 100644 (file)
@@ -23,16 +23,8 @@ static void mga_user_framebuffer_destroy(struct drm_framebuffer *fb)
        kfree(fb);
 }
 
-static int mga_user_framebuffer_create_handle(struct drm_framebuffer *fb,
-                                                struct drm_file *file_priv,
-                                                unsigned int *handle)
-{
-       return 0;
-}
-
 static const struct drm_framebuffer_funcs mga_fb_funcs = {
        .destroy = mga_user_framebuffer_destroy,
-       .create_handle = mga_user_framebuffer_create_handle,
 };
 
 int mgag200_framebuffer_init(struct drm_device *dev,
@@ -40,13 +32,15 @@ int mgag200_framebuffer_init(struct drm_device *dev,
                             struct drm_mode_fb_cmd2 *mode_cmd,
                             struct drm_gem_object *obj)
 {
-       int ret = drm_framebuffer_init(dev, &gfb->base, &mga_fb_funcs);
+       int ret;
+       
+       drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd);
+       gfb->obj = obj;
+       ret = drm_framebuffer_init(dev, &gfb->base, &mga_fb_funcs);
        if (ret) {
                DRM_ERROR("drm_framebuffer_init failed: %d\n", ret);
                return ret;
        }
-       drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd);
-       gfb->obj = obj;
        return 0;
 }
 
index 8a55beeb8bdc210acec214ee07542f20f63f17df..a7ff6d5a34b9cf98de06611988162a7c9ad68643 100644 (file)
@@ -11,8 +11,9 @@ config DRM_NOUVEAU
        select FRAMEBUFFER_CONSOLE if !EXPERT
        select FB_BACKLIGHT if DRM_NOUVEAU_BACKLIGHT
        select ACPI_VIDEO if ACPI && X86 && BACKLIGHT_CLASS_DEVICE && VIDEO_OUTPUT_CONTROL && INPUT
-       select ACPI_WMI if ACPI
-       select MXM_WMI if ACPI
+       select X86_PLATFORM_DEVICES if ACPI && X86
+       select ACPI_WMI if ACPI && X86
+       select MXM_WMI if ACPI && X86
        select POWER_SUPPLY
        help
          Choose this option for open-source nVidia support.
@@ -52,26 +53,3 @@ config DRM_NOUVEAU_BACKLIGHT
        help
          Say Y here if you want to control the backlight of your display
          (e.g. a laptop panel).
-
-menu "I2C encoder or helper chips"
-     depends on DRM && DRM_KMS_HELPER && I2C
-
-config DRM_I2C_CH7006
-       tristate "Chrontel ch7006 TV encoder"
-       default m if DRM_NOUVEAU
-       help
-         Support for Chrontel ch7006 and similar TV encoders, found
-         on some nVidia video cards.
-
-         This driver is currently only useful if you're also using
-         the nouveau driver.
-
-config DRM_I2C_SIL164
-       tristate "Silicon Image sil164 TMDS transmitter"
-       default m if DRM_NOUVEAU
-       help
-         Support for sil164 and similar single-link (or dual-link
-         when used in pairs) TMDS transmitters, used in some nVidia
-         video cards.
-
-endmenu
index ab25752a0b1ee80515900356e39f5c95236186dd..90f9140eeefd70adae09b70158f718562cd153ba 100644 (file)
@@ -11,6 +11,7 @@ nouveau-y := core/core/client.o
 nouveau-y += core/core/engctx.o
 nouveau-y += core/core/engine.o
 nouveau-y += core/core/enum.o
+nouveau-y += core/core/event.o
 nouveau-y += core/core/falcon.o
 nouveau-y += core/core/gpuobj.o
 nouveau-y += core/core/handle.o
@@ -40,6 +41,11 @@ nouveau-y += core/subdev/bios/mxm.o
 nouveau-y += core/subdev/bios/perf.o
 nouveau-y += core/subdev/bios/pll.o
 nouveau-y += core/subdev/bios/therm.o
+nouveau-y += core/subdev/bios/xpio.o
+nouveau-y += core/subdev/bus/nv04.o
+nouveau-y += core/subdev/bus/nv31.o
+nouveau-y += core/subdev/bus/nv50.o
+nouveau-y += core/subdev/bus/nvc0.o
 nouveau-y += core/subdev/clock/nv04.o
 nouveau-y += core/subdev/clock/nv40.o
 nouveau-y += core/subdev/clock/nv50.o
@@ -85,9 +91,16 @@ nouveau-y += core/subdev/gpio/base.o
 nouveau-y += core/subdev/gpio/nv10.o
 nouveau-y += core/subdev/gpio/nv50.o
 nouveau-y += core/subdev/gpio/nvd0.o
+nouveau-y += core/subdev/gpio/nve0.o
 nouveau-y += core/subdev/i2c/base.o
+nouveau-y += core/subdev/i2c/anx9805.o
 nouveau-y += core/subdev/i2c/aux.o
 nouveau-y += core/subdev/i2c/bit.o
+nouveau-y += core/subdev/i2c/nv04.o
+nouveau-y += core/subdev/i2c/nv4e.o
+nouveau-y += core/subdev/i2c/nv50.o
+nouveau-y += core/subdev/i2c/nv94.o
+nouveau-y += core/subdev/i2c/nvd0.o
 nouveau-y += core/subdev/ibus/nvc0.o
 nouveau-y += core/subdev/ibus/nve0.o
 nouveau-y += core/subdev/instmem/base.o
@@ -106,10 +119,15 @@ nouveau-y += core/subdev/mxm/mxms.o
 nouveau-y += core/subdev/mxm/nv50.o
 nouveau-y += core/subdev/therm/base.o
 nouveau-y += core/subdev/therm/fan.o
+nouveau-y += core/subdev/therm/fannil.o
+nouveau-y += core/subdev/therm/fanpwm.o
+nouveau-y += core/subdev/therm/fantog.o
 nouveau-y += core/subdev/therm/ic.o
+nouveau-y += core/subdev/therm/temp.o
 nouveau-y += core/subdev/therm/nv40.o
 nouveau-y += core/subdev/therm/nv50.o
-nouveau-y += core/subdev/therm/temp.o
+nouveau-y += core/subdev/therm/nva3.o
+nouveau-y += core/subdev/therm/nvd0.o
 nouveau-y += core/subdev/timer/base.o
 nouveau-y += core/subdev/timer/nv04.o
 nouveau-y += core/subdev/vm/base.o
@@ -132,6 +150,7 @@ nouveau-y += core/engine/copy/nvc0.o
 nouveau-y += core/engine/copy/nve0.o
 nouveau-y += core/engine/crypt/nv84.o
 nouveau-y += core/engine/crypt/nv98.o
+nouveau-y += core/engine/disp/base.o
 nouveau-y += core/engine/disp/nv04.o
 nouveau-y += core/engine/disp/nv50.o
 nouveau-y += core/engine/disp/nv84.o
@@ -141,11 +160,13 @@ nouveau-y += core/engine/disp/nva3.o
 nouveau-y += core/engine/disp/nvd0.o
 nouveau-y += core/engine/disp/nve0.o
 nouveau-y += core/engine/disp/dacnv50.o
+nouveau-y += core/engine/disp/dport.o
 nouveau-y += core/engine/disp/hdanva3.o
 nouveau-y += core/engine/disp/hdanvd0.o
 nouveau-y += core/engine/disp/hdminv84.o
 nouveau-y += core/engine/disp/hdminva3.o
 nouveau-y += core/engine/disp/hdminvd0.o
+nouveau-y += core/engine/disp/piornv50.o
 nouveau-y += core/engine/disp/sornv50.o
 nouveau-y += core/engine/disp/sornv94.o
 nouveau-y += core/engine/disp/sornvd0.o
@@ -194,7 +215,8 @@ nouveau-y += nouveau_drm.o nouveau_chan.o nouveau_dma.o nouveau_fence.o
 nouveau-y += nouveau_irq.o nouveau_vga.o nouveau_agp.o
 nouveau-y += nouveau_ttm.o nouveau_sgdma.o nouveau_bo.o nouveau_gem.o
 nouveau-y += nouveau_prime.o nouveau_abi16.o
-nouveau-y += nv04_fence.o nv10_fence.o nv50_fence.o nv84_fence.o nvc0_fence.o
+nouveau-y += nv04_fence.o nv10_fence.o nv17_fence.o
+nouveau-y += nv50_fence.o nv84_fence.o nvc0_fence.o
 
 # drm/kms
 nouveau-y += nouveau_bios.o nouveau_fbcon.o nouveau_display.o
@@ -216,7 +238,10 @@ nouveau-y += nouveau_mem.o
 
 # other random bits
 nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
+ifdef CONFIG_X86
 nouveau-$(CONFIG_ACPI) += nouveau_acpi.o
+endif
 nouveau-$(CONFIG_DRM_NOUVEAU_BACKLIGHT) += nouveau_backlight.o
+nouveau-$(CONFIG_DEBUG_FS) += nouveau_debugfs.o
 
 obj-$(CONFIG_DRM_NOUVEAU)+= nouveau.o
index 8bbb58f94a193da85a769a5a387bc57b51ef5530..295c22165eac76e4493ab13a3a229a213405384d 100644 (file)
@@ -99,3 +99,13 @@ nouveau_client_fini(struct nouveau_client *client, bool suspend)
        nv_debug(client, "%s completed with %d\n", name[suspend], ret);
        return ret;
 }
+
+const char *
+nouveau_client_name(void *obj)
+{
+       const char *client_name = "unknown";
+       struct nouveau_client *client = nouveau_client(obj);
+       if (client)
+               client_name = client->name;
+       return client_name;
+}
index 7cc7133d82dee933203effbba2e0560143ad4514..dd434790ccc4ce3011af36971109ed17647fca44 100644 (file)
@@ -40,14 +40,15 @@ nouveau_enum_find(const struct nouveau_enum *en, u32 value)
        return NULL;
 }
 
-void
+const struct nouveau_enum *
 nouveau_enum_print(const struct nouveau_enum *en, u32 value)
 {
        en = nouveau_enum_find(en, value);
        if (en)
-               printk("%s", en->name);
+               pr_cont("%s", en->name);
        else
-               printk("(unknown enum 0x%08x)", value);
+               pr_cont("(unknown enum 0x%08x)", value);
+       return en;
 }
 
 void
@@ -55,7 +56,7 @@ nouveau_bitfield_print(const struct nouveau_bitfield *bf, u32 value)
 {
        while (bf->name) {
                if (value & bf->mask) {
-                       printk(" %s", bf->name);
+                       pr_cont(" %s", bf->name);
                        value &= ~bf->mask;
                }
 
@@ -63,5 +64,5 @@ nouveau_bitfield_print(const struct nouveau_bitfield *bf, u32 value)
        }
 
        if (value)
-               printk(" (unknown bits 0x%08x)", value);
+               pr_cont(" (unknown bits 0x%08x)", value);
 }
diff --git a/drivers/gpu/drm/nouveau/core/core/event.c b/drivers/gpu/drm/nouveau/core/core/event.c
new file mode 100644 (file)
index 0000000..6d01e0f
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <core/os.h>
+#include <core/event.h>
+
+static void
+nouveau_event_put_locked(struct nouveau_event *event, int index,
+                        struct nouveau_eventh *handler)
+{
+       if (!--event->index[index].refs)
+               event->disable(event, index);
+       list_del(&handler->head);
+}
+
+void
+nouveau_event_put(struct nouveau_event *event, int index,
+                 struct nouveau_eventh *handler)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&event->lock, flags);
+       if (index < event->index_nr)
+               nouveau_event_put_locked(event, index, handler);
+       spin_unlock_irqrestore(&event->lock, flags);
+}
+
+void
+nouveau_event_get(struct nouveau_event *event, int index,
+                 struct nouveau_eventh *handler)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&event->lock, flags);
+       if (index < event->index_nr) {
+               list_add(&handler->head, &event->index[index].list);
+               if (!event->index[index].refs++)
+                       event->enable(event, index);
+       }
+       spin_unlock_irqrestore(&event->lock, flags);
+}
+
+void
+nouveau_event_trigger(struct nouveau_event *event, int index)
+{
+       struct nouveau_eventh *handler, *temp;
+       unsigned long flags;
+
+       if (index >= event->index_nr)
+               return;
+
+       spin_lock_irqsave(&event->lock, flags);
+       list_for_each_entry_safe(handler, temp, &event->index[index].list, head) {
+               if (handler->func(handler, index) == NVKM_EVENT_DROP) {
+                       nouveau_event_put_locked(event, index, handler);
+               }
+       }
+       spin_unlock_irqrestore(&event->lock, flags);
+}
+
+void
+nouveau_event_destroy(struct nouveau_event **pevent)
+{
+       struct nouveau_event *event = *pevent;
+       if (event) {
+               kfree(event);
+               *pevent = NULL;
+       }
+}
+
+int
+nouveau_event_create(int index_nr, struct nouveau_event **pevent)
+{
+       struct nouveau_event *event;
+       int i;
+
+       event = *pevent = kzalloc(sizeof(*event) + index_nr *
+                                 sizeof(event->index[0]), GFP_KERNEL);
+       if (!event)
+               return -ENOMEM;
+
+       spin_lock_init(&event->lock);
+       for (i = 0; i < index_nr; i++)
+               INIT_LIST_HEAD(&event->index[i].list);
+       event->index_nr = index_nr;
+       return 0;
+}
index 283248c7b050134cc9174741eed7a311ee6440fe..d6dc2a65ccd1e22414a89c33c57baf8f8181bbaa 100644 (file)
@@ -22,6 +22,7 @@
  * Authors: Ben Skeggs
  */
 
+#include <core/client.h>
 #include <core/falcon.h>
 #include <core/class.h>
 #include <core/enum.h>
@@ -100,8 +101,9 @@ nva3_copy_intr(struct nouveau_subdev *subdev)
        if (stat & 0x00000040) {
                nv_error(falcon, "DISPATCH_ERROR [");
                nouveau_enum_print(nva3_copy_isr_error_name, ssta);
-               printk("] ch %d [0x%010llx] subc %d mthd 0x%04x data 0x%08x\n",
-                      chid, inst << 12, subc, mthd, data);
+               pr_cont("] ch %d [0x%010llx %s] subc %d mthd 0x%04x data 0x%08x\n",
+                      chid, inst << 12, nouveau_client_name(engctx), subc,
+                      mthd, data);
                nv_wo32(falcon, 0x004, 0x00000040);
                stat &= ~0x00000040;
        }
index b974905127237cdf74052cf9bbbc6d702725ad3f..5bc021f471f9f494ce6e30511afde19a50426b23 100644 (file)
@@ -22,6 +22,7 @@
  * Authors: Ben Skeggs
  */
 
+#include <core/client.h>
 #include <core/os.h>
 #include <core/enum.h>
 #include <core/class.h>
@@ -126,10 +127,11 @@ nv84_crypt_intr(struct nouveau_subdev *subdev)
        chid   = pfifo->chid(pfifo, engctx);
 
        if (stat) {
-               nv_error(priv, "");
+               nv_error(priv, "%s", "");
                nouveau_bitfield_print(nv84_crypt_intr_mask, stat);
-               printk(" ch %d [0x%010llx] mthd 0x%04x data 0x%08x\n",
-                      chid, (u64)inst << 12, mthd, data);
+               pr_cont(" ch %d [0x%010llx %s] mthd 0x%04x data 0x%08x\n",
+                      chid, (u64)inst << 12, nouveau_client_name(engctx),
+                      mthd, data);
        }
 
        nv_wr32(priv, 0x102130, stat);
index 21986f3bf0c8702c82ad3d4e233f59b5d9b8ceeb..8bf8955051d4c7e0eb7621bda6e7162071e47a70 100644 (file)
@@ -22,6 +22,7 @@
  * Authors: Ben Skeggs
  */
 
+#include <core/client.h>
 #include <core/os.h>
 #include <core/enum.h>
 #include <core/class.h>
@@ -102,8 +103,9 @@ nv98_crypt_intr(struct nouveau_subdev *subdev)
        if (stat & 0x00000040) {
                nv_error(priv, "DISPATCH_ERROR [");
                nouveau_enum_print(nv98_crypt_isr_error_name, ssta);
-               printk("] ch %d [0x%010llx] subc %d mthd 0x%04x data 0x%08x\n",
-                      chid, (u64)inst << 12, subc, mthd, data);
+               pr_cont("] ch %d [0x%010llx %s] subc %d mthd 0x%04x data 0x%08x\n",
+                      chid, (u64)inst << 12, nouveau_client_name(engctx),
+                      subc, mthd, data);
                nv_wr32(priv, 0x087004, 0x00000040);
                stat &= ~0x00000040;
        }
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/base.c b/drivers/gpu/drm/nouveau/core/engine/disp/base.c
new file mode 100644 (file)
index 0000000..7a5cae4
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <engine/disp.h>
+
+void
+_nouveau_disp_dtor(struct nouveau_object *object)
+{
+       struct nouveau_disp *disp = (void *)object;
+       nouveau_event_destroy(&disp->vblank);
+       nouveau_engine_destroy(&disp->base);
+}
+
+int
+nouveau_disp_create_(struct nouveau_object *parent,
+                    struct nouveau_object *engine,
+                    struct nouveau_oclass *oclass, int heads,
+                    const char *intname, const char *extname,
+                    int length, void **pobject)
+{
+       struct nouveau_disp *disp;
+       int ret;
+
+       ret = nouveau_engine_create_(parent, engine, oclass, true,
+                                    intname, extname, length, pobject);
+       disp = *pobject;
+       if (ret)
+               return ret;
+
+       return nouveau_event_create(heads, &disp->vblank);
+}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dport.c b/drivers/gpu/drm/nouveau/core/engine/disp/dport.c
new file mode 100644 (file)
index 0000000..fa27b02
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/bios.h>
+#include <subdev/bios/dcb.h>
+#include <subdev/bios/dp.h>
+#include <subdev/bios/init.h>
+#include <subdev/i2c.h>
+
+#include <engine/disp.h>
+
+#include "dport.h"
+
+#define DBG(fmt, args...) nv_debug(dp->disp, "DP:%04x:%04x: " fmt,             \
+                                  dp->outp->hasht, dp->outp->hashm, ##args)
+#define ERR(fmt, args...) nv_error(dp->disp, "DP:%04x:%04x: " fmt,             \
+                                  dp->outp->hasht, dp->outp->hashm, ##args)
+
+/******************************************************************************
+ * link training
+ *****************************************************************************/
+struct dp_state {
+       const struct nouveau_dp_func *func;
+       struct nouveau_disp *disp;
+       struct dcb_output *outp;
+       struct nvbios_dpout info;
+       u8 version;
+       struct nouveau_i2c_port *aux;
+       int head;
+       u8  dpcd[4];
+       int link_nr;
+       u32 link_bw;
+       u8  stat[6];
+       u8  conf[4];
+};
+
+static int
+dp_set_link_config(struct dp_state *dp)
+{
+       struct nouveau_disp *disp = dp->disp;
+       struct nouveau_bios *bios = nouveau_bios(disp);
+       struct nvbios_init init = {
+               .subdev = nv_subdev(dp->disp),
+               .bios = bios,
+               .offset = 0x0000,
+               .outp = dp->outp,
+               .crtc = dp->head,
+               .execute = 1,
+       };
+       u32 lnkcmp;
+       u8 sink[2];
+
+       DBG("%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw);
+
+       /* set desired link configuration on the sink */
+       sink[0] = dp->link_bw / 27000;
+       sink[1] = dp->link_nr;
+       if (dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP)
+               sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN;
+
+       nv_wraux(dp->aux, DPCD_LC00, sink, 2);
+
+       /* set desired link configuration on the source */
+       if ((lnkcmp = dp->info.lnkcmp)) {
+               if (dp->version < 0x30) {
+                       while ((dp->link_bw / 10) < nv_ro16(bios, lnkcmp))
+                               lnkcmp += 4;
+                       init.offset = nv_ro16(bios, lnkcmp + 2);
+               } else {
+                       while ((dp->link_bw / 27000) < nv_ro08(bios, lnkcmp))
+                               lnkcmp += 3;
+                       init.offset = nv_ro16(bios, lnkcmp + 1);
+               }
+
+               nvbios_exec(&init);
+       }
+
+       return dp->func->lnk_ctl(dp->disp, dp->outp, dp->head,
+                                dp->link_nr, dp->link_bw / 27000,
+                                dp->dpcd[DPCD_RC02] &
+                                         DPCD_RC02_ENHANCED_FRAME_CAP);
+}
+
+static void
+dp_set_training_pattern(struct dp_state *dp, u8 pattern)
+{
+       u8 sink_tp;
+
+       DBG("training pattern %d\n", pattern);
+       dp->func->pattern(dp->disp, dp->outp, dp->head, pattern);
+
+       nv_rdaux(dp->aux, DPCD_LC02, &sink_tp, 1);
+       sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET;
+       sink_tp |= pattern;
+       nv_wraux(dp->aux, DPCD_LC02, &sink_tp, 1);
+}
+
+static int
+dp_link_train_commit(struct dp_state *dp)
+{
+       int i;
+
+       for (i = 0; i < dp->link_nr; i++) {
+               u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf;
+               u8 lpre = (lane & 0x0c) >> 2;
+               u8 lvsw = (lane & 0x03) >> 0;
+
+               dp->conf[i] = (lpre << 3) | lvsw;
+               if (lvsw == 3)
+                       dp->conf[i] |= DPCD_LC03_MAX_SWING_REACHED;
+               if (lpre == 3)
+                       dp->conf[i] |= DPCD_LC03_MAX_PRE_EMPHASIS_REACHED;
+
+               DBG("config lane %d %02x\n", i, dp->conf[i]);
+               dp->func->drv_ctl(dp->disp, dp->outp, dp->head, i, lvsw, lpre);
+       }
+
+       return nv_wraux(dp->aux, DPCD_LC03(0), dp->conf, 4);
+}
+
+static int
+dp_link_train_update(struct dp_state *dp, u32 delay)
+{
+       int ret;
+
+       udelay(delay);
+
+       ret = nv_rdaux(dp->aux, DPCD_LS02, dp->stat, 6);
+       if (ret)
+               return ret;
+
+       DBG("status %*ph\n", 6, dp->stat);
+       return 0;
+}
+
+static int
+dp_link_train_cr(struct dp_state *dp)
+{
+       bool cr_done = false, abort = false;
+       int voltage = dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET;
+       int tries = 0, i;
+
+       dp_set_training_pattern(dp, 1);
+
+       do {
+               if (dp_link_train_commit(dp) ||
+                   dp_link_train_update(dp, 100))
+                       break;
+
+               cr_done = true;
+               for (i = 0; i < dp->link_nr; i++) {
+                       u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf;
+                       if (!(lane & DPCD_LS02_LANE0_CR_DONE)) {
+                               cr_done = false;
+                               if (dp->conf[i] & DPCD_LC03_MAX_SWING_REACHED)
+                                       abort = true;
+                               break;
+                       }
+               }
+
+               if ((dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET) != voltage) {
+                       voltage = dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET;
+                       tries = 0;
+               }
+       } while (!cr_done && !abort && ++tries < 5);
+
+       return cr_done ? 0 : -1;
+}
+
+static int
+dp_link_train_eq(struct dp_state *dp)
+{
+       bool eq_done, cr_done = true;
+       int tries = 0, i;
+
+       dp_set_training_pattern(dp, 2);
+
+       do {
+               if (dp_link_train_update(dp, 400))
+                       break;
+
+               eq_done = !!(dp->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE);
+               for (i = 0; i < dp->link_nr && eq_done; i++) {
+                       u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf;
+                       if (!(lane & DPCD_LS02_LANE0_CR_DONE))
+                               cr_done = false;
+                       if (!(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) ||
+                           !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED))
+                               eq_done = false;
+               }
+
+               if (dp_link_train_commit(dp))
+                       break;
+       } while (!eq_done && cr_done && ++tries <= 5);
+
+       return eq_done ? 0 : -1;
+}
+
+static void
+dp_link_train_init(struct dp_state *dp, bool spread)
+{
+       struct nvbios_init init = {
+               .subdev = nv_subdev(dp->disp),
+               .bios = nouveau_bios(dp->disp),
+               .outp = dp->outp,
+               .crtc = dp->head,
+               .execute = 1,
+       };
+
+       /* set desired spread */
+       if (spread)
+               init.offset = dp->info.script[2];
+       else
+               init.offset = dp->info.script[3];
+       nvbios_exec(&init);
+
+       /* pre-train script */
+       init.offset = dp->info.script[0];
+       nvbios_exec(&init);
+}
+
+static void
+dp_link_train_fini(struct dp_state *dp)
+{
+       struct nvbios_init init = {
+               .subdev = nv_subdev(dp->disp),
+               .bios = nouveau_bios(dp->disp),
+               .outp = dp->outp,
+               .crtc = dp->head,
+               .execute = 1,
+       };
+
+       /* post-train script */
+       init.offset = dp->info.script[1],
+       nvbios_exec(&init);
+}
+
+int
+nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func,
+                struct dcb_output *outp, int head, u32 datarate)
+{
+       struct nouveau_bios *bios = nouveau_bios(disp);
+       struct nouveau_i2c *i2c = nouveau_i2c(disp);
+       struct dp_state _dp = {
+               .disp = disp,
+               .func = func,
+               .outp = outp,
+               .head = head,
+       }, *dp = &_dp;
+       const u32 bw_list[] = { 270000, 162000, 0 };
+       const u32 *link_bw = bw_list;
+       u8  hdr, cnt, len;
+       u32 data;
+       int ret;
+
+       /* find the bios displayport data relevant to this output */
+       data = nvbios_dpout_match(bios, outp->hasht, outp->hashm, &dp->version,
+                                &hdr, &cnt, &len, &dp->info);
+       if (!data) {
+               ERR("bios data not found\n");
+               return -EINVAL;
+       }
+
+       /* acquire the aux channel and fetch some info about the display */
+       if (outp->location)
+               dp->aux = i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(outp->extdev));
+       else
+               dp->aux = i2c->find(i2c, NV_I2C_TYPE_DCBI2C(outp->i2c_index));
+       if (!dp->aux) {
+               ERR("no aux channel?!\n");
+               return -ENODEV;
+       }
+
+       ret = nv_rdaux(dp->aux, 0x00000, dp->dpcd, sizeof(dp->dpcd));
+       if (ret) {
+               ERR("failed to read DPCD\n");
+               return ret;
+       }
+
+       /* adjust required bandwidth for 8B/10B coding overhead */
+       datarate = (datarate / 8) * 10;
+
+       /* enable down-spreading and execute pre-train script from vbios */
+       dp_link_train_init(dp, dp->dpcd[3] & 0x01);
+
+       /* start off at highest link rate supported by encoder and display */
+       while (*link_bw > (dp->dpcd[1] * 27000))
+               link_bw++;
+
+       while (link_bw[0]) {
+               /* find minimum required lane count at this link rate */
+               dp->link_nr = dp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT;
+               while ((dp->link_nr >> 1) * link_bw[0] > datarate)
+                       dp->link_nr >>= 1;
+
+               /* drop link rate to minimum with this lane count */
+               while ((link_bw[1] * dp->link_nr) > datarate)
+                       link_bw++;
+               dp->link_bw = link_bw[0];
+
+               /* program selected link configuration */
+               ret = dp_set_link_config(dp);
+               if (ret == 0) {
+                       /* attempt to train the link at this configuration */
+                       memset(dp->stat, 0x00, sizeof(dp->stat));
+                       if (!dp_link_train_cr(dp) &&
+                           !dp_link_train_eq(dp))
+                               break;
+               } else
+               if (ret >= 1) {
+                       /* dp_set_link_config() handled training */
+                       break;
+               }
+
+               /* retry at lower rate */
+               link_bw++;
+       }
+
+       /* finish link training */
+       dp_set_training_pattern(dp, 0);
+
+       /* execute post-train script from vbios */
+       dp_link_train_fini(dp);
+       return true;
+}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dport.h b/drivers/gpu/drm/nouveau/core/engine/disp/dport.h
new file mode 100644 (file)
index 0000000..0e1bbd1
--- /dev/null
@@ -0,0 +1,78 @@
+#ifndef __NVKM_DISP_DPORT_H__
+#define __NVKM_DISP_DPORT_H__
+
+/* DPCD Receiver Capabilities */
+#define DPCD_RC00                                                       0x00000
+#define DPCD_RC00_DPCD_REV                                                 0xff
+#define DPCD_RC01                                                       0x00001
+#define DPCD_RC01_MAX_LINK_RATE                                            0xff
+#define DPCD_RC02                                                       0x00002
+#define DPCD_RC02_ENHANCED_FRAME_CAP                                       0x80
+#define DPCD_RC02_MAX_LANE_COUNT                                           0x1f
+#define DPCD_RC03                                                       0x00003
+#define DPCD_RC03_MAX_DOWNSPREAD                                           0x01
+
+/* DPCD Link Configuration */
+#define DPCD_LC00                                                       0x00100
+#define DPCD_LC00_LINK_BW_SET                                              0xff
+#define DPCD_LC01                                                       0x00101
+#define DPCD_LC01_ENHANCED_FRAME_EN                                        0x80
+#define DPCD_LC01_LANE_COUNT_SET                                           0x1f
+#define DPCD_LC02                                                       0x00102
+#define DPCD_LC02_TRAINING_PATTERN_SET                                     0x03
+#define DPCD_LC03(l)                                            ((l) +  0x00103)
+#define DPCD_LC03_MAX_PRE_EMPHASIS_REACHED                                 0x20
+#define DPCD_LC03_PRE_EMPHASIS_SET                                         0x18
+#define DPCD_LC03_MAX_SWING_REACHED                                        0x04
+#define DPCD_LC03_VOLTAGE_SWING_SET                                        0x03
+
+/* DPCD Link/Sink Status */
+#define DPCD_LS02                                                       0x00202
+#define DPCD_LS02_LANE1_SYMBOL_LOCKED                                      0x40
+#define DPCD_LS02_LANE1_CHANNEL_EQ_DONE                                    0x20
+#define DPCD_LS02_LANE1_CR_DONE                                            0x10
+#define DPCD_LS02_LANE0_SYMBOL_LOCKED                                      0x04
+#define DPCD_LS02_LANE0_CHANNEL_EQ_DONE                                    0x02
+#define DPCD_LS02_LANE0_CR_DONE                                            0x01
+#define DPCD_LS03                                                       0x00203
+#define DPCD_LS03_LANE3_SYMBOL_LOCKED                                      0x40
+#define DPCD_LS03_LANE3_CHANNEL_EQ_DONE                                    0x20
+#define DPCD_LS03_LANE3_CR_DONE                                            0x10
+#define DPCD_LS03_LANE2_SYMBOL_LOCKED                                      0x04
+#define DPCD_LS03_LANE2_CHANNEL_EQ_DONE                                    0x02
+#define DPCD_LS03_LANE2_CR_DONE                                            0x01
+#define DPCD_LS04                                                       0x00204
+#define DPCD_LS04_LINK_STATUS_UPDATED                                      0x80
+#define DPCD_LS04_DOWNSTREAM_PORT_STATUS_CHANGED                           0x40
+#define DPCD_LS04_INTERLANE_ALIGN_DONE                                     0x01
+#define DPCD_LS06                                                       0x00206
+#define DPCD_LS06_LANE1_PRE_EMPHASIS                                       0xc0
+#define DPCD_LS06_LANE1_VOLTAGE_SWING                                      0x30
+#define DPCD_LS06_LANE0_PRE_EMPHASIS                                       0x0c
+#define DPCD_LS06_LANE0_VOLTAGE_SWING                                      0x03
+#define DPCD_LS07                                                       0x00207
+#define DPCD_LS07_LANE3_PRE_EMPHASIS                                       0xc0
+#define DPCD_LS07_LANE3_VOLTAGE_SWING                                      0x30
+#define DPCD_LS07_LANE2_PRE_EMPHASIS                                       0x0c
+#define DPCD_LS07_LANE2_VOLTAGE_SWING                                      0x03
+
+struct nouveau_disp;
+struct dcb_output;
+
+struct nouveau_dp_func {
+       int (*pattern)(struct nouveau_disp *, struct dcb_output *,
+                      int head, int pattern);
+       int (*lnk_ctl)(struct nouveau_disp *, struct dcb_output *, int head,
+                      int link_nr, int link_bw, bool enh_frame);
+       int (*drv_ctl)(struct nouveau_disp *, struct dcb_output *, int head,
+                      int lane, int swing, int preem);
+};
+
+extern const struct nouveau_dp_func nv94_sor_dp_func;
+extern const struct nouveau_dp_func nvd0_sor_dp_func;
+extern const struct nouveau_dp_func nv50_pior_dp_func;
+
+int nouveau_dp_train(struct nouveau_disp *, const struct nouveau_dp_func *,
+                    struct dcb_output *, int, u32);
+
+#endif
index 1c919f2af89f818757c75be470bf3e617df472ac..05e903f08a3696d0c101e67700be6dc24ebe4a92 100644 (file)
 
 #include <engine/disp.h>
 
+#include <core/event.h>
+#include <core/class.h>
+
 struct nv04_disp_priv {
        struct nouveau_disp base;
 };
 
 static struct nouveau_oclass
 nv04_disp_sclass[] = {
+       { NV04_DISP_CLASS, &nouveau_object_ofuncs },
        {},
 };
 
+/*******************************************************************************
+ * Display engine implementation
+ ******************************************************************************/
+
+static void
+nv04_disp_vblank_enable(struct nouveau_event *event, int head)
+{
+       nv_wr32(event->priv, 0x600140 + (head * 0x2000) , 0x00000001);
+}
+
 static void
-nv04_disp_intr_vblank(struct nv04_disp_priv *priv, int crtc)
+nv04_disp_vblank_disable(struct nouveau_event *event, int head)
 {
-       struct nouveau_disp *disp = &priv->base;
-       if (disp->vblank.notify)
-               disp->vblank.notify(disp->vblank.data, crtc);
+       nv_wr32(event->priv, 0x600140 + (head * 0x2000) , 0x00000000);
 }
 
 static void
@@ -49,25 +61,25 @@ nv04_disp_intr(struct nouveau_subdev *subdev)
        u32 crtc1 = nv_rd32(priv, 0x602100);
 
        if (crtc0 & 0x00000001) {
-               nv04_disp_intr_vblank(priv, 0);
+               nouveau_event_trigger(priv->base.vblank, 0);
                nv_wr32(priv, 0x600100, 0x00000001);
        }
 
        if (crtc1 & 0x00000001) {
-               nv04_disp_intr_vblank(priv, 1);
+               nouveau_event_trigger(priv->base.vblank, 1);
                nv_wr32(priv, 0x602100, 0x00000001);
        }
 }
 
 static int
 nv04_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-                 struct nouveau_oclass *oclass, void *data, u32 size,
-                 struct nouveau_object **pobject)
+              struct nouveau_oclass *oclass, void *data, u32 size,
+              struct nouveau_object **pobject)
 {
        struct nv04_disp_priv *priv;
        int ret;
 
-       ret = nouveau_disp_create(parent, engine, oclass, "DISPLAY",
+       ret = nouveau_disp_create(parent, engine, oclass, 2, "DISPLAY",
                                  "display", &priv);
        *pobject = nv_object(priv);
        if (ret)
@@ -75,6 +87,9 @@ nv04_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
 
        nv_engine(priv)->sclass = nv04_disp_sclass;
        nv_subdev(priv)->intr = nv04_disp_intr;
+       priv->base.vblank->priv = priv;
+       priv->base.vblank->enable = nv04_disp_vblank_enable;
+       priv->base.vblank->disable = nv04_disp_vblank_disable;
        return 0;
 }
 
index ca1a7d76a95b50d87864e95954b20aebd7906f76..5fa13267bd9f427b7f83251f22e6bd13a80683b3 100644 (file)
@@ -27,7 +27,6 @@
 #include <core/handle.h>
 #include <core/class.h>
 
-#include <engine/software.h>
 #include <engine/disp.h>
 
 #include <subdev/bios.h>
@@ -37,7 +36,6 @@
 #include <subdev/bios/pll.h>
 #include <subdev/timer.h>
 #include <subdev/fb.h>
-#include <subdev/bar.h>
 #include <subdev/clock.h>
 
 #include "nv50.h"
@@ -335,7 +333,7 @@ nv50_disp_sync_ctor(struct nouveau_object *parent,
        struct nv50_disp_dmac *dmac;
        int ret;
 
-       if (size < sizeof(*data) || args->head > 1)
+       if (size < sizeof(*args) || args->head > 1)
                return -EINVAL;
 
        ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
@@ -374,7 +372,7 @@ nv50_disp_ovly_ctor(struct nouveau_object *parent,
        struct nv50_disp_dmac *dmac;
        int ret;
 
-       if (size < sizeof(*data) || args->head > 1)
+       if (size < sizeof(*args) || args->head > 1)
                return -EINVAL;
 
        ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
@@ -543,6 +541,18 @@ nv50_disp_curs_ofuncs = {
  * Base display object
  ******************************************************************************/
 
+static void
+nv50_disp_base_vblank_enable(struct nouveau_event *event, int head)
+{
+       nv_mask(event->priv, 0x61002c, (1 << head), (1 << head));
+}
+
+static void
+nv50_disp_base_vblank_disable(struct nouveau_event *event, int head)
+{
+       nv_mask(event->priv, 0x61002c, (1 << head), (0 << head));
+}
+
 static int
 nv50_disp_base_ctor(struct nouveau_object *parent,
                    struct nouveau_object *engine,
@@ -559,6 +569,9 @@ nv50_disp_base_ctor(struct nouveau_object *parent,
        if (ret)
                return ret;
 
+       priv->base.vblank->priv = priv;
+       priv->base.vblank->enable = nv50_disp_base_vblank_enable;
+       priv->base.vblank->disable = nv50_disp_base_vblank_disable;
        return nouveau_ramht_new(parent, parent, 0x1000, 0, &base->ramht);
 }
 
@@ -613,7 +626,7 @@ nv50_disp_base_init(struct nouveau_object *object)
                nv_wr32(priv, 0x6101e0 + (i * 0x04), tmp);
        }
 
-       /* ... EXT caps */
+       /* ... PIOR caps */
        for (i = 0; i < 3; i++) {
                tmp = nv_rd32(priv, 0x61e000 + (i * 0x800));
                nv_wr32(priv, 0x6101f0 + (i * 0x04), tmp);
@@ -665,6 +678,9 @@ nv50_disp_base_omthds[] = {
        { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
        { DAC_MTHD(NV50_DISP_DAC_PWR)         , nv50_dac_mthd },
        { DAC_MTHD(NV50_DISP_DAC_LOAD)        , nv50_dac_mthd },
+       { PIOR_MTHD(NV50_DISP_PIOR_PWR)       , nv50_pior_mthd },
+       { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR)  , nv50_pior_mthd },
+       { PIOR_MTHD(NV50_DISP_PIOR_DP_PWR)    , nv50_pior_mthd },
        {},
 };
 
@@ -756,50 +772,6 @@ nv50_disp_intr_error(struct nv50_disp_priv *priv)
        }
 }
 
-static void
-nv50_disp_intr_vblank(struct nv50_disp_priv *priv, int crtc)
-{
-       struct nouveau_bar *bar = nouveau_bar(priv);
-       struct nouveau_disp *disp = &priv->base;
-       struct nouveau_software_chan *chan, *temp;
-       unsigned long flags;
-
-       spin_lock_irqsave(&disp->vblank.lock, flags);
-       list_for_each_entry_safe(chan, temp, &disp->vblank.list, vblank.head) {
-               if (chan->vblank.crtc != crtc)
-                       continue;
-
-               if (nv_device(priv)->chipset >= 0xc0) {
-                       nv_wr32(priv, 0x001718, 0x80000000 | chan->vblank.channel);
-                       bar->flush(bar);
-                       nv_wr32(priv, 0x06000c,
-                               upper_32_bits(chan->vblank.offset));
-                       nv_wr32(priv, 0x060010,
-                               lower_32_bits(chan->vblank.offset));
-                       nv_wr32(priv, 0x060014, chan->vblank.value);
-               } else {
-                       nv_wr32(priv, 0x001704, chan->vblank.channel);
-                       nv_wr32(priv, 0x001710, 0x80000000 | chan->vblank.ctxdma);
-                       bar->flush(bar);
-                       if (nv_device(priv)->chipset == 0x50) {
-                               nv_wr32(priv, 0x001570, chan->vblank.offset);
-                               nv_wr32(priv, 0x001574, chan->vblank.value);
-                       } else {
-                               nv_wr32(priv, 0x060010, chan->vblank.offset);
-                               nv_wr32(priv, 0x060014, chan->vblank.value);
-                       }
-               }
-
-               list_del(&chan->vblank.head);
-               if (disp->vblank.put)
-                       disp->vblank.put(disp->vblank.data, crtc);
-       }
-       spin_unlock_irqrestore(&disp->vblank.lock, flags);
-
-       if (disp->vblank.notify)
-               disp->vblank.notify(disp->vblank.data, crtc);
-}
-
 static u16
 exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
            struct dcb_output *dcb, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
@@ -811,8 +783,8 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
        if (outp < 4) {
                type = DCB_OUTPUT_ANALOG;
                mask = 0;
-       } else {
-               outp -= 4;
+       } else
+       if (outp < 8) {
                switch (ctrl & 0x00000f00) {
                case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break;
                case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break;
@@ -824,6 +796,17 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
                        nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl);
                        return 0x0000;
                }
+               outp -= 4;
+       } else {
+               outp = outp - 8;
+               type = 0x0010;
+               mask = 0;
+               switch (ctrl & 0x00000f00) {
+               case 0x00000000: type |= priv->pior.type[outp]; break;
+               default:
+                       nv_error(priv, "unknown PIOR mc 0x%08x\n", ctrl);
+                       return 0x0000;
+               }
        }
 
        mask  = 0x00c0 & (mask << 6);
@@ -834,6 +817,10 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
        if (!data)
                return 0x0000;
 
+       /* off-chip encoders require matching the exact encoder type */
+       if (dcb->location != 0)
+               type |= dcb->extdev << 8;
+
        return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info);
 }
 
@@ -848,9 +835,11 @@ exec_script(struct nv50_disp_priv *priv, int head, int id)
        u32 ctrl = 0x00000000;
        int i;
 
+       /* DAC */
        for (i = 0; !(ctrl & (1 << head)) && i < 3; i++)
                ctrl = nv_rd32(priv, 0x610b5c + (i * 8));
 
+       /* SOR */
        if (!(ctrl & (1 << head))) {
                if (nv_device(priv)->chipset  < 0x90 ||
                    nv_device(priv)->chipset == 0x92 ||
@@ -865,6 +854,13 @@ exec_script(struct nv50_disp_priv *priv, int head, int id)
                }
        }
 
+       /* PIOR */
+       if (!(ctrl & (1 << head))) {
+               for (i = 0; !(ctrl & (1 << head)) && i < 3; i++)
+                       ctrl = nv_rd32(priv, 0x610b84 + (i * 8));
+               i += 8;
+       }
+
        if (!(ctrl & (1 << head)))
                return false;
        i--;
@@ -894,13 +890,15 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk,
        struct nvbios_outp info1;
        struct nvbios_ocfg info2;
        u8  ver, hdr, cnt, len;
-       u16 data, conf;
        u32 ctrl = 0x00000000;
+       u32 data, conf = ~0;
        int i;
 
+       /* DAC */
        for (i = 0; !(ctrl & (1 << head)) && i < 3; i++)
                ctrl = nv_rd32(priv, 0x610b58 + (i * 8));
 
+       /* SOR */
        if (!(ctrl & (1 << head))) {
                if (nv_device(priv)->chipset  < 0x90 ||
                    nv_device(priv)->chipset == 0x92 ||
@@ -915,34 +913,46 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk,
                }
        }
 
+       /* PIOR */
+       if (!(ctrl & (1 << head))) {
+               for (i = 0; !(ctrl & (1 << head)) && i < 3; i++)
+                       ctrl = nv_rd32(priv, 0x610b80 + (i * 8));
+               i += 8;
+       }
+
        if (!(ctrl & (1 << head)))
-               return 0x0000;
+               return conf;
        i--;
 
        data = exec_lookup(priv, head, i, ctrl, outp, &ver, &hdr, &cnt, &len, &info1);
        if (!data)
-               return 0x0000;
-
-       switch (outp->type) {
-       case DCB_OUTPUT_TMDS:
-               conf = (ctrl & 0x00000f00) >> 8;
-               if (pclk >= 165000)
-                       conf |= 0x0100;
-               break;
-       case DCB_OUTPUT_LVDS:
-               conf = priv->sor.lvdsconf;
-               break;
-       case DCB_OUTPUT_DP:
+               return conf;
+
+       if (outp->location == 0) {
+               switch (outp->type) {
+               case DCB_OUTPUT_TMDS:
+                       conf = (ctrl & 0x00000f00) >> 8;
+                       if (pclk >= 165000)
+                               conf |= 0x0100;
+                       break;
+               case DCB_OUTPUT_LVDS:
+                       conf = priv->sor.lvdsconf;
+                       break;
+               case DCB_OUTPUT_DP:
+                       conf = (ctrl & 0x00000f00) >> 8;
+                       break;
+               case DCB_OUTPUT_ANALOG:
+               default:
+                       conf = 0x00ff;
+                       break;
+               }
+       } else {
                conf = (ctrl & 0x00000f00) >> 8;
-               break;
-       case DCB_OUTPUT_ANALOG:
-       default:
-               conf = 0x00ff;
-               break;
+               pclk = pclk / 2;
        }
 
        data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2);
-       if (data) {
+       if (data && id < 0xff) {
                data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
                if (data) {
                        struct nvbios_init init = {
@@ -954,32 +964,37 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk,
                                .execute = 1,
                        };
 
-                       if (nvbios_exec(&init))
-                               return 0x0000;
-                       return conf;
+                       nvbios_exec(&init);
                }
        }
 
-       return 0x0000;
+       return conf;
 }
 
 static void
-nv50_disp_intr_unk10(struct nv50_disp_priv *priv, u32 super)
+nv50_disp_intr_unk10_0(struct nv50_disp_priv *priv, int head)
 {
-       int head = ffs((super & 0x00000060) >> 5) - 1;
-       if (head >= 0) {
-               head = ffs((super & 0x00000180) >> 7) - 1;
-               if (head >= 0)
-                       exec_script(priv, head, 1);
-       }
+       exec_script(priv, head, 1);
+}
 
-       nv_wr32(priv, 0x610024, 0x00000010);
-       nv_wr32(priv, 0x610030, 0x80000000);
+static void
+nv50_disp_intr_unk20_0(struct nv50_disp_priv *priv, int head)
+{
+       exec_script(priv, head, 2);
+}
+
+static void
+nv50_disp_intr_unk20_1(struct nv50_disp_priv *priv, int head)
+{
+       struct nouveau_clock *clk = nouveau_clock(priv);
+       u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
+       if (pclk)
+               clk->pll_set(clk, PLL_VPLL0 + head, pclk);
 }
 
 static void
-nv50_disp_intr_unk20_dp(struct nv50_disp_priv *priv,
-                       struct dcb_output *outp, u32 pclk)
+nv50_disp_intr_unk20_2_dp(struct nv50_disp_priv *priv,
+                         struct dcb_output *outp, u32 pclk)
 {
        const int link = !(outp->sorconf.link & 1);
        const int   or = ffs(outp->or) - 1;
@@ -1085,53 +1100,54 @@ nv50_disp_intr_unk20_dp(struct nv50_disp_priv *priv,
 }
 
 static void
-nv50_disp_intr_unk20(struct nv50_disp_priv *priv, u32 super)
+nv50_disp_intr_unk20_2(struct nv50_disp_priv *priv, int head)
 {
        struct dcb_output outp;
-       u32 addr, mask, data;
-       int head;
+       u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
+       u32 hval, hreg = 0x614200 + (head * 0x800);
+       u32 oval, oreg;
+       u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp);
+       if (conf != ~0) {
+               if (outp.location == 0 && outp.type == DCB_OUTPUT_DP) {
+                       u32 soff = (ffs(outp.or) - 1) * 0x08;
+                       u32 ctrl = nv_rd32(priv, 0x610798 + soff);
+                       u32 datarate;
+
+                       switch ((ctrl & 0x000f0000) >> 16) {
+                       case 6: datarate = pclk * 30 / 8; break;
+                       case 5: datarate = pclk * 24 / 8; break;
+                       case 2:
+                       default:
+                               datarate = pclk * 18 / 8;
+                               break;
+                       }
 
-       /* finish detaching encoder? */
-       head = ffs((super & 0x00000180) >> 7) - 1;
-       if (head >= 0)
-               exec_script(priv, head, 2);
-
-       /* check whether a vpll change is required */
-       head = ffs((super & 0x00000600) >> 9) - 1;
-       if (head >= 0) {
-               u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
-               if (pclk) {
-                       struct nouveau_clock *clk = nouveau_clock(priv);
-                       clk->pll_set(clk, PLL_VPLL0 + head, pclk);
+                       nouveau_dp_train(&priv->base, priv->sor.dp,
+                                        &outp, head, datarate);
                }
 
-               nv_mask(priv, 0x614200 + head * 0x800, 0x0000000f, 0x00000000);
-       }
-
-       /* (re)attach the relevant OR to the head */
-       head = ffs((super & 0x00000180) >> 7) - 1;
-       if (head >= 0) {
-               u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
-               u32 conf = exec_clkcmp(priv, head, 0, pclk, &outp);
-               if (conf) {
-                       if (outp.type == DCB_OUTPUT_ANALOG) {
-                               addr = 0x614280 + (ffs(outp.or) - 1) * 0x800;
-                               mask = 0xffffffff;
-                               data = 0x00000000;
-                       } else {
-                               if (outp.type == DCB_OUTPUT_DP)
-                                       nv50_disp_intr_unk20_dp(priv, &outp, pclk);
-                               addr = 0x614300 + (ffs(outp.or) - 1) * 0x800;
-                               mask = 0x00000707;
-                               data = (conf & 0x0100) ? 0x0101 : 0x0000;
-                       }
-
-                       nv_mask(priv, addr, mask, data);
+               exec_clkcmp(priv, head, 0, pclk, &outp);
+
+               if (!outp.location && outp.type == DCB_OUTPUT_ANALOG) {
+                       oreg = 0x614280 + (ffs(outp.or) - 1) * 0x800;
+                       oval = 0x00000000;
+                       hval = 0x00000000;
+               } else
+               if (!outp.location) {
+                       if (outp.type == DCB_OUTPUT_DP)
+                               nv50_disp_intr_unk20_2_dp(priv, &outp, pclk);
+                       oreg = 0x614300 + (ffs(outp.or) - 1) * 0x800;
+                       oval = (conf & 0x0100) ? 0x00000101 : 0x00000000;
+                       hval = 0x00000000;
+               } else {
+                       oreg = 0x614380 + (ffs(outp.or) - 1) * 0x800;
+                       oval = 0x00000001;
+                       hval = 0x00000001;
                }
-       }
 
-       nv_wr32(priv, 0x610024, 0x00000020);
-       nv_wr32(priv, 0x610030, 0x80000000);
+               nv_mask(priv, hreg, 0x0000000f, hval);
+               nv_mask(priv, oreg, 0x00000707, oval);
+       }
 }
 
 /* If programming a TMDS output on a SOR that can also be configured for
@@ -1143,7 +1159,7 @@ nv50_disp_intr_unk20(struct nv50_disp_priv *priv, u32 super)
  * programmed for DisplayPort.
  */
 static void
-nv50_disp_intr_unk40_tmds(struct nv50_disp_priv *priv, struct dcb_output *outp)
+nv50_disp_intr_unk40_0_tmds(struct nv50_disp_priv *priv, struct dcb_output *outp)
 {
        struct nouveau_bios *bios = nouveau_bios(priv);
        const int link = !(outp->sorconf.link & 1);
@@ -1157,35 +1173,79 @@ nv50_disp_intr_unk40_tmds(struct nv50_disp_priv *priv, struct dcb_output *outp)
 }
 
 static void
-nv50_disp_intr_unk40(struct nv50_disp_priv *priv, u32 super)
+nv50_disp_intr_unk40_0(struct nv50_disp_priv *priv, int head)
 {
-       int head = ffs((super & 0x00000180) >> 7) - 1;
-       if (head >= 0) {
-               struct dcb_output outp;
-               u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
-               if (pclk && exec_clkcmp(priv, head, 1, pclk, &outp)) {
-                       if (outp.type == DCB_OUTPUT_TMDS)
-                               nv50_disp_intr_unk40_tmds(priv, &outp);
+       struct dcb_output outp;
+       u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
+       if (exec_clkcmp(priv, head, 1, pclk, &outp) != ~0) {
+               if (outp.location == 0 && outp.type == DCB_OUTPUT_TMDS)
+                       nv50_disp_intr_unk40_0_tmds(priv, &outp);
+               else
+               if (outp.location == 1 && outp.type == DCB_OUTPUT_DP) {
+                       u32 soff = (ffs(outp.or) - 1) * 0x08;
+                       u32 ctrl = nv_rd32(priv, 0x610b84 + soff);
+                       u32 datarate;
+
+                       switch ((ctrl & 0x000f0000) >> 16) {
+                       case 6: datarate = pclk * 30 / 8; break;
+                       case 5: datarate = pclk * 24 / 8; break;
+                       case 2:
+                       default:
+                               datarate = pclk * 18 / 8;
+                               break;
+                       }
+
+                       nouveau_dp_train(&priv->base, priv->pior.dp,
+                                        &outp, head, datarate);
                }
        }
-
-       nv_wr32(priv, 0x610024, 0x00000040);
-       nv_wr32(priv, 0x610030, 0x80000000);
 }
 
-static void
-nv50_disp_intr_super(struct nv50_disp_priv *priv, u32 intr1)
+void
+nv50_disp_intr_supervisor(struct work_struct *work)
 {
+       struct nv50_disp_priv *priv =
+               container_of(work, struct nv50_disp_priv, supervisor);
        u32 super = nv_rd32(priv, 0x610030);
+       int head;
 
-       nv_debug(priv, "supervisor 0x%08x 0x%08x\n", intr1, super);
+       nv_debug(priv, "supervisor 0x%08x 0x%08x\n", priv->super, super);
 
-       if (intr1 & 0x00000010)
-               nv50_disp_intr_unk10(priv, super);
-       if (intr1 & 0x00000020)
-               nv50_disp_intr_unk20(priv, super);
-       if (intr1 & 0x00000040)
-               nv50_disp_intr_unk40(priv, super);
+       if (priv->super & 0x00000010) {
+               for (head = 0; head < priv->head.nr; head++) {
+                       if (!(super & (0x00000020 << head)))
+                               continue;
+                       if (!(super & (0x00000080 << head)))
+                               continue;
+                       nv50_disp_intr_unk10_0(priv, head);
+               }
+       } else
+       if (priv->super & 0x00000020) {
+               for (head = 0; head < priv->head.nr; head++) {
+                       if (!(super & (0x00000080 << head)))
+                               continue;
+                       nv50_disp_intr_unk20_0(priv, head);
+               }
+               for (head = 0; head < priv->head.nr; head++) {
+                       if (!(super & (0x00000200 << head)))
+                               continue;
+                       nv50_disp_intr_unk20_1(priv, head);
+               }
+               for (head = 0; head < priv->head.nr; head++) {
+                       if (!(super & (0x00000080 << head)))
+                               continue;
+                       nv50_disp_intr_unk20_2(priv, head);
+               }
+       } else
+       if (priv->super & 0x00000040) {
+               for (head = 0; head < priv->head.nr; head++) {
+                       if (!(super & (0x00000080 << head)))
+                               continue;
+                       nv50_disp_intr_unk40_0(priv, head);
+               }
+       }
+
+       nv_wr32(priv, 0x610030, 0x80000000);
 }
 
 void
@@ -1201,19 +1261,21 @@ nv50_disp_intr(struct nouveau_subdev *subdev)
        }
 
        if (intr1 & 0x00000004) {
-               nv50_disp_intr_vblank(priv, 0);
+               nouveau_event_trigger(priv->base.vblank, 0);
                nv_wr32(priv, 0x610024, 0x00000004);
                intr1 &= ~0x00000004;
        }
 
        if (intr1 & 0x00000008) {
-               nv50_disp_intr_vblank(priv, 1);
+               nouveau_event_trigger(priv->base.vblank, 1);
                nv_wr32(priv, 0x610024, 0x00000008);
                intr1 &= ~0x00000008;
        }
 
        if (intr1 & 0x00000070) {
-               nv50_disp_intr_super(priv, intr1);
+               priv->super = (intr1 & 0x00000070);
+               schedule_work(&priv->supervisor);
+               nv_wr32(priv, 0x610024, priv->super);
                intr1 &= ~0x00000070;
        }
 }
@@ -1226,7 +1288,7 @@ nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv50_disp_priv *priv;
        int ret;
 
-       ret = nouveau_disp_create(parent, engine, oclass, "PDISP",
+       ret = nouveau_disp_create(parent, engine, oclass, 2, "PDISP",
                                  "display", &priv);
        *pobject = nv_object(priv);
        if (ret)
@@ -1235,16 +1297,17 @@ nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        nv_engine(priv)->sclass = nv50_disp_base_oclass;
        nv_engine(priv)->cclass = &nv50_disp_cclass;
        nv_subdev(priv)->intr = nv50_disp_intr;
+       INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor);
        priv->sclass = nv50_disp_sclass;
        priv->head.nr = 2;
        priv->dac.nr = 3;
        priv->sor.nr = 2;
+       priv->pior.nr = 3;
        priv->dac.power = nv50_dac_power;
        priv->dac.sense = nv50_dac_sense;
        priv->sor.power = nv50_sor_power;
-
-       INIT_LIST_HEAD(&priv->base.vblank.list);
-       spin_lock_init(&priv->base.vblank.lock);
+       priv->pior.power = nv50_pior_power;
+       priv->pior.dp = &nv50_pior_dp_func;
        return 0;
 }
 
index a6bb931450f16c630dd663ada1186c687800f5b9..1ae6ceb5670403b0150f931f2cb01815a4a56569 100644 (file)
@@ -3,16 +3,22 @@
 
 #include <core/parent.h>
 #include <core/namedb.h>
+#include <core/engctx.h>
 #include <core/ramht.h>
+#include <core/event.h>
 
 #include <engine/dmaobj.h>
 #include <engine/disp.h>
 
-struct dcb_output;
+#include "dport.h"
 
 struct nv50_disp_priv {
        struct nouveau_disp base;
        struct nouveau_oclass *sclass;
+
+       struct work_struct supervisor;
+       u32 super;
+
        struct {
                int nr;
        } head;
@@ -26,23 +32,15 @@ struct nv50_disp_priv {
                int (*power)(struct nv50_disp_priv *, int sor, u32 data);
                int (*hda_eld)(struct nv50_disp_priv *, int sor, u8 *, u32);
                int (*hdmi)(struct nv50_disp_priv *, int head, int sor, u32);
-               int (*dp_train_init)(struct nv50_disp_priv *, int sor, int link,
-                                    int head, u16 type, u16 mask, u32 data,
-                                    struct dcb_output *);
-               int (*dp_train_fini)(struct nv50_disp_priv *, int sor, int link,
-                                    int head, u16 type, u16 mask, u32 data,
-                                    struct dcb_output *);
-               int (*dp_train)(struct nv50_disp_priv *, int sor, int link,
-                               u16 type, u16 mask, u32 data,
-                               struct dcb_output *);
-               int (*dp_lnkctl)(struct nv50_disp_priv *, int sor, int link,
-                                int head, u16 type, u16 mask, u32 data,
-                                struct dcb_output *);
-               int (*dp_drvctl)(struct nv50_disp_priv *, int sor, int link,
-                                int lane, u16 type, u16 mask, u32 data,
-                                struct dcb_output *);
                u32 lvdsconf;
+               const struct nouveau_dp_func *dp;
        } sor;
+       struct {
+               int nr;
+               int (*power)(struct nv50_disp_priv *, int ext, u32 data);
+               u8 type[3];
+               const struct nouveau_dp_func *dp;
+       } pior;
 };
 
 #define DAC_MTHD(n) (n), (n) + 0x03
@@ -81,6 +79,11 @@ int nvd0_sor_dp_lnkctl(struct nv50_disp_priv *, int, int, int, u16, u16, u32,
 int nvd0_sor_dp_drvctl(struct nv50_disp_priv *, int, int, int, u16, u16, u32,
                       struct dcb_output *);
 
+#define PIOR_MTHD(n) (n), (n) + 0x03
+
+int nv50_pior_mthd(struct nouveau_object *, u32, void *, u32);
+int nv50_pior_power(struct nv50_disp_priv *, int, u32);
+
 struct nv50_disp_base {
        struct nouveau_parent base;
        struct nouveau_ramht *ramht;
@@ -124,6 +127,7 @@ extern struct nouveau_ofuncs nv50_disp_oimm_ofuncs;
 extern struct nouveau_ofuncs nv50_disp_curs_ofuncs;
 extern struct nouveau_ofuncs nv50_disp_base_ofuncs;
 extern struct nouveau_oclass nv50_disp_cclass;
+void nv50_disp_intr_supervisor(struct work_struct *);
 void nv50_disp_intr(struct nouveau_subdev *);
 
 extern struct nouveau_omthds nv84_disp_base_omthds[];
@@ -137,6 +141,7 @@ extern struct nouveau_ofuncs nvd0_disp_oimm_ofuncs;
 extern struct nouveau_ofuncs nvd0_disp_curs_ofuncs;
 extern struct nouveau_ofuncs nvd0_disp_base_ofuncs;
 extern struct nouveau_oclass nvd0_disp_cclass;
+void nvd0_disp_intr_supervisor(struct work_struct *);
 void nvd0_disp_intr(struct nouveau_subdev *);
 
 #endif
index fc84eacdfbec72da6164d89b4c6f7c730e770623..d8c74c0883a16904b92081857552182721eca183 100644 (file)
@@ -46,6 +46,9 @@ nv84_disp_base_omthds[] = {
        { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
        { DAC_MTHD(NV50_DISP_DAC_PWR)         , nv50_dac_mthd },
        { DAC_MTHD(NV50_DISP_DAC_LOAD)        , nv50_dac_mthd },
+       { PIOR_MTHD(NV50_DISP_PIOR_PWR)       , nv50_pior_mthd },
+       { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR)  , nv50_pior_mthd },
+       { PIOR_MTHD(NV50_DISP_PIOR_DP_PWR)    , nv50_pior_mthd },
        {},
 };
 
@@ -63,7 +66,7 @@ nv84_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv50_disp_priv *priv;
        int ret;
 
-       ret = nouveau_disp_create(parent, engine, oclass, "PDISP",
+       ret = nouveau_disp_create(parent, engine, oclass, 2, "PDISP",
                                  "display", &priv);
        *pobject = nv_object(priv);
        if (ret)
@@ -72,17 +75,18 @@ nv84_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        nv_engine(priv)->sclass = nv84_disp_base_oclass;
        nv_engine(priv)->cclass = &nv50_disp_cclass;
        nv_subdev(priv)->intr = nv50_disp_intr;
+       INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor);
        priv->sclass = nv84_disp_sclass;
        priv->head.nr = 2;
        priv->dac.nr = 3;
        priv->sor.nr = 2;
+       priv->pior.nr = 3;
        priv->dac.power = nv50_dac_power;
        priv->dac.sense = nv50_dac_sense;
        priv->sor.power = nv50_sor_power;
        priv->sor.hdmi = nv84_hdmi_ctrl;
-
-       INIT_LIST_HEAD(&priv->base.vblank.list);
-       spin_lock_init(&priv->base.vblank.lock);
+       priv->pior.power = nv50_pior_power;
+       priv->pior.dp = &nv50_pior_dp_func;
        return 0;
 }
 
index ba9dfd4669a2d721b68950ca61aadb87842b0777..a66f949c1f84119479f5c5ffbe809fad278db4d2 100644 (file)
@@ -44,14 +44,11 @@ nv94_disp_base_omthds[] = {
        { SOR_MTHD(NV50_DISP_SOR_PWR)         , nv50_sor_mthd },
        { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR)    , nv50_sor_mthd },
        { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
-       { SOR_MTHD(NV94_DISP_SOR_DP_TRAIN)    , nv50_sor_mthd },
-       { SOR_MTHD(NV94_DISP_SOR_DP_LNKCTL)   , nv50_sor_mthd },
-       { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(0)), nv50_sor_mthd },
-       { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(1)), nv50_sor_mthd },
-       { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(2)), nv50_sor_mthd },
-       { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(3)), nv50_sor_mthd },
        { DAC_MTHD(NV50_DISP_DAC_PWR)         , nv50_dac_mthd },
        { DAC_MTHD(NV50_DISP_DAC_LOAD)        , nv50_dac_mthd },
+       { PIOR_MTHD(NV50_DISP_PIOR_PWR)       , nv50_pior_mthd },
+       { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR)  , nv50_pior_mthd },
+       { PIOR_MTHD(NV50_DISP_PIOR_DP_PWR)    , nv50_pior_mthd },
        {},
 };
 
@@ -69,7 +66,7 @@ nv94_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv50_disp_priv *priv;
        int ret;
 
-       ret = nouveau_disp_create(parent, engine, oclass, "PDISP",
+       ret = nouveau_disp_create(parent, engine, oclass, 2, "PDISP",
                                  "display", &priv);
        *pobject = nv_object(priv);
        if (ret)
@@ -78,22 +75,19 @@ nv94_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        nv_engine(priv)->sclass = nv94_disp_base_oclass;
        nv_engine(priv)->cclass = &nv50_disp_cclass;
        nv_subdev(priv)->intr = nv50_disp_intr;
+       INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor);
        priv->sclass = nv94_disp_sclass;
        priv->head.nr = 2;
        priv->dac.nr = 3;
        priv->sor.nr = 4;
+       priv->pior.nr = 3;
        priv->dac.power = nv50_dac_power;
        priv->dac.sense = nv50_dac_sense;
        priv->sor.power = nv50_sor_power;
        priv->sor.hdmi = nv84_hdmi_ctrl;
-       priv->sor.dp_train = nv94_sor_dp_train;
-       priv->sor.dp_train_init = nv94_sor_dp_train_init;
-       priv->sor.dp_train_fini = nv94_sor_dp_train_fini;
-       priv->sor.dp_lnkctl = nv94_sor_dp_lnkctl;
-       priv->sor.dp_drvctl = nv94_sor_dp_drvctl;
-
-       INIT_LIST_HEAD(&priv->base.vblank.list);
-       spin_lock_init(&priv->base.vblank.lock);
+       priv->sor.dp = &nv94_sor_dp_func;
+       priv->pior.power = nv50_pior_power;
+       priv->pior.dp = &nv50_pior_dp_func;
        return 0;
 }
 
index 5d63902cdeda857f820f3c7c714554bd41be2069..6cf8eefac3686bbd806225bdd21a1ff9b77ab0d6 100644 (file)
@@ -53,7 +53,7 @@ nva0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv50_disp_priv *priv;
        int ret;
 
-       ret = nouveau_disp_create(parent, engine, oclass, "PDISP",
+       ret = nouveau_disp_create(parent, engine, oclass, 2, "PDISP",
                                  "display", &priv);
        *pobject = nv_object(priv);
        if (ret)
@@ -62,17 +62,18 @@ nva0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        nv_engine(priv)->sclass = nva0_disp_base_oclass;
        nv_engine(priv)->cclass = &nv50_disp_cclass;
        nv_subdev(priv)->intr = nv50_disp_intr;
+       INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor);
        priv->sclass = nva0_disp_sclass;
        priv->head.nr = 2;
        priv->dac.nr = 3;
        priv->sor.nr = 2;
+       priv->pior.nr = 3;
        priv->dac.power = nv50_dac_power;
        priv->dac.sense = nv50_dac_sense;
        priv->sor.power = nv50_sor_power;
        priv->sor.hdmi = nv84_hdmi_ctrl;
-
-       INIT_LIST_HEAD(&priv->base.vblank.list);
-       spin_lock_init(&priv->base.vblank.lock);
+       priv->pior.power = nv50_pior_power;
+       priv->pior.dp = &nv50_pior_dp_func;
        return 0;
 }
 
index e9192ca389fa091d5276fcfec39f932542c0b809..b75413169eaee91e07dd73dd08e1a7efada6ed34 100644 (file)
@@ -45,14 +45,11 @@ nva3_disp_base_omthds[] = {
        { SOR_MTHD(NVA3_DISP_SOR_HDA_ELD)     , nv50_sor_mthd },
        { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR)    , nv50_sor_mthd },
        { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
-       { SOR_MTHD(NV94_DISP_SOR_DP_TRAIN)    , nv50_sor_mthd },
-       { SOR_MTHD(NV94_DISP_SOR_DP_LNKCTL)   , nv50_sor_mthd },
-       { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(0)), nv50_sor_mthd },
-       { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(1)), nv50_sor_mthd },
-       { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(2)), nv50_sor_mthd },
-       { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(3)), nv50_sor_mthd },
        { DAC_MTHD(NV50_DISP_DAC_PWR)         , nv50_dac_mthd },
        { DAC_MTHD(NV50_DISP_DAC_LOAD)        , nv50_dac_mthd },
+       { PIOR_MTHD(NV50_DISP_PIOR_PWR)       , nv50_pior_mthd },
+       { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR)  , nv50_pior_mthd },
+       { PIOR_MTHD(NV50_DISP_PIOR_DP_PWR)    , nv50_pior_mthd },
        {},
 };
 
@@ -70,7 +67,7 @@ nva3_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv50_disp_priv *priv;
        int ret;
 
-       ret = nouveau_disp_create(parent, engine, oclass, "PDISP",
+       ret = nouveau_disp_create(parent, engine, oclass, 2, "PDISP",
                                  "display", &priv);
        *pobject = nv_object(priv);
        if (ret)
@@ -79,23 +76,20 @@ nva3_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        nv_engine(priv)->sclass = nva3_disp_base_oclass;
        nv_engine(priv)->cclass = &nv50_disp_cclass;
        nv_subdev(priv)->intr = nv50_disp_intr;
+       INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor);
        priv->sclass = nva3_disp_sclass;
        priv->head.nr = 2;
        priv->dac.nr = 3;
        priv->sor.nr = 4;
+       priv->pior.nr = 3;
        priv->dac.power = nv50_dac_power;
        priv->dac.sense = nv50_dac_sense;
        priv->sor.power = nv50_sor_power;
        priv->sor.hda_eld = nva3_hda_eld;
        priv->sor.hdmi = nva3_hdmi_ctrl;
-       priv->sor.dp_train = nv94_sor_dp_train;
-       priv->sor.dp_train_init = nv94_sor_dp_train_init;
-       priv->sor.dp_train_fini = nv94_sor_dp_train_fini;
-       priv->sor.dp_lnkctl = nv94_sor_dp_lnkctl;
-       priv->sor.dp_drvctl = nv94_sor_dp_drvctl;
-
-       INIT_LIST_HEAD(&priv->base.vblank.list);
-       spin_lock_init(&priv->base.vblank.lock);
+       priv->sor.dp = &nv94_sor_dp_func;
+       priv->pior.power = nv50_pior_power;
+       priv->pior.dp = &nv50_pior_dp_func;
        return 0;
 }
 
index 9e38ebff5fb3f38b70c0cb5cb18069027b424cfe..788dd34ccb54ae7997586149410808afa125e105 100644 (file)
 #include <core/handle.h>
 #include <core/class.h>
 
-#include <engine/software.h>
 #include <engine/disp.h>
 
 #include <subdev/timer.h>
 #include <subdev/fb.h>
-#include <subdev/bar.h>
 #include <subdev/clock.h>
 
 #include <subdev/bios.h>
@@ -230,7 +228,7 @@ nvd0_disp_sync_ctor(struct nouveau_object *parent,
        struct nv50_disp_dmac *dmac;
        int ret;
 
-       if (size < sizeof(*data) || args->head >= priv->head.nr)
+       if (size < sizeof(*args) || args->head >= priv->head.nr)
                return -EINVAL;
 
        ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
@@ -270,7 +268,7 @@ nvd0_disp_ovly_ctor(struct nouveau_object *parent,
        struct nv50_disp_dmac *dmac;
        int ret;
 
-       if (size < sizeof(*data) || args->head >= priv->head.nr)
+       if (size < sizeof(*args) || args->head >= priv->head.nr)
                return -EINVAL;
 
        ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
@@ -443,6 +441,18 @@ nvd0_disp_curs_ofuncs = {
  * Base display object
  ******************************************************************************/
 
+static void
+nvd0_disp_base_vblank_enable(struct nouveau_event *event, int head)
+{
+       nv_mask(event->priv, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000001);
+}
+
+static void
+nvd0_disp_base_vblank_disable(struct nouveau_event *event, int head)
+{
+       nv_mask(event->priv, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000000);
+}
+
 static int
 nvd0_disp_base_ctor(struct nouveau_object *parent,
                    struct nouveau_object *engine,
@@ -459,6 +469,10 @@ nvd0_disp_base_ctor(struct nouveau_object *parent,
        if (ret)
                return ret;
 
+       priv->base.vblank->priv = priv;
+       priv->base.vblank->enable = nvd0_disp_base_vblank_enable;
+       priv->base.vblank->disable = nvd0_disp_base_vblank_disable;
+
        return nouveau_ramht_new(parent, parent, 0x1000, 0, &base->ramht);
 }
 
@@ -609,13 +623,24 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
 }
 
 static bool
-exec_script(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, int id)
+exec_script(struct nv50_disp_priv *priv, int head, int id)
 {
        struct nouveau_bios *bios = nouveau_bios(priv);
        struct nvbios_outp info;
        struct dcb_output dcb;
        u8  ver, hdr, cnt, len;
+       u32 ctrl = 0x00000000;
        u16 data;
+       int outp;
+
+       for (outp = 0; !(ctrl & (1 << head)) && outp < 8; outp++) {
+               ctrl = nv_rd32(priv, 0x640180 + (outp * 0x20));
+               if (ctrl & (1 << head))
+                       break;
+       }
+
+       if (outp == 8)
+               return false;
 
        data = exec_lookup(priv, head, outp, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info);
        if (data) {
@@ -635,21 +660,31 @@ exec_script(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, int id)
 }
 
 static u32
-exec_clkcmp(struct nv50_disp_priv *priv, int head, int outp,
-           u32 ctrl, int id, u32 pclk)
+exec_clkcmp(struct nv50_disp_priv *priv, int head, int id,
+           u32 pclk, struct dcb_output *dcb)
 {
        struct nouveau_bios *bios = nouveau_bios(priv);
        struct nvbios_outp info1;
        struct nvbios_ocfg info2;
-       struct dcb_output dcb;
        u8  ver, hdr, cnt, len;
-       u16 data, conf;
+       u32 ctrl = 0x00000000;
+       u32 data, conf = ~0;
+       int outp;
 
-       data = exec_lookup(priv, head, outp, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info1);
-       if (data == 0x0000)
+       for (outp = 0; !(ctrl & (1 << head)) && outp < 8; outp++) {
+               ctrl = nv_rd32(priv, 0x660180 + (outp * 0x20));
+               if (ctrl & (1 << head))
+                       break;
+       }
+
+       if (outp == 8)
                return false;
 
-       switch (dcb.type) {
+       data = exec_lookup(priv, head, outp, ctrl, dcb, &ver, &hdr, &cnt, &len, &info1);
+       if (data == 0x0000)
+               return conf;
+
+       switch (dcb->type) {
        case DCB_OUTPUT_TMDS:
                conf = (ctrl & 0x00000f00) >> 8;
                if (pclk >= 165000)
@@ -668,46 +703,52 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int outp,
        }
 
        data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2);
-       if (data) {
+       if (data && id < 0xff) {
                data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
                if (data) {
                        struct nvbios_init init = {
                                .subdev = nv_subdev(priv),
                                .bios = bios,
                                .offset = data,
-                               .outp = &dcb,
+                               .outp = dcb,
                                .crtc = head,
                                .execute = 1,
                        };
 
-                       if (nvbios_exec(&init))
-                               return 0x0000;
-                       return conf;
+                       nvbios_exec(&init);
                }
        }
 
-       return 0x0000;
+       return conf;
 }
 
 static void
-nvd0_display_unk1_handler(struct nv50_disp_priv *priv, u32 head, u32 mask)
+nvd0_disp_intr_unk1_0(struct nv50_disp_priv *priv, int head)
 {
-       int i;
+       exec_script(priv, head, 1);
+}
 
-       for (i = 0; mask && i < 8; i++) {
-               u32 mcc = nv_rd32(priv, 0x640180 + (i * 0x20));
-               if (mcc & (1 << head))
-                       exec_script(priv, head, i, mcc, 1);
-       }
+static void
+nvd0_disp_intr_unk2_0(struct nv50_disp_priv *priv, int head)
+{
+       exec_script(priv, head, 2);
+}
 
-       nv_wr32(priv, 0x6101d4, 0x00000000);
-       nv_wr32(priv, 0x6109d4, 0x00000000);
-       nv_wr32(priv, 0x6101d0, 0x80000000);
+static void
+nvd0_disp_intr_unk2_1(struct nv50_disp_priv *priv, int head)
+{
+       struct nouveau_clock *clk = nouveau_clock(priv);
+       u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
+       if (pclk)
+               clk->pll_set(clk, PLL_VPLL0 + head, pclk);
+       nv_wr32(priv, 0x612200 + (head * 0x800), 0x00000000);
 }
 
 static void
-nvd0_display_unk2_calc_tu(struct nv50_disp_priv *priv, int head, int or)
+nvd0_disp_intr_unk2_2_tu(struct nv50_disp_priv *priv, int head,
+                        struct dcb_output *outp)
 {
+       const int or = ffs(outp->or) - 1;
        const u32 ctrl = nv_rd32(priv, 0x660200 + (or   * 0x020));
        const u32 conf = nv_rd32(priv, 0x660404 + (head * 0x300));
        const u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
@@ -750,105 +791,102 @@ nvd0_display_unk2_calc_tu(struct nv50_disp_priv *priv, int head, int or)
 }
 
 static void
-nvd0_display_unk2_handler(struct nv50_disp_priv *priv, u32 head, u32 mask)
+nvd0_disp_intr_unk2_2(struct nv50_disp_priv *priv, int head)
 {
-       u32 pclk;
-       int i;
-
-       for (i = 0; mask && i < 8; i++) {
-               u32 mcc = nv_rd32(priv, 0x640180 + (i * 0x20));
-               if (mcc & (1 << head))
-                       exec_script(priv, head, i, mcc, 2);
-       }
+       struct dcb_output outp;
+       u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
+       u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp);
+       if (conf != ~0) {
+               u32 addr, data;
+
+               if (outp.type == DCB_OUTPUT_DP) {
+                       u32 sync = nv_rd32(priv, 0x660404 + (head * 0x300));
+                       switch ((sync & 0x000003c0) >> 6) {
+                       case 6: pclk = pclk * 30 / 8; break;
+                       case 5: pclk = pclk * 24 / 8; break;
+                       case 2:
+                       default:
+                               pclk = pclk * 18 / 8;
+                               break;
+                       }
 
-       pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
-       nv_debug(priv, "head %d pclk %d mask 0x%08x\n", head, pclk, mask);
-       if (pclk && (mask & 0x00010000)) {
-               struct nouveau_clock *clk = nouveau_clock(priv);
-               clk->pll_set(clk, PLL_VPLL0 + head, pclk);
-       }
+                       nouveau_dp_train(&priv->base, priv->sor.dp,
+                                        &outp, head, pclk);
+               }
 
-       nv_wr32(priv, 0x612200 + (head * 0x800), 0x00000000);
+               exec_clkcmp(priv, head, 0, pclk, &outp);
 
-       for (i = 0; mask && i < 8; i++) {
-               u32 mcp = nv_rd32(priv, 0x660180 + (i * 0x20)), cfg;
-               if (mcp & (1 << head)) {
-                       if ((cfg = exec_clkcmp(priv, head, i, mcp, 0, pclk))) {
-                               u32 addr, mask, data = 0x00000000;
-                               if (i < 4) {
-                                       addr = 0x612280 + ((i - 0) * 0x800);
-                                       mask = 0xffffffff;
-                               } else {
-                                       switch (mcp & 0x00000f00) {
-                                       case 0x00000800:
-                                       case 0x00000900:
-                                               nvd0_display_unk2_calc_tu(priv, head, i - 4);
-                                               break;
-                                       default:
-                                               break;
-                                       }
-
-                                       addr = 0x612300 + ((i - 4) * 0x800);
-                                       mask = 0x00000707;
-                                       if (cfg & 0x00000100)
-                                               data = 0x00000101;
-                               }
-                               nv_mask(priv, addr, mask, data);
-                       }
-                       break;
+               if (outp.type == DCB_OUTPUT_ANALOG) {
+                       addr = 0x612280 + (ffs(outp.or) - 1) * 0x800;
+                       data = 0x00000000;
+               } else {
+                       if (outp.type == DCB_OUTPUT_DP)
+                               nvd0_disp_intr_unk2_2_tu(priv, head, &outp);
+                       addr = 0x612300 + (ffs(outp.or) - 1) * 0x800;
+                       data = (conf & 0x0100) ? 0x00000101 : 0x00000000;
                }
-       }
 
-       nv_wr32(priv, 0x6101d4, 0x00000000);
-       nv_wr32(priv, 0x6109d4, 0x00000000);
-       nv_wr32(priv, 0x6101d0, 0x80000000);
+               nv_mask(priv, addr, 0x00000707, data);
+       }
 }
 
 static void
-nvd0_display_unk4_handler(struct nv50_disp_priv *priv, u32 head, u32 mask)
+nvd0_disp_intr_unk4_0(struct nv50_disp_priv *priv, int head)
 {
-       int pclk, i;
-
-       pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
+       struct dcb_output outp;
+       u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
+       exec_clkcmp(priv, head, 1, pclk, &outp);
+}
 
-       for (i = 0; mask && i < 8; i++) {
-               u32 mcp = nv_rd32(priv, 0x660180 + (i * 0x20));
-               if (mcp & (1 << head))
-                       exec_clkcmp(priv, head, i, mcp, 1, pclk);
+void
+nvd0_disp_intr_supervisor(struct work_struct *work)
+{
+       struct nv50_disp_priv *priv =
+               container_of(work, struct nv50_disp_priv, supervisor);
+       u32 mask[4];
+       int head;
+
+       nv_debug(priv, "supervisor %08x\n", priv->super);
+       for (head = 0; head < priv->head.nr; head++) {
+               mask[head] = nv_rd32(priv, 0x6101d4 + (head * 0x800));
+               nv_debug(priv, "head %d: 0x%08x\n", head, mask[head]);
        }
 
-       nv_wr32(priv, 0x6101d4, 0x00000000);
-       nv_wr32(priv, 0x6109d4, 0x00000000);
-       nv_wr32(priv, 0x6101d0, 0x80000000);
-}
-
-static void
-nvd0_disp_intr_vblank(struct nv50_disp_priv *priv, int crtc)
-{
-       struct nouveau_bar *bar = nouveau_bar(priv);
-       struct nouveau_disp *disp = &priv->base;
-       struct nouveau_software_chan *chan, *temp;
-       unsigned long flags;
-
-       spin_lock_irqsave(&disp->vblank.lock, flags);
-       list_for_each_entry_safe(chan, temp, &disp->vblank.list, vblank.head) {
-               if (chan->vblank.crtc != crtc)
-                       continue;
-
-               nv_wr32(priv, 0x001718, 0x80000000 | chan->vblank.channel);
-               bar->flush(bar);
-               nv_wr32(priv, 0x06000c, upper_32_bits(chan->vblank.offset));
-               nv_wr32(priv, 0x060010, lower_32_bits(chan->vblank.offset));
-               nv_wr32(priv, 0x060014, chan->vblank.value);
-
-               list_del(&chan->vblank.head);
-               if (disp->vblank.put)
-                       disp->vblank.put(disp->vblank.data, crtc);
+       if (priv->super & 0x00000001) {
+               for (head = 0; head < priv->head.nr; head++) {
+                       if (!(mask[head] & 0x00001000))
+                               continue;
+                       nvd0_disp_intr_unk1_0(priv, head);
+               }
+       } else
+       if (priv->super & 0x00000002) {
+               for (head = 0; head < priv->head.nr; head++) {
+                       if (!(mask[head] & 0x00001000))
+                               continue;
+                       nvd0_disp_intr_unk2_0(priv, head);
+               }
+               for (head = 0; head < priv->head.nr; head++) {
+                       if (!(mask[head] & 0x00010000))
+                               continue;
+                       nvd0_disp_intr_unk2_1(priv, head);
+               }
+               for (head = 0; head < priv->head.nr; head++) {
+                       if (!(mask[head] & 0x00001000))
+                               continue;
+                       nvd0_disp_intr_unk2_2(priv, head);
+               }
+       } else
+       if (priv->super & 0x00000004) {
+               for (head = 0; head < priv->head.nr; head++) {
+                       if (!(mask[head] & 0x00001000))
+                               continue;
+                       nvd0_disp_intr_unk4_0(priv, head);
+               }
        }
-       spin_unlock_irqrestore(&disp->vblank.lock, flags);
 
-       if (disp->vblank.notify)
-               disp->vblank.notify(disp->vblank.data, crtc);
+       for (head = 0; head < priv->head.nr; head++)
+               nv_wr32(priv, 0x6101d4 + (head * 0x800), 0x00000000);
+       nv_wr32(priv, 0x6101d0, 0x80000000);
 }
 
 void
@@ -884,27 +922,11 @@ nvd0_disp_intr(struct nouveau_subdev *subdev)
 
        if (intr & 0x00100000) {
                u32 stat = nv_rd32(priv, 0x6100ac);
-               u32 mask = 0, crtc = ~0;
-
-               while (!mask && ++crtc < priv->head.nr)
-                       mask = nv_rd32(priv, 0x6101d4 + (crtc * 0x800));
-
-               if (stat & 0x00000001) {
-                       nv_wr32(priv, 0x6100ac, 0x00000001);
-                       nvd0_display_unk1_handler(priv, crtc, mask);
-                       stat &= ~0x00000001;
-               }
-
-               if (stat & 0x00000002) {
-                       nv_wr32(priv, 0x6100ac, 0x00000002);
-                       nvd0_display_unk2_handler(priv, crtc, mask);
-                       stat &= ~0x00000002;
-               }
-
-               if (stat & 0x00000004) {
-                       nv_wr32(priv, 0x6100ac, 0x00000004);
-                       nvd0_display_unk4_handler(priv, crtc, mask);
-                       stat &= ~0x00000004;
+               if (stat & 0x00000007) {
+                       priv->super = (stat & 0x00000007);
+                       schedule_work(&priv->supervisor);
+                       nv_wr32(priv, 0x6100ac, priv->super);
+                       stat &= ~0x00000007;
                }
 
                if (stat) {
@@ -920,7 +942,7 @@ nvd0_disp_intr(struct nouveau_subdev *subdev)
                if (mask & intr) {
                        u32 stat = nv_rd32(priv, 0x6100bc + (i * 0x800));
                        if (stat & 0x00000001)
-                               nvd0_disp_intr_vblank(priv, i);
+                               nouveau_event_trigger(priv->base.vblank, i);
                        nv_mask(priv, 0x6100bc + (i * 0x800), 0, 0);
                        nv_rd32(priv, 0x6100c0 + (i * 0x800));
                }
@@ -933,10 +955,11 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
               struct nouveau_object **pobject)
 {
        struct nv50_disp_priv *priv;
+       int heads = nv_rd32(parent, 0x022448);
        int ret;
 
-       ret = nouveau_disp_create(parent, engine, oclass, "PDISP",
-                                 "display", &priv);
+       ret = nouveau_disp_create(parent, engine, oclass, heads,
+                                 "PDISP", "display", &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
@@ -944,8 +967,9 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        nv_engine(priv)->sclass = nvd0_disp_base_oclass;
        nv_engine(priv)->cclass = &nv50_disp_cclass;
        nv_subdev(priv)->intr = nvd0_disp_intr;
+       INIT_WORK(&priv->supervisor, nvd0_disp_intr_supervisor);
        priv->sclass = nvd0_disp_sclass;
-       priv->head.nr = nv_rd32(priv, 0x022448);
+       priv->head.nr = heads;
        priv->dac.nr = 3;
        priv->sor.nr = 4;
        priv->dac.power = nv50_dac_power;
@@ -953,14 +977,7 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        priv->sor.power = nv50_sor_power;
        priv->sor.hda_eld = nvd0_hda_eld;
        priv->sor.hdmi = nvd0_hdmi_ctrl;
-       priv->sor.dp_train = nvd0_sor_dp_train;
-       priv->sor.dp_train_init = nv94_sor_dp_train_init;
-       priv->sor.dp_train_fini = nv94_sor_dp_train_fini;
-       priv->sor.dp_lnkctl = nvd0_sor_dp_lnkctl;
-       priv->sor.dp_drvctl = nvd0_sor_dp_drvctl;
-
-       INIT_LIST_HEAD(&priv->base.vblank.list);
-       spin_lock_init(&priv->base.vblank.lock);
+       priv->sor.dp = &nvd0_sor_dp_func;
        return 0;
 }
 
index 259537c4587e32a3f9b7a7e4e7b47d9d5b852612..20725b363d582b3485b55f5ef4692247d20adce2 100644 (file)
@@ -51,10 +51,11 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
               struct nouveau_object **pobject)
 {
        struct nv50_disp_priv *priv;
+       int heads = nv_rd32(parent, 0x022448);
        int ret;
 
-       ret = nouveau_disp_create(parent, engine, oclass, "PDISP",
-                                 "display", &priv);
+       ret = nouveau_disp_create(parent, engine, oclass, heads,
+                                 "PDISP", "display", &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
@@ -62,8 +63,9 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        nv_engine(priv)->sclass = nve0_disp_base_oclass;
        nv_engine(priv)->cclass = &nv50_disp_cclass;
        nv_subdev(priv)->intr = nvd0_disp_intr;
+       INIT_WORK(&priv->supervisor, nvd0_disp_intr_supervisor);
        priv->sclass = nve0_disp_sclass;
-       priv->head.nr = nv_rd32(priv, 0x022448);
+       priv->head.nr = heads;
        priv->dac.nr = 3;
        priv->sor.nr = 4;
        priv->dac.power = nv50_dac_power;
@@ -71,14 +73,7 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        priv->sor.power = nv50_sor_power;
        priv->sor.hda_eld = nvd0_hda_eld;
        priv->sor.hdmi = nvd0_hdmi_ctrl;
-       priv->sor.dp_train = nvd0_sor_dp_train;
-       priv->sor.dp_train_init = nv94_sor_dp_train_init;
-       priv->sor.dp_train_fini = nv94_sor_dp_train_fini;
-       priv->sor.dp_lnkctl = nvd0_sor_dp_lnkctl;
-       priv->sor.dp_drvctl = nvd0_sor_dp_drvctl;
-
-       INIT_LIST_HEAD(&priv->base.vblank.list);
-       spin_lock_init(&priv->base.vblank.lock);
+       priv->sor.dp = &nvd0_sor_dp_func;
        return 0;
 }
 
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c
new file mode 100644 (file)
index 0000000..2c8ce35
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <core/os.h>
+#include <core/class.h>
+
+#include <subdev/bios.h>
+#include <subdev/bios/dcb.h>
+#include <subdev/timer.h>
+#include <subdev/i2c.h>
+
+#include "nv50.h"
+
+/******************************************************************************
+ * DisplayPort
+ *****************************************************************************/
+static struct nouveau_i2c_port *
+nv50_pior_dp_find(struct nouveau_disp *disp, struct dcb_output *outp)
+{
+       struct nouveau_i2c *i2c = nouveau_i2c(disp);
+       return i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(outp->extdev));
+}
+
+static int
+nv50_pior_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp,
+                    int head, int pattern)
+{
+       struct nouveau_i2c_port *port;
+       int ret = -EINVAL;
+
+       port = nv50_pior_dp_find(disp, outp);
+       if (port) {
+               if (port->func->pattern)
+                       ret = port->func->pattern(port, pattern);
+               else
+                       ret = 0;
+       }
+
+       return ret;
+}
+
+static int
+nv50_pior_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
+                    int head, int lane_nr, int link_bw, bool enh)
+{
+       struct nouveau_i2c_port *port;
+       int ret = -EINVAL;
+
+       port = nv50_pior_dp_find(disp, outp);
+       if (port && port->func->lnk_ctl)
+               ret = port->func->lnk_ctl(port, lane_nr, link_bw, enh);
+
+       return ret;
+}
+
+static int
+nv50_pior_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
+                    int head, int lane, int vsw, int pre)
+{
+       struct nouveau_i2c_port *port;
+       int ret = -EINVAL;
+
+       port = nv50_pior_dp_find(disp, outp);
+       if (port) {
+               if (port->func->drv_ctl)
+                       ret = port->func->drv_ctl(port, lane, vsw, pre);
+               else
+                       ret = 0;
+       }
+
+       return ret;
+}
+
+const struct nouveau_dp_func
+nv50_pior_dp_func = {
+       .pattern = nv50_pior_dp_pattern,
+       .lnk_ctl = nv50_pior_dp_lnk_ctl,
+       .drv_ctl = nv50_pior_dp_drv_ctl,
+};
+
+/******************************************************************************
+ * General PIOR handling
+ *****************************************************************************/
+int
+nv50_pior_power(struct nv50_disp_priv *priv, int or, u32 data)
+{
+       const u32 stat = data & NV50_DISP_PIOR_PWR_STATE;
+       const u32 soff = (or * 0x800);
+       nv_wait(priv, 0x61e004 + soff, 0x80000000, 0x00000000);
+       nv_mask(priv, 0x61e004 + soff, 0x80000101, 0x80000000 | stat);
+       nv_wait(priv, 0x61e004 + soff, 0x80000000, 0x00000000);
+       return 0;
+}
+
+int
+nv50_pior_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size)
+{
+       struct nv50_disp_priv *priv = (void *)object->engine;
+       const u8 type = (mthd & NV50_DISP_PIOR_MTHD_TYPE) >> 12;
+       const u8 or   = (mthd & NV50_DISP_PIOR_MTHD_OR);
+       u32 *data = args;
+       int ret;
+
+       if (size < sizeof(u32))
+               return -EINVAL;
+
+       mthd &= ~NV50_DISP_PIOR_MTHD_TYPE;
+       mthd &= ~NV50_DISP_PIOR_MTHD_OR;
+       switch (mthd) {
+       case NV50_DISP_PIOR_PWR:
+               ret = priv->pior.power(priv, or, data[0]);
+               priv->pior.type[or] = type;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return ret;
+}
index 39b6b67732d0b064569b6c11ea729a67395d8ca2..ab1e918469a822f76bc09f4c09eb215f97a56990 100644 (file)
@@ -79,31 +79,6 @@ nv50_sor_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size)
                priv->sor.lvdsconf = data & NV50_DISP_SOR_LVDS_SCRIPT_ID;
                ret = 0;
                break;
-       case NV94_DISP_SOR_DP_TRAIN:
-               switch (data & NV94_DISP_SOR_DP_TRAIN_OP) {
-               case NV94_DISP_SOR_DP_TRAIN_OP_PATTERN:
-                       ret = priv->sor.dp_train(priv, or, link, type, mask, data, &outp);
-                       break;
-               case NV94_DISP_SOR_DP_TRAIN_OP_INIT:
-                       ret = priv->sor.dp_train_init(priv, or, link, head, type, mask, data, &outp);
-                       break;
-               case NV94_DISP_SOR_DP_TRAIN_OP_FINI:
-                       ret = priv->sor.dp_train_fini(priv, or, link, head, type, mask, data, &outp);
-                       break;
-               default:
-                       break;
-               }
-               break;
-       case NV94_DISP_SOR_DP_LNKCTL:
-               ret = priv->sor.dp_lnkctl(priv, or, link, head, type, mask, data, &outp);
-               break;
-       case NV94_DISP_SOR_DP_DRVCTL(0):
-       case NV94_DISP_SOR_DP_DRVCTL(1):
-       case NV94_DISP_SOR_DP_DRVCTL(2):
-       case NV94_DISP_SOR_DP_DRVCTL(3):
-               ret = priv->sor.dp_drvctl(priv, or, link, (mthd & 0xc0) >> 6,
-                                         type, mask, data, &outp);
-               break;
        default:
                BUG_ON(1);
        }
index f6edd009762ee3127e78d4ba36fdd15b3c4cc4f2..7ec4ee83fb641463383402742f85ebdf2368ca0d 100644 (file)
 #include "nv50.h"
 
 static inline u32
-nv94_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane)
+nv94_sor_soff(struct dcb_output *outp)
 {
-       static const u8 nvaf[] = { 24, 16, 8, 0 }; /* thanks, apple.. */
-       static const u8 nv94[] = { 16, 8, 0, 24 };
-       if (nv_device(priv)->chipset == 0xaf)
-               return nvaf[lane];
-       return nv94[lane];
+       return (ffs(outp->or) - 1) * 0x800;
 }
 
-int
-nv94_sor_dp_train_init(struct nv50_disp_priv *priv, int or, int link, int head,
-                      u16 type, u16 mask, u32 data, struct dcb_output *dcbo)
+static inline u32
+nv94_sor_loff(struct dcb_output *outp)
 {
-       struct nouveau_bios *bios = nouveau_bios(priv);
-       struct nvbios_dpout info;
-       u8  ver, hdr, cnt, len;
-       u16 outp;
-
-       outp = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &info);
-       if (outp) {
-               struct nvbios_init init = {
-                       .subdev = nv_subdev(priv),
-                       .bios = bios,
-                       .outp = dcbo,
-                       .crtc = head,
-                       .execute = 1,
-               };
-
-               if (data & NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_ON)
-                       init.offset = info.script[2];
-               else
-                       init.offset = info.script[3];
-               nvbios_exec(&init);
-
-               init.offset = info.script[0];
-               nvbios_exec(&init);
-       }
-
-       return 0;
+       return nv94_sor_soff(outp) + !(outp->sorconf.link & 1) * 0x80;
 }
 
-int
-nv94_sor_dp_train_fini(struct nv50_disp_priv *priv, int or, int link, int head,
-                      u16 type, u16 mask, u32 data, struct dcb_output *dcbo)
+static inline u32
+nv94_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane)
 {
-       struct nouveau_bios *bios = nouveau_bios(priv);
-       struct nvbios_dpout info;
-       u8  ver, hdr, cnt, len;
-       u16 outp;
-
-       outp = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &info);
-       if (outp) {
-               struct nvbios_init init = {
-                       .subdev = nv_subdev(priv),
-                       .bios = bios,
-                       .offset = info.script[1],
-                       .outp = dcbo,
-                       .crtc = head,
-                       .execute = 1,
-               };
-
-               nvbios_exec(&init);
-       }
-
-       return 0;
+       static const u8 nvaf[] = { 24, 16, 8, 0 }; /* thanks, apple.. */
+       static const u8 nv94[] = { 16, 8, 0, 24 };
+       if (nv_device(priv)->chipset == 0xaf)
+               return nvaf[lane];
+       return nv94[lane];
 }
 
-int
-nv94_sor_dp_train(struct nv50_disp_priv *priv, int or, int link,
-                 u16 type, u16 mask, u32 data, struct dcb_output *info)
+static int
+nv94_sor_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp,
+                   int head, int pattern)
 {
-       const u32 loff = (or * 0x800) + (link * 0x80);
-       const u32 patt = (data & NV94_DISP_SOR_DP_TRAIN_PATTERN);
-       nv_mask(priv, 0x61c10c + loff, 0x0f000000, patt << 24);
+       struct nv50_disp_priv *priv = (void *)disp;
+       const u32 loff = nv94_sor_loff(outp);
+       nv_mask(priv, 0x61c10c + loff, 0x0f000000, pattern << 24);
        return 0;
 }
 
-int
-nv94_sor_dp_lnkctl(struct nv50_disp_priv *priv, int or, int link, int head,
-                  u16 type, u16 mask, u32 data, struct dcb_output *dcbo)
+static int
+nv94_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
+                   int head, int link_nr, int link_bw, bool enh_frame)
 {
-       struct nouveau_bios *bios = nouveau_bios(priv);
-       const u32 loff = (or * 0x800) + (link * 0x80);
-       const u32 soff = (or * 0x800);
-       u16 link_bw = (data & NV94_DISP_SOR_DP_LNKCTL_WIDTH) >> 8;
-       u8  link_nr = (data & NV94_DISP_SOR_DP_LNKCTL_COUNT);
+       struct nv50_disp_priv *priv = (void *)disp;
+       const u32 soff = nv94_sor_soff(outp);
+       const u32 loff = nv94_sor_loff(outp);
        u32 dpctrl = 0x00000000;
        u32 clksor = 0x00000000;
-       u32 outp, lane = 0;
-       u8  ver, hdr, cnt, len;
-       struct nvbios_dpout info;
+       u32 lane = 0;
        int i;
 
-       /* -> 10Khz units */
-       link_bw *= 2700;
-
-       outp = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &info);
-       if (outp && info.lnkcmp) {
-               struct nvbios_init init = {
-                       .subdev = nv_subdev(priv),
-                       .bios = bios,
-                       .offset = 0x0000,
-                       .outp = dcbo,
-                       .crtc = head,
-                       .execute = 1,
-               };
-
-               while (link_bw < nv_ro16(bios, info.lnkcmp))
-                       info.lnkcmp += 4;
-               init.offset = nv_ro16(bios, info.lnkcmp + 2);
-
-               nvbios_exec(&init);
-       }
-
        dpctrl |= ((1 << link_nr) - 1) << 16;
-       if (data & NV94_DISP_SOR_DP_LNKCTL_FRAME_ENH)
+       if (enh_frame)
                dpctrl |= 0x00004000;
-       if (link_bw > 16200)
+       if (link_bw > 0x06)
                clksor |= 0x00040000;
 
        for (i = 0; i < link_nr; i++)
@@ -162,24 +91,25 @@ nv94_sor_dp_lnkctl(struct nv50_disp_priv *priv, int or, int link, int head,
        return 0;
 }
 
-int
-nv94_sor_dp_drvctl(struct nv50_disp_priv *priv, int or, int link, int lane,
-                  u16 type, u16 mask, u32 data, struct dcb_output *dcbo)
+static int
+nv94_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
+                   int head, int lane, int swing, int preem)
 {
-       struct nouveau_bios *bios = nouveau_bios(priv);
-       const u32 loff = (or * 0x800) + (link * 0x80);
-       const u8 swing = (data & NV94_DISP_SOR_DP_DRVCTL_VS) >> 8;
-       const u8 preem = (data & NV94_DISP_SOR_DP_DRVCTL_PE);
+       struct nouveau_bios *bios = nouveau_bios(disp);
+       struct nv50_disp_priv *priv = (void *)disp;
+       const u32 loff = nv94_sor_loff(outp);
        u32 addr, shift = nv94_sor_dp_lane_map(priv, lane);
        u8  ver, hdr, cnt, len;
-       struct nvbios_dpout outp;
+       struct nvbios_dpout info;
        struct nvbios_dpcfg ocfg;
 
-       addr = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &outp);
+       addr = nvbios_dpout_match(bios, outp->hasht, outp->hashm,
+                                &ver, &hdr, &cnt, &len, &info);
        if (!addr)
                return -ENODEV;
 
-       addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem, &ver, &hdr, &cnt, &len, &ocfg);
+       addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem,
+                                &ver, &hdr, &cnt, &len, &ocfg);
        if (!addr)
                return -EINVAL;
 
@@ -188,3 +118,10 @@ nv94_sor_dp_drvctl(struct nv50_disp_priv *priv, int or, int link, int lane,
        nv_mask(priv, 0x61c130 + loff, 0x0000ff00, ocfg.unk << 8);
        return 0;
 }
+
+const struct nouveau_dp_func
+nv94_sor_dp_func = {
+       .pattern = nv94_sor_dp_pattern,
+       .lnk_ctl = nv94_sor_dp_lnk_ctl,
+       .drv_ctl = nv94_sor_dp_drv_ctl,
+};
index c37ce7e29f5d32db089d0722b3f27eda2390dafa..9e1d435d72820c3c4d7825fdd9b40ee787b21aaf 100644 (file)
 
 #include "nv50.h"
 
+static inline u32
+nvd0_sor_soff(struct dcb_output *outp)
+{
+       return (ffs(outp->or) - 1) * 0x800;
+}
+
+static inline u32
+nvd0_sor_loff(struct dcb_output *outp)
+{
+       return nvd0_sor_soff(outp) + !(outp->sorconf.link & 1) * 0x80;
+}
+
 static inline u32
 nvd0_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane)
 {
@@ -39,53 +51,31 @@ nvd0_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane)
        return nvd0[lane];
 }
 
-int
-nvd0_sor_dp_train(struct nv50_disp_priv *priv, int or, int link,
-                 u16 type, u16 mask, u32 data, struct dcb_output *info)
+static int
+nvd0_sor_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp,
+                   int head, int pattern)
 {
-       const u32 loff = (or * 0x800) + (link * 0x80);
-       const u32 patt = (data & NV94_DISP_SOR_DP_TRAIN_PATTERN);
-       nv_mask(priv, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * patt);
+       struct nv50_disp_priv *priv = (void *)disp;
+       const u32 loff = nvd0_sor_loff(outp);
+       nv_mask(priv, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * pattern);
        return 0;
 }
 
-int
-nvd0_sor_dp_lnkctl(struct nv50_disp_priv *priv, int or, int link, int head,
-                  u16 type, u16 mask, u32 data, struct dcb_output *dcbo)
+static int
+nvd0_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
+                   int head, int link_nr, int link_bw, bool enh_frame)
 {
-       struct nouveau_bios *bios = nouveau_bios(priv);
-       const u32 loff = (or * 0x800) + (link * 0x80);
-       const u32 soff = (or * 0x800);
-       const u8  link_bw = (data & NV94_DISP_SOR_DP_LNKCTL_WIDTH) >> 8;
-       const u8  link_nr = (data & NV94_DISP_SOR_DP_LNKCTL_COUNT);
+       struct nv50_disp_priv *priv = (void *)disp;
+       const u32 soff = nvd0_sor_soff(outp);
+       const u32 loff = nvd0_sor_loff(outp);
        u32 dpctrl = 0x00000000;
        u32 clksor = 0x00000000;
-       u32 outp, lane = 0;
-       u8  ver, hdr, cnt, len;
-       struct nvbios_dpout info;
+       u32 lane = 0;
        int i;
 
-       outp = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &info);
-       if (outp && info.lnkcmp) {
-               struct nvbios_init init = {
-                       .subdev = nv_subdev(priv),
-                       .bios = bios,
-                       .offset = 0x0000,
-                       .outp = dcbo,
-                       .crtc = head,
-                       .execute = 1,
-               };
-
-               while (nv_ro08(bios, info.lnkcmp) < link_bw)
-                       info.lnkcmp += 3;
-               init.offset = nv_ro16(bios, info.lnkcmp + 1);
-
-               nvbios_exec(&init);
-       }
-
        clksor |= link_bw << 18;
        dpctrl |= ((1 << link_nr) - 1) << 16;
-       if (data & NV94_DISP_SOR_DP_LNKCTL_FRAME_ENH)
+       if (enh_frame)
                dpctrl |= 0x00004000;
 
        for (i = 0; i < link_nr; i++)
@@ -97,24 +87,25 @@ nvd0_sor_dp_lnkctl(struct nv50_disp_priv *priv, int or, int link, int head,
        return 0;
 }
 
-int
-nvd0_sor_dp_drvctl(struct nv50_disp_priv *priv, int or, int link, int lane,
-                  u16 type, u16 mask, u32 data, struct dcb_output *dcbo)
+static int
+nvd0_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
+                   int head, int lane, int swing, int preem)
 {
-       struct nouveau_bios *bios = nouveau_bios(priv);
-       const u32 loff = (or * 0x800) + (link * 0x80);
-       const u8 swing = (data & NV94_DISP_SOR_DP_DRVCTL_VS) >> 8;
-       const u8 preem = (data & NV94_DISP_SOR_DP_DRVCTL_PE);
+       struct nouveau_bios *bios = nouveau_bios(disp);
+       struct nv50_disp_priv *priv = (void *)disp;
+       const u32 loff = nvd0_sor_loff(outp);
        u32 addr, shift = nvd0_sor_dp_lane_map(priv, lane);
        u8  ver, hdr, cnt, len;
-       struct nvbios_dpout outp;
+       struct nvbios_dpout info;
        struct nvbios_dpcfg ocfg;
 
-       addr = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &outp);
+       addr = nvbios_dpout_match(bios, outp->hasht, outp->hashm,
+                                &ver, &hdr, &cnt, &len, &info);
        if (!addr)
                return -ENODEV;
 
-       addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem, &ver, &hdr, &cnt, &len, &ocfg);
+       addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem,
+                                &ver, &hdr, &cnt, &len, &ocfg);
        if (!addr)
                return -EINVAL;
 
@@ -124,3 +115,10 @@ nvd0_sor_dp_drvctl(struct nv50_disp_priv *priv, int or, int link, int lane,
        nv_mask(priv, 0x61c13c + loff, 0x00000000, 0x00000000);
        return 0;
 }
+
+const struct nouveau_dp_func
+nvd0_sor_dp_func = {
+       .pattern = nvd0_sor_dp_pattern,
+       .lnk_ctl = nvd0_sor_dp_lnk_ctl,
+       .drv_ctl = nvd0_sor_dp_drv_ctl,
+};
index c2b9db33581626483cdd38dc6f2976eec669357e..7341ebe131fabc9de63a5ae37849a7f180f3b3e7 100644 (file)
  * Authors: Ben Skeggs
  */
 
+#include <core/client.h>
 #include <core/object.h>
 #include <core/handle.h>
+#include <core/event.h>
 #include <core/class.h>
 
 #include <engine/dmaobj.h>
@@ -146,10 +148,25 @@ nouveau_fifo_chid(struct nouveau_fifo *priv, struct nouveau_object *object)
        return -1;
 }
 
+const char *
+nouveau_client_name_for_fifo_chid(struct nouveau_fifo *fifo, u32 chid)
+{
+       struct nouveau_fifo_chan *chan = NULL;
+       unsigned long flags;
+
+       spin_lock_irqsave(&fifo->lock, flags);
+       if (chid >= fifo->min && chid <= fifo->max)
+               chan = (void *)fifo->channel[chid];
+       spin_unlock_irqrestore(&fifo->lock, flags);
+
+       return nouveau_client_name(chan);
+}
+
 void
 nouveau_fifo_destroy(struct nouveau_fifo *priv)
 {
        kfree(priv->channel);
+       nouveau_event_destroy(&priv->uevent);
        nouveau_engine_destroy(&priv->base);
 }
 
@@ -174,6 +191,10 @@ nouveau_fifo_create_(struct nouveau_object *parent,
        if (!priv->channel)
                return -ENOMEM;
 
+       ret = nouveau_event_create(1, &priv->uevent);
+       if (ret)
+               return ret;
+
        priv->chid = nouveau_fifo_chid;
        spin_lock_init(&priv->lock);
        return 0;
index a47a8548f9e0596806d5a48424d6b3d4092622c7..f877bd524a922a81389a9e646a86441cb52b47c5 100644 (file)
@@ -28,6 +28,7 @@
 #include <core/namedb.h>
 #include <core/handle.h>
 #include <core/ramht.h>
+#include <core/event.h>
 
 #include <subdev/instmem.h>
 #include <subdev/instmem/nv04.h>
@@ -398,6 +399,98 @@ out:
        return handled;
 }
 
+static void
+nv04_fifo_cache_error(struct nouveau_device *device,
+               struct nv04_fifo_priv *priv, u32 chid, u32 get)
+{
+       u32 mthd, data;
+       int ptr;
+
+       /* NV_PFIFO_CACHE1_GET actually goes to 0xffc before wrapping on my
+        * G80 chips, but CACHE1 isn't big enough for this much data.. Tests
+        * show that it wraps around to the start at GET=0x800.. No clue as to
+        * why..
+        */
+       ptr = (get & 0x7ff) >> 2;
+
+       if (device->card_type < NV_40) {
+               mthd = nv_rd32(priv, NV04_PFIFO_CACHE1_METHOD(ptr));
+               data = nv_rd32(priv, NV04_PFIFO_CACHE1_DATA(ptr));
+       } else {
+               mthd = nv_rd32(priv, NV40_PFIFO_CACHE1_METHOD(ptr));
+               data = nv_rd32(priv, NV40_PFIFO_CACHE1_DATA(ptr));
+       }
+
+       if (!nv04_fifo_swmthd(priv, chid, mthd, data)) {
+               const char *client_name =
+                       nouveau_client_name_for_fifo_chid(&priv->base, chid);
+               nv_error(priv,
+                        "CACHE_ERROR - ch %d [%s] subc %d mthd 0x%04x data 0x%08x\n",
+                        chid, client_name, (mthd >> 13) & 7, mthd & 0x1ffc,
+                        data);
+       }
+
+       nv_wr32(priv, NV04_PFIFO_CACHE1_DMA_PUSH, 0);
+       nv_wr32(priv, NV03_PFIFO_INTR_0, NV_PFIFO_INTR_CACHE_ERROR);
+
+       nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0,
+               nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH0) & ~1);
+       nv_wr32(priv, NV03_PFIFO_CACHE1_GET, get + 4);
+       nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0,
+               nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH0) | 1);
+       nv_wr32(priv, NV04_PFIFO_CACHE1_HASH, 0);
+
+       nv_wr32(priv, NV04_PFIFO_CACHE1_DMA_PUSH,
+               nv_rd32(priv, NV04_PFIFO_CACHE1_DMA_PUSH) | 1);
+       nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1);
+}
+
+static void
+nv04_fifo_dma_pusher(struct nouveau_device *device, struct nv04_fifo_priv *priv,
+               u32 chid)
+{
+       const char *client_name;
+       u32 dma_get = nv_rd32(priv, 0x003244);
+       u32 dma_put = nv_rd32(priv, 0x003240);
+       u32 push = nv_rd32(priv, 0x003220);
+       u32 state = nv_rd32(priv, 0x003228);
+
+       client_name = nouveau_client_name_for_fifo_chid(&priv->base, chid);
+
+       if (device->card_type == NV_50) {
+               u32 ho_get = nv_rd32(priv, 0x003328);
+               u32 ho_put = nv_rd32(priv, 0x003320);
+               u32 ib_get = nv_rd32(priv, 0x003334);
+               u32 ib_put = nv_rd32(priv, 0x003330);
+
+               nv_error(priv,
+                        "DMA_PUSHER - ch %d [%s] get 0x%02x%08x put 0x%02x%08x ib_get 0x%08x ib_put 0x%08x state 0x%08x (err: %s) push 0x%08x\n",
+                        chid, client_name, ho_get, dma_get, ho_put, dma_put,
+                        ib_get, ib_put, state, nv_dma_state_err(state), push);
+
+               /* METHOD_COUNT, in DMA_STATE on earlier chipsets */
+               nv_wr32(priv, 0x003364, 0x00000000);
+               if (dma_get != dma_put || ho_get != ho_put) {
+                       nv_wr32(priv, 0x003244, dma_put);
+                       nv_wr32(priv, 0x003328, ho_put);
+               } else
+               if (ib_get != ib_put)
+                       nv_wr32(priv, 0x003334, ib_put);
+       } else {
+               nv_error(priv,
+                        "DMA_PUSHER - ch %d [%s] get 0x%08x put 0x%08x state 0x%08x (err: %s) push 0x%08x\n",
+                        chid, client_name, dma_get, dma_put, state,
+                        nv_dma_state_err(state), push);
+
+               if (dma_get != dma_put)
+                       nv_wr32(priv, 0x003244, dma_put);
+       }
+
+       nv_wr32(priv, 0x003228, 0x00000000);
+       nv_wr32(priv, 0x003220, 0x00000001);
+       nv_wr32(priv, 0x002100, NV_PFIFO_INTR_DMA_PUSHER);
+}
+
 void
 nv04_fifo_intr(struct nouveau_subdev *subdev)
 {
@@ -416,96 +509,12 @@ nv04_fifo_intr(struct nouveau_subdev *subdev)
                get  = nv_rd32(priv, NV03_PFIFO_CACHE1_GET);
 
                if (status & NV_PFIFO_INTR_CACHE_ERROR) {
-                       uint32_t mthd, data;
-                       int ptr;
-
-                       /* NV_PFIFO_CACHE1_GET actually goes to 0xffc before
-                        * wrapping on my G80 chips, but CACHE1 isn't big
-                        * enough for this much data.. Tests show that it
-                        * wraps around to the start at GET=0x800.. No clue
-                        * as to why..
-                        */
-                       ptr = (get & 0x7ff) >> 2;
-
-                       if (device->card_type < NV_40) {
-                               mthd = nv_rd32(priv,
-                                       NV04_PFIFO_CACHE1_METHOD(ptr));
-                               data = nv_rd32(priv,
-                                       NV04_PFIFO_CACHE1_DATA(ptr));
-                       } else {
-                               mthd = nv_rd32(priv,
-                                       NV40_PFIFO_CACHE1_METHOD(ptr));
-                               data = nv_rd32(priv,
-                                       NV40_PFIFO_CACHE1_DATA(ptr));
-                       }
-
-                       if (!nv04_fifo_swmthd(priv, chid, mthd, data)) {
-                               nv_error(priv, "CACHE_ERROR - Ch %d/%d "
-                                             "Mthd 0x%04x Data 0x%08x\n",
-                                       chid, (mthd >> 13) & 7, mthd & 0x1ffc,
-                                       data);
-                       }
-
-                       nv_wr32(priv, NV04_PFIFO_CACHE1_DMA_PUSH, 0);
-                       nv_wr32(priv, NV03_PFIFO_INTR_0,
-                                               NV_PFIFO_INTR_CACHE_ERROR);
-
-                       nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0,
-                               nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH0) & ~1);
-                       nv_wr32(priv, NV03_PFIFO_CACHE1_GET, get + 4);
-                       nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0,
-                               nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH0) | 1);
-                       nv_wr32(priv, NV04_PFIFO_CACHE1_HASH, 0);
-
-                       nv_wr32(priv, NV04_PFIFO_CACHE1_DMA_PUSH,
-                               nv_rd32(priv, NV04_PFIFO_CACHE1_DMA_PUSH) | 1);
-                       nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1);
-
+                       nv04_fifo_cache_error(device, priv, chid, get);
                        status &= ~NV_PFIFO_INTR_CACHE_ERROR;
                }
 
                if (status & NV_PFIFO_INTR_DMA_PUSHER) {
-                       u32 dma_get = nv_rd32(priv, 0x003244);
-                       u32 dma_put = nv_rd32(priv, 0x003240);
-                       u32 push = nv_rd32(priv, 0x003220);
-                       u32 state = nv_rd32(priv, 0x003228);
-
-                       if (device->card_type == NV_50) {
-                               u32 ho_get = nv_rd32(priv, 0x003328);
-                               u32 ho_put = nv_rd32(priv, 0x003320);
-                               u32 ib_get = nv_rd32(priv, 0x003334);
-                               u32 ib_put = nv_rd32(priv, 0x003330);
-
-                               nv_error(priv, "DMA_PUSHER - Ch %d Get 0x%02x%08x "
-                                    "Put 0x%02x%08x IbGet 0x%08x IbPut 0x%08x "
-                                    "State 0x%08x (err: %s) Push 0x%08x\n",
-                                       chid, ho_get, dma_get, ho_put,
-                                       dma_put, ib_get, ib_put, state,
-                                       nv_dma_state_err(state),
-                                       push);
-
-                               /* METHOD_COUNT, in DMA_STATE on earlier chipsets */
-                               nv_wr32(priv, 0x003364, 0x00000000);
-                               if (dma_get != dma_put || ho_get != ho_put) {
-                                       nv_wr32(priv, 0x003244, dma_put);
-                                       nv_wr32(priv, 0x003328, ho_put);
-                               } else
-                               if (ib_get != ib_put) {
-                                       nv_wr32(priv, 0x003334, ib_put);
-                               }
-                       } else {
-                               nv_error(priv, "DMA_PUSHER - Ch %d Get 0x%08x "
-                                            "Put 0x%08x State 0x%08x (err: %s) Push 0x%08x\n",
-                                       chid, dma_get, dma_put, state,
-                                       nv_dma_state_err(state), push);
-
-                               if (dma_get != dma_put)
-                                       nv_wr32(priv, 0x003244, dma_put);
-                       }
-
-                       nv_wr32(priv, 0x003228, 0x00000000);
-                       nv_wr32(priv, 0x003220, 0x00000001);
-                       nv_wr32(priv, 0x002100, NV_PFIFO_INTR_DMA_PUSHER);
+                       nv04_fifo_dma_pusher(device, priv, chid);
                        status &= ~NV_PFIFO_INTR_DMA_PUSHER;
                }
 
@@ -528,6 +537,12 @@ nv04_fifo_intr(struct nouveau_subdev *subdev)
                                status &= ~0x00000010;
                                nv_wr32(priv, 0x002100, 0x00000010);
                        }
+
+                       if (status & 0x40000000) {
+                               nouveau_event_trigger(priv->base.uevent, 0);
+                               nv_wr32(priv, 0x002100, 0x40000000);
+                               status &= ~0x40000000;
+                       }
                }
 
                if (status) {
index bd096364f6803c616ec039a56d97c7e99c5a873d..840af61727883ec2367b75a5f0cd2be3856dc32f 100644 (file)
@@ -129,7 +129,8 @@ nv50_fifo_context_detach(struct nouveau_object *parent, bool suspend,
        /* do the kickoff... */
        nv_wr32(priv, 0x0032fc, nv_gpuobj(base)->addr >> 12);
        if (!nv_wait_ne(priv, 0x0032fc, 0xffffffff, 0xffffffff)) {
-               nv_error(priv, "channel %d unload timeout\n", chan->base.chid);
+               nv_error(priv, "channel %d [%s] unload timeout\n",
+                        chan->base.chid, nouveau_client_name(chan));
                if (suspend)
                        ret = -EBUSY;
        }
@@ -480,7 +481,7 @@ nv50_fifo_init(struct nouveau_object *object)
        nv_wr32(priv, 0x002044, 0x01003fff);
 
        nv_wr32(priv, 0x002100, 0xffffffff);
-       nv_wr32(priv, 0x002140, 0xffffffff);
+       nv_wr32(priv, 0x002140, 0xbfffffff);
 
        for (i = 0; i < 128; i++)
                nv_wr32(priv, 0x002600 + (i * 4), 0x00000000);
index 1eb1c512f5032048b29db31d7c9781698f4847de..094000e87871c35f5467efb1d262832e97b57056 100644 (file)
@@ -26,6 +26,7 @@
 #include <core/client.h>
 #include <core/engctx.h>
 #include <core/ramht.h>
+#include <core/event.h>
 #include <core/class.h>
 #include <core/math.h>
 
@@ -100,7 +101,8 @@ nv84_fifo_context_detach(struct nouveau_object *parent, bool suspend,
        done = nv_wait_ne(priv, 0x0032fc, 0xffffffff, 0xffffffff);
        nv_wr32(priv, 0x002520, save);
        if (!done) {
-               nv_error(priv, "channel %d unload timeout\n", chan->base.chid);
+               nv_error(priv, "channel %d [%s] unload timeout\n",
+                        chan->base.chid, nouveau_client_name(chan));
                if (suspend)
                        return -EBUSY;
        }
@@ -378,6 +380,20 @@ nv84_fifo_cclass = {
  * PFIFO engine
  ******************************************************************************/
 
+static void
+nv84_fifo_uevent_enable(struct nouveau_event *event, int index)
+{
+       struct nv84_fifo_priv *priv = event->priv;
+       nv_mask(priv, 0x002140, 0x40000000, 0x40000000);
+}
+
+static void
+nv84_fifo_uevent_disable(struct nouveau_event *event, int index)
+{
+       struct nv84_fifo_priv *priv = event->priv;
+       nv_mask(priv, 0x002140, 0x40000000, 0x00000000);
+}
+
 static int
 nv84_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
               struct nouveau_oclass *oclass, void *data, u32 size,
@@ -401,6 +417,10 @@ nv84_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
+       priv->base.uevent->enable = nv84_fifo_uevent_enable;
+       priv->base.uevent->disable = nv84_fifo_uevent_disable;
+       priv->base.uevent->priv = priv;
+
        nv_subdev(priv)->unit = 0x00000100;
        nv_subdev(priv)->intr = nv04_fifo_intr;
        nv_engine(priv)->cclass = &nv84_fifo_cclass;
index b4365dde1859bfd332a11548e9034b662c3ce51a..4f226afb5591b33e34ccc64e193b8d421f00abeb 100644 (file)
@@ -27,6 +27,7 @@
 #include <core/namedb.h>
 #include <core/gpuobj.h>
 #include <core/engctx.h>
+#include <core/event.h>
 #include <core/class.h>
 #include <core/math.h>
 #include <core/enum.h>
@@ -149,7 +150,8 @@ nvc0_fifo_context_detach(struct nouveau_object *parent, bool suspend,
 
        nv_wr32(priv, 0x002634, chan->base.chid);
        if (!nv_wait(priv, 0x002634, 0xffffffff, chan->base.chid)) {
-               nv_error(priv, "channel %d kick timeout\n", chan->base.chid);
+               nv_error(priv, "channel %d [%s] kick timeout\n",
+                        chan->base.chid, nouveau_client_name(chan));
                if (suspend)
                        return -EBUSY;
        }
@@ -333,17 +335,17 @@ nvc0_fifo_cclass = {
  ******************************************************************************/
 
 static const struct nouveau_enum nvc0_fifo_fault_unit[] = {
-       { 0x00, "PGRAPH" },
+       { 0x00, "PGRAPH", NULL, NVDEV_ENGINE_GR },
        { 0x03, "PEEPHOLE" },
        { 0x04, "BAR1" },
        { 0x05, "BAR3" },
-       { 0x07, "PFIFO" },
-       { 0x10, "PBSP" },
-       { 0x11, "PPPP" },
+       { 0x07, "PFIFO", NULL, NVDEV_ENGINE_FIFO },
+       { 0x10, "PBSP", NULL, NVDEV_ENGINE_BSP },
+       { 0x11, "PPPP", NULL, NVDEV_ENGINE_PPP },
        { 0x13, "PCOUNTER" },
-       { 0x14, "PVP" },
-       { 0x15, "PCOPY0" },
-       { 0x16, "PCOPY1" },
+       { 0x14, "PVP", NULL, NVDEV_ENGINE_VP },
+       { 0x15, "PCOPY0", NULL, NVDEV_ENGINE_COPY0 },
+       { 0x16, "PCOPY1", NULL, NVDEV_ENGINE_COPY1 },
        { 0x17, "PDAEMON" },
        {}
 };
@@ -402,6 +404,9 @@ nvc0_fifo_isr_vm_fault(struct nvc0_fifo_priv *priv, int unit)
        u32 vahi = nv_rd32(priv, 0x002808 + (unit * 0x10));
        u32 stat = nv_rd32(priv, 0x00280c + (unit * 0x10));
        u32 client = (stat & 0x00001f00) >> 8;
+       const struct nouveau_enum *en;
+       struct nouveau_engine *engine;
+       struct nouveau_object *engctx = NULL;
 
        switch (unit) {
        case 3: /* PEEPHOLE */
@@ -420,16 +425,26 @@ nvc0_fifo_isr_vm_fault(struct nvc0_fifo_priv *priv, int unit)
        nv_error(priv, "%s fault at 0x%010llx [", (stat & 0x00000080) ?
                 "write" : "read", (u64)vahi << 32 | valo);
        nouveau_enum_print(nvc0_fifo_fault_reason, stat & 0x0000000f);
-       printk("] from ");
-       nouveau_enum_print(nvc0_fifo_fault_unit, unit);
+       pr_cont("] from ");
+       en = nouveau_enum_print(nvc0_fifo_fault_unit, unit);
        if (stat & 0x00000040) {
-               printk("/");
+               pr_cont("/");
                nouveau_enum_print(nvc0_fifo_fault_hubclient, client);
        } else {
-               printk("/GPC%d/", (stat & 0x1f000000) >> 24);
+               pr_cont("/GPC%d/", (stat & 0x1f000000) >> 24);
                nouveau_enum_print(nvc0_fifo_fault_gpcclient, client);
        }
-       printk(" on channel 0x%010llx\n", (u64)inst << 12);
+
+       if (en && en->data2) {
+               engine = nouveau_engine(priv, en->data2);
+               if (engine)
+                       engctx = nouveau_engctx_get(engine, inst);
+
+       }
+       pr_cont(" on channel 0x%010llx [%s]\n", (u64)inst << 12,
+                       nouveau_client_name(engctx));
+
+       nouveau_engctx_put(engctx);
 }
 
 static int
@@ -484,10 +499,12 @@ nvc0_fifo_isr_subfifo_intr(struct nvc0_fifo_priv *priv, int unit)
        if (show) {
                nv_error(priv, "SUBFIFO%d:", unit);
                nouveau_bitfield_print(nvc0_fifo_subfifo_intr, show);
-               printk("\n");
-               nv_error(priv, "SUBFIFO%d: ch %d subc %d mthd 0x%04x "
-                              "data 0x%08x\n",
-                        unit, chid, subc, mthd, data);
+               pr_cont("\n");
+               nv_error(priv,
+                        "SUBFIFO%d: ch %d [%s] subc %d mthd 0x%04x data 0x%08x\n",
+                        unit, chid,
+                        nouveau_client_name_for_fifo_chid(&priv->base, chid),
+                        subc, mthd, data);
        }
 
        nv_wr32(priv, 0x0400c0 + (unit * 0x2000), 0x80600008);
@@ -501,12 +518,34 @@ nvc0_fifo_intr(struct nouveau_subdev *subdev)
        u32 mask = nv_rd32(priv, 0x002140);
        u32 stat = nv_rd32(priv, 0x002100) & mask;
 
+       if (stat & 0x00000001) {
+               u32 intr = nv_rd32(priv, 0x00252c);
+               nv_warn(priv, "INTR 0x00000001: 0x%08x\n", intr);
+               nv_wr32(priv, 0x002100, 0x00000001);
+               stat &= ~0x00000001;
+       }
+
        if (stat & 0x00000100) {
-               nv_warn(priv, "unknown status 0x00000100\n");
+               u32 intr = nv_rd32(priv, 0x00254c);
+               nv_warn(priv, "INTR 0x00000100: 0x%08x\n", intr);
                nv_wr32(priv, 0x002100, 0x00000100);
                stat &= ~0x00000100;
        }
 
+       if (stat & 0x00010000) {
+               u32 intr = nv_rd32(priv, 0x00256c);
+               nv_warn(priv, "INTR 0x00010000: 0x%08x\n", intr);
+               nv_wr32(priv, 0x002100, 0x00010000);
+               stat &= ~0x00010000;
+       }
+
+       if (stat & 0x01000000) {
+               u32 intr = nv_rd32(priv, 0x00258c);
+               nv_warn(priv, "INTR 0x01000000: 0x%08x\n", intr);
+               nv_wr32(priv, 0x002100, 0x01000000);
+               stat &= ~0x01000000;
+       }
+
        if (stat & 0x10000000) {
                u32 units = nv_rd32(priv, 0x00259c);
                u32 u = units;
@@ -536,11 +575,20 @@ nvc0_fifo_intr(struct nouveau_subdev *subdev)
        }
 
        if (stat & 0x40000000) {
-               nv_warn(priv, "unknown status 0x40000000\n");
-               nv_mask(priv, 0x002a00, 0x00000000, 0x00000000);
+               u32 intr0 = nv_rd32(priv, 0x0025a4);
+               u32 intr1 = nv_mask(priv, 0x002a00, 0x00000000, 0x00000);
+               nv_debug(priv, "INTR 0x40000000: 0x%08x 0x%08x\n",
+                              intr0, intr1);
                stat &= ~0x40000000;
        }
 
+       if (stat & 0x80000000) {
+               u32 intr = nv_mask(priv, 0x0025a8, 0x00000000, 0x00000000);
+               nouveau_event_trigger(priv->base.uevent, 0);
+               nv_debug(priv, "INTR 0x80000000: 0x%08x\n", intr);
+               stat &= ~0x80000000;
+       }
+
        if (stat) {
                nv_fatal(priv, "unhandled status 0x%08x\n", stat);
                nv_wr32(priv, 0x002100, stat);
@@ -548,6 +596,20 @@ nvc0_fifo_intr(struct nouveau_subdev *subdev)
        }
 }
 
+static void
+nvc0_fifo_uevent_enable(struct nouveau_event *event, int index)
+{
+       struct nvc0_fifo_priv *priv = event->priv;
+       nv_mask(priv, 0x002140, 0x80000000, 0x80000000);
+}
+
+static void
+nvc0_fifo_uevent_disable(struct nouveau_event *event, int index)
+{
+       struct nvc0_fifo_priv *priv = event->priv;
+       nv_mask(priv, 0x002140, 0x80000000, 0x00000000);
+}
+
 static int
 nvc0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
               struct nouveau_oclass *oclass, void *data, u32 size,
@@ -581,6 +643,10 @@ nvc0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
+       priv->base.uevent->enable = nvc0_fifo_uevent_enable;
+       priv->base.uevent->disable = nvc0_fifo_uevent_disable;
+       priv->base.uevent->priv = priv;
+
        nv_subdev(priv)->unit = 0x00000100;
        nv_subdev(priv)->intr = nvc0_fifo_intr;
        nv_engine(priv)->cclass = &nvc0_fifo_cclass;
@@ -639,7 +705,8 @@ nvc0_fifo_init(struct nouveau_object *object)
 
        nv_wr32(priv, 0x002a00, 0xffffffff); /* clears PFIFO.INTR bit 30 */
        nv_wr32(priv, 0x002100, 0xffffffff);
-       nv_wr32(priv, 0x002140, 0xbfffffff);
+       nv_wr32(priv, 0x002140, 0x3fffffff);
+       nv_wr32(priv, 0x002628, 0x00000001); /* makes mthd 0x20 work */
        return 0;
 }
 
index c930da99c2c16df8f0185d6b0a41d8196fe884fe..4419e40d88e97ec5779dd01fc7cbc193e73714cf 100644 (file)
@@ -27,6 +27,7 @@
 #include <core/namedb.h>
 #include <core/gpuobj.h>
 #include <core/engctx.h>
+#include <core/event.h>
 #include <core/class.h>
 #include <core/math.h>
 #include <core/enum.h>
@@ -184,7 +185,8 @@ nve0_fifo_context_detach(struct nouveau_object *parent, bool suspend,
 
        nv_wr32(priv, 0x002634, chan->base.chid);
        if (!nv_wait(priv, 0x002634, 0xffffffff, chan->base.chid)) {
-               nv_error(priv, "channel %d kick timeout\n", chan->base.chid);
+               nv_error(priv, "channel %d [%s] kick timeout\n",
+                        chan->base.chid, nouveau_client_name(chan));
                if (suspend)
                        return -EBUSY;
        }
@@ -412,20 +414,34 @@ nve0_fifo_isr_vm_fault(struct nve0_fifo_priv *priv, int unit)
        u32 vahi = nv_rd32(priv, 0x2808 + (unit * 0x10));
        u32 stat = nv_rd32(priv, 0x280c + (unit * 0x10));
        u32 client = (stat & 0x00001f00) >> 8;
+       const struct nouveau_enum *en;
+       struct nouveau_engine *engine;
+       struct nouveau_object *engctx = NULL;
 
        nv_error(priv, "PFIFO: %s fault at 0x%010llx [", (stat & 0x00000080) ?
                       "write" : "read", (u64)vahi << 32 | valo);
        nouveau_enum_print(nve0_fifo_fault_reason, stat & 0x0000000f);
-       printk("] from ");
-       nouveau_enum_print(nve0_fifo_fault_unit, unit);
+       pr_cont("] from ");
+       en = nouveau_enum_print(nve0_fifo_fault_unit, unit);
        if (stat & 0x00000040) {
-               printk("/");
+               pr_cont("/");
                nouveau_enum_print(nve0_fifo_fault_hubclient, client);
        } else {
-               printk("/GPC%d/", (stat & 0x1f000000) >> 24);
+               pr_cont("/GPC%d/", (stat & 0x1f000000) >> 24);
                nouveau_enum_print(nve0_fifo_fault_gpcclient, client);
        }
-       printk(" on channel 0x%010llx\n", (u64)inst << 12);
+
+       if (en && en->data2) {
+               engine = nouveau_engine(priv, en->data2);
+               if (engine)
+                       engctx = nouveau_engctx_get(engine, inst);
+
+       }
+
+       pr_cont(" on channel 0x%010llx [%s]\n", (u64)inst << 12,
+                       nouveau_client_name(engctx));
+
+       nouveau_engctx_put(engctx);
 }
 
 static int
@@ -480,10 +496,12 @@ nve0_fifo_isr_subfifo_intr(struct nve0_fifo_priv *priv, int unit)
        if (show) {
                nv_error(priv, "SUBFIFO%d:", unit);
                nouveau_bitfield_print(nve0_fifo_subfifo_intr, show);
-               printk("\n");
-               nv_error(priv, "SUBFIFO%d: ch %d subc %d mthd 0x%04x "
-                              "data 0x%08x\n",
-                        unit, chid, subc, mthd, data);
+               pr_cont("\n");
+               nv_error(priv,
+                        "SUBFIFO%d: ch %d [%s] subc %d mthd 0x%04x data 0x%08x\n",
+                        unit, chid,
+                        nouveau_client_name_for_fifo_chid(&priv->base, chid),
+                        subc, mthd, data);
        }
 
        nv_wr32(priv, 0x0400c0 + (unit * 0x2000), 0x80600008);
@@ -537,6 +555,12 @@ nve0_fifo_intr(struct nouveau_subdev *subdev)
                stat &= ~0x40000000;
        }
 
+       if (stat & 0x80000000) {
+               nouveau_event_trigger(priv->base.uevent, 0);
+               nv_wr32(priv, 0x002100, 0x80000000);
+               stat &= ~0x80000000;
+       }
+
        if (stat) {
                nv_fatal(priv, "unhandled status 0x%08x\n", stat);
                nv_wr32(priv, 0x002100, stat);
@@ -544,6 +568,20 @@ nve0_fifo_intr(struct nouveau_subdev *subdev)
        }
 }
 
+static void
+nve0_fifo_uevent_enable(struct nouveau_event *event, int index)
+{
+       struct nve0_fifo_priv *priv = event->priv;
+       nv_mask(priv, 0x002140, 0x80000000, 0x80000000);
+}
+
+static void
+nve0_fifo_uevent_disable(struct nouveau_event *event, int index)
+{
+       struct nve0_fifo_priv *priv = event->priv;
+       nv_mask(priv, 0x002140, 0x80000000, 0x00000000);
+}
+
 static int
 nve0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
               struct nouveau_oclass *oclass, void *data, u32 size,
@@ -567,6 +605,10 @@ nve0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
+       priv->base.uevent->enable = nve0_fifo_uevent_enable;
+       priv->base.uevent->disable = nve0_fifo_uevent_disable;
+       priv->base.uevent->priv = priv;
+
        nv_subdev(priv)->unit = 0x00000100;
        nv_subdev(priv)->intr = nve0_fifo_intr;
        nv_engine(priv)->cclass = &nve0_fifo_cclass;
@@ -617,7 +659,7 @@ nve0_fifo_init(struct nouveau_object *object)
 
        nv_wr32(priv, 0x002a00, 0xffffffff);
        nv_wr32(priv, 0x002100, 0xffffffff);
-       nv_wr32(priv, 0x002140, 0xbfffffff);
+       nv_wr32(priv, 0x002140, 0x3fffffff);
        return 0;
 }
 
index e30a9c5ff1fceb0a093fcbe7261956cd2cc6c0bf..ad13dcdd15f98b9abc9c3739563d8cd5a0837c37 100644 (file)
@@ -22,6 +22,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
+#include <core/client.h>
 #include <core/os.h>
 #include <core/class.h>
 #include <core/handle.h>
@@ -1297,16 +1298,17 @@ nv04_graph_intr(struct nouveau_subdev *subdev)
        nv_wr32(priv, NV04_PGRAPH_FIFO, 0x00000001);
 
        if (show) {
-               nv_error(priv, "");
+               nv_error(priv, "%s", "");
                nouveau_bitfield_print(nv04_graph_intr_name, show);
-               printk(" nsource:");
+               pr_cont(" nsource:");
                nouveau_bitfield_print(nv04_graph_nsource, nsource);
-               printk(" nstatus:");
+               pr_cont(" nstatus:");
                nouveau_bitfield_print(nv04_graph_nstatus, nstatus);
-               printk("\n");
-               nv_error(priv, "ch %d/%d class 0x%04x "
-                              "mthd 0x%04x data 0x%08x\n",
-                        chid, subc, class, mthd, data);
+               pr_cont("\n");
+               nv_error(priv,
+                        "ch %d [%s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+                        chid, nouveau_client_name(chan), subc, class, mthd,
+                        data);
        }
 
        nouveau_namedb_put(handle);
index 5c0f843ea2491af49724e18d8d6432b0beed90cd..23c143aaa55640cb5b3a50f7862e38950d8427b7 100644 (file)
@@ -22,6 +22,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
+#include <core/client.h>
 #include <core/os.h>
 #include <core/class.h>
 #include <core/handle.h>
@@ -1193,16 +1194,17 @@ nv10_graph_intr(struct nouveau_subdev *subdev)
        nv_wr32(priv, NV04_PGRAPH_FIFO, 0x00000001);
 
        if (show) {
-               nv_error(priv, "");
+               nv_error(priv, "%s", "");
                nouveau_bitfield_print(nv10_graph_intr_name, show);
-               printk(" nsource:");
+               pr_cont(" nsource:");
                nouveau_bitfield_print(nv04_graph_nsource, nsource);
-               printk(" nstatus:");
+               pr_cont(" nstatus:");
                nouveau_bitfield_print(nv10_graph_nstatus, nstatus);
-               printk("\n");
-               nv_error(priv, "ch %d/%d class 0x%04x "
-                              "mthd 0x%04x data 0x%08x\n",
-                        chid, subc, class, mthd, data);
+               pr_cont("\n");
+               nv_error(priv,
+                        "ch %d [%s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+                        chid, nouveau_client_name(chan), subc, class, mthd,
+                        data);
        }
 
        nouveau_namedb_put(handle);
index 5b20401bf9118639c35636f804341400ac203bc3..0607b9801748481e16d40ea2103d4855bb627f47 100644 (file)
@@ -1,3 +1,4 @@
+#include <core/client.h>
 #include <core/os.h>
 #include <core/class.h>
 #include <core/engctx.h>
@@ -224,15 +225,17 @@ nv20_graph_intr(struct nouveau_subdev *subdev)
        nv_wr32(priv, NV04_PGRAPH_FIFO, 0x00000001);
 
        if (show) {
-               nv_error(priv, "");
+               nv_error(priv, "%s", "");
                nouveau_bitfield_print(nv10_graph_intr_name, show);
-               printk(" nsource:");
+               pr_cont(" nsource:");
                nouveau_bitfield_print(nv04_graph_nsource, nsource);
-               printk(" nstatus:");
+               pr_cont(" nstatus:");
                nouveau_bitfield_print(nv10_graph_nstatus, nstatus);
-               printk("\n");
-               nv_error(priv, "ch %d/%d class 0x%04x mthd 0x%04x data 0x%08x\n",
-                       chid, subc, class, mthd, data);
+               pr_cont("\n");
+               nv_error(priv,
+                        "ch %d [%s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+                        chid, nouveau_client_name(engctx), subc, class, mthd,
+                        data);
        }
 
        nouveau_engctx_put(engctx);
index 0b36dd3deebd1013aa721081ec189061f414bdb4..17049d5c723d15e4ca382c3345c9e9186d4ba7e6 100644 (file)
@@ -22,6 +22,7 @@
  * Authors: Ben Skeggs
  */
 
+#include <core/client.h>
 #include <core/os.h>
 #include <core/class.h>
 #include <core/handle.h>
@@ -321,16 +322,17 @@ nv40_graph_intr(struct nouveau_subdev *subdev)
        nv_wr32(priv, NV04_PGRAPH_FIFO, 0x00000001);
 
        if (show) {
-               nv_error(priv, "");
+               nv_error(priv, "%s", "");
                nouveau_bitfield_print(nv10_graph_intr_name, show);
-               printk(" nsource:");
+               pr_cont(" nsource:");
                nouveau_bitfield_print(nv04_graph_nsource, nsource);
-               printk(" nstatus:");
+               pr_cont(" nstatus:");
                nouveau_bitfield_print(nv10_graph_nstatus, nstatus);
-               printk("\n");
-               nv_error(priv, "ch %d [0x%08x] subc %d class 0x%04x "
-                              "mthd 0x%04x data 0x%08x\n",
-                        chid, inst << 4, subc, class, mthd, data);
+               pr_cont("\n");
+               nv_error(priv,
+                        "ch %d [0x%08x %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+                        chid, inst << 4, nouveau_client_name(engctx), subc,
+                        class, mthd, data);
        }
 
        nouveau_engctx_put(engctx);
index b1c3d835b4c257ee1f6c74853c257bb0ee771dfa..f2b1a7a124f2f58ac43a7c94e2b55a3d41546cff 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <core/os.h>
 #include <core/class.h>
+#include <core/client.h>
 #include <core/handle.h>
 #include <core/engctx.h>
 #include <core/enum.h>
@@ -418,7 +419,7 @@ nv50_priv_mp_trap(struct nv50_graph_priv *priv, int tpid, int display)
                        nv_error(priv, "TRAP_MP_EXEC - "
                                        "TP %d MP %d: ", tpid, i);
                        nouveau_enum_print(nv50_mp_exec_error_names, status);
-                       printk(" at %06x warp %d, opcode %08x %08x\n",
+                       pr_cont(" at %06x warp %d, opcode %08x %08x\n",
                                        pc&0xffffff, pc >> 24,
                                        oplow, ophigh);
                }
@@ -532,7 +533,7 @@ nv50_priv_tp_trap(struct nv50_graph_priv *priv, int type, u32 ustatus_old,
 
 static int
 nv50_graph_trap_handler(struct nv50_graph_priv *priv, u32 display,
-                       int chid, u64 inst)
+                       int chid, u64 inst, struct nouveau_object *engctx)
 {
        u32 status = nv_rd32(priv, 0x400108);
        u32 ustatus;
@@ -565,12 +566,11 @@ nv50_graph_trap_handler(struct nv50_graph_priv *priv, u32 display,
 
                        nv_error(priv, "TRAP DISPATCH_FAULT\n");
                        if (display && (addr & 0x80000000)) {
-                               nv_error(priv, "ch %d [0x%010llx] "
-                                            "subc %d class 0x%04x mthd 0x%04x "
-                                            "data 0x%08x%08x "
-                                            "400808 0x%08x 400848 0x%08x\n",
-                                       chid, inst, subc, class, mthd, datah,
-                                       datal, addr, r848);
+                               nv_error(priv,
+                                        "ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x%08x 400808 0x%08x 400848 0x%08x\n",
+                                        chid, inst,
+                                        nouveau_client_name(engctx), subc,
+                                        class, mthd, datah, datal, addr, r848);
                        } else
                        if (display) {
                                nv_error(priv, "no stuck command?\n");
@@ -591,11 +591,11 @@ nv50_graph_trap_handler(struct nv50_graph_priv *priv, u32 display,
 
                        nv_error(priv, "TRAP DISPATCH_QUERY\n");
                        if (display && (addr & 0x80000000)) {
-                               nv_error(priv, "ch %d [0x%010llx] "
-                                            "subc %d class 0x%04x mthd 0x%04x "
-                                            "data 0x%08x 40084c 0x%08x\n",
-                                       chid, inst, subc, class, mthd,
-                                       data, addr);
+                               nv_error(priv,
+                                        "ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x 40084c 0x%08x\n",
+                                        chid, inst,
+                                        nouveau_client_name(engctx), subc,
+                                        class, mthd, data, addr);
                        } else
                        if (display) {
                                nv_error(priv, "no stuck command?\n");
@@ -623,7 +623,7 @@ nv50_graph_trap_handler(struct nv50_graph_priv *priv, u32 display,
                if (display) {
                        nv_error(priv, "TRAP_M2MF");
                        nouveau_bitfield_print(nv50_graph_trap_m2mf, ustatus);
-                       printk("\n");
+                       pr_cont("\n");
                        nv_error(priv, "TRAP_M2MF %08x %08x %08x %08x\n",
                                nv_rd32(priv, 0x406804), nv_rd32(priv, 0x406808),
                                nv_rd32(priv, 0x40680c), nv_rd32(priv, 0x406810));
@@ -644,7 +644,7 @@ nv50_graph_trap_handler(struct nv50_graph_priv *priv, u32 display,
                if (display) {
                        nv_error(priv, "TRAP_VFETCH");
                        nouveau_bitfield_print(nv50_graph_trap_vfetch, ustatus);
-                       printk("\n");
+                       pr_cont("\n");
                        nv_error(priv, "TRAP_VFETCH %08x %08x %08x %08x\n",
                                nv_rd32(priv, 0x400c00), nv_rd32(priv, 0x400c08),
                                nv_rd32(priv, 0x400c0c), nv_rd32(priv, 0x400c10));
@@ -661,7 +661,7 @@ nv50_graph_trap_handler(struct nv50_graph_priv *priv, u32 display,
                if (display) {
                        nv_error(priv, "TRAP_STRMOUT");
                        nouveau_bitfield_print(nv50_graph_trap_strmout, ustatus);
-                       printk("\n");
+                       pr_cont("\n");
                        nv_error(priv, "TRAP_STRMOUT %08x %08x %08x %08x\n",
                                nv_rd32(priv, 0x401804), nv_rd32(priv, 0x401808),
                                nv_rd32(priv, 0x40180c), nv_rd32(priv, 0x401810));
@@ -682,7 +682,7 @@ nv50_graph_trap_handler(struct nv50_graph_priv *priv, u32 display,
                if (display) {
                        nv_error(priv, "TRAP_CCACHE");
                        nouveau_bitfield_print(nv50_graph_trap_ccache, ustatus);
-                       printk("\n");
+                       pr_cont("\n");
                        nv_error(priv, "TRAP_CCACHE %08x %08x %08x %08x"
                                     " %08x %08x %08x\n",
                                nv_rd32(priv, 0x405000), nv_rd32(priv, 0x405004),
@@ -774,11 +774,12 @@ nv50_graph_intr(struct nouveau_subdev *subdev)
                u32 ecode = nv_rd32(priv, 0x400110);
                nv_error(priv, "DATA_ERROR ");
                nouveau_enum_print(nv50_data_error_names, ecode);
-               printk("\n");
+               pr_cont("\n");
        }
 
        if (stat & 0x00200000) {
-               if (!nv50_graph_trap_handler(priv, show, chid, (u64)inst << 12))
+               if (!nv50_graph_trap_handler(priv, show, chid, (u64)inst << 12,
+                               engctx))
                        show &= ~0x00200000;
        }
 
@@ -786,12 +787,13 @@ nv50_graph_intr(struct nouveau_subdev *subdev)
        nv_wr32(priv, 0x400500, 0x00010001);
 
        if (show) {
-               nv_error(priv, "");
+               nv_error(priv, "%s", "");
                nouveau_bitfield_print(nv50_graph_intr_name, show);
-               printk("\n");
-               nv_error(priv, "ch %d [0x%010llx] subc %d class 0x%04x "
-                              "mthd 0x%04x data 0x%08x\n",
-                        chid, (u64)inst << 12, subc, class, mthd, data);
+               pr_cont("\n");
+               nv_error(priv,
+                        "ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+                        chid, (u64)inst << 12, nouveau_client_name(engctx),
+                        subc, class, mthd, data);
        }
 
        if (nv_rd32(priv, 0x400824) & (1 << 31))
@@ -907,9 +909,8 @@ nv50_graph_init(struct nouveau_object *object)
        nv_wr32(priv, 0x400828, 0x00000000);
        nv_wr32(priv, 0x40082c, 0x00000000);
        nv_wr32(priv, 0x400830, 0x00000000);
-       nv_wr32(priv, 0x400724, 0x00000000);
        nv_wr32(priv, 0x40032c, 0x00000000);
-       nv_wr32(priv, 0x400320, 4);     /* CTXCTL_CMD = NEWCTXDMA */
+       nv_wr32(priv, 0x400330, 0x00000000);
 
        /* some unknown zcull magic */
        switch (nv_device(priv)->chipset & 0xf0) {
index 45aff5f5085aa83d8837309cb8117f620950d0bd..0de0dd724afff9643125a0ed2bf11e74e231353b 100644 (file)
@@ -433,10 +433,10 @@ nvc0_graph_intr(struct nouveau_subdev *subdev)
        if (stat & 0x00000010) {
                handle = nouveau_handle_get_class(engctx, class);
                if (!handle || nv_call(handle->object, mthd, data)) {
-                       nv_error(priv, "ILLEGAL_MTHD ch %d [0x%010llx] "
-                                    "subc %d class 0x%04x mthd 0x%04x "
-                                    "data 0x%08x\n",
-                                chid, inst << 12, subc, class, mthd, data);
+                       nv_error(priv,
+                                "ILLEGAL_MTHD ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+                                chid, inst << 12, nouveau_client_name(engctx),
+                                subc, class, mthd, data);
                }
                nouveau_handle_put(handle);
                nv_wr32(priv, 0x400100, 0x00000010);
@@ -444,9 +444,10 @@ nvc0_graph_intr(struct nouveau_subdev *subdev)
        }
 
        if (stat & 0x00000020) {
-               nv_error(priv, "ILLEGAL_CLASS ch %d [0x%010llx] subc %d "
-                            "class 0x%04x mthd 0x%04x data 0x%08x\n",
-                       chid, inst << 12, subc, class, mthd, data);
+               nv_error(priv,
+                        "ILLEGAL_CLASS ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+                        chid, inst << 12, nouveau_client_name(engctx), subc,
+                        class, mthd, data);
                nv_wr32(priv, 0x400100, 0x00000020);
                stat &= ~0x00000020;
        }
@@ -454,15 +455,16 @@ nvc0_graph_intr(struct nouveau_subdev *subdev)
        if (stat & 0x00100000) {
                nv_error(priv, "DATA_ERROR [");
                nouveau_enum_print(nv50_data_error_names, code);
-               printk("] ch %d [0x%010llx] subc %d class 0x%04x "
-                      "mthd 0x%04x data 0x%08x\n",
-                      chid, inst << 12, subc, class, mthd, data);
+               pr_cont("] ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+                       chid, inst << 12, nouveau_client_name(engctx), subc,
+                       class, mthd, data);
                nv_wr32(priv, 0x400100, 0x00100000);
                stat &= ~0x00100000;
        }
 
        if (stat & 0x00200000) {
-               nv_error(priv, "TRAP ch %d [0x%010llx]\n", chid, inst << 12);
+               nv_error(priv, "TRAP ch %d [0x%010llx %s]\n", chid, inst << 12,
+                        nouveau_client_name(engctx));
                nvc0_graph_trap_intr(priv);
                nv_wr32(priv, 0x400100, 0x00200000);
                stat &= ~0x00200000;
@@ -611,10 +613,8 @@ nvc0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
 static void
 nvc0_graph_dtor_fw(struct nvc0_graph_fuc *fuc)
 {
-       if (fuc->data) {
-               kfree(fuc->data);
-               fuc->data = NULL;
-       }
+       kfree(fuc->data);
+       fuc->data = NULL;
 }
 
 void
@@ -622,8 +622,7 @@ nvc0_graph_dtor(struct nouveau_object *object)
 {
        struct nvc0_graph_priv *priv = (void *)object;
 
-       if (priv->data)
-               kfree(priv->data);
+       kfree(priv->data);
 
        nvc0_graph_dtor_fw(&priv->fuc409c);
        nvc0_graph_dtor_fw(&priv->fuc409d);
index 9f82e9702b4661208cf1f021ee246fcb721a986e..61cec0f6ff1cae895c85562351cfcdaf777df82f 100644 (file)
@@ -78,15 +78,16 @@ nve0_graph_ctxctl_isr(struct nvc0_graph_priv *priv)
 }
 
 static void
-nve0_graph_trap_isr(struct nvc0_graph_priv *priv, int chid, u64 inst)
+nve0_graph_trap_isr(struct nvc0_graph_priv *priv, int chid, u64 inst,
+               struct nouveau_object *engctx)
 {
        u32 trap = nv_rd32(priv, 0x400108);
        int rop;
 
        if (trap & 0x00000001) {
                u32 stat = nv_rd32(priv, 0x404000);
-               nv_error(priv, "DISPATCH ch %d [0x%010llx] 0x%08x\n",
-                        chid, inst, stat);
+               nv_error(priv, "DISPATCH ch %d [0x%010llx %s] 0x%08x\n",
+                        chid, inst, nouveau_client_name(engctx), stat);
                nv_wr32(priv, 0x404000, 0xc0000000);
                nv_wr32(priv, 0x400108, 0x00000001);
                trap &= ~0x00000001;
@@ -94,8 +95,8 @@ nve0_graph_trap_isr(struct nvc0_graph_priv *priv, int chid, u64 inst)
 
        if (trap & 0x00000010) {
                u32 stat = nv_rd32(priv, 0x405840);
-               nv_error(priv, "SHADER ch %d [0x%010llx] 0x%08x\n",
-                        chid, inst, stat);
+               nv_error(priv, "SHADER ch %d [0x%010llx %s] 0x%08x\n",
+                        chid, inst, nouveau_client_name(engctx), stat);
                nv_wr32(priv, 0x405840, 0xc0000000);
                nv_wr32(priv, 0x400108, 0x00000010);
                trap &= ~0x00000010;
@@ -105,8 +106,10 @@ nve0_graph_trap_isr(struct nvc0_graph_priv *priv, int chid, u64 inst)
                for (rop = 0; rop < priv->rop_nr; rop++) {
                        u32 statz = nv_rd32(priv, ROP_UNIT(rop, 0x070));
                        u32 statc = nv_rd32(priv, ROP_UNIT(rop, 0x144));
-                       nv_error(priv, "ROP%d ch %d [0x%010llx] 0x%08x 0x%08x\n",
-                                rop, chid, inst, statz, statc);
+                       nv_error(priv,
+                                "ROP%d ch %d [0x%010llx %s] 0x%08x 0x%08x\n",
+                                rop, chid, inst, nouveau_client_name(engctx),
+                                statz, statc);
                        nv_wr32(priv, ROP_UNIT(rop, 0x070), 0xc0000000);
                        nv_wr32(priv, ROP_UNIT(rop, 0x144), 0xc0000000);
                }
@@ -115,8 +118,8 @@ nve0_graph_trap_isr(struct nvc0_graph_priv *priv, int chid, u64 inst)
        }
 
        if (trap) {
-               nv_error(priv, "TRAP ch %d [0x%010llx] 0x%08x\n",
-                        chid, inst, trap);
+               nv_error(priv, "TRAP ch %d [0x%010llx %s] 0x%08x\n",
+                        chid, inst, nouveau_client_name(engctx), trap);
                nv_wr32(priv, 0x400108, trap);
        }
 }
@@ -145,10 +148,10 @@ nve0_graph_intr(struct nouveau_subdev *subdev)
        if (stat & 0x00000010) {
                handle = nouveau_handle_get_class(engctx, class);
                if (!handle || nv_call(handle->object, mthd, data)) {
-                       nv_error(priv, "ILLEGAL_MTHD ch %d [0x%010llx] "
-                                    "subc %d class 0x%04x mthd 0x%04x "
-                                    "data 0x%08x\n",
-                                chid, inst, subc, class, mthd, data);
+                       nv_error(priv,
+                                "ILLEGAL_MTHD ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+                                chid, inst, nouveau_client_name(engctx), subc,
+                                class, mthd, data);
                }
                nouveau_handle_put(handle);
                nv_wr32(priv, 0x400100, 0x00000010);
@@ -156,9 +159,10 @@ nve0_graph_intr(struct nouveau_subdev *subdev)
        }
 
        if (stat & 0x00000020) {
-               nv_error(priv, "ILLEGAL_CLASS ch %d [0x%010llx] subc %d "
-                            "class 0x%04x mthd 0x%04x data 0x%08x\n",
-                        chid, inst, subc, class, mthd, data);
+               nv_error(priv,
+                        "ILLEGAL_CLASS ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+                        chid, inst, nouveau_client_name(engctx), subc, class,
+                        mthd, data);
                nv_wr32(priv, 0x400100, 0x00000020);
                stat &= ~0x00000020;
        }
@@ -166,15 +170,15 @@ nve0_graph_intr(struct nouveau_subdev *subdev)
        if (stat & 0x00100000) {
                nv_error(priv, "DATA_ERROR [");
                nouveau_enum_print(nv50_data_error_names, code);
-               printk("] ch %d [0x%010llx] subc %d class 0x%04x "
-                      "mthd 0x%04x data 0x%08x\n",
-                      chid, inst, subc, class, mthd, data);
+               pr_cont("] ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
+                       chid, inst, nouveau_client_name(engctx), subc, class,
+                       mthd, data);
                nv_wr32(priv, 0x400100, 0x00100000);
                stat &= ~0x00100000;
        }
 
        if (stat & 0x00200000) {
-               nve0_graph_trap_isr(priv, chid, inst);
+               nve0_graph_trap_isr(priv, chid, inst, engctx);
                nv_wr32(priv, 0x400100, 0x00200000);
                stat &= ~0x00200000;
        }
index 9fd86375f4c48af81d1789771c61a0f9d1705a22..49ecbb859b251974eefd6c5767d1b31ae2b624dc 100644 (file)
@@ -22,6 +22,7 @@
  * Authors: Ben Skeggs
  */
 
+#include <core/client.h>
 #include <core/os.h>
 #include <core/class.h>
 #include <core/engctx.h>
@@ -231,8 +232,10 @@ nv31_mpeg_intr(struct nouveau_subdev *subdev)
        nv_wr32(priv, 0x00b230, 0x00000001);
 
        if (show) {
-               nv_error(priv, "ch %d [0x%08x] 0x%08x 0x%08x 0x%08x 0x%08x\n",
-                        chid, inst << 4, stat, type, mthd, data);
+               nv_error(priv,
+                        "ch %d [0x%08x %s] 0x%08x 0x%08x 0x%08x 0x%08x\n",
+                        chid, inst << 4, nouveau_client_name(engctx), stat,
+                        type, mthd, data);
        }
 
        nouveau_engctx_put(engctx);
index b0e7e1c01ce69b42d7c9150e2678858a07376663..c48e74953771a6c02352d43c0bfa59a84be13c0e 100644 (file)
@@ -28,6 +28,9 @@
 #include <core/namedb.h>
 #include <core/handle.h>
 #include <core/gpuobj.h>
+#include <core/event.h>
+
+#include <subdev/bar.h>
 
 #include <engine/software.h>
 #include <engine/disp.h>
@@ -90,18 +93,11 @@ nv50_software_mthd_vblsem_release(struct nouveau_object *object, u32 mthd,
 {
        struct nv50_software_chan *chan = (void *)nv_engctx(object->parent);
        struct nouveau_disp *disp = nouveau_disp(object);
-       unsigned long flags;
        u32 crtc = *(u32 *)args;
-
        if (crtc > 1)
                return -EINVAL;
 
-       disp->vblank.get(disp->vblank.data, crtc);
-
-       spin_lock_irqsave(&disp->vblank.lock, flags);
-       list_add(&chan->base.vblank.head, &disp->vblank.list);
-       chan->base.vblank.crtc = crtc;
-       spin_unlock_irqrestore(&disp->vblank.lock, flags);
+       nouveau_event_get(disp->vblank, crtc, &chan->base.vblank.event);
        return 0;
 }
 
@@ -135,6 +131,29 @@ nv50_software_sclass[] = {
  * software context
  ******************************************************************************/
 
+static int
+nv50_software_vblsem_release(struct nouveau_eventh *event, int head)
+{
+       struct nouveau_software_chan *chan =
+               container_of(event, struct nouveau_software_chan, vblank.event);
+       struct nv50_software_priv *priv = (void *)nv_object(chan)->engine;
+       struct nouveau_bar *bar = nouveau_bar(priv);
+
+       nv_wr32(priv, 0x001704, chan->vblank.channel);
+       nv_wr32(priv, 0x001710, 0x80000000 | chan->vblank.ctxdma);
+       bar->flush(bar);
+
+       if (nv_device(priv)->chipset == 0x50) {
+               nv_wr32(priv, 0x001570, chan->vblank.offset);
+               nv_wr32(priv, 0x001574, chan->vblank.value);
+       } else {
+               nv_wr32(priv, 0x060010, chan->vblank.offset);
+               nv_wr32(priv, 0x060014, chan->vblank.value);
+       }
+
+       return NVKM_EVENT_DROP;
+}
+
 static int
 nv50_software_context_ctor(struct nouveau_object *parent,
                           struct nouveau_object *engine,
@@ -150,6 +169,7 @@ nv50_software_context_ctor(struct nouveau_object *parent,
                return ret;
 
        chan->base.vblank.channel = nv_gpuobj(parent->parent)->addr >> 12;
+       chan->base.vblank.event.func = nv50_software_vblsem_release;
        return 0;
 }
 
@@ -170,8 +190,8 @@ nv50_software_cclass = {
 
 static int
 nv50_software_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-             struct nouveau_oclass *oclass, void *data, u32 size,
-             struct nouveau_object **pobject)
+                  struct nouveau_oclass *oclass, void *data, u32 size,
+                  struct nouveau_object **pobject)
 {
        struct nv50_software_priv *priv;
        int ret;
index 282a1cd1bc2facbc887ffe721763e0e0e9df34a0..a523eaad47e3efb30206b953e2206828da97ea46 100644 (file)
@@ -25,6 +25,9 @@
 #include <core/os.h>
 #include <core/class.h>
 #include <core/engctx.h>
+#include <core/event.h>
+
+#include <subdev/bar.h>
 
 #include <engine/software.h>
 #include <engine/disp.h>
@@ -72,18 +75,12 @@ nvc0_software_mthd_vblsem_release(struct nouveau_object *object, u32 mthd,
 {
        struct nvc0_software_chan *chan = (void *)nv_engctx(object->parent);
        struct nouveau_disp *disp = nouveau_disp(object);
-       unsigned long flags;
        u32 crtc = *(u32 *)args;
 
        if ((nv_device(object)->card_type < NV_E0 && crtc > 1) || crtc > 3)
                return -EINVAL;
 
-       disp->vblank.get(disp->vblank.data, crtc);
-
-       spin_lock_irqsave(&disp->vblank.lock, flags);
-       list_add(&chan->base.vblank.head, &disp->vblank.list);
-       chan->base.vblank.crtc = crtc;
-       spin_unlock_irqrestore(&disp->vblank.lock, flags);
+       nouveau_event_get(disp->vblank, crtc, &chan->base.vblank.event);
        return 0;
 }
 
@@ -117,6 +114,23 @@ nvc0_software_sclass[] = {
  * software context
  ******************************************************************************/
 
+static int
+nvc0_software_vblsem_release(struct nouveau_eventh *event, int head)
+{
+       struct nouveau_software_chan *chan =
+               container_of(event, struct nouveau_software_chan, vblank.event);
+       struct nvc0_software_priv *priv = (void *)nv_object(chan)->engine;
+       struct nouveau_bar *bar = nouveau_bar(priv);
+
+       nv_wr32(priv, 0x001718, 0x80000000 | chan->vblank.channel);
+       bar->flush(bar);
+       nv_wr32(priv, 0x06000c, upper_32_bits(chan->vblank.offset));
+       nv_wr32(priv, 0x060010, lower_32_bits(chan->vblank.offset));
+       nv_wr32(priv, 0x060014, chan->vblank.value);
+
+       return NVKM_EVENT_DROP;
+}
+
 static int
 nvc0_software_context_ctor(struct nouveau_object *parent,
                           struct nouveau_object *engine,
@@ -132,6 +146,7 @@ nvc0_software_context_ctor(struct nouveau_object *parent,
                return ret;
 
        chan->base.vblank.channel = nv_gpuobj(parent->parent)->addr >> 12;
+       chan->base.vblank.event.func = nvc0_software_vblsem_release;
        return 0;
 }
 
index 47c4b3a5bd3a282e4c4f10232ed9de4ecef3a594..92d3ab11d962b07993cb3794e0d5e5105050d51a 100644 (file)
@@ -154,6 +154,14 @@ struct nve0_channel_ind_class {
        u32 engine;
 };
 
+/* 0046: NV04_DISP
+ */
+
+#define NV04_DISP_CLASS                                              0x00000046
+
+struct nv04_display_class {
+};
+
 /* 5070: NV50_DISP
  * 8270: NV84_DISP
  * 8370: NVA0_DISP
@@ -190,25 +198,6 @@ struct nve0_channel_ind_class {
 #define NV84_DISP_SOR_HDMI_PWR_REKEY                                 0x0000007f
 #define NV50_DISP_SOR_LVDS_SCRIPT                                    0x00013000
 #define NV50_DISP_SOR_LVDS_SCRIPT_ID                                 0x0000ffff
-#define NV94_DISP_SOR_DP_TRAIN                                       0x00016000
-#define NV94_DISP_SOR_DP_TRAIN_OP                                    0xf0000000
-#define NV94_DISP_SOR_DP_TRAIN_OP_PATTERN                            0x00000000
-#define NV94_DISP_SOR_DP_TRAIN_OP_INIT                               0x10000000
-#define NV94_DISP_SOR_DP_TRAIN_OP_FINI                               0x20000000
-#define NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD                           0x00000001
-#define NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_OFF                       0x00000000
-#define NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_ON                        0x00000001
-#define NV94_DISP_SOR_DP_TRAIN_PATTERN                               0x00000003
-#define NV94_DISP_SOR_DP_TRAIN_PATTERN_DISABLED                      0x00000000
-#define NV94_DISP_SOR_DP_LNKCTL                                      0x00016040
-#define NV94_DISP_SOR_DP_LNKCTL_FRAME                                0x80000000
-#define NV94_DISP_SOR_DP_LNKCTL_FRAME_STD                            0x00000000
-#define NV94_DISP_SOR_DP_LNKCTL_FRAME_ENH                            0x80000000
-#define NV94_DISP_SOR_DP_LNKCTL_WIDTH                                0x00001f00
-#define NV94_DISP_SOR_DP_LNKCTL_COUNT                                0x00000007
-#define NV94_DISP_SOR_DP_DRVCTL(l)                     ((l) * 0x40 + 0x00016100)
-#define NV94_DISP_SOR_DP_DRVCTL_VS                                   0x00000300
-#define NV94_DISP_SOR_DP_DRVCTL_PE                                   0x00000003
 
 #define NV50_DISP_DAC_MTHD                                           0x00020000
 #define NV50_DISP_DAC_MTHD_TYPE                                      0x0000f000
@@ -230,6 +219,23 @@ struct nve0_channel_ind_class {
 #define NV50_DISP_DAC_LOAD                                           0x0002000c
 #define NV50_DISP_DAC_LOAD_VALUE                                     0x00000007
 
+#define NV50_DISP_PIOR_MTHD                                          0x00030000
+#define NV50_DISP_PIOR_MTHD_TYPE                                     0x0000f000
+#define NV50_DISP_PIOR_MTHD_OR                                       0x00000003
+
+#define NV50_DISP_PIOR_PWR                                           0x00030000
+#define NV50_DISP_PIOR_PWR_STATE                                     0x00000001
+#define NV50_DISP_PIOR_PWR_STATE_ON                                  0x00000001
+#define NV50_DISP_PIOR_PWR_STATE_OFF                                 0x00000000
+#define NV50_DISP_PIOR_TMDS_PWR                                      0x00032000
+#define NV50_DISP_PIOR_TMDS_PWR_STATE                                0x00000001
+#define NV50_DISP_PIOR_TMDS_PWR_STATE_ON                             0x00000001
+#define NV50_DISP_PIOR_TMDS_PWR_STATE_OFF                            0x00000000
+#define NV50_DISP_PIOR_DP_PWR                                        0x00036000
+#define NV50_DISP_PIOR_DP_PWR_STATE                                  0x00000001
+#define NV50_DISP_PIOR_DP_PWR_STATE_ON                               0x00000001
+#define NV50_DISP_PIOR_DP_PWR_STATE_OFF                              0x00000000
+
 struct nv50_display_class {
 };
 
index 63acc0346ff2c437cda76e06df68c709d51bc46e..c66eac5138038cdf3c15d1b54dc6cfe0a8383770 100644 (file)
@@ -7,7 +7,7 @@ struct nouveau_client {
        struct nouveau_namedb base;
        struct nouveau_handle *root;
        struct nouveau_object *device;
-       char name[16];
+       char name[32];
        u32 debug;
        struct nouveau_vm *vm;
 };
@@ -41,5 +41,6 @@ int  nouveau_client_create_(const char *name, u64 device, const char *cfg,
 
 int  nouveau_client_init(struct nouveau_client *);
 int  nouveau_client_fini(struct nouveau_client *, bool suspend);
+const char *nouveau_client_name(void *obj);
 
 #endif
index e58b6f0984c195d340ece405da50600bcd0333b1..d351a4e5819cda1d5f7b9a204a7347a89e209648 100644 (file)
@@ -26,6 +26,7 @@ enum nv_subdev_type {
         */
        NVDEV_SUBDEV_MXM,
        NVDEV_SUBDEV_MC,
+       NVDEV_SUBDEV_BUS,
        NVDEV_SUBDEV_TIMER,
        NVDEV_SUBDEV_FB,
        NVDEV_SUBDEV_LTCG,
index e7b1e181943bb767d6006e72455565e369b84b0a..4fc62bb8c1f0d63d201ac4f83223e1ea321a7b9a 100644 (file)
@@ -5,12 +5,13 @@ struct nouveau_enum {
        u32 value;
        const char *name;
        const void *data;
+       u32 data2;
 };
 
 const struct nouveau_enum *
 nouveau_enum_find(const struct nouveau_enum *, u32 value);
 
-void
+const struct nouveau_enum *
 nouveau_enum_print(const struct nouveau_enum *en, u32 value);
 
 struct nouveau_bitfield {
diff --git a/drivers/gpu/drm/nouveau/core/include/core/event.h b/drivers/gpu/drm/nouveau/core/include/core/event.h
new file mode 100644 (file)
index 0000000..9e09440
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef __NVKM_EVENT_H__
+#define __NVKM_EVENT_H__
+
+/* return codes from event handlers */
+#define NVKM_EVENT_DROP 0
+#define NVKM_EVENT_KEEP 1
+
+struct nouveau_eventh {
+       struct list_head head;
+       int (*func)(struct nouveau_eventh *, int index);
+};
+
+struct nouveau_event {
+       spinlock_t lock;
+
+       void *priv;
+       void (*enable)(struct nouveau_event *, int index);
+       void (*disable)(struct nouveau_event *, int index);
+
+       int index_nr;
+       struct {
+               struct list_head list;
+               int refs;
+       } index[];
+};
+
+int  nouveau_event_create(int index_nr, struct nouveau_event **);
+void nouveau_event_destroy(struct nouveau_event **);
+void nouveau_event_trigger(struct nouveau_event *, int index);
+
+void nouveau_event_get(struct nouveau_event *, int index,
+                      struct nouveau_eventh *);
+void nouveau_event_put(struct nouveau_event *, int index,
+                      struct nouveau_eventh *);
+
+#endif
index 5982935ee23a96896fb12d98e2dc5f723ab245e0..6a902672f6f47e321f1a9c3b334f67b0e308a508 100644 (file)
@@ -133,7 +133,7 @@ static inline u8
 nv_ro08(void *obj, u64 addr)
 {
        u8 data = nv_ofuncs(obj)->rd08(obj, addr);
-       nv_spam(obj, "nv_ro08 0x%08x 0x%02x\n", addr, data);
+       nv_spam(obj, "nv_ro08 0x%08llx 0x%02x\n", addr, data);
        return data;
 }
 
@@ -141,7 +141,7 @@ static inline u16
 nv_ro16(void *obj, u64 addr)
 {
        u16 data = nv_ofuncs(obj)->rd16(obj, addr);
-       nv_spam(obj, "nv_ro16 0x%08x 0x%04x\n", addr, data);
+       nv_spam(obj, "nv_ro16 0x%08llx 0x%04x\n", addr, data);
        return data;
 }
 
@@ -149,28 +149,28 @@ static inline u32
 nv_ro32(void *obj, u64 addr)
 {
        u32 data = nv_ofuncs(obj)->rd32(obj, addr);
-       nv_spam(obj, "nv_ro32 0x%08x 0x%08x\n", addr, data);
+       nv_spam(obj, "nv_ro32 0x%08llx 0x%08x\n", addr, data);
        return data;
 }
 
 static inline void
 nv_wo08(void *obj, u64 addr, u8 data)
 {
-       nv_spam(obj, "nv_wo08 0x%08x 0x%02x\n", addr, data);
+       nv_spam(obj, "nv_wo08 0x%08llx 0x%02x\n", addr, data);
        nv_ofuncs(obj)->wr08(obj, addr, data);
 }
 
 static inline void
 nv_wo16(void *obj, u64 addr, u16 data)
 {
-       nv_spam(obj, "nv_wo16 0x%08x 0x%04x\n", addr, data);
+       nv_spam(obj, "nv_wo16 0x%08llx 0x%04x\n", addr, data);
        nv_ofuncs(obj)->wr16(obj, addr, data);
 }
 
 static inline void
 nv_wo32(void *obj, u64 addr, u32 data)
 {
-       nv_spam(obj, "nv_wo32 0x%08x 0x%08x\n", addr, data);
+       nv_spam(obj, "nv_wo32 0x%08llx 0x%08x\n", addr, data);
        nv_ofuncs(obj)->wr32(obj, addr, data);
 }
 
index 1d629664f32d17c9e655ca5f1ae2d8b89f3f4c2c..febed2ea5c80e50f369c151fc669909200475237 100644 (file)
@@ -15,7 +15,8 @@ struct nouveau_object;
 #define NV_PRINTK_TRACE    KERN_DEBUG
 #define NV_PRINTK_SPAM     KERN_DEBUG
 
-void nv_printk_(struct nouveau_object *, const char *, int, const char *, ...);
+void __printf(4, 5)
+nv_printk_(struct nouveau_object *, const char *, int, const char *, ...);
 
 #define nv_printk(o,l,f,a...) do {                                             \
        if (NV_DBG_##l <= CONFIG_NOUVEAU_DEBUG)                                \
index 46948285f3e73d30b286905ad19ddea9ae36020e..28da6772c095560ec6ece447c971438ca37cb294 100644 (file)
@@ -4,18 +4,11 @@
 #include <core/object.h>
 #include <core/engine.h>
 #include <core/device.h>
+#include <core/event.h>
 
 struct nouveau_disp {
        struct nouveau_engine base;
-
-       struct {
-               struct list_head list;
-               spinlock_t lock;
-               void (*notify)(void *, int);
-               void (*get)(void *, int);
-               void (*put)(void *, int);
-               void *data;
-       } vblank;
+       struct nouveau_event *vblank;
 };
 
 static inline struct nouveau_disp *
@@ -24,16 +17,22 @@ nouveau_disp(void *obj)
        return (void *)nv_device(obj)->subdev[NVDEV_ENGINE_DISP];
 }
 
-#define nouveau_disp_create(p,e,c,i,x,d)                                       \
-       nouveau_engine_create((p), (e), (c), true, (i), (x), (d))
-#define nouveau_disp_destroy(d)                                                \
-       nouveau_engine_destroy(&(d)->base)
+#define nouveau_disp_create(p,e,c,h,i,x,d)                                     \
+       nouveau_disp_create_((p), (e), (c), (h), (i), (x),                     \
+                            sizeof(**d), (void **)d)
+#define nouveau_disp_destroy(d) ({                                             \
+       struct nouveau_disp *disp = (d);                                       \
+       _nouveau_disp_dtor(nv_object(disp));                                   \
+})
 #define nouveau_disp_init(d)                                                   \
        nouveau_engine_init(&(d)->base)
 #define nouveau_disp_fini(d,s)                                                 \
        nouveau_engine_fini(&(d)->base, (s))
 
-#define _nouveau_disp_dtor _nouveau_engine_dtor
+int  nouveau_disp_create_(struct nouveau_object *, struct nouveau_object *,
+                         struct nouveau_oclass *, int heads,
+                         const char *, const char *, int, void **);
+void _nouveau_disp_dtor(struct nouveau_object *);
 #define _nouveau_disp_init _nouveau_engine_init
 #define _nouveau_disp_fini _nouveau_engine_fini
 
index f18846c8c6febce1cbb6900fb3e1bd19b6b9d76e..b46c197709f394c4224d57b7bcde433bb8e71c2d 100644 (file)
@@ -65,6 +65,8 @@ struct nouveau_fifo_base {
 struct nouveau_fifo {
        struct nouveau_engine base;
 
+       struct nouveau_event *uevent;
+
        struct nouveau_object **channel;
        spinlock_t lock;
        u16 min;
@@ -92,6 +94,8 @@ int nouveau_fifo_create_(struct nouveau_object *, struct nouveau_object *,
                         struct nouveau_oclass *, int min, int max,
                         int size, void **);
 void nouveau_fifo_destroy(struct nouveau_fifo *);
+const char *
+nouveau_client_name_for_fifo_chid(struct nouveau_fifo *fifo, u32 chid);
 
 #define _nouveau_fifo_init _nouveau_engine_init
 #define _nouveau_fifo_fini _nouveau_engine_fini
index c945691c8564d0d0f05b13f16524c877ccdb1e91..45799487e573e226856247d21087f18ccfbf09c6 100644 (file)
@@ -3,17 +3,17 @@
 
 #include <core/engine.h>
 #include <core/engctx.h>
+#include <core/event.h>
 
 struct nouveau_software_chan {
        struct nouveau_engctx base;
 
        struct {
-               struct list_head head;
+               struct nouveau_eventh event;
                u32 channel;
                u32 ctxdma;
                u64 offset;
                u32 value;
-               u32 crtc;
        } vblank;
 
        int (*flip)(void *);
index b79025da581ef5b8ea50cadb563313c70a6cd781..123270e9813a86c896d4a7d452c545eeb8ae87be 100644 (file)
@@ -16,6 +16,8 @@ enum dcb_output_type {
 
 struct dcb_output {
        int index;      /* may not be raw dcb index if merging has happened */
+       u16 hasht;
+       u16 hashm;
        enum dcb_output_type type;
        uint8_t i2c_index;
        uint8_t heads;
@@ -25,6 +27,7 @@ struct dcb_output {
        uint8_t or;
        uint8_t link;
        bool duallink_possible;
+       uint8_t extdev;
        union {
                struct sor_conf {
                        int link;
index e6563b5cb08edb7c48ac4811627fa6007fe7b3d4..96d3364f6db30bca72ac8c9b9947c5230b3eb678 100644 (file)
@@ -1,17 +1,22 @@
 #ifndef __NVBIOS_GPIO_H__
 #define __NVBIOS_GPIO_H__
 
-struct nouveau_bios;
-
 enum dcb_gpio_func_name {
        DCB_GPIO_PANEL_POWER = 0x01,
        DCB_GPIO_TVDAC0 = 0x0c,
        DCB_GPIO_TVDAC1 = 0x2d,
-       DCB_GPIO_PWM_FAN = 0x09,
+       DCB_GPIO_FAN = 0x09,
        DCB_GPIO_FAN_SENSE = 0x3d,
        DCB_GPIO_UNUSED = 0xff
 };
 
+#define DCB_GPIO_LOG_DIR     0x02
+#define DCB_GPIO_LOG_DIR_OUT 0x00
+#define DCB_GPIO_LOG_DIR_IN  0x02
+#define DCB_GPIO_LOG_VAL     0x01
+#define DCB_GPIO_LOG_VAL_LO  0x00
+#define DCB_GPIO_LOG_VAL_HI  0x01
+
 struct dcb_gpio_func {
        u8 func;
        u8 line;
index 5079bedfd985132b9524889f6edb269761ad7e55..10b57a19a7decb7f6a5604ed6ce5befe4ecd18e9 100644 (file)
@@ -15,7 +15,7 @@ struct dcb_i2c_entry {
        enum dcb_i2c_type type;
        u8 drive;
        u8 sense;
-       u32 data;
+       u8 share;
 };
 
 u16 dcb_i2c_table(struct nouveau_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
index a2c4296fc5f67ce06fa5a0e7c7558b4957bf1e93..083541dbe9c8147fe940d36177074a77d95ea9e0 100644 (file)
@@ -23,11 +23,27 @@ struct nvbios_therm_sensor {
        struct nvbios_therm_threshold thrs_shutdown;
 };
 
+/* no vbios have more than 6 */
+#define NOUVEAU_TEMP_FAN_TRIP_MAX 10
+struct nouveau_therm_trip_point {
+       int fan_duty;
+       int temp;
+       int hysteresis;
+};
+
 struct nvbios_therm_fan {
        u16 pwm_freq;
 
        u8 min_duty;
        u8 max_duty;
+
+       u16 bump_period;
+       u16 slow_down_period;
+
+       struct nouveau_therm_trip_point trip[NOUVEAU_TEMP_FAN_TRIP_MAX];
+       u8 nr_fan_trip;
+       u8 linear_min_temp;
+       u8 linear_max_temp;
 };
 
 enum nvbios_therm_domain {
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/xpio.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/xpio.h
new file mode 100644 (file)
index 0000000..360baab
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef __NVBIOS_XPIO_H__
+#define __NVBIOS_XPIO_H__
+
+#define NVBIOS_XPIO_FLAG_AUX  0x10
+#define NVBIOS_XPIO_FLAG_AUX0 0x00
+#define NVBIOS_XPIO_FLAG_AUX1 0x10
+
+struct nvbios_xpio {
+       u8 type;
+       u8 addr;
+       u8 flags;
+};
+
+u16 dcb_xpio_table(struct nouveau_bios *, u8 idx,
+                  u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
+u16 dcb_xpio_parse(struct nouveau_bios *, u8 idx,
+                  u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_xpio *);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bus.h b/drivers/gpu/drm/nouveau/core/include/subdev/bus.h
new file mode 100644 (file)
index 0000000..7d88ec4
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef __NOUVEAU_BUS_H__
+#define __NOUVEAU_BUS_H__
+
+#include <core/subdev.h>
+#include <core/device.h>
+
+struct nouveau_bus_intr {
+       u32 stat;
+       u32 unit;
+};
+
+struct nouveau_bus {
+       struct nouveau_subdev base;
+};
+
+static inline struct nouveau_bus *
+nouveau_bus(void *obj)
+{
+       return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_BUS];
+}
+
+#define nouveau_bus_create(p, e, o, d)                                         \
+       nouveau_subdev_create_((p), (e), (o), 0, "PBUS", "master",             \
+                              sizeof(**d), (void **)d)
+#define nouveau_bus_destroy(p)                                                 \
+       nouveau_subdev_destroy(&(p)->base)
+#define nouveau_bus_init(p)                                                    \
+       nouveau_subdev_init(&(p)->base)
+#define nouveau_bus_fini(p, s)                                                 \
+       nouveau_subdev_fini(&(p)->base, (s))
+
+#define _nouveau_bus_dtor _nouveau_subdev_dtor
+#define _nouveau_bus_init _nouveau_subdev_init
+#define _nouveau_bus_fini _nouveau_subdev_fini
+
+extern struct nouveau_oclass nv04_bus_oclass;
+extern struct nouveau_oclass nv31_bus_oclass;
+extern struct nouveau_oclass nv50_bus_oclass;
+extern struct nouveau_oclass nvc0_bus_oclass;
+
+#endif
index b75e8f18e52c8432a2506e4eb9924f31afc56989..c85b9f1579ad89a2f2d0094d75375720d4a97dac 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <core/subdev.h>
 #include <core/device.h>
+#include <core/event.h>
 
 #include <subdev/bios.h>
 #include <subdev/bios/gpio.h>
 struct nouveau_gpio {
        struct nouveau_subdev base;
 
+       struct nouveau_event *events;
+
        /* hardware interfaces */
        void (*reset)(struct nouveau_gpio *, u8 func);
        int  (*drive)(struct nouveau_gpio *, int line, int dir, int out);
        int  (*sense)(struct nouveau_gpio *, int line);
-       void (*irq_enable)(struct nouveau_gpio *, int line, bool);
 
        /* software interfaces */
        int  (*find)(struct nouveau_gpio *, int idx, u8 tag, u8 line,
                     struct dcb_gpio_func *);
        int  (*set)(struct nouveau_gpio *, int idx, u8 tag, u8 line, int state);
        int  (*get)(struct nouveau_gpio *, int idx, u8 tag, u8 line);
-       int  (*irq)(struct nouveau_gpio *, int idx, u8 tag, u8 line, bool on);
-
-       /* interrupt handling */
-       struct list_head isr;
-       spinlock_t lock;
-
-       void (*isr_run)(struct nouveau_gpio *, int idx, u32 mask);
-       int  (*isr_add)(struct nouveau_gpio *, int idx, u8 tag, u8 line,
-                       void (*)(void *, int state), void *data);
-       void (*isr_del)(struct nouveau_gpio *, int idx, u8 tag, u8 line,
-                       void (*)(void *, int state), void *data);
 };
 
 static inline struct nouveau_gpio *
@@ -40,25 +31,23 @@ nouveau_gpio(void *obj)
        return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_GPIO];
 }
 
-#define nouveau_gpio_create(p,e,o,d)                                           \
-       nouveau_gpio_create_((p), (e), (o), sizeof(**d), (void **)d)
-#define nouveau_gpio_destroy(p)                                                \
-       nouveau_subdev_destroy(&(p)->base)
+#define nouveau_gpio_create(p,e,o,l,d)                                         \
+       nouveau_gpio_create_((p), (e), (o), (l), sizeof(**d), (void **)d)
+#define nouveau_gpio_destroy(p) ({                                             \
+       struct nouveau_gpio *gpio = (p);                                       \
+       _nouveau_gpio_dtor(nv_object(gpio));                                   \
+})
 #define nouveau_gpio_fini(p,s)                                                 \
        nouveau_subdev_fini(&(p)->base, (s))
 
-int nouveau_gpio_create_(struct nouveau_object *, struct nouveau_object *,
-                        struct nouveau_oclass *, int, void **);
-int nouveau_gpio_init(struct nouveau_gpio *);
+int  nouveau_gpio_create_(struct nouveau_object *, struct nouveau_object *,
+                         struct nouveau_oclass *, int, int, void **);
+void _nouveau_gpio_dtor(struct nouveau_object *);
+int  nouveau_gpio_init(struct nouveau_gpio *);
 
 extern struct nouveau_oclass nv10_gpio_oclass;
 extern struct nouveau_oclass nv50_gpio_oclass;
 extern struct nouveau_oclass nvd0_gpio_oclass;
-
-void nv50_gpio_dtor(struct nouveau_object *);
-int  nv50_gpio_init(struct nouveau_object *);
-int  nv50_gpio_fini(struct nouveau_object *, bool);
-void nv50_gpio_intr(struct nouveau_subdev *);
-void nv50_gpio_irq_enable(struct nouveau_gpio *, int line, bool);
+extern struct nouveau_oclass nve0_gpio_oclass;
 
 #endif
index b93ab01e37851487710d54458e66438ccb9bf228..888384c0bed86230914dac0a1991a37c6fff21c8 100644 (file)
 #define NV_I2C_PORT(n)    (0x00 + (n))
 #define NV_I2C_DEFAULT(n) (0x80 + (n))
 
+#define NV_I2C_TYPE_DCBI2C(n) (0x0000 | (n))
+#define NV_I2C_TYPE_EXTDDC(e) (0x0005 | (e) << 8)
+#define NV_I2C_TYPE_EXTAUX(e) (0x0006 | (e) << 8)
+
 struct nouveau_i2c_port {
+       struct nouveau_object base;
        struct i2c_adapter adapter;
-       struct nouveau_i2c *i2c;
-       struct i2c_algo_bit_data bit;
+
        struct list_head head;
        u8  index;
-       u8  type;
-       u32 dcb;
-       u32 drive;
-       u32 sense;
-       u32 state;
+
+       const struct nouveau_i2c_func *func;
+};
+
+struct nouveau_i2c_func {
+       void (*acquire)(struct nouveau_i2c_port *);
+       void (*release)(struct nouveau_i2c_port *);
+
+       void (*drive_scl)(struct nouveau_i2c_port *, int);
+       void (*drive_sda)(struct nouveau_i2c_port *, int);
+       int  (*sense_scl)(struct nouveau_i2c_port *);
+       int  (*sense_sda)(struct nouveau_i2c_port *);
+
+       int  (*aux)(struct nouveau_i2c_port *, u8, u32, u8 *, u8);
+       int  (*pattern)(struct nouveau_i2c_port *, int pattern);
+       int  (*lnk_ctl)(struct nouveau_i2c_port *, int nr, int bw, bool enh);
+       int  (*drv_ctl)(struct nouveau_i2c_port *, int lane, int sw, int pe);
 };
 
+#define nouveau_i2c_port_create(p,e,o,i,a,d)                                   \
+       nouveau_i2c_port_create_((p), (e), (o), (i), (a),                      \
+                                sizeof(**d), (void **)d)
+#define nouveau_i2c_port_destroy(p) ({                                         \
+       struct nouveau_i2c_port *port = (p);                                   \
+       _nouveau_i2c_port_dtor(nv_object(i2c));                                \
+})
+#define nouveau_i2c_port_init(p)                                               \
+       nouveau_object_init(&(p)->base)
+#define nouveau_i2c_port_fini(p,s)                                             \
+       nouveau_object_fini(&(p)->base, (s))
+
+int nouveau_i2c_port_create_(struct nouveau_object *, struct nouveau_object *,
+                            struct nouveau_oclass *, u8,
+                            const struct i2c_algorithm *, int, void **);
+void _nouveau_i2c_port_dtor(struct nouveau_object *);
+#define _nouveau_i2c_port_init nouveau_object_init
+#define _nouveau_i2c_port_fini nouveau_object_fini
+
 struct nouveau_i2c {
        struct nouveau_subdev base;
 
        struct nouveau_i2c_port *(*find)(struct nouveau_i2c *, u8 index);
+       struct nouveau_i2c_port *(*find_type)(struct nouveau_i2c *, u16 type);
        int (*identify)(struct nouveau_i2c *, int index,
                        const char *what, struct i2c_board_info *,
                        bool (*match)(struct nouveau_i2c_port *,
@@ -40,21 +76,76 @@ nouveau_i2c(void *obj)
        return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_I2C];
 }
 
-extern struct nouveau_oclass nouveau_i2c_oclass;
+#define nouveau_i2c_create(p,e,o,s,d)                                          \
+       nouveau_i2c_create_((p), (e), (o), (s), sizeof(**d), (void **)d)
+#define nouveau_i2c_destroy(p) ({                                              \
+       struct nouveau_i2c *i2c = (p);                                         \
+       _nouveau_i2c_dtor(nv_object(i2c));                                     \
+})
+#define nouveau_i2c_init(p) ({                                                 \
+       struct nouveau_i2c *i2c = (p);                                         \
+       _nouveau_i2c_init(nv_object(i2c));                                     \
+})
+#define nouveau_i2c_fini(p,s) ({                                               \
+       struct nouveau_i2c *i2c = (p);                                         \
+       _nouveau_i2c_fini(nv_object(i2c), (s));                                \
+})
 
-void nouveau_i2c_drive_scl(void *, int);
-void nouveau_i2c_drive_sda(void *, int);
-int  nouveau_i2c_sense_scl(void *);
-int  nouveau_i2c_sense_sda(void *);
+int nouveau_i2c_create_(struct nouveau_object *, struct nouveau_object *,
+                       struct nouveau_oclass *, struct nouveau_oclass *,
+                       int, void **);
+void _nouveau_i2c_dtor(struct nouveau_object *);
+int  _nouveau_i2c_init(struct nouveau_object *);
+int  _nouveau_i2c_fini(struct nouveau_object *, bool);
 
-int  nv_rdi2cr(struct nouveau_i2c_port *, u8 addr, u8 reg);
-int  nv_wri2cr(struct nouveau_i2c_port *, u8 addr, u8 reg, u8 val);
-bool nv_probe_i2c(struct nouveau_i2c_port *, u8 addr);
-
-int nv_rdaux(struct nouveau_i2c_port *, u32 addr, u8 *data, u8 size);
-int nv_wraux(struct nouveau_i2c_port *, u32 addr, u8 *data, u8 size);
+extern struct nouveau_oclass nv04_i2c_oclass;
+extern struct nouveau_oclass nv4e_i2c_oclass;
+extern struct nouveau_oclass nv50_i2c_oclass;
+extern struct nouveau_oclass nv94_i2c_oclass;
+extern struct nouveau_oclass nvd0_i2c_oclass;
+extern struct nouveau_oclass nouveau_anx9805_sclass[];
 
 extern const struct i2c_algorithm nouveau_i2c_bit_algo;
 extern const struct i2c_algorithm nouveau_i2c_aux_algo;
 
+static inline int
+nv_rdi2cr(struct nouveau_i2c_port *port, u8 addr, u8 reg)
+{
+       u8 val;
+       struct i2c_msg msgs[] = {
+               { .addr = addr, .flags = 0, .len = 1, .buf = &reg },
+               { .addr = addr, .flags = I2C_M_RD, .len = 1, .buf = &val },
+       };
+
+       int ret = i2c_transfer(&port->adapter, msgs, 2);
+       if (ret != 2)
+               return -EIO;
+
+       return val;
+}
+
+static inline int
+nv_wri2cr(struct nouveau_i2c_port *port, u8 addr, u8 reg, u8 val)
+{
+       u8 buf[2] = { reg, val };
+       struct i2c_msg msgs[] = {
+               { .addr = addr, .flags = 0, .len = 2, .buf = buf },
+       };
+
+       int ret = i2c_transfer(&port->adapter, msgs, 1);
+       if (ret != 1)
+               return -EIO;
+
+       return 0;
+}
+
+static inline bool
+nv_probe_i2c(struct nouveau_i2c_port *port, u8 addr)
+{
+       return nv_rdi2cr(port, addr, 0) >= 0;
+}
+
+int nv_rdaux(struct nouveau_i2c_port *, u32 addr, u8 *data, u8 size);
+int nv_wraux(struct nouveau_i2c_port *, u32 addr, u8 *data, u8 size);
+
 #endif
index faee569fd4581c6ef43cacc7db1b09f819f21137..6b17b614629f64172a6ace41ed5a1593889c94ad 100644 (file)
@@ -4,10 +4,10 @@
 #include <core/device.h>
 #include <core/subdev.h>
 
-enum nouveau_therm_fan_mode {
-       FAN_CONTROL_NONE = 0,
-       FAN_CONTROL_MANUAL = 1,
-       FAN_CONTROL_NR,
+enum nouveau_therm_mode {
+       NOUVEAU_THERM_CTRL_NONE = 0,
+       NOUVEAU_THERM_CTRL_MANUAL = 1,
+       NOUVEAU_THERM_CTRL_AUTO = 2,
 };
 
 enum nouveau_therm_attr_type {
@@ -28,6 +28,11 @@ enum nouveau_therm_attr_type {
 struct nouveau_therm {
        struct nouveau_subdev base;
 
+       int (*pwm_ctrl)(struct nouveau_therm *, int line, bool);
+       int (*pwm_get)(struct nouveau_therm *, int line, u32 *, u32 *);
+       int (*pwm_set)(struct nouveau_therm *, int line, u32, u32);
+       int (*pwm_clock)(struct nouveau_therm *);
+
        int (*fan_get)(struct nouveau_therm *);
        int (*fan_set)(struct nouveau_therm *, int);
        int (*fan_sense)(struct nouveau_therm *);
@@ -46,13 +51,29 @@ nouveau_therm(void *obj)
 }
 
 #define nouveau_therm_create(p,e,o,d)                                          \
-       nouveau_subdev_create((p), (e), (o), 0, "THERM", "therm", d)
-#define nouveau_therm_destroy(p)                                               \
-       nouveau_subdev_destroy(&(p)->base)
+       nouveau_therm_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nouveau_therm_destroy(p) ({                                            \
+       struct nouveau_therm *therm = (p);                                     \
+        _nouveau_therm_dtor(nv_object(therm));                                 \
+})
+#define nouveau_therm_init(p) ({                                               \
+       struct nouveau_therm *therm = (p);                                     \
+        _nouveau_therm_init(nv_object(therm));                                 \
+})
+#define nouveau_therm_fini(p,s) ({                                             \
+       struct nouveau_therm *therm = (p);                                     \
+        _nouveau_therm_init(nv_object(therm), (s));                            \
+})
 
-#define _nouveau_therm_dtor _nouveau_subdev_dtor
+int  nouveau_therm_create_(struct nouveau_object *, struct nouveau_object *,
+                          struct nouveau_oclass *, int, void **);
+void _nouveau_therm_dtor(struct nouveau_object *);
+int  _nouveau_therm_init(struct nouveau_object *);
+int  _nouveau_therm_fini(struct nouveau_object *, bool);
 
 extern struct nouveau_oclass nv40_therm_oclass;
 extern struct nouveau_oclass nv50_therm_oclass;
+extern struct nouveau_oclass nva3_therm_oclass;
+extern struct nouveau_oclass nvd0_therm_oclass;
 
 #endif
index c24ec8ab3db441204480cdc18fb3d86a84b256d8..e465d158d352edd1fbc9f830674b4038227fed4b 100644 (file)
@@ -10,6 +10,14 @@ struct nouveau_alarm {
        void (*func)(struct nouveau_alarm *);
 };
 
+static inline void
+nouveau_alarm_init(struct nouveau_alarm *alarm,
+                  void (*func)(struct nouveau_alarm *))
+{
+       INIT_LIST_HEAD(&alarm->head);
+       alarm->func = func;
+}
+
 bool nouveau_timer_wait_eq(void *, u64 nsec, u32 addr, u32 mask, u32 data);
 bool nouveau_timer_wait_ne(void *, u64 nsec, u32 addr, u32 mask, u32 data);
 bool nouveau_timer_wait_cb(void *, u64 nsec, bool (*func)(void *), void *data);
index cfe3b9cad156961d1ac94da8c13047068b5a1b3c..eb496033b55c7c07ae8aea54f3acb34b6afceea0 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/vmalloc.h>
 #include <linux/acpi.h>
 #include <linux/dmi.h>
+#include <linux/reboot.h>
 
 #include <asm/unaligned.h>
 
index f621f69fa1a245a1f5d633b493d7542b71c21d38..e816f06637a7210e9862a68734b753d447ffc28e 100644 (file)
@@ -172,7 +172,7 @@ out:
        nv_wr32(bios, pcireg, access);
 }
 
-#if defined(CONFIG_ACPI)
+#if defined(CONFIG_ACPI) && defined(CONFIG_X86)
 int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len);
 bool nouveau_acpi_rom_supported(struct pci_dev *pdev);
 #else
index 0fd87df99dd650a1abb1df42ac99fe0cf5724d75..2d9b9d7a7992111cc1d4bd0ff3e60254bf52dcb5 100644 (file)
@@ -107,6 +107,18 @@ dcb_outp(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len)
        return 0x0000;
 }
 
+static inline u16
+dcb_outp_hasht(struct dcb_output *outp)
+{
+       return (outp->extdev << 8) | (outp->location << 4) | outp->type;
+}
+
+static inline u16
+dcb_outp_hashm(struct dcb_output *outp)
+{
+       return (outp->heads << 8) | (outp->link << 6) | outp->or;
+}
+
 u16
 dcb_outp_parse(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len,
               struct dcb_output *outp)
@@ -135,34 +147,28 @@ dcb_outp_parse(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len,
                        case DCB_OUTPUT_DP:
                                outp->link = (conf & 0x00000030) >> 4;
                                outp->sorconf.link = outp->link; /*XXX*/
+                               outp->extdev = 0x00;
+                               if (outp->location != 0)
+                                       outp->extdev = (conf & 0x0000ff00) >> 8;
                                break;
                        default:
                                break;
                        }
                }
+
+               outp->hasht = dcb_outp_hasht(outp);
+               outp->hashm = dcb_outp_hashm(outp);
        }
        return dcb;
 }
 
-static inline u16
-dcb_outp_hasht(struct dcb_output *outp)
-{
-       return outp->type;
-}
-
-static inline u16
-dcb_outp_hashm(struct dcb_output *outp)
-{
-       return (outp->heads << 8) | (outp->link << 6) | outp->or;
-}
-
 u16
 dcb_outp_match(struct nouveau_bios *bios, u16 type, u16 mask,
               u8 *ver, u8 *len, struct dcb_output *outp)
 {
        u16 dcb, idx = 0;
        while ((dcb = dcb_outp_parse(bios, idx++, ver, len, outp))) {
-               if (dcb_outp_hasht(outp) == type) {
+               if ((dcb_outp_hasht(outp) & 0x00ff) == (type & 0x00ff)) {
                        if ((dcb_outp_hashm(outp) & mask) == mask)
                                break;
                }
index 5afb568b2d69511af0397a3b2c2e41d4ac73f9b3..b2a676e53580ca6cd143e238b211cbcd7b5a027a 100644 (file)
@@ -48,7 +48,7 @@ extdev_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *len, u8 *cnt)
        return extdev + *hdr;
 }
 
-u16
+static u16
 nvbios_extdev_entry(struct nouveau_bios *bios, int idx, u8 *ver, u8 *len)
 {
        u8 hdr, cnt;
index c84e93fa6d9550cf258a23096967a8b6846f440f..172a4f9999902d5c648be9f184df8a76d467a11c 100644 (file)
@@ -25,6 +25,7 @@
 #include <subdev/bios.h>
 #include <subdev/bios/dcb.h>
 #include <subdev/bios/gpio.h>
+#include <subdev/bios/xpio.h>
 
 u16
 dcb_gpio_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
@@ -60,8 +61,14 @@ dcb_gpio_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
 u16
 dcb_gpio_entry(struct nouveau_bios *bios, int idx, int ent, u8 *ver, u8 *len)
 {
-       u8  hdr, cnt;
-       u16 gpio = !idx ? dcb_gpio_table(bios, ver, &hdr, &cnt, len) : 0x0000;
+       u8  hdr, cnt, xver; /* use gpio version for xpio entry parsing */
+       u16 gpio;
+
+       if (!idx--)
+               gpio = dcb_gpio_table(bios, ver, &hdr, &cnt, len);
+       else
+               gpio = dcb_xpio_table(bios, idx, &xver, &hdr, &cnt, len);
+
        if (gpio && ent < cnt)
                return gpio + hdr + (ent * *len);
        return 0x0000;
index ad577db83766130ecfeae0df2c499211f46657d9..cfb9288c6d28cbd2f2ea662bf77176c386ad14d9 100644 (file)
@@ -70,12 +70,12 @@ dcb_i2c_parse(struct nouveau_bios *bios, u8 idx, struct dcb_i2c_entry *info)
        u8  ver, len;
        u16 ent = dcb_i2c_entry(bios, idx, &ver, &len);
        if (ent) {
-               info->data = nv_ro32(bios, ent + 0);
-               info->type = nv_ro08(bios, ent + 3);
+               info->type  = nv_ro08(bios, ent + 3);
+               info->share = DCB_I2C_UNUSED;
                if (ver < 0x30) {
                        info->type &= 0x07;
                        if (info->type == 0x07)
-                               info->type = 0xff;
+                               info->type = DCB_I2C_UNUSED;
                }
 
                switch (info->type) {
@@ -88,7 +88,11 @@ dcb_i2c_parse(struct nouveau_bios *bios, u8 idx, struct dcb_i2c_entry *info)
                        return 0;
                case DCB_I2C_NVIO_BIT:
                case DCB_I2C_NVIO_AUX:
-                       info->drive = nv_ro08(bios, ent + 0);
+                       info->drive = nv_ro08(bios, ent + 0) & 0x0f;
+                       if (nv_ro08(bios, ent + 1) & 0x01) {
+                               info->share  = nv_ro08(bios, ent + 1) >> 1;
+                               info->share &= 0x0f;
+                       }
                        return 0;
                case DCB_I2C_UNUSED:
                        return 0;
@@ -121,7 +125,8 @@ dcb_i2c_parse(struct nouveau_bios *bios, u8 idx, struct dcb_i2c_entry *info)
                        if (!info->sense) info->sense = 0x36;
                }
 
-               info->type = DCB_I2C_NV04_BIT;
+               info->type  = DCB_I2C_NV04_BIT;
+               info->share = DCB_I2C_UNUSED;
                return 0;
        }
 
index 690ed438b2adbcf6c3263d5962e84054a62fef5d..2cc1e6a5eb6aa2b46eb27632ecafe71222cc9041 100644 (file)
@@ -231,6 +231,11 @@ init_i2c(struct nvbios_init *init, int index)
                        return NULL;
                }
 
+               if (index == -2 && init->outp->location) {
+                       index = NV_I2C_TYPE_EXTAUX(init->outp->extdev);
+                       return i2c->find_type(i2c, index);
+               }
+
                index = init->outp->i2c_index;
        }
 
@@ -258,7 +263,7 @@ init_wri2cr(struct nvbios_init *init, u8 index, u8 addr, u8 reg, u8 val)
 static int
 init_rdauxr(struct nvbios_init *init, u32 addr)
 {
-       struct nouveau_i2c_port *port = init_i2c(init, -1);
+       struct nouveau_i2c_port *port = init_i2c(init, -2);
        u8 data;
 
        if (port && init_exec(init)) {
@@ -274,7 +279,7 @@ init_rdauxr(struct nvbios_init *init, u32 addr)
 static int
 init_wrauxr(struct nvbios_init *init, u32 addr, u8 data)
 {
-       struct nouveau_i2c_port *port = init_i2c(init, -1);
+       struct nouveau_i2c_port *port = init_i2c(init, -2);
        if (port && init_exec(init))
                return nv_wraux(port, addr, &data, 1);
        return -ENODEV;
@@ -1816,7 +1821,7 @@ init_ram_restrict_zm_reg_group(struct nvbios_init *init)
        u8 i, j;
 
        trace("RAM_RESTRICT_ZM_REG_GROUP\t"
-             "R[%08x] 0x%02x 0x%02x\n", addr, incr, num);
+             "R[0x%08x] 0x%02x 0x%02x\n", addr, incr, num);
        init->offset += 7;
 
        for (i = 0; i < num; i++) {
@@ -1849,7 +1854,7 @@ init_copy_zm_reg(struct nvbios_init *init)
        u32 sreg = nv_ro32(bios, init->offset + 1);
        u32 dreg = nv_ro32(bios, init->offset + 5);
 
-       trace("COPY_ZM_REG\tR[0x%06x] = R[0x%06x]\n", sreg, dreg);
+       trace("COPY_ZM_REG\tR[0x%06x] = R[0x%06x]\n", dreg, sreg);
        init->offset += 9;
 
        init_wr32(init, dreg, init_rd32(init, sreg));
@@ -1866,7 +1871,7 @@ init_zm_reg_group(struct nvbios_init *init)
        u32 addr = nv_ro32(bios, init->offset + 1);
        u8 count = nv_ro08(bios, init->offset + 5);
 
-       trace("ZM_REG_GROUP\tR[0x%06x] =\n");
+       trace("ZM_REG_GROUP\tR[0x%06x] =\n", addr);
        init->offset += 6;
 
        while (count--) {
index 862a08a2ae27c232d89db82e810e4e489030d670..22a20573ed1b01d2220e5e6942ec2b06f5bf84f3 100644 (file)
@@ -55,7 +55,7 @@ therm_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *len, u8 *cnt)
        return therm + nv_ro08(bios, therm + 1);
 }
 
-u16
+static u16
 nvbios_therm_entry(struct nouveau_bios *bios, int idx, u8 *ver, u8 *len)
 {
        u8 hdr, cnt;
@@ -155,10 +155,15 @@ int
 nvbios_therm_fan_parse(struct nouveau_bios *bios,
                          struct nvbios_therm_fan *fan)
 {
+       struct nouveau_therm_trip_point *cur_trip = NULL;
        u8 ver, len, i;
        u16 entry;
 
+       uint8_t duty_lut[] = { 0, 0, 25, 0, 40, 0, 50, 0,
+                               75, 0, 85, 0, 100, 0, 100, 0 };
+
        i = 0;
+       fan->nr_fan_trip = 0;
        while ((entry = nvbios_therm_entry(bios, i++, &ver, &len))) {
                s16 value = nv_ro16(bios, entry + 1);
 
@@ -167,9 +172,30 @@ nvbios_therm_fan_parse(struct nouveau_bios *bios,
                        fan->min_duty = value & 0xff;
                        fan->max_duty = (value & 0xff00) >> 8;
                        break;
+               case 0x24:
+                       fan->nr_fan_trip++;
+                       cur_trip = &fan->trip[fan->nr_fan_trip - 1];
+                       cur_trip->hysteresis = value & 0xf;
+                       cur_trip->temp = (value & 0xff0) >> 4;
+                       cur_trip->fan_duty = duty_lut[(value & 0xf000) >> 12];
+                       break;
+               case 0x25:
+                       cur_trip = &fan->trip[fan->nr_fan_trip - 1];
+                       cur_trip->fan_duty = value;
+                       break;
                case 0x26:
                        fan->pwm_freq = value;
                        break;
+               case 0x3b:
+                       fan->bump_period = value;
+                       break;
+               case 0x3c:
+                       fan->slow_down_period = value;
+                       break;
+               case 0x46:
+                       fan->linear_min_temp = nv_ro08(bios, entry + 1);
+                       fan->linear_max_temp = nv_ro08(bios, entry + 2);
+                       break;
                }
        }
 
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/xpio.c b/drivers/gpu/drm/nouveau/core/subdev/bios/xpio.c
new file mode 100644 (file)
index 0000000..e9b8e5d
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/bios.h>
+#include <subdev/bios/gpio.h>
+#include <subdev/bios/xpio.h>
+
+static u16
+dcb_xpiod_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+       u16 data = dcb_gpio_table(bios, ver, hdr, cnt, len);
+       if (data && *ver >= 0x40 && *hdr >= 0x06) {
+               u16 xpio = nv_ro16(bios, data + 0x04);
+               if (xpio) {
+                       *ver = nv_ro08(bios, data + 0x00);
+                       *hdr = nv_ro08(bios, data + 0x01);
+                       *cnt = nv_ro08(bios, data + 0x02);
+                       *len = nv_ro08(bios, data + 0x03);
+                       return xpio;
+               }
+       }
+       return 0x0000;
+}
+
+u16
+dcb_xpio_table(struct nouveau_bios *bios, u8 idx,
+              u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+       u16 data = dcb_xpiod_table(bios, ver, hdr, cnt, len);
+       if (data && idx < *cnt) {
+               u16 xpio = nv_ro16(bios, data + *hdr + (idx * *len));
+               if (xpio) {
+                       *ver = nv_ro08(bios, data + 0x00);
+                       *hdr = nv_ro08(bios, data + 0x01);
+                       *cnt = nv_ro08(bios, data + 0x02);
+                       *len = nv_ro08(bios, data + 0x03);
+                       return xpio;
+               }
+       }
+       return 0x0000;
+}
+
+u16
+dcb_xpio_parse(struct nouveau_bios *bios, u8 idx,
+              u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+              struct nvbios_xpio *info)
+{
+       u16 data = dcb_xpio_table(bios, idx, ver, hdr, cnt, len);
+       if (data && *len >= 6) {
+               info->type = nv_ro08(bios, data + 0x04);
+               info->addr = nv_ro08(bios, data + 0x05);
+               info->flags = nv_ro08(bios, data + 0x06);
+       }
+       return 0x0000;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bus/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/bus/nv04.c
new file mode 100644 (file)
index 0000000..8c7f805
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2012 Nouveau Community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres <martin.peres@labri.fr>
+ *          Ben Skeggs
+ */
+
+#include <subdev/bus.h>
+
+struct nv04_bus_priv {
+       struct nouveau_bus base;
+};
+
+static void
+nv04_bus_intr(struct nouveau_subdev *subdev)
+{
+       struct nouveau_bus *pbus = nouveau_bus(subdev);
+       u32 stat = nv_rd32(pbus, 0x001100) & nv_rd32(pbus, 0x001140);
+
+       if (stat & 0x00000001) {
+               nv_error(pbus, "BUS ERROR\n");
+               stat &= ~0x00000001;
+               nv_wr32(pbus, 0x001100, 0x00000001);
+       }
+
+       if (stat & 0x00000110) {
+               subdev = nouveau_subdev(subdev, NVDEV_SUBDEV_GPIO);
+               if (subdev && subdev->intr)
+                       subdev->intr(subdev);
+               stat &= ~0x00000110;
+               nv_wr32(pbus, 0x001100, 0x00000110);
+       }
+
+       if (stat) {
+               nv_error(pbus, "unknown intr 0x%08x\n", stat);
+               nv_mask(pbus, 0x001140, stat, 0x00000000);
+       }
+}
+
+static int
+nv04_bus_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 size,
+             struct nouveau_object **pobject)
+{
+       struct nv04_bus_priv *priv;
+       int ret;
+
+       ret = nouveau_bus_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       nv_subdev(priv)->intr = nv04_bus_intr;
+       return 0;
+}
+
+static int
+nv04_bus_init(struct nouveau_object *object)
+{
+       struct nv04_bus_priv *priv = (void *)object;
+
+       nv_wr32(priv, 0x001100, 0xffffffff);
+       nv_wr32(priv, 0x001140, 0x00000111);
+
+       return nouveau_bus_init(&priv->base);
+}
+
+struct nouveau_oclass
+nv04_bus_oclass = {
+       .handle = NV_SUBDEV(BUS, 0x04),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_bus_ctor,
+               .dtor = _nouveau_bus_dtor,
+               .init = nv04_bus_init,
+               .fini = _nouveau_bus_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bus/nv31.c b/drivers/gpu/drm/nouveau/core/subdev/bus/nv31.c
new file mode 100644 (file)
index 0000000..34132ae
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2012 Nouveau Community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres <martin.peres@labri.fr>
+ *          Ben Skeggs
+ */
+
+#include <subdev/bus.h>
+
+struct nv31_bus_priv {
+       struct nouveau_bus base;
+};
+
+static void
+nv31_bus_intr(struct nouveau_subdev *subdev)
+{
+       struct nouveau_bus *pbus = nouveau_bus(subdev);
+       u32 stat = nv_rd32(pbus, 0x001100) & nv_rd32(pbus, 0x001140);
+       u32 gpio = nv_rd32(pbus, 0x001104) & nv_rd32(pbus, 0x001144);
+
+       if (gpio) {
+               subdev = nouveau_subdev(pbus, NVDEV_SUBDEV_GPIO);
+               if (subdev && subdev->intr)
+                       subdev->intr(subdev);
+       }
+
+       if (stat & 0x00000008) {  /* NV41- */
+               u32 addr = nv_rd32(pbus, 0x009084);
+               u32 data = nv_rd32(pbus, 0x009088);
+
+               nv_error(pbus, "MMIO %s of 0x%08x FAULT at 0x%06x\n",
+                        (addr & 0x00000002) ? "write" : "read", data,
+                        (addr & 0x00fffffc));
+
+               stat &= ~0x00000008;
+               nv_wr32(pbus, 0x001100, 0x00000008);
+       }
+
+       if (stat & 0x00070000) {
+               subdev = nouveau_subdev(pbus, NVDEV_SUBDEV_THERM);
+               if (subdev && subdev->intr)
+                       subdev->intr(subdev);
+               stat &= ~0x00070000;
+               nv_wr32(pbus, 0x001100, 0x00070000);
+       }
+
+       if (stat) {
+               nv_error(pbus, "unknown intr 0x%08x\n", stat);
+               nv_mask(pbus, 0x001140, stat, 0x00000000);
+       }
+}
+
+static int
+nv31_bus_init(struct nouveau_object *object)
+{
+       struct nv31_bus_priv *priv = (void *)object;
+       int ret;
+
+       ret = nouveau_bus_init(&priv->base);
+       if (ret)
+               return ret;
+
+       nv_wr32(priv, 0x001100, 0xffffffff);
+       nv_wr32(priv, 0x001140, 0x00070008);
+       return 0;
+}
+
+static int
+nv31_bus_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 size,
+             struct nouveau_object **pobject)
+{
+       struct nv31_bus_priv *priv;
+       int ret;
+
+       ret = nouveau_bus_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       nv_subdev(priv)->intr = nv31_bus_intr;
+       return 0;
+}
+
+struct nouveau_oclass
+nv31_bus_oclass = {
+       .handle = NV_SUBDEV(BUS, 0x31),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv31_bus_ctor,
+               .dtor = _nouveau_bus_dtor,
+               .init = nv31_bus_init,
+               .fini = _nouveau_bus_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bus/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/bus/nv50.c
new file mode 100644 (file)
index 0000000..f5b2117
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2012 Nouveau Community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres <martin.peres@labri.fr>
+ *          Ben Skeggs
+ */
+
+#include <subdev/bus.h>
+
+struct nv50_bus_priv {
+       struct nouveau_bus base;
+};
+
+static void
+nv50_bus_intr(struct nouveau_subdev *subdev)
+{
+       struct nouveau_bus *pbus = nouveau_bus(subdev);
+       u32 stat = nv_rd32(pbus, 0x001100) & nv_rd32(pbus, 0x001140);
+
+       if (stat & 0x00000008) {
+               u32 addr = nv_rd32(pbus, 0x009084);
+               u32 data = nv_rd32(pbus, 0x009088);
+
+               nv_error(pbus, "MMIO %s of 0x%08x FAULT at 0x%06x\n",
+                        (addr & 0x00000002) ? "write" : "read", data,
+                        (addr & 0x00fffffc));
+
+               stat &= ~0x00000008;
+               nv_wr32(pbus, 0x001100, 0x00000008);
+       }
+
+       if (stat & 0x00010000) {
+               subdev = nouveau_subdev(pbus, NVDEV_SUBDEV_THERM);
+               if (subdev && subdev->intr)
+                       subdev->intr(subdev);
+               stat &= ~0x00010000;
+               nv_wr32(pbus, 0x001100, 0x00010000);
+       }
+
+       if (stat) {
+               nv_error(pbus, "unknown intr 0x%08x\n", stat);
+               nv_mask(pbus, 0x001140, stat, 0);
+       }
+}
+
+static int
+nv50_bus_init(struct nouveau_object *object)
+{
+       struct nv50_bus_priv *priv = (void *)object;
+       int ret;
+
+       ret = nouveau_bus_init(&priv->base);
+       if (ret)
+               return ret;
+
+       nv_wr32(priv, 0x001100, 0xffffffff);
+       nv_wr32(priv, 0x001140, 0x00010008);
+       return 0;
+}
+
+static int
+nv50_bus_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 size,
+             struct nouveau_object **pobject)
+{
+       struct nv50_bus_priv *priv;
+       int ret;
+
+       ret = nouveau_bus_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       nv_subdev(priv)->intr = nv50_bus_intr;
+       return 0;
+}
+
+struct nouveau_oclass
+nv50_bus_oclass = {
+       .handle = NV_SUBDEV(BUS, 0x50),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv50_bus_ctor,
+               .dtor = _nouveau_bus_dtor,
+               .init = nv50_bus_init,
+               .fini = _nouveau_bus_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bus/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/bus/nvc0.c
new file mode 100644 (file)
index 0000000..b192d62
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2012 Nouveau Community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres <martin.peres@labri.fr>
+ *          Ben Skeggs
+ */
+
+#include <subdev/bus.h>
+
+struct nvc0_bus_priv {
+       struct nouveau_bus base;
+};
+
+static void
+nvc0_bus_intr(struct nouveau_subdev *subdev)
+{
+       struct nouveau_bus *pbus = nouveau_bus(subdev);
+       u32 stat = nv_rd32(pbus, 0x001100) & nv_rd32(pbus, 0x001140);
+
+       if (stat & 0x0000000e) {
+               u32 addr = nv_rd32(pbus, 0x009084);
+               u32 data = nv_rd32(pbus, 0x009088);
+
+               nv_error(pbus, "MMIO %s of 0x%08x FAULT at 0x%06x [ %s%s%s]\n",
+                        (addr & 0x00000002) ? "write" : "read", data,
+                        (addr & 0x00fffffc),
+                        (stat & 0x00000002) ? "!ENGINE " : "",
+                        (stat & 0x00000004) ? "IBUS " : "",
+                        (stat & 0x00000008) ? "TIMEOUT " : "");
+
+               nv_wr32(pbus, 0x009084, 0x00000000);
+               nv_wr32(pbus, 0x001100, (stat & 0x0000000e));
+               stat &= ~0x0000000e;
+       }
+
+       if (stat) {
+               nv_error(pbus, "unknown intr 0x%08x\n", stat);
+               nv_mask(pbus, 0x001140, stat, 0x00000000);
+       }
+}
+
+static int
+nvc0_bus_init(struct nouveau_object *object)
+{
+       struct nvc0_bus_priv *priv = (void *)object;
+       int ret;
+
+       ret = nouveau_bus_init(&priv->base);
+       if (ret)
+               return ret;
+
+       nv_wr32(priv, 0x001100, 0xffffffff);
+       nv_wr32(priv, 0x001140, 0x0000000e);
+       return 0;
+}
+
+static int
+nvc0_bus_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 size,
+             struct nouveau_object **pobject)
+{
+       struct nvc0_bus_priv *priv;
+       int ret;
+
+       ret = nouveau_bus_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       nv_subdev(priv)->intr = nvc0_bus_intr;
+       return 0;
+}
+
+struct nouveau_oclass
+nvc0_bus_oclass = {
+       .handle = NV_SUBDEV(BUS, 0xc0),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_bus_ctor,
+               .dtor = _nouveau_bus_dtor,
+               .init = nvc0_bus_init,
+               .fini = _nouveau_bus_fini,
+       },
+};
index f8a7ed4166cf0b3cea80ecddb94bd0197dd1b362..3937ced5c75310ce7978aa606e4a5a6afae5d1eb 100644 (file)
@@ -66,6 +66,7 @@ static const u64 disable_map[] = {
        [NVDEV_SUBDEV_CLOCK]    = NV_DEVICE_DISABLE_CORE,
        [NVDEV_SUBDEV_MXM]      = NV_DEVICE_DISABLE_CORE,
        [NVDEV_SUBDEV_MC]       = NV_DEVICE_DISABLE_CORE,
+       [NVDEV_SUBDEV_BUS]      = NV_DEVICE_DISABLE_CORE,
        [NVDEV_SUBDEV_TIMER]    = NV_DEVICE_DISABLE_CORE,
        [NVDEV_SUBDEV_FB]       = NV_DEVICE_DISABLE_CORE,
        [NVDEV_SUBDEV_LTCG]     = NV_DEVICE_DISABLE_CORE,
@@ -103,8 +104,8 @@ nouveau_devobj_ctor(struct nouveau_object *parent,
        struct nouveau_device *device;
        struct nouveau_devobj *devobj;
        struct nv_device_class *args = data;
-       u64 disable, boot0, strap;
-       u64 mmio_base, mmio_size;
+       u32 boot0, strap;
+       u64 disable, mmio_base, mmio_size;
        void __iomem *map;
        int ret, i, c;
 
index 8626d0d6cbbcc1d5a3cf6de4d4144744f295b170..473c5c03d3c97189456fed469886d8fd1e0323cd 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <subdev/device.h>
 #include <subdev/bios.h>
+#include <subdev/bus.h>
 #include <subdev/i2c.h>
 #include <subdev/clock.h>
 #include <subdev/devinit.h>
@@ -46,10 +47,11 @@ nv04_identify(struct nouveau_device *device)
        case 0x04:
                device->cname = "NV04";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv04_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv04_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -63,10 +65,11 @@ nv04_identify(struct nouveau_device *device)
        case 0x05:
                device->cname = "NV05";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv05_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv04_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
index 9c40b0fb23f62a67737159491281eef02f6ba329..d0774f5bebe12e2b4cb4385ea55901bf414c0005 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <subdev/device.h>
 #include <subdev/bios.h>
+#include <subdev/bus.h>
 #include <subdev/gpio.h>
 #include <subdev/i2c.h>
 #include <subdev/clock.h>
@@ -48,10 +49,11 @@ nv10_identify(struct nouveau_device *device)
                device->cname = "NV10";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -64,10 +66,11 @@ nv10_identify(struct nouveau_device *device)
                device->cname = "NV15";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -82,10 +85,11 @@ nv10_identify(struct nouveau_device *device)
                device->cname = "NV16";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -100,10 +104,11 @@ nv10_identify(struct nouveau_device *device)
                device->cname = "nForce";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv1a_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -118,10 +123,11 @@ nv10_identify(struct nouveau_device *device)
                device->cname = "NV11";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -136,10 +142,11 @@ nv10_identify(struct nouveau_device *device)
                device->cname = "NV17";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -154,10 +161,11 @@ nv10_identify(struct nouveau_device *device)
                device->cname = "nForce2";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv1a_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -172,10 +180,11 @@ nv10_identify(struct nouveau_device *device)
                device->cname = "NV18";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
index 74f88f48e1c2cd53a346b1320ef1815e367c7ead..ab920e0dc45b21d1c1261c9ff190212456b0998b 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <subdev/device.h>
 #include <subdev/bios.h>
+#include <subdev/bus.h>
 #include <subdev/gpio.h>
 #include <subdev/i2c.h>
 #include <subdev/clock.h>
@@ -49,10 +50,11 @@ nv20_identify(struct nouveau_device *device)
                device->cname = "NV20";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv20_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -67,10 +69,11 @@ nv20_identify(struct nouveau_device *device)
                device->cname = "NV25";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv25_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -85,10 +88,11 @@ nv20_identify(struct nouveau_device *device)
                device->cname = "NV28";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv25_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -103,10 +107,11 @@ nv20_identify(struct nouveau_device *device)
                device->cname = "NV2A";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv25_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
index 0ac1b2c4f61d2b16bdde8e855c9aac5bfb56fee5..5f2110261b04c4ad0e253cf56a5f1cb8b2635c9b 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <subdev/device.h>
 #include <subdev/bios.h>
+#include <subdev/bus.h>
 #include <subdev/gpio.h>
 #include <subdev/i2c.h>
 #include <subdev/clock.h>
@@ -49,10 +50,11 @@ nv30_identify(struct nouveau_device *device)
                device->cname = "NV30";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv30_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -67,10 +69,11 @@ nv30_identify(struct nouveau_device *device)
                device->cname = "NV35";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv04_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv35_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -85,10 +88,11 @@ nv30_identify(struct nouveau_device *device)
                device->cname = "NV31";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv30_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -104,10 +108,11 @@ nv30_identify(struct nouveau_device *device)
                device->cname = "NV36";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv36_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
@@ -123,10 +128,11 @@ nv30_identify(struct nouveau_device *device)
                device->cname = "NV34";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass;
index 41d59689a021ffd558b59b58704555c555f9ae14..f3d55efe9ac9e281ca5a1e032bd198350c7c9466 100644 (file)
@@ -24,6 +24,8 @@
 
 #include <subdev/device.h>
 #include <subdev/bios.h>
+#include <subdev/bus.h>
+#include <subdev/vm.h>
 #include <subdev/gpio.h>
 #include <subdev/i2c.h>
 #include <subdev/clock.h>
@@ -50,11 +52,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "NV40";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv40_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -70,11 +73,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "NV41";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv41_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -90,11 +94,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "NV42";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv41_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -110,11 +115,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "NV43";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv41_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -130,11 +136,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "NV45";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv40_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -150,11 +157,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "G70";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv47_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -170,11 +178,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "G71";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv49_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -190,11 +199,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "G73";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv49_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -210,11 +220,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "NV44";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv44_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -230,11 +241,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "G72";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv46_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -250,11 +262,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "NV44A";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv44_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -270,11 +283,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "C61";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv46_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -290,11 +304,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "C51";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv4e_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv4e_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -310,11 +325,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "C73";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv46_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -330,11 +346,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "C67";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv46_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
@@ -350,11 +367,12 @@ nv40_identify(struct nouveau_device *device)
                device->cname = "C68";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv31_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv46_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass;
index 6ccfd8585ba2620af1e44b3ea9076db17d49a8bd..5ed2fa51ddc2aea82062e1ede5bddd48efc65e06 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <subdev/device.h>
 #include <subdev/bios.h>
+#include <subdev/bus.h>
 #include <subdev/gpio.h>
 #include <subdev/i2c.h>
 #include <subdev/clock.h>
@@ -57,12 +58,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "G80";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv50_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -79,12 +81,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "G84";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv50_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -104,12 +107,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "G86";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv50_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -129,12 +133,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "G92";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv50_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -154,12 +159,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "G94";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -179,12 +185,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "G96";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -204,12 +211,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "G98";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -229,12 +237,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "G200";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv50_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -254,12 +263,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "MCP77/MCP78";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -279,12 +289,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "MCP79/MCP7A";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv50_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -304,12 +315,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "GT215";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nva3_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -330,12 +342,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "GT216";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nva3_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -355,12 +368,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "GT218";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nva3_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
@@ -380,12 +394,13 @@ nv50_identify(struct nouveau_device *device)
                device->cname = "MCP89";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nva3_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
                device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass;
index f0461685a422e50a2db2c49b6e5868cb0c3699ae..4393eb4d6564a8fe0d6f497cdcc39e72eaeb615c 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <subdev/device.h>
 #include <subdev/bios.h>
+#include <subdev/bus.h>
 #include <subdev/gpio.h>
 #include <subdev/i2c.h>
 #include <subdev/clock.h>
@@ -57,12 +58,13 @@ nvc0_identify(struct nouveau_device *device)
                device->cname = "GF100";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
@@ -85,12 +87,13 @@ nvc0_identify(struct nouveau_device *device)
                device->cname = "GF104";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
@@ -113,12 +116,13 @@ nvc0_identify(struct nouveau_device *device)
                device->cname = "GF106";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
@@ -141,12 +145,13 @@ nvc0_identify(struct nouveau_device *device)
                device->cname = "GF114";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
@@ -169,12 +174,13 @@ nvc0_identify(struct nouveau_device *device)
                device->cname = "GF116";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
@@ -197,12 +203,13 @@ nvc0_identify(struct nouveau_device *device)
                device->cname = "GF108";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
@@ -225,12 +232,13 @@ nvc0_identify(struct nouveau_device *device)
                device->cname = "GF110";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
@@ -253,12 +261,13 @@ nvc0_identify(struct nouveau_device *device)
                device->cname = "GF119";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
                device->oclass[NVDEV_SUBDEV_GPIO   ] = &nvd0_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nvd0_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
@@ -282,4 +291,4 @@ nvc0_identify(struct nouveau_device *device)
        }
 
        return 0;
-}
+       }
index 03a652876e731d4e1428b984adb8825223bad053..5c12391619fd309f2be79641d709c9757d18ba1d 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <subdev/device.h>
 #include <subdev/bios.h>
+#include <subdev/bus.h>
 #include <subdev/gpio.h>
 #include <subdev/i2c.h>
 #include <subdev/clock.h>
@@ -56,13 +57,14 @@ nve0_identify(struct nouveau_device *device)
        case 0xe4:
                device->cname = "GK104";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nvd0_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nve0_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nvd0_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
@@ -84,13 +86,14 @@ nve0_identify(struct nouveau_device *device)
        case 0xe7:
                device->cname = "GK107";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nvd0_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nve0_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nvd0_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
@@ -112,13 +115,14 @@ nve0_identify(struct nouveau_device *device)
        case 0xe6:
                device->cname = "GK106";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nvd0_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nouveau_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nve0_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] = &nvd0_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
-               device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
                device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
index ae7249b097971ca4b3ab5eae6d97425014be0cf2..4a85778384172292202cc115f92c9a50132e5c26 100644 (file)
@@ -78,12 +78,13 @@ nv50_devinit_init(struct nouveau_object *object)
        if (ret)
                return ret;
 
-       /* if we ran the init tables, execute first script pointer for each
-        * display table output entry that has a matching dcb entry.
+       /* if we ran the init tables, we have to execute the first script
+        * pointer of each dcb entry's display encoder table in order
+        * to properly initialise each encoder.
         */
-       while (priv->base.post && ver) {
-               u16 data = nvbios_outp_parse(bios, i++, &ver, &hdr, &cnt, &len, &info);
-               if (data && dcb_outp_match(bios, info.type, info.mask, &ver, &len, &outp)) {
+       while (priv->base.post && dcb_outp_parse(bios, i, &ver, &hdr, &outp)) {
+               if (nvbios_outp_match(bios, outp.hasht, outp.hashm,
+                                    &ver, &hdr, &cnt, &len, &info)) {
                        struct nvbios_init init = {
                                .subdev = nv_subdev(priv),
                                .bios = bios,
@@ -95,7 +96,8 @@ nv50_devinit_init(struct nouveau_object *object)
 
                        nvbios_exec(&init);
                }
-       };
+               i++;
+       }
 
        return 0;
 }
index 487cb8c6c204dda270d1957e869b42beae4fdccd..a4338d92b02e45bd2d561c64c50821a3d88d9b7c 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include <core/object.h>
+#include <core/client.h>
 #include <core/enum.h>
+#include <core/engctx.h>
+#include <core/object.h>
 
 #include <subdev/fb.h>
 #include <subdev/bios.h>
@@ -302,17 +304,18 @@ static const struct nouveau_enum vm_client[] = {
 };
 
 static const struct nouveau_enum vm_engine[] = {
-       { 0x00000000, "PGRAPH", NULL },
-       { 0x00000001, "PVP", NULL },
+       { 0x00000000, "PGRAPH", NULL, NVDEV_ENGINE_GR },
+       { 0x00000001, "PVP", NULL, NVDEV_ENGINE_VP },
        { 0x00000004, "PEEPHOLE", NULL },
-       { 0x00000005, "PFIFO", vm_pfifo_subclients },
+       { 0x00000005, "PFIFO", vm_pfifo_subclients, NVDEV_ENGINE_FIFO },
        { 0x00000006, "BAR", vm_bar_subclients },
-       { 0x00000008, "PPPP", NULL },
-       { 0x00000009, "PBSP", NULL },
-       { 0x0000000a, "PCRYPT", NULL },
+       { 0x00000008, "PPPP", NULL, NVDEV_ENGINE_PPP },
+       { 0x00000008, "PMPEG", NULL, NVDEV_ENGINE_MPEG },
+       { 0x00000009, "PBSP", NULL, NVDEV_ENGINE_BSP },
+       { 0x0000000a, "PCRYPT", NULL, NVDEV_ENGINE_CRYPT },
        { 0x0000000b, "PCOUNTER", NULL },
        { 0x0000000c, "SEMAPHORE_BG", NULL },
-       { 0x0000000d, "PCOPY", NULL },
+       { 0x0000000d, "PCOPY", NULL, NVDEV_ENGINE_COPY0 },
        { 0x0000000e, "PDAEMON", NULL },
        {}
 };
@@ -334,8 +337,10 @@ static void
 nv50_fb_intr(struct nouveau_subdev *subdev)
 {
        struct nouveau_device *device = nv_device(subdev);
+       struct nouveau_engine *engine;
        struct nv50_fb_priv *priv = (void *)subdev;
        const struct nouveau_enum *en, *cl;
+       struct nouveau_object *engctx = NULL;
        u32 trap[6], idx, chan;
        u8 st0, st1, st2, st3;
        int i;
@@ -366,36 +371,55 @@ nv50_fb_intr(struct nouveau_subdev *subdev)
        }
        chan = (trap[2] << 16) | trap[1];
 
-       nv_error(priv, "trapped %s at 0x%02x%04x%04x on channel 0x%08x ",
+       en = nouveau_enum_find(vm_engine, st0);
+
+       if (en && en->data2) {
+               const struct nouveau_enum *orig_en = en;
+               while (en->name && en->value == st0 && en->data2) {
+                       engine = nouveau_engine(subdev, en->data2);
+                       if (engine) {
+                               engctx = nouveau_engctx_get(engine, chan);
+                               if (engctx)
+                                       break;
+                       }
+                       en++;
+               }
+               if (!engctx)
+                       en = orig_en;
+       }
+
+       nv_error(priv, "trapped %s at 0x%02x%04x%04x on channel 0x%08x [%s] ",
                 (trap[5] & 0x00000100) ? "read" : "write",
-                trap[5] & 0xff, trap[4] & 0xffff, trap[3] & 0xffff, chan);
+                trap[5] & 0xff, trap[4] & 0xffff, trap[3] & 0xffff, chan,
+                nouveau_client_name(engctx));
+
+       nouveau_engctx_put(engctx);
 
-       en = nouveau_enum_find(vm_engine, st0);
        if (en)
-               printk("%s/", en->name);
+               pr_cont("%s/", en->name);
        else
-               printk("%02x/", st0);
+               pr_cont("%02x/", st0);
 
        cl = nouveau_enum_find(vm_client, st2);
        if (cl)
-               printk("%s/", cl->name);
+               pr_cont("%s/", cl->name);
        else
-               printk("%02x/", st2);
+               pr_cont("%02x/", st2);
 
        if      (cl && cl->data) cl = nouveau_enum_find(cl->data, st3);
        else if (en && en->data) cl = nouveau_enum_find(en->data, st3);
        else                     cl = NULL;
        if (cl)
-               printk("%s", cl->name);
+               pr_cont("%s", cl->name);
        else
-               printk("%02x", st3);
+               pr_cont("%02x", st3);
 
-       printk(" reason: ");
+       pr_cont(" reason: ");
        en = nouveau_enum_find(vm_fault, st1);
        if (en)
-               printk("%s\n", en->name);
+               pr_cont("%s\n", en->name);
        else
-               printk("0x%08x\n", st1);
+               pr_cont("0x%08x\n", st1);
 }
 
 static int
index 9fb0f9b92d49efacf84ae5826b61b4662d59699c..d422acc9af156bc0ab2cdea474d37d0691e4e9ba 100644 (file)
@@ -102,135 +102,19 @@ nouveau_gpio_get(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line)
        return ret;
 }
 
-static int
-nouveau_gpio_irq(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line, bool on)
-{
-       struct dcb_gpio_func func;
-       int ret;
-
-       ret = nouveau_gpio_find(gpio, idx, tag, line, &func);
-       if (ret == 0) {
-               if (idx == 0 && gpio->irq_enable)
-                       gpio->irq_enable(gpio, func.line, on);
-               else
-                       ret = -ENODEV;
-       }
-
-       return ret;
-}
-
-struct gpio_isr {
-       struct nouveau_gpio *gpio;
-       struct list_head head;
-       struct work_struct work;
-       int idx;
-       struct dcb_gpio_func func;
-       void (*handler)(void *, int);
-       void *data;
-       bool inhibit;
-};
-
-static void
-nouveau_gpio_isr_bh(struct work_struct *work)
-{
-       struct gpio_isr *isr = container_of(work, struct gpio_isr, work);
-       struct nouveau_gpio *gpio = isr->gpio;
-       unsigned long flags;
-       int state;
-
-       state = nouveau_gpio_get(gpio, isr->idx, isr->func.func,
-                                                isr->func.line);
-       if (state >= 0)
-               isr->handler(isr->data, state);
-
-       spin_lock_irqsave(&gpio->lock, flags);
-       isr->inhibit = false;
-       spin_unlock_irqrestore(&gpio->lock, flags);
-}
-
-static void
-nouveau_gpio_isr_run(struct nouveau_gpio *gpio, int idx, u32 line_mask)
-{
-       struct gpio_isr *isr;
-
-       if (idx != 0)
-               return;
-
-       spin_lock(&gpio->lock);
-       list_for_each_entry(isr, &gpio->isr, head) {
-               if (line_mask & (1 << isr->func.line)) {
-                       if (isr->inhibit)
-                               continue;
-                       isr->inhibit = true;
-                       schedule_work(&isr->work);
-               }
-       }
-       spin_unlock(&gpio->lock);
-}
-
-static int
-nouveau_gpio_isr_add(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line,
-                    void (*handler)(void *, int), void *data)
-{
-       struct gpio_isr *isr;
-       unsigned long flags;
-       int ret;
-
-       isr = kzalloc(sizeof(*isr), GFP_KERNEL);
-       if (!isr)
-               return -ENOMEM;
-
-       ret = nouveau_gpio_find(gpio, idx, tag, line, &isr->func);
-       if (ret) {
-               kfree(isr);
-               return ret;
-       }
-
-       INIT_WORK(&isr->work, nouveau_gpio_isr_bh);
-       isr->gpio = gpio;
-       isr->handler = handler;
-       isr->data = data;
-       isr->idx = idx;
-
-       spin_lock_irqsave(&gpio->lock, flags);
-       list_add(&isr->head, &gpio->isr);
-       spin_unlock_irqrestore(&gpio->lock, flags);
-       return 0;
-}
-
-static void
-nouveau_gpio_isr_del(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line,
-                    void (*handler)(void *, int), void *data)
+void
+_nouveau_gpio_dtor(struct nouveau_object *object)
 {
-       struct gpio_isr *isr, *tmp;
-       struct dcb_gpio_func func;
-       unsigned long flags;
-       LIST_HEAD(tofree);
-       int ret;
-
-       ret = nouveau_gpio_find(gpio, idx, tag, line, &func);
-       if (ret == 0) {
-               spin_lock_irqsave(&gpio->lock, flags);
-               list_for_each_entry_safe(isr, tmp, &gpio->isr, head) {
-                       if (memcmp(&isr->func, &func, sizeof(func)) ||
-                           isr->idx != idx ||
-                           isr->handler != handler || isr->data != data)
-                               continue;
-                       list_move_tail(&isr->head, &tofree);
-               }
-               spin_unlock_irqrestore(&gpio->lock, flags);
-
-               list_for_each_entry_safe(isr, tmp, &tofree, head) {
-                       flush_work(&isr->work);
-                       kfree(isr);
-               }
-       }
+       struct nouveau_gpio *gpio = (void *)object;
+       nouveau_event_destroy(&gpio->events);
+       nouveau_subdev_destroy(&gpio->base);
 }
 
 int
 nouveau_gpio_create_(struct nouveau_object *parent,
                     struct nouveau_object *engine,
-                    struct nouveau_oclass *oclass, int length, void **pobject)
+                    struct nouveau_oclass *oclass, int lines,
+                    int length, void **pobject)
 {
        struct nouveau_gpio *gpio;
        int ret;
@@ -241,15 +125,13 @@ nouveau_gpio_create_(struct nouveau_object *parent,
        if (ret)
                return ret;
 
+       ret = nouveau_event_create(lines, &gpio->events);
+       if (ret)
+               return ret;
+
        gpio->find = nouveau_gpio_find;
        gpio->set  = nouveau_gpio_set;
        gpio->get  = nouveau_gpio_get;
-       gpio->irq  = nouveau_gpio_irq;
-       gpio->isr_run = nouveau_gpio_isr_run;
-       gpio->isr_add = nouveau_gpio_isr_add;
-       gpio->isr_del = nouveau_gpio_isr_del;
-       INIT_LIST_HEAD(&gpio->isr);
-       spin_lock_init(&gpio->lock);
        return 0;
 }
 
index 168d16a9a8e9ebe8c9b876c20d0c79bfb12037dd..76d5d5465ddd2c541c7427508f0046e429109ea1 100644 (file)
@@ -24,7 +24,7 @@
  *
  */
 
-#include <subdev/gpio.h>
+#include "priv.h"
 
 struct nv10_gpio_priv {
        struct nouveau_gpio base;
@@ -82,15 +82,6 @@ nv10_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out)
        return 0;
 }
 
-static void
-nv10_gpio_irq_enable(struct nouveau_gpio *gpio, int line, bool on)
-{
-       u32 mask = 0x00010001 << line;
-
-       nv_wr32(gpio, 0x001104, mask);
-       nv_mask(gpio, 0x001144, mask, on ? mask : 0);
-}
-
 static void
 nv10_gpio_intr(struct nouveau_subdev *subdev)
 {
@@ -98,12 +89,30 @@ nv10_gpio_intr(struct nouveau_subdev *subdev)
        u32 intr = nv_rd32(priv, 0x001104);
        u32 hi = (intr & 0x0000ffff) >> 0;
        u32 lo = (intr & 0xffff0000) >> 16;
+       int i;
 
-       priv->base.isr_run(&priv->base, 0, hi | lo);
+       for (i = 0; (hi | lo) && i < 32; i++) {
+               if ((hi | lo) & (1 << i))
+                       nouveau_event_trigger(priv->base.events, i);
+       }
 
        nv_wr32(priv, 0x001104, intr);
 }
 
+static void
+nv10_gpio_intr_enable(struct nouveau_event *event, int line)
+{
+       nv_wr32(event->priv, 0x001104, 0x00010001 << line);
+       nv_mask(event->priv, 0x001144, 0x00010001 << line, 0x00010001 << line);
+}
+
+static void
+nv10_gpio_intr_disable(struct nouveau_event *event, int line)
+{
+       nv_wr32(event->priv, 0x001104, 0x00010001 << line);
+       nv_mask(event->priv, 0x001144, 0x00010001 << line, 0x00000000);
+}
+
 static int
 nv10_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
               struct nouveau_oclass *oclass, void *data, u32 size,
@@ -112,14 +121,16 @@ nv10_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv10_gpio_priv *priv;
        int ret;
 
-       ret = nouveau_gpio_create(parent, engine, oclass, &priv);
+       ret = nouveau_gpio_create(parent, engine, oclass, 16, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
 
        priv->base.drive = nv10_gpio_drive;
        priv->base.sense = nv10_gpio_sense;
-       priv->base.irq_enable = nv10_gpio_irq_enable;
+       priv->base.events->priv = priv;
+       priv->base.events->enable = nv10_gpio_intr_enable;
+       priv->base.events->disable = nv10_gpio_intr_disable;
        nv_subdev(priv)->intr = nv10_gpio_intr;
        return 0;
 }
@@ -141,8 +152,6 @@ nv10_gpio_init(struct nouveau_object *object)
        if (ret)
                return ret;
 
-       nv_wr32(priv, 0x001140, 0x00000000);
-       nv_wr32(priv, 0x001100, 0xffffffff);
        nv_wr32(priv, 0x001144, 0x00000000);
        nv_wr32(priv, 0x001104, 0xffffffff);
        return 0;
@@ -152,7 +161,6 @@ static int
 nv10_gpio_fini(struct nouveau_object *object, bool suspend)
 {
        struct nv10_gpio_priv *priv = (void *)object;
-       nv_wr32(priv, 0x001140, 0x00000000);
        nv_wr32(priv, 0x001144, 0x00000000);
        return nouveau_gpio_fini(&priv->base, suspend);
 }
index bf13a1200f26188935d22dd8183855129a51e187..bf489dcf46e2796fed5e0e1b1f109ae2dcfe015b 100644 (file)
@@ -22,7 +22,7 @@
  * Authors: Ben Skeggs
  */
 
-#include <subdev/gpio.h>
+#include "priv.h"
 
 struct nv50_gpio_priv {
        struct nouveau_gpio base;
@@ -94,22 +94,13 @@ nv50_gpio_sense(struct nouveau_gpio *gpio, int line)
        return !!(nv_rd32(gpio, reg) & (4 << shift));
 }
 
-void
-nv50_gpio_irq_enable(struct nouveau_gpio *gpio, int line, bool on)
-{
-       u32 reg  = line < 16 ? 0xe050 : 0xe070;
-       u32 mask = 0x00010001 << (line & 0xf);
-
-       nv_wr32(gpio, reg + 4, mask);
-       nv_mask(gpio, reg + 0, mask, on ? mask : 0);
-}
-
 void
 nv50_gpio_intr(struct nouveau_subdev *subdev)
 {
        struct nv50_gpio_priv *priv = (void *)subdev;
        u32 intr0, intr1 = 0;
        u32 hi, lo;
+       int i;
 
        intr0 = nv_rd32(priv, 0xe054) & nv_rd32(priv, 0xe050);
        if (nv_device(priv)->chipset >= 0x90)
@@ -117,13 +108,35 @@ nv50_gpio_intr(struct nouveau_subdev *subdev)
 
        hi = (intr0 & 0x0000ffff) | (intr1 << 16);
        lo = (intr0 >> 16) | (intr1 & 0xffff0000);
-       priv->base.isr_run(&priv->base, 0, hi | lo);
+
+       for (i = 0; (hi | lo) && i < 32; i++) {
+               if ((hi | lo) & (1 << i))
+                       nouveau_event_trigger(priv->base.events, i);
+       }
 
        nv_wr32(priv, 0xe054, intr0);
        if (nv_device(priv)->chipset >= 0x90)
                nv_wr32(priv, 0xe074, intr1);
 }
 
+void
+nv50_gpio_intr_enable(struct nouveau_event *event, int line)
+{
+       const u32 addr = line < 16 ? 0xe050 : 0xe070;
+       const u32 mask = 0x00010001 << (line & 0xf);
+       nv_wr32(event->priv, addr + 0x04, mask);
+       nv_mask(event->priv, addr + 0x00, mask, mask);
+}
+
+void
+nv50_gpio_intr_disable(struct nouveau_event *event, int line)
+{
+       const u32 addr = line < 16 ? 0xe050 : 0xe070;
+       const u32 mask = 0x00010001 << (line & 0xf);
+       nv_wr32(event->priv, addr + 0x04, mask);
+       nv_mask(event->priv, addr + 0x00, mask, 0x00000000);
+}
+
 static int
 nv50_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
               struct nouveau_oclass *oclass, void *data, u32 size,
@@ -132,7 +145,9 @@ nv50_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv50_gpio_priv *priv;
        int ret;
 
-       ret = nouveau_gpio_create(parent, engine, oclass, &priv);
+       ret = nouveau_gpio_create(parent, engine, oclass,
+                                 nv_device(parent)->chipset >= 0x90 ? 32 : 16,
+                                 &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
@@ -140,7 +155,9 @@ nv50_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        priv->base.reset = nv50_gpio_reset;
        priv->base.drive = nv50_gpio_drive;
        priv->base.sense = nv50_gpio_sense;
-       priv->base.irq_enable = nv50_gpio_irq_enable;
+       priv->base.events->priv = priv;
+       priv->base.events->enable = nv50_gpio_intr_enable;
+       priv->base.events->disable = nv50_gpio_intr_disable;
        nv_subdev(priv)->intr = nv50_gpio_intr;
        return 0;
 }
index 83e8b8f16e6ad1f3ad4a3ba5e8676ff078107c69..010431e3acecef1198c7533685e1427bb6362456 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include <subdev/gpio.h>
+#include "priv.h"
 
 struct nvd0_gpio_priv {
        struct nouveau_gpio base;
 };
 
-static void
+void
 nvd0_gpio_reset(struct nouveau_gpio *gpio, u8 match)
 {
        struct nouveau_bios *bios = nouveau_bios(gpio);
@@ -57,7 +57,7 @@ nvd0_gpio_reset(struct nouveau_gpio *gpio, u8 match)
        }
 }
 
-static int
+int
 nvd0_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out)
 {
        u32 data = ((dir ^ 1) << 13) | (out << 12);
@@ -66,7 +66,7 @@ nvd0_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out)
        return 0;
 }
 
-static int
+int
 nvd0_gpio_sense(struct nouveau_gpio *gpio, int line)
 {
        return !!(nv_rd32(gpio, 0x00d610 + (line * 4)) & 0x00004000);
@@ -80,7 +80,7 @@ nvd0_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nvd0_gpio_priv *priv;
        int ret;
 
-       ret = nouveau_gpio_create(parent, engine, oclass, &priv);
+       ret = nouveau_gpio_create(parent, engine, oclass, 32, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
@@ -88,7 +88,9 @@ nvd0_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        priv->base.reset = nvd0_gpio_reset;
        priv->base.drive = nvd0_gpio_drive;
        priv->base.sense = nvd0_gpio_sense;
-       priv->base.irq_enable = nv50_gpio_irq_enable;
+       priv->base.events->priv = priv;
+       priv->base.events->enable = nv50_gpio_intr_enable;
+       priv->base.events->disable = nv50_gpio_intr_disable;
        nv_subdev(priv)->intr = nv50_gpio_intr;
        return 0;
 }
diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c
new file mode 100644 (file)
index 0000000..16b8c5b
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "priv.h"
+
+struct nve0_gpio_priv {
+       struct nouveau_gpio base;
+};
+
+void
+nve0_gpio_intr(struct nouveau_subdev *subdev)
+{
+       struct nve0_gpio_priv *priv = (void *)subdev;
+       u32 intr0 = nv_rd32(priv, 0xdc00) & nv_rd32(priv, 0xdc08);
+       u32 intr1 = nv_rd32(priv, 0xdc80) & nv_rd32(priv, 0xdc88);
+       u32 hi = (intr0 & 0x0000ffff) | (intr1 << 16);
+       u32 lo = (intr0 >> 16) | (intr1 & 0xffff0000);
+       int i;
+
+       for (i = 0; (hi | lo) && i < 32; i++) {
+               if ((hi | lo) & (1 << i))
+                       nouveau_event_trigger(priv->base.events, i);
+       }
+
+       nv_wr32(priv, 0xdc00, intr0);
+       nv_wr32(priv, 0xdc88, intr1);
+}
+
+void
+nve0_gpio_intr_enable(struct nouveau_event *event, int line)
+{
+       const u32 addr = line < 16 ? 0xdc00 : 0xdc80;
+       const u32 mask = 0x00010001 << (line & 0xf);
+       nv_wr32(event->priv, addr + 0x08, mask);
+       nv_mask(event->priv, addr + 0x00, mask, mask);
+}
+
+void
+nve0_gpio_intr_disable(struct nouveau_event *event, int line)
+{
+       const u32 addr = line < 16 ? 0xdc00 : 0xdc80;
+       const u32 mask = 0x00010001 << (line & 0xf);
+       nv_wr32(event->priv, addr + 0x08, mask);
+       nv_mask(event->priv, addr + 0x00, mask, 0x00000000);
+}
+
+int
+nve0_gpio_fini(struct nouveau_object *object, bool suspend)
+{
+       struct nve0_gpio_priv *priv = (void *)object;
+       nv_wr32(priv, 0xdc08, 0x00000000);
+       nv_wr32(priv, 0xdc88, 0x00000000);
+       return nouveau_gpio_fini(&priv->base, suspend);
+}
+
+int
+nve0_gpio_init(struct nouveau_object *object)
+{
+       struct nve0_gpio_priv *priv = (void *)object;
+       int ret;
+
+       ret = nouveau_gpio_init(&priv->base);
+       if (ret)
+               return ret;
+
+       nv_wr32(priv, 0xdc00, 0xffffffff);
+       nv_wr32(priv, 0xdc80, 0xffffffff);
+       return 0;
+}
+
+void
+nve0_gpio_dtor(struct nouveau_object *object)
+{
+       struct nve0_gpio_priv *priv = (void *)object;
+       nouveau_gpio_destroy(&priv->base);
+}
+
+static int
+nve0_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+              struct nouveau_oclass *oclass, void *data, u32 size,
+              struct nouveau_object **pobject)
+{
+       struct nve0_gpio_priv *priv;
+       int ret;
+
+       ret = nouveau_gpio_create(parent, engine, oclass, 32, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       priv->base.reset = nvd0_gpio_reset;
+       priv->base.drive = nvd0_gpio_drive;
+       priv->base.sense = nvd0_gpio_sense;
+       priv->base.events->priv = priv;
+       priv->base.events->enable = nve0_gpio_intr_enable;
+       priv->base.events->disable = nve0_gpio_intr_disable;
+       nv_subdev(priv)->intr = nve0_gpio_intr;
+       return 0;
+}
+
+struct nouveau_oclass
+nve0_gpio_oclass = {
+       .handle = NV_SUBDEV(GPIO, 0xe0),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nve0_gpio_ctor,
+               .dtor = nv50_gpio_dtor,
+               .init = nve0_gpio_init,
+               .fini = nve0_gpio_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h b/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h
new file mode 100644 (file)
index 0000000..2ee1c89
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef __NVKM_GPIO_H__
+#define __NVKM_GPIO_H__
+
+#include <subdev/gpio.h>
+
+void nv50_gpio_dtor(struct nouveau_object *);
+int  nv50_gpio_init(struct nouveau_object *);
+int  nv50_gpio_fini(struct nouveau_object *, bool);
+void nv50_gpio_intr(struct nouveau_subdev *);
+void nv50_gpio_intr_enable(struct nouveau_event *, int line);
+void nv50_gpio_intr_disable(struct nouveau_event *, int line);
+
+void nvd0_gpio_reset(struct nouveau_gpio *, u8);
+int  nvd0_gpio_drive(struct nouveau_gpio *, int, int, int);
+int  nvd0_gpio_sense(struct nouveau_gpio *, int);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c
new file mode 100644 (file)
index 0000000..dec94e9
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include <subdev/i2c.h>
+
+struct anx9805_i2c_port {
+       struct nouveau_i2c_port base;
+       u32 addr;
+       u32 ctrl;
+};
+
+static int
+anx9805_train(struct nouveau_i2c_port *port, int link_nr, int link_bw, bool enh)
+{
+       struct anx9805_i2c_port *chan = (void *)port;
+       struct nouveau_i2c_port *mast = (void *)nv_object(chan)->parent;
+       u8 tmp, i;
+
+       nv_wri2cr(mast, chan->addr, 0xa0, link_bw);
+       nv_wri2cr(mast, chan->addr, 0xa1, link_nr | (enh ? 0x80 : 0x00));
+       nv_wri2cr(mast, chan->addr, 0xa2, 0x01);
+       nv_wri2cr(mast, chan->addr, 0xa8, 0x01);
+
+       i = 0;
+       while ((tmp = nv_rdi2cr(mast, chan->addr, 0xa8)) & 0x01) {
+               mdelay(5);
+               if (i++ == 100) {
+                       nv_error(port, "link training timed out\n");
+                       return -ETIMEDOUT;
+               }
+       }
+
+       if (tmp & 0x70) {
+               nv_error(port, "link training failed: 0x%02x\n", tmp);
+               return -EIO;
+       }
+
+       return 1;
+}
+
+static int
+anx9805_aux(struct nouveau_i2c_port *port, u8 type, u32 addr, u8 *data, u8 size)
+{
+       struct anx9805_i2c_port *chan = (void *)port;
+       struct nouveau_i2c_port *mast = (void *)nv_object(chan)->parent;
+       int i, ret = -ETIMEDOUT;
+       u8 tmp;
+
+       tmp = nv_rdi2cr(mast, chan->ctrl, 0x07) & ~0x04;
+       nv_wri2cr(mast, chan->ctrl, 0x07, tmp | 0x04);
+       nv_wri2cr(mast, chan->ctrl, 0x07, tmp);
+       nv_wri2cr(mast, chan->ctrl, 0xf7, 0x01);
+
+       nv_wri2cr(mast, chan->addr, 0xe4, 0x80);
+       for (i = 0; !(type & 1) && i < size; i++)
+               nv_wri2cr(mast, chan->addr, 0xf0 + i, data[i]);
+       nv_wri2cr(mast, chan->addr, 0xe5, ((size - 1) << 4) | type);
+       nv_wri2cr(mast, chan->addr, 0xe6, (addr & 0x000ff) >>  0);
+       nv_wri2cr(mast, chan->addr, 0xe7, (addr & 0x0ff00) >>  8);
+       nv_wri2cr(mast, chan->addr, 0xe8, (addr & 0xf0000) >> 16);
+       nv_wri2cr(mast, chan->addr, 0xe9, 0x01);
+
+       i = 0;
+       while ((tmp = nv_rdi2cr(mast, chan->addr, 0xe9)) & 0x01) {
+               mdelay(5);
+               if (i++ == 32)
+                       goto done;
+       }
+
+       if ((tmp = nv_rdi2cr(mast, chan->ctrl, 0xf7)) & 0x01) {
+               ret = -EIO;
+               goto done;
+       }
+
+       for (i = 0; (type & 1) && i < size; i++)
+               data[i] = nv_rdi2cr(mast, chan->addr, 0xf0 + i);
+       ret = 0;
+done:
+       nv_wri2cr(mast, chan->ctrl, 0xf7, 0x01);
+       return ret;
+}
+
+static const struct nouveau_i2c_func
+anx9805_aux_func = {
+       .aux = anx9805_aux,
+       .lnk_ctl = anx9805_train,
+};
+
+static int
+anx9805_aux_chan_ctor(struct nouveau_object *parent,
+                     struct nouveau_object *engine,
+                     struct nouveau_oclass *oclass, void *data, u32 index,
+                     struct nouveau_object **pobject)
+{
+       struct nouveau_i2c_port *mast = (void *)parent;
+       struct anx9805_i2c_port *chan;
+       int ret;
+
+       ret = nouveau_i2c_port_create(parent, engine, oclass, index,
+                                    &nouveau_i2c_aux_algo, &chan);
+       *pobject = nv_object(chan);
+       if (ret)
+               return ret;
+
+       switch ((oclass->handle & 0xff00) >> 8) {
+       case 0x0d:
+               chan->addr = 0x38;
+               chan->ctrl = 0x39;
+               break;
+       case 0x0e:
+               chan->addr = 0x3c;
+               chan->ctrl = 0x3b;
+               break;
+       default:
+               BUG_ON(1);
+       }
+
+       if (mast->adapter.algo == &i2c_bit_algo) {
+               struct i2c_algo_bit_data *algo = mast->adapter.algo_data;
+               algo->udelay = max(algo->udelay, 40);
+       }
+
+       chan->base.func = &anx9805_aux_func;
+       return 0;
+}
+
+static struct nouveau_ofuncs
+anx9805_aux_ofuncs = {
+       .ctor =  anx9805_aux_chan_ctor,
+       .dtor = _nouveau_i2c_port_dtor,
+       .init = _nouveau_i2c_port_init,
+       .fini = _nouveau_i2c_port_fini,
+};
+
+static int
+anx9805_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+       struct anx9805_i2c_port *port = adap->algo_data;
+       struct nouveau_i2c_port *mast = (void *)nv_object(port)->parent;
+       struct i2c_msg *msg = msgs;
+       int ret = -ETIMEDOUT;
+       int i, j, cnt = num;
+       u8 seg = 0x00, off = 0x00, tmp;
+
+       tmp = nv_rdi2cr(mast, port->ctrl, 0x07) & ~0x10;
+       nv_wri2cr(mast, port->ctrl, 0x07, tmp | 0x10);
+       nv_wri2cr(mast, port->ctrl, 0x07, tmp);
+       nv_wri2cr(mast, port->addr, 0x43, 0x05);
+       mdelay(5);
+
+       while (cnt--) {
+               if ( (msg->flags & I2C_M_RD) && msg->addr == 0x50) {
+                       nv_wri2cr(mast, port->addr, 0x40, msg->addr << 1);
+                       nv_wri2cr(mast, port->addr, 0x41, seg);
+                       nv_wri2cr(mast, port->addr, 0x42, off);
+                       nv_wri2cr(mast, port->addr, 0x44, msg->len);
+                       nv_wri2cr(mast, port->addr, 0x45, 0x00);
+                       nv_wri2cr(mast, port->addr, 0x43, 0x01);
+                       for (i = 0; i < msg->len; i++) {
+                               j = 0;
+                               while (nv_rdi2cr(mast, port->addr, 0x46) & 0x10) {
+                                       mdelay(5);
+                                       if (j++ == 32)
+                                               goto done;
+                               }
+                               msg->buf[i] = nv_rdi2cr(mast, port->addr, 0x47);
+                       }
+               } else
+               if (!(msg->flags & I2C_M_RD)) {
+                       if (msg->addr == 0x50 && msg->len == 0x01) {
+                               off = msg->buf[0];
+                       } else
+                       if (msg->addr == 0x30 && msg->len == 0x01) {
+                               seg = msg->buf[0];
+                       } else
+                               goto done;
+               } else {
+                       goto done;
+               }
+               msg++;
+       }
+
+       ret = num;
+done:
+       nv_wri2cr(mast, port->addr, 0x43, 0x00);
+       return ret;
+}
+
+static u32
+anx9805_func(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm
+anx9805_i2c_algo = {
+       .master_xfer = anx9805_xfer,
+       .functionality = anx9805_func
+};
+
+static const struct nouveau_i2c_func
+anx9805_i2c_func = {
+};
+
+static int
+anx9805_ddc_port_ctor(struct nouveau_object *parent,
+                     struct nouveau_object *engine,
+                     struct nouveau_oclass *oclass, void *data, u32 index,
+                     struct nouveau_object **pobject)
+{
+       struct nouveau_i2c_port *mast = (void *)parent;
+       struct anx9805_i2c_port *port;
+       int ret;
+
+       ret = nouveau_i2c_port_create(parent, engine, oclass, index,
+                                    &anx9805_i2c_algo, &port);
+       *pobject = nv_object(port);
+       if (ret)
+               return ret;
+
+       switch ((oclass->handle & 0xff00) >> 8) {
+       case 0x0d:
+               port->addr = 0x3d;
+               port->ctrl = 0x39;
+               break;
+       case 0x0e:
+               port->addr = 0x3f;
+               port->ctrl = 0x3b;
+               break;
+       default:
+               BUG_ON(1);
+       }
+
+       if (mast->adapter.algo == &i2c_bit_algo) {
+               struct i2c_algo_bit_data *algo = mast->adapter.algo_data;
+               algo->udelay = max(algo->udelay, 40);
+       }
+
+       port->base.func = &anx9805_i2c_func;
+       return 0;
+}
+
+static struct nouveau_ofuncs
+anx9805_ddc_ofuncs = {
+       .ctor =  anx9805_ddc_port_ctor,
+       .dtor = _nouveau_i2c_port_dtor,
+       .init = _nouveau_i2c_port_init,
+       .fini = _nouveau_i2c_port_fini,
+};
+
+struct nouveau_oclass
+nouveau_anx9805_sclass[] = {
+       { .handle = NV_I2C_TYPE_EXTDDC(0x0d), .ofuncs = &anx9805_ddc_ofuncs },
+       { .handle = NV_I2C_TYPE_EXTAUX(0x0d), .ofuncs = &anx9805_aux_ofuncs },
+       { .handle = NV_I2C_TYPE_EXTDDC(0x0e), .ofuncs = &anx9805_ddc_ofuncs },
+       { .handle = NV_I2C_TYPE_EXTAUX(0x0e), .ofuncs = &anx9805_aux_ofuncs },
+       {}
+};
index dc27e794a8515c978dc779a14ba420981d93d210..5de074ad170b42ae37722ba8975018428b59a485 100644 (file)
 
 #include <subdev/i2c.h>
 
-/******************************************************************************
- * aux channel util functions
- *****************************************************************************/
-#define AUX_DBG(fmt, args...) nv_debug(aux, "AUXCH(%d): " fmt, ch, ##args)
-#define AUX_ERR(fmt, args...) nv_error(aux, "AUXCH(%d): " fmt, ch, ##args)
-
-static void
-auxch_fini(struct nouveau_i2c *aux, int ch)
-{
-       nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00310000, 0x00000000);
-}
-
-static int
-auxch_init(struct nouveau_i2c *aux, int ch)
-{
-       const u32 unksel = 1; /* nfi which to use, or if it matters.. */
-       const u32 ureq = unksel ? 0x00100000 : 0x00200000;
-       const u32 urep = unksel ? 0x01000000 : 0x02000000;
-       u32 ctrl, timeout;
-
-       /* wait up to 1ms for any previous transaction to be done... */
-       timeout = 1000;
-       do {
-               ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
-               udelay(1);
-               if (!timeout--) {
-                       AUX_ERR("begin idle timeout 0x%08x\n", ctrl);
-                       return -EBUSY;
-               }
-       } while (ctrl & 0x03010000);
-
-       /* set some magic, and wait up to 1ms for it to appear */
-       nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00300000, ureq);
-       timeout = 1000;
-       do {
-               ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
-               udelay(1);
-               if (!timeout--) {
-                       AUX_ERR("magic wait 0x%08x\n", ctrl);
-                       auxch_fini(aux, ch);
-                       return -EBUSY;
-               }
-       } while ((ctrl & 0x03000000) != urep);
-
-       return 0;
-}
-
-static int
-auxch_tx(struct nouveau_i2c *aux, int ch, u8 type, u32 addr, u8 *data, u8 size)
-{
-       u32 ctrl, stat, timeout, retries;
-       u32 xbuf[4] = {};
-       int ret, i;
-
-       AUX_DBG("%d: 0x%08x %d\n", type, addr, size);
-
-       ret = auxch_init(aux, ch);
-       if (ret)
-               goto out;
-
-       stat = nv_rd32(aux, 0x00e4e8 + (ch * 0x50));
-       if (!(stat & 0x10000000)) {
-               AUX_DBG("sink not detected\n");
-               ret = -ENXIO;
-               goto out;
-       }
-
-       if (!(type & 1)) {
-               memcpy(xbuf, data, size);
-               for (i = 0; i < 16; i += 4) {
-                       AUX_DBG("wr 0x%08x\n", xbuf[i / 4]);
-                       nv_wr32(aux, 0x00e4c0 + (ch * 0x50) + i, xbuf[i / 4]);
-               }
-       }
-
-       ctrl  = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
-       ctrl &= ~0x0001f0ff;
-       ctrl |= type << 12;
-       ctrl |= size - 1;
-       nv_wr32(aux, 0x00e4e0 + (ch * 0x50), addr);
-
-       /* retry transaction a number of times on failure... */
-       ret = -EREMOTEIO;
-       for (retries = 0; retries < 32; retries++) {
-               /* reset, and delay a while if this is a retry */
-               nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x80000000 | ctrl);
-               nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00000000 | ctrl);
-               if (retries)
-                       udelay(400);
-
-               /* transaction request, wait up to 1ms for it to complete */
-               nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00010000 | ctrl);
-
-               timeout = 1000;
-               do {
-                       ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
-                       udelay(1);
-                       if (!timeout--) {
-                               AUX_ERR("tx req timeout 0x%08x\n", ctrl);
-                               goto out;
-                       }
-               } while (ctrl & 0x00010000);
-
-               /* read status, and check if transaction completed ok */
-               stat = nv_mask(aux, 0x00e4e8 + (ch * 0x50), 0, 0);
-               if (!(stat & 0x000f0f00)) {
-                       ret = 0;
-                       break;
-               }
-
-               AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat);
-       }
-
-       if (type & 1) {
-               for (i = 0; i < 16; i += 4) {
-                       xbuf[i / 4] = nv_rd32(aux, 0x00e4d0 + (ch * 0x50) + i);
-                       AUX_DBG("rd 0x%08x\n", xbuf[i / 4]);
-               }
-               memcpy(data, xbuf, size);
-       }
-
-out:
-       auxch_fini(aux, ch);
-       return ret;
-}
-
 int
-nv_rdaux(struct nouveau_i2c_port *auxch, u32 addr, u8 *data, u8 size)
+nv_rdaux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size)
 {
-       return auxch_tx(auxch->i2c, auxch->drive, 9, addr, data, size);
+       if (port->func->aux) {
+               if (port->func->acquire)
+                       port->func->acquire(port);
+               return port->func->aux(port, 9, addr, data, size);
+       }
+       return -ENODEV;
 }
 
 int
-nv_wraux(struct nouveau_i2c_port *auxch, u32 addr, u8 *data, u8 size)
+nv_wraux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size)
 {
-       return auxch_tx(auxch->i2c, auxch->drive, 8, addr, data, size);
+       if (port->func->aux) {
+               if (port->func->acquire)
+                       port->func->acquire(port);
+               return port->func->aux(port, 8, addr, data, size);
+       }
+       return -ENODEV;
 }
 
 static int
 aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 {
-       struct nouveau_i2c_port *auxch = (struct nouveau_i2c_port *)adap;
+       struct nouveau_i2c_port *port = adap->algo_data;
        struct i2c_msg *msg = msgs;
        int ret, mcnt = num;
 
+       if (!port->func->aux)
+               return -ENODEV;
+       if ( port->func->acquire)
+               port->func->acquire(port);
+
        while (mcnt--) {
                u8 remaining = msg->len;
                u8 *ptr = msg->buf;
@@ -185,8 +74,7 @@ aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
                        if (mcnt || remaining > 16)
                                cmd |= 4; /* MOT */
 
-                       ret = auxch_tx(auxch->i2c, auxch->drive, cmd,
-                                      msg->addr, ptr, cnt);
+                       ret = port->func->aux(port, cmd, msg->addr, ptr, cnt);
                        if (ret < 0)
                                return ret;
 
index dbfc2abf0cfe1a98379a567fa8a7a90fcd409d5f..a114a0ed7e98d13546a2d8672990985fd3f4c257 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 Red Hat Inc.
+ * Copyright 2013 Red Hat Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
  * Authors: Ben Skeggs
  */
 
-#include "core/option.h"
+#include <core/option.h>
 
-#include "subdev/i2c.h"
-#include "subdev/vga.h"
+#include <subdev/bios.h>
+#include <subdev/bios/dcb.h>
+#include <subdev/bios/i2c.h>
+#include <subdev/i2c.h>
+#include <subdev/vga.h>
 
-int
-nv_rdi2cr(struct nouveau_i2c_port *port, u8 addr, u8 reg)
+/******************************************************************************
+ * interface to linux i2c bit-banging algorithm
+ *****************************************************************************/
+
+#ifdef CONFIG_NOUVEAU_I2C_INTERNAL_DEFAULT
+#define CSTMSEL true
+#else
+#define CSTMSEL false
+#endif
+
+static int
+nouveau_i2c_pre_xfer(struct i2c_adapter *adap)
 {
-       u8 val;
-       struct i2c_msg msgs[] = {
-               { .addr = addr, .flags = 0, .len = 1, .buf = &reg },
-               { .addr = addr, .flags = I2C_M_RD, .len = 1, .buf = &val },
-       };
+       struct i2c_algo_bit_data *bit = adap->algo_data;
+       struct nouveau_i2c_port *port = bit->data;
+       if (port->func->acquire)
+               port->func->acquire(port);
+       return 0;
+}
 
-       int ret = i2c_transfer(&port->adapter, msgs, 2);
-       if (ret != 2)
-               return -EIO;
+static void
+nouveau_i2c_setscl(void *data, int state)
+{
+       struct nouveau_i2c_port *port = data;
+       port->func->drive_scl(port, state);
+}
 
-       return val;
+static void
+nouveau_i2c_setsda(void *data, int state)
+{
+       struct nouveau_i2c_port *port = data;
+       port->func->drive_sda(port, state);
 }
 
-int
-nv_wri2cr(struct nouveau_i2c_port *port, u8 addr, u8 reg, u8 val)
+static int
+nouveau_i2c_getscl(void *data)
 {
-       struct i2c_msg msgs[] = {
-               { .addr = addr, .flags = 0, .len = 1, .buf = &reg },
-               { .addr = addr, .flags = 0, .len = 1, .buf = &val },
-       };
+       struct nouveau_i2c_port *port = data;
+       return port->func->sense_scl(port);
+}
 
-       int ret = i2c_transfer(&port->adapter, msgs, 2);
-       if (ret != 2)
-               return -EIO;
+static int
+nouveau_i2c_getsda(void *data)
+{
+       struct nouveau_i2c_port *port = data;
+       return port->func->sense_sda(port);
+}
 
-       return 0;
+/******************************************************************************
+ * base i2c "port" class implementation
+ *****************************************************************************/
+
+void
+_nouveau_i2c_port_dtor(struct nouveau_object *object)
+{
+       struct nouveau_i2c_port *port = (void *)object;
+       i2c_del_adapter(&port->adapter);
+       nouveau_object_destroy(&port->base);
 }
 
-bool
-nv_probe_i2c(struct nouveau_i2c_port *port, u8 addr)
+int
+nouveau_i2c_port_create_(struct nouveau_object *parent,
+                        struct nouveau_object *engine,
+                        struct nouveau_oclass *oclass, u8 index,
+                        const struct i2c_algorithm *algo,
+                        int size, void **pobject)
 {
-       u8 buf[] = { 0 };
-       struct i2c_msg msgs[] = {
-               {
-                       .addr = addr,
-                       .flags = 0,
-                       .len = 1,
-                       .buf = buf,
-               },
-               {
-                       .addr = addr,
-                       .flags = I2C_M_RD,
-                       .len = 1,
-                       .buf = buf,
-               }
-       };
+       struct nouveau_device *device = nv_device(parent);
+       struct nouveau_i2c *i2c = (void *)engine;
+       struct nouveau_i2c_port *port;
+       int ret;
 
-       return i2c_transfer(&port->adapter, msgs, 2) == 2;
+       ret = nouveau_object_create_(parent, engine, oclass, 0, size, pobject);
+       port = *pobject;
+       if (ret)
+               return ret;
+
+       snprintf(port->adapter.name, sizeof(port->adapter.name),
+                "nouveau-%s-%d", device->name, index);
+       port->adapter.owner = THIS_MODULE;
+       port->adapter.dev.parent = &device->pdev->dev;
+       port->index = index;
+       i2c_set_adapdata(&port->adapter, i2c);
+
+       if ( algo == &nouveau_i2c_bit_algo &&
+           !nouveau_boolopt(device->cfgopt, "NvI2C", CSTMSEL)) {
+               struct i2c_algo_bit_data *bit;
+
+               bit = kzalloc(sizeof(*bit), GFP_KERNEL);
+               if (!bit)
+                       return -ENOMEM;
+
+               bit->udelay = 10;
+               bit->timeout = usecs_to_jiffies(2200);
+               bit->data = port;
+               bit->pre_xfer = nouveau_i2c_pre_xfer;
+               bit->setsda = nouveau_i2c_setsda;
+               bit->setscl = nouveau_i2c_setscl;
+               bit->getsda = nouveau_i2c_getsda;
+               bit->getscl = nouveau_i2c_getscl;
+
+               port->adapter.algo_data = bit;
+               ret = i2c_bit_add_bus(&port->adapter);
+       } else {
+               port->adapter.algo_data = port;
+               port->adapter.algo = algo;
+               ret = i2c_add_adapter(&port->adapter);
+       }
+
+       /* drop port's i2c subdev refcount, i2c handles this itself */
+       if (ret == 0) {
+               list_add_tail(&port->head, &i2c->ports);
+               atomic_dec(&engine->refcount);
+       }
+
+       return ret;
 }
 
+/******************************************************************************
+ * base i2c subdev class implementation
+ *****************************************************************************/
+
 static struct nouveau_i2c_port *
 nouveau_i2c_find(struct nouveau_i2c *i2c, u8 index)
 {
@@ -103,29 +175,23 @@ nouveau_i2c_find(struct nouveau_i2c *i2c, u8 index)
 
        list_for_each_entry(port, &i2c->ports, head) {
                if (port->index == index)
-                       break;
+                       return port;
        }
 
-       if (&port->head == &i2c->ports)
-               return NULL;
+       return NULL;
+}
 
-       if (nv_device(i2c)->card_type >= NV_50 && (port->dcb & 0x00000100)) {
-               u32 reg = 0x00e500, val;
-               if (port->type == 6) {
-                       reg += port->drive * 0x50;
-                       val  = 0x2002;
-               } else {
-                       reg += ((port->dcb & 0x1e00) >> 9) * 0x50;
-                       val  = 0xe001;
-               }
+static struct nouveau_i2c_port *
+nouveau_i2c_find_type(struct nouveau_i2c *i2c, u16 type)
+{
+       struct nouveau_i2c_port *port;
 
-               /* nfi, but neither auxch or i2c work if it's 1 */
-               nv_mask(i2c, reg + 0x0c, 0x00000001, 0x00000000);
-               /* nfi, but switches auxch vs normal i2c */
-               nv_mask(i2c, reg + 0x00, 0x0000f003, val);
+       list_for_each_entry(port, &i2c->ports, head) {
+               if (nv_hclass(port) == type)
+                       return port;
        }
 
-       return port;
+       return NULL;
 }
 
 static int
@@ -155,109 +221,86 @@ nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what,
        return -ENODEV;
 }
 
-void
-nouveau_i2c_drive_scl(void *data, int state)
+int
+_nouveau_i2c_fini(struct nouveau_object *object, bool suspend)
 {
-       struct nouveau_i2c_port *port = data;
+       struct nouveau_i2c *i2c = (void *)object;
+       struct nouveau_i2c_port *port;
+       int ret;
 
-       if (port->type == DCB_I2C_NV04_BIT) {
-               u8 val = nv_rdvgac(port->i2c, 0, port->drive);
-               if (state) val |= 0x20;
-               else       val &= 0xdf;
-               nv_wrvgac(port->i2c, 0, port->drive, val | 0x01);
-       } else
-       if (port->type == DCB_I2C_NV4E_BIT) {
-               nv_mask(port->i2c, port->drive, 0x2f, state ? 0x21 : 0x01);
-       } else
-       if (port->type == DCB_I2C_NVIO_BIT) {
-               if (state) port->state |= 0x01;
-               else       port->state &= 0xfe;
-               nv_wr32(port->i2c, port->drive, 4 | port->state);
+       list_for_each_entry(port, &i2c->ports, head) {
+               ret = nv_ofuncs(port)->fini(nv_object(port), suspend);
+               if (ret && suspend)
+                       goto fail;
        }
-}
-
-void
-nouveau_i2c_drive_sda(void *data, int state)
-{
-       struct nouveau_i2c_port *port = data;
 
-       if (port->type == DCB_I2C_NV04_BIT) {
-               u8 val = nv_rdvgac(port->i2c, 0, port->drive);
-               if (state) val |= 0x10;
-               else       val &= 0xef;
-               nv_wrvgac(port->i2c, 0, port->drive, val | 0x01);
-       } else
-       if (port->type == DCB_I2C_NV4E_BIT) {
-               nv_mask(port->i2c, port->drive, 0x1f, state ? 0x11 : 0x01);
-       } else
-       if (port->type == DCB_I2C_NVIO_BIT) {
-               if (state) port->state |= 0x02;
-               else       port->state &= 0xfd;
-               nv_wr32(port->i2c, port->drive, 4 | port->state);
+       return nouveau_subdev_fini(&i2c->base, suspend);
+fail:
+       list_for_each_entry_continue_reverse(port, &i2c->ports, head) {
+               nv_ofuncs(port)->init(nv_object(port));
        }
+
+       return ret;
 }
 
 int
-nouveau_i2c_sense_scl(void *data)
+_nouveau_i2c_init(struct nouveau_object *object)
 {
-       struct nouveau_i2c_port *port = data;
-       struct nouveau_device *device = nv_device(port->i2c);
-
-       if (port->type == DCB_I2C_NV04_BIT) {
-               return !!(nv_rdvgac(port->i2c, 0, port->sense) & 0x04);
-       } else
-       if (port->type == DCB_I2C_NV4E_BIT) {
-               return !!(nv_rd32(port->i2c, port->sense) & 0x00040000);
-       } else
-       if (port->type == DCB_I2C_NVIO_BIT) {
-               if (device->card_type < NV_D0)
-                       return !!(nv_rd32(port->i2c, port->sense) & 0x01);
-               else
-                       return !!(nv_rd32(port->i2c, port->sense) & 0x10);
+       struct nouveau_i2c *i2c = (void *)object;
+       struct nouveau_i2c_port *port;
+       int ret;
+
+       ret = nouveau_subdev_init(&i2c->base);
+       if (ret == 0) {
+               list_for_each_entry(port, &i2c->ports, head) {
+                       ret = nv_ofuncs(port)->init(nv_object(port));
+                       if (ret)
+                               goto fail;
+               }
        }
 
-       return 0;
+       return ret;
+fail:
+       list_for_each_entry_continue_reverse(port, &i2c->ports, head) {
+               nv_ofuncs(port)->fini(nv_object(port), false);
+       }
+
+       return ret;
 }
 
-int
-nouveau_i2c_sense_sda(void *data)
+void
+_nouveau_i2c_dtor(struct nouveau_object *object)
 {
-       struct nouveau_i2c_port *port = data;
-       struct nouveau_device *device = nv_device(port->i2c);
-
-       if (port->type == DCB_I2C_NV04_BIT) {
-               return !!(nv_rdvgac(port->i2c, 0, port->sense) & 0x08);
-       } else
-       if (port->type == DCB_I2C_NV4E_BIT) {
-               return !!(nv_rd32(port->i2c, port->sense) & 0x00080000);
-       } else
-       if (port->type == DCB_I2C_NVIO_BIT) {
-               if (device->card_type < NV_D0)
-                       return !!(nv_rd32(port->i2c, port->sense) & 0x02);
-               else
-                       return !!(nv_rd32(port->i2c, port->sense) & 0x20);
+       struct nouveau_i2c *i2c = (void *)object;
+       struct nouveau_i2c_port *port, *temp;
+
+       list_for_each_entry_safe(port, temp, &i2c->ports, head) {
+               nouveau_object_ref(NULL, (struct nouveau_object **)&port);
        }
 
-       return 0;
+       nouveau_subdev_destroy(&i2c->base);
 }
 
-static const u32 nv50_i2c_port[] = {
-       0x00e138, 0x00e150, 0x00e168, 0x00e180,
-       0x00e254, 0x00e274, 0x00e764, 0x00e780,
-       0x00e79c, 0x00e7b8
+static struct nouveau_oclass *
+nouveau_i2c_extdev_sclass[] = {
+       nouveau_anx9805_sclass,
 };
 
-static int
-nouveau_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-                struct nouveau_oclass *oclass, void *data, u32 size,
-                struct nouveau_object **pobject)
+int
+nouveau_i2c_create_(struct nouveau_object *parent,
+                   struct nouveau_object *engine,
+                   struct nouveau_oclass *oclass,
+                   struct nouveau_oclass *sclass,
+                   int length, void **pobject)
 {
-       struct nouveau_device *device = nv_device(parent);
        struct nouveau_bios *bios = nouveau_bios(parent);
-       struct nouveau_i2c_port *port;
        struct nouveau_i2c *i2c;
+       struct nouveau_object *object;
        struct dcb_i2c_entry info;
-       int ret, i = -1;
+       int ret, i, j, index = -1;
+       struct dcb_output outp;
+       u8  ver, hdr;
+       u32 data;
 
        ret = nouveau_subdev_create(parent, engine, oclass, 0,
                                    "I2C", "i2c", &i2c);
@@ -266,142 +309,60 @@ nouveau_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                return ret;
 
        i2c->find = nouveau_i2c_find;
+       i2c->find_type = nouveau_i2c_find_type;
        i2c->identify = nouveau_i2c_identify;
        INIT_LIST_HEAD(&i2c->ports);
 
-       while (!dcb_i2c_parse(bios, ++i, &info)) {
+       while (!dcb_i2c_parse(bios, ++index, &info)) {
                if (info.type == DCB_I2C_UNUSED)
                        continue;
 
-               port = kzalloc(sizeof(*port), GFP_KERNEL);
-               if (!port) {
-                       nv_error(i2c, "failed port memory alloc at %d\n", i);
-                       break;
-               }
-
-               port->type = info.type;
-               switch (port->type) {
-               case DCB_I2C_NV04_BIT:
-                       port->drive = info.drive;
-                       port->sense = info.sense;
-                       break;
-               case DCB_I2C_NV4E_BIT:
-                       port->drive = 0x600800 + info.drive;
-                       port->sense = port->drive;
-                       break;
-               case DCB_I2C_NVIO_BIT:
-                       port->drive = info.drive & 0x0f;
-                       if (device->card_type < NV_D0) {
-                               if (port->drive >= ARRAY_SIZE(nv50_i2c_port))
-                                       break;
-                               port->drive = nv50_i2c_port[port->drive];
-                               port->sense = port->drive;
-                       } else {
-                               port->drive = 0x00d014 + (port->drive * 0x20);
-                               port->sense = port->drive;
+               oclass = sclass;
+               do {
+                       ret = -EINVAL;
+                       if (oclass->handle == info.type) {
+                               ret = nouveau_object_ctor(*pobject, *pobject,
+                                                         oclass, &info,
+                                                         index, &object);
                        }
+               } while (ret && (++oclass)->handle);
+       }
+
+       /* in addition to the busses specified in the i2c table, there
+        * may be ddc/aux channels hiding behind external tmds/dp/etc
+        * transmitters.
+        */
+       index = ((index + 0x0f) / 0x10) * 0x10;
+       i = -1;
+       while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &outp))) {
+               if (!outp.location || !outp.extdev)
+                       continue;
+
+               switch (outp.type) {
+               case DCB_OUTPUT_TMDS:
+                       info.type = NV_I2C_TYPE_EXTDDC(outp.extdev);
                        break;
-               case DCB_I2C_NVIO_AUX:
-                       port->drive = info.drive & 0x0f;
-                       port->sense = port->drive;
-                       port->adapter.algo = &nouveau_i2c_aux_algo;
+               case DCB_OUTPUT_DP:
+                       info.type = NV_I2C_TYPE_EXTAUX(outp.extdev);
                        break;
                default:
-                       break;
-               }
-
-               if (!port->adapter.algo && !port->drive) {
-                       nv_error(i2c, "I2C%d: type %d index %x/%x unknown\n",
-                                i, port->type, port->drive, port->sense);
-                       kfree(port);
                        continue;
                }
 
-               snprintf(port->adapter.name, sizeof(port->adapter.name),
-                        "nouveau-%s-%d", device->name, i);
-               port->adapter.owner = THIS_MODULE;
-               port->adapter.dev.parent = &device->pdev->dev;
-               port->i2c = i2c;
-               port->index = i;
-               port->dcb = info.data;
-               i2c_set_adapdata(&port->adapter, i2c);
-
-               if (port->adapter.algo != &nouveau_i2c_aux_algo) {
-                       nouveau_i2c_drive_scl(port, 0);
-                       nouveau_i2c_drive_sda(port, 1);
-                       nouveau_i2c_drive_scl(port, 1);
-
-#ifdef CONFIG_NOUVEAU_I2C_INTERNAL_DEFAULT
-                       if (nouveau_boolopt(device->cfgopt, "NvI2C", true)) {
-#else
-                       if (nouveau_boolopt(device->cfgopt, "NvI2C", false)) {
-#endif
-                               port->adapter.algo = &nouveau_i2c_bit_algo;
-                               ret = i2c_add_adapter(&port->adapter);
-                       } else {
-                               port->adapter.algo_data = &port->bit;
-                               port->bit.udelay = 10;
-                               port->bit.timeout = usecs_to_jiffies(2200);
-                               port->bit.data = port;
-                               port->bit.setsda = nouveau_i2c_drive_sda;
-                               port->bit.setscl = nouveau_i2c_drive_scl;
-                               port->bit.getsda = nouveau_i2c_sense_sda;
-                               port->bit.getscl = nouveau_i2c_sense_scl;
-                               ret = i2c_bit_add_bus(&port->adapter);
-                       }
-               } else {
-                       port->adapter.algo = &nouveau_i2c_aux_algo;
-                       ret = i2c_add_adapter(&port->adapter);
-               }
-
-               if (ret) {
-                       nv_error(i2c, "I2C%d: failed register: %d\n", i, ret);
-                       kfree(port);
-                       continue;
+               ret = -ENODEV;
+               j = -1;
+               while (ret && ++j < ARRAY_SIZE(nouveau_i2c_extdev_sclass)) {
+                       parent = nv_object(i2c->find(i2c, outp.i2c_index));
+                       oclass = nouveau_i2c_extdev_sclass[j];
+                       do {
+                               if (oclass->handle != info.type)
+                                       continue;
+                               ret = nouveau_object_ctor(parent, *pobject,
+                                                         oclass, NULL,
+                                                         index++, &object);
+                       } while (ret && (++oclass)->handle);
                }
-
-               list_add_tail(&port->head, &i2c->ports);
        }
 
        return 0;
 }
-
-static void
-nouveau_i2c_dtor(struct nouveau_object *object)
-{
-       struct nouveau_i2c *i2c = (void *)object;
-       struct nouveau_i2c_port *port, *temp;
-
-       list_for_each_entry_safe(port, temp, &i2c->ports, head) {
-               i2c_del_adapter(&port->adapter);
-               list_del(&port->head);
-               kfree(port);
-       }
-
-       nouveau_subdev_destroy(&i2c->base);
-}
-
-static int
-nouveau_i2c_init(struct nouveau_object *object)
-{
-       struct nouveau_i2c *i2c = (void *)object;
-       return nouveau_subdev_init(&i2c->base);
-}
-
-static int
-nouveau_i2c_fini(struct nouveau_object *object, bool suspend)
-{
-       struct nouveau_i2c *i2c = (void *)object;
-       return nouveau_subdev_fini(&i2c->base, suspend);
-}
-
-struct nouveau_oclass
-nouveau_i2c_oclass = {
-       .handle = NV_SUBDEV(I2C, 0x00),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nouveau_i2c_ctor,
-               .dtor = nouveau_i2c_dtor,
-               .init = nouveau_i2c_init,
-               .fini = nouveau_i2c_fini,
-       },
-};
index 1c4c9a5c8e2ef61e3a4ff2e89589d8882f6f5261..a6e72d3b06b594e1e4939eebae23353024c4280c 100644 (file)
 static inline void
 i2c_drive_scl(struct nouveau_i2c_port *port, int state)
 {
-       nouveau_i2c_drive_scl(port, state);
+       port->func->drive_scl(port, state);
 }
 
 static inline void
 i2c_drive_sda(struct nouveau_i2c_port *port, int state)
 {
-       nouveau_i2c_drive_sda(port, state);
+       port->func->drive_sda(port, state);
 }
 
 static inline int
 i2c_sense_scl(struct nouveau_i2c_port *port)
 {
-       return nouveau_i2c_sense_scl(port);
+       return port->func->sense_scl(port);
 }
 
 static inline int
 i2c_sense_sda(struct nouveau_i2c_port *port)
 {
-       return nouveau_i2c_sense_sda(port);
+       return port->func->sense_sda(port);
 }
 
 static void
@@ -77,9 +77,8 @@ i2c_start(struct nouveau_i2c_port *port)
 {
        int ret = 0;
 
-       port->state  = i2c_sense_scl(port);
-       port->state |= i2c_sense_sda(port) << 1;
-       if (port->state != 3) {
+       if (!i2c_sense_scl(port) ||
+           !i2c_sense_sda(port)) {
                i2c_drive_scl(port, 0);
                i2c_drive_sda(port, 1);
                if (!i2c_raise_scl(port))
@@ -184,10 +183,13 @@ i2c_addr(struct nouveau_i2c_port *port, struct i2c_msg *msg)
 static int
 i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 {
-       struct nouveau_i2c_port *port = (struct nouveau_i2c_port *)adap;
+       struct nouveau_i2c_port *port = adap->algo_data;
        struct i2c_msg *msg = msgs;
        int ret = 0, mcnt = num;
 
+       if (port->func->acquire)
+               port->func->acquire(port);
+
        while (!ret && mcnt--) {
                u8 remaining = msg->len;
                u8 *ptr = msg->buf;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c
new file mode 100644 (file)
index 0000000..2ad1884
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/i2c.h>
+#include <subdev/vga.h>
+
+struct nv04_i2c_priv {
+       struct nouveau_i2c base;
+};
+
+struct nv04_i2c_port {
+       struct nouveau_i2c_port base;
+       u8 drive;
+       u8 sense;
+};
+
+static void
+nv04_i2c_drive_scl(struct nouveau_i2c_port *base, int state)
+{
+       struct nv04_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv04_i2c_port *port = (void *)base;
+       u8 val = nv_rdvgac(priv, 0, port->drive);
+       if (state) val |= 0x20;
+       else       val &= 0xdf;
+       nv_wrvgac(priv, 0, port->drive, val | 0x01);
+}
+
+static void
+nv04_i2c_drive_sda(struct nouveau_i2c_port *base, int state)
+{
+       struct nv04_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv04_i2c_port *port = (void *)base;
+       u8 val = nv_rdvgac(priv, 0, port->drive);
+       if (state) val |= 0x10;
+       else       val &= 0xef;
+       nv_wrvgac(priv, 0, port->drive, val | 0x01);
+}
+
+static int
+nv04_i2c_sense_scl(struct nouveau_i2c_port *base)
+{
+       struct nv04_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv04_i2c_port *port = (void *)base;
+       return !!(nv_rdvgac(priv, 0, port->sense) & 0x04);
+}
+
+static int
+nv04_i2c_sense_sda(struct nouveau_i2c_port *base)
+{
+       struct nv04_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv04_i2c_port *port = (void *)base;
+       return !!(nv_rdvgac(priv, 0, port->sense) & 0x08);
+}
+
+static const struct nouveau_i2c_func
+nv04_i2c_func = {
+       .drive_scl = nv04_i2c_drive_scl,
+       .drive_sda = nv04_i2c_drive_sda,
+       .sense_scl = nv04_i2c_sense_scl,
+       .sense_sda = nv04_i2c_sense_sda,
+};
+
+static int
+nv04_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                  struct nouveau_oclass *oclass, void *data, u32 index,
+                  struct nouveau_object **pobject)
+{
+       struct dcb_i2c_entry *info = data;
+       struct nv04_i2c_port *port;
+       int ret;
+
+       ret = nouveau_i2c_port_create(parent, engine, oclass, index,
+                                    &nouveau_i2c_bit_algo, &port);
+       *pobject = nv_object(port);
+       if (ret)
+               return ret;
+
+       port->base.func = &nv04_i2c_func;
+       port->drive = info->drive;
+       port->sense = info->sense;
+       return 0;
+}
+
+static struct nouveau_oclass
+nv04_i2c_sclass[] = {
+       { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NV04_BIT),
+         .ofuncs = &(struct nouveau_ofuncs) {
+                 .ctor = nv04_i2c_port_ctor,
+                 .dtor = _nouveau_i2c_port_dtor,
+                 .init = _nouveau_i2c_port_init,
+                 .fini = _nouveau_i2c_port_fini,
+         },
+       },
+       {}
+};
+
+static int
+nv04_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 size,
+             struct nouveau_object **pobject)
+{
+       struct nv04_i2c_priv *priv;
+       int ret;
+
+       ret = nouveau_i2c_create(parent, engine, oclass, nv04_i2c_sclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+struct nouveau_oclass
+nv04_i2c_oclass = {
+       .handle = NV_SUBDEV(I2C, 0x04),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_i2c_ctor,
+               .dtor = _nouveau_i2c_dtor,
+               .init = _nouveau_i2c_init,
+               .fini = _nouveau_i2c_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c
new file mode 100644 (file)
index 0000000..f501ae2
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/i2c.h>
+#include <subdev/vga.h>
+
+struct nv4e_i2c_priv {
+       struct nouveau_i2c base;
+};
+
+struct nv4e_i2c_port {
+       struct nouveau_i2c_port base;
+       u32 addr;
+};
+
+static void
+nv4e_i2c_drive_scl(struct nouveau_i2c_port *base, int state)
+{
+       struct nv4e_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv4e_i2c_port *port = (void *)base;
+       nv_mask(priv, port->addr, 0x2f, state ? 0x21 : 0x01);
+}
+
+static void
+nv4e_i2c_drive_sda(struct nouveau_i2c_port *base, int state)
+{
+       struct nv4e_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv4e_i2c_port *port = (void *)base;
+       nv_mask(priv, port->addr, 0x1f, state ? 0x11 : 0x01);
+}
+
+static int
+nv4e_i2c_sense_scl(struct nouveau_i2c_port *base)
+{
+       struct nv4e_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv4e_i2c_port *port = (void *)base;
+       return !!(nv_rd32(priv, port->addr) & 0x00040000);
+}
+
+static int
+nv4e_i2c_sense_sda(struct nouveau_i2c_port *base)
+{
+       struct nv4e_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv4e_i2c_port *port = (void *)base;
+       return !!(nv_rd32(priv, port->addr) & 0x00080000);
+}
+
+static const struct nouveau_i2c_func
+nv4e_i2c_func = {
+       .drive_scl = nv4e_i2c_drive_scl,
+       .drive_sda = nv4e_i2c_drive_sda,
+       .sense_scl = nv4e_i2c_sense_scl,
+       .sense_sda = nv4e_i2c_sense_sda,
+};
+
+static int
+nv4e_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                  struct nouveau_oclass *oclass, void *data, u32 index,
+                  struct nouveau_object **pobject)
+{
+       struct dcb_i2c_entry *info = data;
+       struct nv4e_i2c_port *port;
+       int ret;
+
+       ret = nouveau_i2c_port_create(parent, engine, oclass, index,
+                                    &nouveau_i2c_bit_algo, &port);
+       *pobject = nv_object(port);
+       if (ret)
+               return ret;
+
+       port->base.func = &nv4e_i2c_func;
+       port->addr = 0x600800 + info->drive;
+       return 0;
+}
+
+static struct nouveau_oclass
+nv4e_i2c_sclass[] = {
+       { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NV4E_BIT),
+         .ofuncs = &(struct nouveau_ofuncs) {
+                 .ctor = nv4e_i2c_port_ctor,
+                 .dtor = _nouveau_i2c_port_dtor,
+                 .init = _nouveau_i2c_port_init,
+                 .fini = _nouveau_i2c_port_fini,
+         },
+       },
+       {}
+};
+
+static int
+nv4e_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 size,
+             struct nouveau_object **pobject)
+{
+       struct nv4e_i2c_priv *priv;
+       int ret;
+
+       ret = nouveau_i2c_create(parent, engine, oclass, nv4e_i2c_sclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+struct nouveau_oclass
+nv4e_i2c_oclass = {
+       .handle = NV_SUBDEV(I2C, 0x4e),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv4e_i2c_ctor,
+               .dtor = _nouveau_i2c_dtor,
+               .init = _nouveau_i2c_init,
+               .fini = _nouveau_i2c_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c
new file mode 100644 (file)
index 0000000..378dfa3
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nv50.h"
+
+void
+nv50_i2c_drive_scl(struct nouveau_i2c_port *base, int state)
+{
+       struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv50_i2c_port *port = (void *)base;
+       if (state) port->state |= 0x01;
+       else       port->state &= 0xfe;
+       nv_wr32(priv, port->addr, port->state);
+}
+
+void
+nv50_i2c_drive_sda(struct nouveau_i2c_port *base, int state)
+{
+       struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv50_i2c_port *port = (void *)base;
+       if (state) port->state |= 0x02;
+       else       port->state &= 0xfd;
+       nv_wr32(priv, port->addr, port->state);
+}
+
+int
+nv50_i2c_sense_scl(struct nouveau_i2c_port *base)
+{
+       struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv50_i2c_port *port = (void *)base;
+       return !!(nv_rd32(priv, port->addr) & 0x00000001);
+}
+
+int
+nv50_i2c_sense_sda(struct nouveau_i2c_port *base)
+{
+       struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv50_i2c_port *port = (void *)base;
+       return !!(nv_rd32(priv, port->addr) & 0x00000002);
+}
+
+static const struct nouveau_i2c_func
+nv50_i2c_func = {
+       .drive_scl = nv50_i2c_drive_scl,
+       .drive_sda = nv50_i2c_drive_sda,
+       .sense_scl = nv50_i2c_sense_scl,
+       .sense_sda = nv50_i2c_sense_sda,
+};
+
+const u32 nv50_i2c_addr[] = {
+       0x00e138, 0x00e150, 0x00e168, 0x00e180,
+       0x00e254, 0x00e274, 0x00e764, 0x00e780,
+       0x00e79c, 0x00e7b8
+};
+const int nv50_i2c_addr_nr = ARRAY_SIZE(nv50_i2c_addr);
+
+static int
+nv50_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                  struct nouveau_oclass *oclass, void *data, u32 index,
+                  struct nouveau_object **pobject)
+{
+       struct dcb_i2c_entry *info = data;
+       struct nv50_i2c_port *port;
+       int ret;
+
+       ret = nouveau_i2c_port_create(parent, engine, oclass, index,
+                                    &nouveau_i2c_bit_algo, &port);
+       *pobject = nv_object(port);
+       if (ret)
+               return ret;
+
+       if (info->drive >= nv50_i2c_addr_nr)
+               return -EINVAL;
+
+       port->base.func = &nv50_i2c_func;
+       port->state = 0x00000007;
+       port->addr = nv50_i2c_addr[info->drive];
+       return 0;
+}
+
+int
+nv50_i2c_port_init(struct nouveau_object *object)
+{
+       struct nv50_i2c_priv *priv = (void *)object->engine;
+       struct nv50_i2c_port *port = (void *)object;
+       nv_wr32(priv, port->addr, port->state);
+       return nouveau_i2c_port_init(&port->base);
+}
+
+static struct nouveau_oclass
+nv50_i2c_sclass[] = {
+       { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT),
+         .ofuncs = &(struct nouveau_ofuncs) {
+                 .ctor = nv50_i2c_port_ctor,
+                 .dtor = _nouveau_i2c_port_dtor,
+                 .init = nv50_i2c_port_init,
+                 .fini = _nouveau_i2c_port_fini,
+         },
+       },
+       {}
+};
+
+static int
+nv50_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 size,
+             struct nouveau_object **pobject)
+{
+       struct nv50_i2c_priv *priv;
+       int ret;
+
+       ret = nouveau_i2c_create(parent, engine, oclass, nv50_i2c_sclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+struct nouveau_oclass
+nv50_i2c_oclass = {
+       .handle = NV_SUBDEV(I2C, 0x50),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv50_i2c_ctor,
+               .dtor = _nouveau_i2c_dtor,
+               .init = _nouveau_i2c_init,
+               .fini = _nouveau_i2c_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h
new file mode 100644 (file)
index 0000000..4e5ba48
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef __NV50_I2C_H__
+#define __NV50_I2C_H__
+
+#include <subdev/i2c.h>
+
+struct nv50_i2c_priv {
+       struct nouveau_i2c base;
+};
+
+struct nv50_i2c_port {
+       struct nouveau_i2c_port base;
+       u32 addr;
+       u32 ctrl;
+       u32 data;
+       u32 state;
+};
+
+extern const u32 nv50_i2c_addr[];
+extern const int nv50_i2c_addr_nr;
+int  nv50_i2c_port_init(struct nouveau_object *);
+int  nv50_i2c_sense_scl(struct nouveau_i2c_port *);
+int  nv50_i2c_sense_sda(struct nouveau_i2c_port *);
+void nv50_i2c_drive_scl(struct nouveau_i2c_port *, int state);
+void nv50_i2c_drive_sda(struct nouveau_i2c_port *, int state);
+
+int  nv94_aux_port_ctor(struct nouveau_object *, struct nouveau_object *,
+                       struct nouveau_oclass *, void *, u32,
+                       struct nouveau_object **);
+void nv94_i2c_acquire(struct nouveau_i2c_port *);
+void nv94_i2c_release(struct nouveau_i2c_port *);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c
new file mode 100644 (file)
index 0000000..61b7716
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nv50.h"
+
+#define AUX_DBG(fmt, args...) nv_debug(aux, "AUXCH(%d): " fmt, ch, ##args)
+#define AUX_ERR(fmt, args...) nv_error(aux, "AUXCH(%d): " fmt, ch, ##args)
+
+static void
+auxch_fini(struct nouveau_i2c *aux, int ch)
+{
+       nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00310000, 0x00000000);
+}
+
+static int
+auxch_init(struct nouveau_i2c *aux, int ch)
+{
+       const u32 unksel = 1; /* nfi which to use, or if it matters.. */
+       const u32 ureq = unksel ? 0x00100000 : 0x00200000;
+       const u32 urep = unksel ? 0x01000000 : 0x02000000;
+       u32 ctrl, timeout;
+
+       /* wait up to 1ms for any previous transaction to be done... */
+       timeout = 1000;
+       do {
+               ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
+               udelay(1);
+               if (!timeout--) {
+                       AUX_ERR("begin idle timeout 0x%08x\n", ctrl);
+                       return -EBUSY;
+               }
+       } while (ctrl & 0x03010000);
+
+       /* set some magic, and wait up to 1ms for it to appear */
+       nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00300000, ureq);
+       timeout = 1000;
+       do {
+               ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
+               udelay(1);
+               if (!timeout--) {
+                       AUX_ERR("magic wait 0x%08x\n", ctrl);
+                       auxch_fini(aux, ch);
+                       return -EBUSY;
+               }
+       } while ((ctrl & 0x03000000) != urep);
+
+       return 0;
+}
+
+int
+nv94_aux(struct nouveau_i2c_port *base, u8 type, u32 addr, u8 *data, u8 size)
+{
+       struct nouveau_i2c *aux = nouveau_i2c(base);
+       struct nv50_i2c_port *port = (void *)base;
+       u32 ctrl, stat, timeout, retries;
+       u32 xbuf[4] = {};
+       int ch = port->addr;
+       int ret, i;
+
+       AUX_DBG("%d: 0x%08x %d\n", type, addr, size);
+
+       ret = auxch_init(aux, ch);
+       if (ret)
+               goto out;
+
+       stat = nv_rd32(aux, 0x00e4e8 + (ch * 0x50));
+       if (!(stat & 0x10000000)) {
+               AUX_DBG("sink not detected\n");
+               ret = -ENXIO;
+               goto out;
+       }
+
+       if (!(type & 1)) {
+               memcpy(xbuf, data, size);
+               for (i = 0; i < 16; i += 4) {
+                       AUX_DBG("wr 0x%08x\n", xbuf[i / 4]);
+                       nv_wr32(aux, 0x00e4c0 + (ch * 0x50) + i, xbuf[i / 4]);
+               }
+       }
+
+       ctrl  = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
+       ctrl &= ~0x0001f0ff;
+       ctrl |= type << 12;
+       ctrl |= size - 1;
+       nv_wr32(aux, 0x00e4e0 + (ch * 0x50), addr);
+
+       /* retry transaction a number of times on failure... */
+       ret = -EREMOTEIO;
+       for (retries = 0; retries < 32; retries++) {
+               /* reset, and delay a while if this is a retry */
+               nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x80000000 | ctrl);
+               nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00000000 | ctrl);
+               if (retries)
+                       udelay(400);
+
+               /* transaction request, wait up to 1ms for it to complete */
+               nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00010000 | ctrl);
+
+               timeout = 1000;
+               do {
+                       ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
+                       udelay(1);
+                       if (!timeout--) {
+                               AUX_ERR("tx req timeout 0x%08x\n", ctrl);
+                               goto out;
+                       }
+               } while (ctrl & 0x00010000);
+
+               /* read status, and check if transaction completed ok */
+               stat = nv_mask(aux, 0x00e4e8 + (ch * 0x50), 0, 0);
+               if (!(stat & 0x000f0f00)) {
+                       ret = 0;
+                       break;
+               }
+
+               AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat);
+       }
+
+       if (type & 1) {
+               for (i = 0; i < 16; i += 4) {
+                       xbuf[i / 4] = nv_rd32(aux, 0x00e4d0 + (ch * 0x50) + i);
+                       AUX_DBG("rd 0x%08x\n", xbuf[i / 4]);
+               }
+               memcpy(data, xbuf, size);
+       }
+
+out:
+       auxch_fini(aux, ch);
+       return ret;
+}
+
+void
+nv94_i2c_acquire(struct nouveau_i2c_port *base)
+{
+       struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv50_i2c_port *port = (void *)base;
+       if (port->ctrl) {
+               nv_mask(priv, port->ctrl + 0x0c, 0x00000001, 0x00000000);
+               nv_mask(priv, port->ctrl + 0x00, 0x0000f003, port->data);
+       }
+}
+
+void
+nv94_i2c_release(struct nouveau_i2c_port *base)
+{
+}
+
+static const struct nouveau_i2c_func
+nv94_i2c_func = {
+       .acquire   = nv94_i2c_acquire,
+       .release   = nv94_i2c_release,
+       .drive_scl = nv50_i2c_drive_scl,
+       .drive_sda = nv50_i2c_drive_sda,
+       .sense_scl = nv50_i2c_sense_scl,
+       .sense_sda = nv50_i2c_sense_sda,
+};
+
+static int
+nv94_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                  struct nouveau_oclass *oclass, void *data, u32 index,
+                  struct nouveau_object **pobject)
+{
+       struct dcb_i2c_entry *info = data;
+       struct nv50_i2c_port *port;
+       int ret;
+
+       ret = nouveau_i2c_port_create(parent, engine, oclass, index,
+                                    &nouveau_i2c_bit_algo, &port);
+       *pobject = nv_object(port);
+       if (ret)
+               return ret;
+
+       if (info->drive >= nv50_i2c_addr_nr)
+               return -EINVAL;
+
+       port->base.func = &nv94_i2c_func;
+       port->state = 7;
+       port->addr = nv50_i2c_addr[info->drive];
+       if (info->share != DCB_I2C_UNUSED) {
+               port->ctrl = 0x00e500 + (info->share * 0x50);
+               port->data = 0x0000e001;
+       }
+       return 0;
+}
+
+static const struct nouveau_i2c_func
+nv94_aux_func = {
+       .acquire   = nv94_i2c_acquire,
+       .release   = nv94_i2c_release,
+       .aux       = nv94_aux,
+};
+
+int
+nv94_aux_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                  struct nouveau_oclass *oclass, void *data, u32 index,
+                  struct nouveau_object **pobject)
+{
+       struct dcb_i2c_entry *info = data;
+       struct nv50_i2c_port *port;
+       int ret;
+
+       ret = nouveau_i2c_port_create(parent, engine, oclass, index,
+                                    &nouveau_i2c_aux_algo, &port);
+       *pobject = nv_object(port);
+       if (ret)
+               return ret;
+
+       port->base.func = &nv94_aux_func;
+       port->addr = info->drive;
+       if (info->share != DCB_I2C_UNUSED) {
+               port->ctrl = 0x00e500 + (info->drive * 0x50);
+               port->data = 0x00002002;
+       }
+
+       return 0;
+}
+
+static struct nouveau_oclass
+nv94_i2c_sclass[] = {
+       { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT),
+         .ofuncs = &(struct nouveau_ofuncs) {
+                 .ctor = nv94_i2c_port_ctor,
+                 .dtor = _nouveau_i2c_port_dtor,
+                 .init = nv50_i2c_port_init,
+                 .fini = _nouveau_i2c_port_fini,
+         },
+       },
+       { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX),
+         .ofuncs = &(struct nouveau_ofuncs) {
+                 .ctor = nv94_aux_port_ctor,
+                 .dtor = _nouveau_i2c_port_dtor,
+                 .init = _nouveau_i2c_port_init,
+                 .fini = _nouveau_i2c_port_fini,
+         },
+       },
+       {}
+};
+
+static int
+nv94_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 size,
+             struct nouveau_object **pobject)
+{
+       struct nv50_i2c_priv *priv;
+       int ret;
+
+       ret = nouveau_i2c_create(parent, engine, oclass, nv94_i2c_sclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+struct nouveau_oclass
+nv94_i2c_oclass = {
+       .handle = NV_SUBDEV(I2C, 0x94),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv94_i2c_ctor,
+               .dtor = _nouveau_i2c_dtor,
+               .init = _nouveau_i2c_init,
+               .fini = _nouveau_i2c_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c
new file mode 100644 (file)
index 0000000..f761b8a
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nv50.h"
+
+static int
+nvd0_i2c_sense_scl(struct nouveau_i2c_port *base)
+{
+       struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv50_i2c_port *port = (void *)base;
+       return !!(nv_rd32(priv, port->addr) & 0x00000010);
+}
+
+static int
+nvd0_i2c_sense_sda(struct nouveau_i2c_port *base)
+{
+       struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine;
+       struct nv50_i2c_port *port = (void *)base;
+       return !!(nv_rd32(priv, port->addr) & 0x00000020);
+}
+
+static const struct nouveau_i2c_func
+nvd0_i2c_func = {
+       .acquire   = nv94_i2c_acquire,
+       .release   = nv94_i2c_release,
+       .drive_scl = nv50_i2c_drive_scl,
+       .drive_sda = nv50_i2c_drive_sda,
+       .sense_scl = nvd0_i2c_sense_scl,
+       .sense_sda = nvd0_i2c_sense_sda,
+};
+
+static int
+nvd0_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                  struct nouveau_oclass *oclass, void *data, u32 index,
+                  struct nouveau_object **pobject)
+{
+       struct dcb_i2c_entry *info = data;
+       struct nv50_i2c_port *port;
+       int ret;
+
+       ret = nouveau_i2c_port_create(parent, engine, oclass, index,
+                                    &nouveau_i2c_bit_algo, &port);
+       *pobject = nv_object(port);
+       if (ret)
+               return ret;
+
+       port->base.func = &nvd0_i2c_func;
+       port->state = 0x00000007;
+       port->addr = 0x00d014 + (info->drive * 0x20);
+       if (info->share != DCB_I2C_UNUSED) {
+               port->ctrl = 0x00e500 + (info->share * 0x50);
+               port->data = 0x0000e001;
+       }
+       return 0;
+}
+
+static struct nouveau_oclass
+nvd0_i2c_sclass[] = {
+       { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT),
+         .ofuncs = &(struct nouveau_ofuncs) {
+                 .ctor = nvd0_i2c_port_ctor,
+                 .dtor = _nouveau_i2c_port_dtor,
+                 .init = nv50_i2c_port_init,
+                 .fini = _nouveau_i2c_port_fini,
+         },
+       },
+       { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX),
+         .ofuncs = &(struct nouveau_ofuncs) {
+                 .ctor = nv94_aux_port_ctor,
+                 .dtor = _nouveau_i2c_port_dtor,
+                 .init = _nouveau_i2c_port_init,
+                 .fini = _nouveau_i2c_port_fini,
+         },
+       },
+       {}
+};
+
+static int
+nvd0_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 size,
+             struct nouveau_object **pobject)
+{
+       struct nv50_i2c_priv *priv;
+       int ret;
+
+       ret = nouveau_i2c_create(parent, engine, oclass, nvd0_i2c_sclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+struct nouveau_oclass
+nvd0_i2c_oclass = {
+       .handle = NV_SUBDEV(I2C, 0xd0),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvd0_i2c_ctor,
+               .dtor = _nouveau_i2c_dtor,
+               .init = _nouveau_i2c_init,
+               .fini = _nouveau_i2c_fini,
+       },
+};
index 23ebe477a6f0c0f40cf3f7e2af4662b0dd286c85..89da8fa7ea0fadcb4b6ae576e851d3e6e67d7d74 100644 (file)
@@ -37,7 +37,7 @@ nv04_mc_intr[] = {
        { 0x00100000, NVDEV_SUBDEV_TIMER },
        { 0x01000000, NVDEV_ENGINE_DISP },      /* NV04- PCRTC0 */
        { 0x02000000, NVDEV_ENGINE_DISP },      /* NV11- PCRTC1 */
-       { 0x10000000, NVDEV_SUBDEV_GPIO },      /* PBUS */
+       { 0x10000000, NVDEV_SUBDEV_BUS },
        { 0x80000000, NVDEV_ENGINE_SW },
        {}
 };
index 8d759f830323d28fbb4054867ce9c865a178b8c7..5965add6daee95dc14ef7414d97e15d91e0f42ff 100644 (file)
@@ -38,6 +38,7 @@ nv50_mc_intr[] = {
        { 0x00100000, NVDEV_SUBDEV_TIMER },
        { 0x00200000, NVDEV_SUBDEV_GPIO },
        { 0x04000000, NVDEV_ENGINE_DISP },
+       { 0x10000000, NVDEV_SUBDEV_BUS },
        { 0x80000000, NVDEV_ENGINE_SW },
        { 0x0000d101, NVDEV_SUBDEV_FB },
        {},
index ceb5c83f9459511b18ea8a49744423b218605efe..3a80b29dce0fc75a1a03640a2d68e52b2052d9ac 100644 (file)
@@ -35,10 +35,12 @@ nv98_mc_intr[] = {
        { 0x00001000, NVDEV_ENGINE_GR },
        { 0x00004000, NVDEV_ENGINE_CRYPT },     /* NV84:NVA3 */
        { 0x00008000, NVDEV_ENGINE_BSP },
+       { 0x00080000, NVDEV_SUBDEV_THERM },     /* NVA3:NVC0 */
        { 0x00100000, NVDEV_SUBDEV_TIMER },
        { 0x00200000, NVDEV_SUBDEV_GPIO },
        { 0x00400000, NVDEV_ENGINE_COPY0 },     /* NVA3-     */
        { 0x04000000, NVDEV_ENGINE_DISP },
+       { 0x10000000, NVDEV_SUBDEV_BUS },
        { 0x80000000, NVDEV_ENGINE_SW },
        { 0x0040d101, NVDEV_SUBDEV_FB },
        {},
index 92796682722d9f44dc533cf8b0ba1f07c348ec86..42bbf72023a8b735c6bc490a2afcf913bd568110 100644 (file)
@@ -36,11 +36,13 @@ nvc0_mc_intr[] = {
        { 0x00000100, NVDEV_ENGINE_FIFO },
        { 0x00001000, NVDEV_ENGINE_GR },
        { 0x00008000, NVDEV_ENGINE_BSP },
+       { 0x00040000, NVDEV_SUBDEV_THERM },
        { 0x00020000, NVDEV_ENGINE_VP },
        { 0x00100000, NVDEV_SUBDEV_TIMER },
        { 0x00200000, NVDEV_SUBDEV_GPIO },
        { 0x02000000, NVDEV_SUBDEV_LTCG },
        { 0x04000000, NVDEV_ENGINE_DISP },
+       { 0x10000000, NVDEV_SUBDEV_BUS },
        { 0x40000000, NVDEV_SUBDEV_IBUS },
        { 0x80000000, NVDEV_ENGINE_SW },
        {},
index 839ca1edc132e9c1db915522c3b573f549821574..4bde7f7f7b81d2e14363e9990d93aa07d3e168fe 100644 (file)
@@ -156,15 +156,15 @@ mxms_foreach(struct nouveau_mxm *mxm, u8 types,
 
                        nv_debug(mxm, "%4s: ", mxms_desc_name[type]);
                        for (j = headerlen - 1; j >= 0; j--)
-                               printk("%02x", dump[j]);
-                       printk("\n");
+                               pr_cont("%02x", dump[j]);
+                       pr_cont("\n");
                        dump += headerlen;
 
                        for (i = 0; i < entries; i++, dump += recordlen) {
                                nv_debug(mxm, "      ");
                                for (j = recordlen - 1; j >= 0; j--)
-                                       printk("%02x", dump[j]);
-                               printk("\n");
+                                       pr_cont("%02x", dump[j]);
+                               pr_cont("\n");
                        }
                }
 
index 1674c74a76c8815789ec85b7dd340f7219e55975..f794dc89a3b2a1ddaf68dc686b5ef13c0f9bbbcb 100644 (file)
 
 #include "priv.h"
 
+static int
+nouveau_therm_update_trip(struct nouveau_therm *therm)
+{
+       struct nouveau_therm_priv *priv = (void *)therm;
+       struct nouveau_therm_trip_point *trip = priv->fan->bios.trip,
+                                       *cur_trip = NULL,
+                                       *last_trip = priv->last_trip;
+       u8  temp = therm->temp_get(therm);
+       u16 duty, i;
+
+       /* look for the trip point corresponding to the current temperature */
+       cur_trip = NULL;
+       for (i = 0; i < priv->fan->bios.nr_fan_trip; i++) {
+               if (temp >= trip[i].temp)
+                       cur_trip = &trip[i];
+       }
+
+       /* account for the hysteresis cycle */
+       if (last_trip && temp <= (last_trip->temp) &&
+           temp > (last_trip->temp - last_trip->hysteresis))
+               cur_trip = last_trip;
+
+       if (cur_trip) {
+               duty = cur_trip->fan_duty;
+               priv->last_trip = cur_trip;
+       } else {
+               duty = 0;
+               priv->last_trip = NULL;
+       }
+
+       return duty;
+}
+
+static int
+nouveau_therm_update_linear(struct nouveau_therm *therm)
+{
+       struct nouveau_therm_priv *priv = (void *)therm;
+       u8  linear_min_temp = priv->fan->bios.linear_min_temp;
+       u8  linear_max_temp = priv->fan->bios.linear_max_temp;
+       u8  temp = therm->temp_get(therm);
+       u16 duty;
+
+       /* handle the non-linear part first */
+       if (temp < linear_min_temp)
+               return priv->fan->bios.min_duty;
+       else if (temp > linear_max_temp)
+               return priv->fan->bios.max_duty;
+
+       /* we are in the linear zone */
+       duty  = (temp - linear_min_temp);
+       duty *= (priv->fan->bios.max_duty - priv->fan->bios.min_duty);
+       duty /= (linear_max_temp - linear_min_temp);
+       duty += priv->fan->bios.min_duty;
+
+       return duty;
+}
+
+static void
+nouveau_therm_update(struct nouveau_therm *therm, int mode)
+{
+       struct nouveau_timer *ptimer = nouveau_timer(therm);
+       struct nouveau_therm_priv *priv = (void *)therm;
+       unsigned long flags;
+       int duty;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       if (mode < 0)
+               mode = priv->mode;
+       priv->mode = mode;
+
+       switch (mode) {
+       case NOUVEAU_THERM_CTRL_MANUAL:
+               duty = nouveau_therm_fan_get(therm);
+               if (duty < 0)
+                       duty = 100;
+               break;
+       case NOUVEAU_THERM_CTRL_AUTO:
+               if (priv->fan->bios.nr_fan_trip)
+                       duty = nouveau_therm_update_trip(therm);
+               else
+                       duty = nouveau_therm_update_linear(therm);
+               break;
+       case NOUVEAU_THERM_CTRL_NONE:
+       default:
+               goto done;
+       }
+
+       nv_debug(therm, "FAN target request: %d%%\n", duty);
+       nouveau_therm_fan_set(therm, (mode != NOUVEAU_THERM_CTRL_AUTO), duty);
+
+done:
+       if (list_empty(&priv->alarm.head) && (mode == NOUVEAU_THERM_CTRL_AUTO))
+               ptimer->alarm(ptimer, 1000000000ULL, &priv->alarm);
+       spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void
+nouveau_therm_alarm(struct nouveau_alarm *alarm)
+{
+       struct nouveau_therm_priv *priv =
+              container_of(alarm, struct nouveau_therm_priv, alarm);
+       nouveau_therm_update(&priv->base, -1);
+}
+
+int
+nouveau_therm_mode(struct nouveau_therm *therm, int mode)
+{
+       struct nouveau_therm_priv *priv = (void *)therm;
+       struct nouveau_device *device = nv_device(therm);
+       static const char *name[] = {
+               "disabled",
+               "manual",
+               "automatic"
+       };
+
+       /* The default PDAEMON ucode interferes with fan management */
+       if ((mode >= ARRAY_SIZE(name)) ||
+           (mode != NOUVEAU_THERM_CTRL_NONE && device->card_type >= NV_C0))
+               return -EINVAL;
+
+       if (priv->mode == mode)
+               return 0;
+
+       nv_info(therm, "Thermal management: %s\n", name[mode]);
+       nouveau_therm_update(therm, mode);
+       return 0;
+}
+
 int
 nouveau_therm_attr_get(struct nouveau_therm *therm,
                       enum nouveau_therm_attr_type type)
@@ -37,11 +165,11 @@ nouveau_therm_attr_get(struct nouveau_therm *therm,
 
        switch (type) {
        case NOUVEAU_THERM_ATTR_FAN_MIN_DUTY:
-               return priv->bios_fan.min_duty;
+               return priv->fan->bios.min_duty;
        case NOUVEAU_THERM_ATTR_FAN_MAX_DUTY:
-               return priv->bios_fan.max_duty;
+               return priv->fan->bios.max_duty;
        case NOUVEAU_THERM_ATTR_FAN_MODE:
-               return priv->fan.mode;
+               return priv->mode;
        case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST:
                return priv->bios_sensor.thrs_fan_boost.temp;
        case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST:
@@ -73,42 +201,50 @@ nouveau_therm_attr_set(struct nouveau_therm *therm,
        case NOUVEAU_THERM_ATTR_FAN_MIN_DUTY:
                if (value < 0)
                        value = 0;
-               if (value > priv->bios_fan.max_duty)
-                       value = priv->bios_fan.max_duty;
-               priv->bios_fan.min_duty = value;
+               if (value > priv->fan->bios.max_duty)
+                       value = priv->fan->bios.max_duty;
+               priv->fan->bios.min_duty = value;
                return 0;
        case NOUVEAU_THERM_ATTR_FAN_MAX_DUTY:
                if (value < 0)
                        value = 0;
-               if (value < priv->bios_fan.min_duty)
-                       value = priv->bios_fan.min_duty;
-               priv->bios_fan.max_duty = value;
+               if (value < priv->fan->bios.min_duty)
+                       value = priv->fan->bios.min_duty;
+               priv->fan->bios.max_duty = value;
                return 0;
        case NOUVEAU_THERM_ATTR_FAN_MODE:
-               return nouveau_therm_fan_set_mode(therm, value);
+               return nouveau_therm_mode(therm, value);
        case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST:
                priv->bios_sensor.thrs_fan_boost.temp = value;
+               priv->sensor.program_alarms(therm);
                return 0;
        case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST:
                priv->bios_sensor.thrs_fan_boost.hysteresis = value;
+               priv->sensor.program_alarms(therm);
                return 0;
        case NOUVEAU_THERM_ATTR_THRS_DOWN_CLK:
                priv->bios_sensor.thrs_down_clock.temp = value;
+               priv->sensor.program_alarms(therm);
                return 0;
        case NOUVEAU_THERM_ATTR_THRS_DOWN_CLK_HYST:
                priv->bios_sensor.thrs_down_clock.hysteresis = value;
+               priv->sensor.program_alarms(therm);
                return 0;
        case NOUVEAU_THERM_ATTR_THRS_CRITICAL:
                priv->bios_sensor.thrs_critical.temp = value;
+               priv->sensor.program_alarms(therm);
                return 0;
        case NOUVEAU_THERM_ATTR_THRS_CRITICAL_HYST:
                priv->bios_sensor.thrs_critical.hysteresis = value;
+               priv->sensor.program_alarms(therm);
                return 0;
        case NOUVEAU_THERM_ATTR_THRS_SHUTDOWN:
                priv->bios_sensor.thrs_shutdown.temp = value;
+               priv->sensor.program_alarms(therm);
                return 0;
        case NOUVEAU_THERM_ATTR_THRS_SHUTDOWN_HYST:
                priv->bios_sensor.thrs_shutdown.hysteresis = value;
+               priv->sensor.program_alarms(therm);
                return 0;
        }
 
@@ -116,7 +252,7 @@ nouveau_therm_attr_set(struct nouveau_therm *therm,
 }
 
 int
-nouveau_therm_init(struct nouveau_object *object)
+_nouveau_therm_init(struct nouveau_object *object)
 {
        struct nouveau_therm *therm = (void *)object;
        struct nouveau_therm_priv *priv = (void *)therm;
@@ -126,19 +262,69 @@ nouveau_therm_init(struct nouveau_object *object)
        if (ret)
                return ret;
 
-       if (priv->fan.percent >= 0)
-               therm->fan_set(therm, priv->fan.percent);
-
+       if (priv->suspend >= 0)
+               nouveau_therm_mode(therm, priv->mode);
+       priv->sensor.program_alarms(therm);
        return 0;
 }
 
 int
-nouveau_therm_fini(struct nouveau_object *object, bool suspend)
+_nouveau_therm_fini(struct nouveau_object *object, bool suspend)
 {
        struct nouveau_therm *therm = (void *)object;
        struct nouveau_therm_priv *priv = (void *)therm;
 
-       priv->fan.percent = therm->fan_get(therm);
+       if (suspend) {
+               priv->suspend = priv->mode;
+               priv->mode = NOUVEAU_THERM_CTRL_NONE;
+       }
 
        return nouveau_subdev_fini(&therm->base, suspend);
 }
+
+int
+nouveau_therm_create_(struct nouveau_object *parent,
+                     struct nouveau_object *engine,
+                     struct nouveau_oclass *oclass,
+                     int length, void **pobject)
+{
+       struct nouveau_therm_priv *priv;
+       int ret;
+
+       ret = nouveau_subdev_create_(parent, engine, oclass, 0, "PTHERM",
+                                    "therm", length, pobject);
+       priv = *pobject;
+       if (ret)
+               return ret;
+
+       nouveau_alarm_init(&priv->alarm, nouveau_therm_alarm);
+       spin_lock_init(&priv->lock);
+       spin_lock_init(&priv->sensor.alarm_program_lock);
+
+       priv->base.fan_get = nouveau_therm_fan_user_get;
+       priv->base.fan_set = nouveau_therm_fan_user_set;
+       priv->base.fan_sense = nouveau_therm_fan_sense;
+       priv->base.attr_get = nouveau_therm_attr_get;
+       priv->base.attr_set = nouveau_therm_attr_set;
+       priv->mode = priv->suspend = -1; /* undefined */
+       return 0;
+}
+
+int
+nouveau_therm_preinit(struct nouveau_therm *therm)
+{
+       nouveau_therm_ic_ctor(therm);
+       nouveau_therm_sensor_ctor(therm);
+       nouveau_therm_fan_ctor(therm);
+
+       nouveau_therm_mode(therm, NOUVEAU_THERM_CTRL_NONE);
+       return 0;
+}
+
+void
+_nouveau_therm_dtor(struct nouveau_object *object)
+{
+       struct nouveau_therm_priv *priv = (void *)object;
+       kfree(priv->fan);
+       nouveau_subdev_destroy(&priv->base.base);
+}
index 5231786851805da5e68840668a29db856f56cdb6..c728380d3d625f4b6ff238f1363700fc324fe703 100644 (file)
 
 #include <core/object.h>
 #include <core/device.h>
+
 #include <subdev/gpio.h>
 #include <subdev/timer.h>
 
-int
-nouveau_therm_fan_get(struct nouveau_therm *therm)
+static int
+nouveau_fan_update(struct nouveau_fan *fan, bool immediate, int target)
 {
+       struct nouveau_therm *therm = fan->parent;
        struct nouveau_therm_priv *priv = (void *)therm;
-       struct nouveau_gpio *gpio = nouveau_gpio(therm);
-       struct dcb_gpio_func func;
-       int card_type = nv_device(therm)->card_type;
-       u32 divs, duty;
-       int ret;
-
-       if (!priv->fan.pwm_get)
-               return -ENODEV;
+       struct nouveau_timer *ptimer = nouveau_timer(priv);
+       unsigned long flags;
+       int ret = 0;
+       int duty;
+
+       /* update target fan speed, restricting to allowed range */
+       spin_lock_irqsave(&fan->lock, flags);
+       if (target < 0)
+               target = fan->percent;
+       target = max_t(u8, target, fan->bios.min_duty);
+       target = min_t(u8, target, fan->bios.max_duty);
+       if (fan->percent != target) {
+               nv_debug(therm, "FAN target: %d\n", target);
+               fan->percent = target;
+       }
 
-       ret = gpio->find(gpio, 0, DCB_GPIO_PWM_FAN, 0xff, &func);
-       if (ret == 0) {
-               ret = priv->fan.pwm_get(therm, func.line, &divs, &duty);
-               if (ret == 0 && divs) {
-                       divs = max(divs, duty);
-                       if (card_type <= NV_40 || (func.log[0] & 1))
-                               duty = divs - duty;
-                       return (duty * 100) / divs;
-               }
+       /* check that we're not already at the target duty cycle */
+       duty = fan->get(therm);
+       if (duty == target)
+               goto done;
+
+       /* smooth out the fanspeed increase/decrease */
+       if (!immediate && duty >= 0) {
+               /* the constant "3" is a rough approximation taken from
+                * nvidia's behaviour.
+                * it is meant to bump the fan speed more incrementally
+                */
+               if (duty < target)
+                       duty = min(duty + 3, target);
+               else if (duty > target)
+                       duty = max(duty - 3, target);
+       } else {
+               duty = target;
+       }
 
-               return gpio->get(gpio, 0, func.func, func.line) * 100;
+       nv_debug(therm, "FAN update: %d\n", duty);
+       ret = fan->set(therm, duty);
+       if (ret)
+               goto done;
+
+       /* schedule next fan update, if not at target speed already */
+       if (list_empty(&fan->alarm.head) && target != duty) {
+               u16 bump_period = fan->bios.bump_period;
+               u16 slow_down_period = fan->bios.slow_down_period;
+               u64 delay;
+
+               if (duty > target)
+                       delay = slow_down_period;
+               else if (duty == target)
+                       delay = min(bump_period, slow_down_period) ;
+               else
+                       delay = bump_period;
+
+               ptimer->alarm(ptimer, delay * 1000 * 1000, &fan->alarm);
        }
 
-       return -ENODEV;
+done:
+       spin_unlock_irqrestore(&fan->lock, flags);
+       return ret;
+}
+
+static void
+nouveau_fan_alarm(struct nouveau_alarm *alarm)
+{
+       struct nouveau_fan *fan = container_of(alarm, struct nouveau_fan, alarm);
+       nouveau_fan_update(fan, false, -1);
 }
 
 int
-nouveau_therm_fan_set(struct nouveau_therm *therm, int percent)
+nouveau_therm_fan_get(struct nouveau_therm *therm)
 {
        struct nouveau_therm_priv *priv = (void *)therm;
-       struct nouveau_gpio *gpio = nouveau_gpio(therm);
-       struct dcb_gpio_func func;
-       int card_type = nv_device(therm)->card_type;
-       u32 divs, duty;
-       int ret;
-
-       if (priv->fan.mode == FAN_CONTROL_NONE)
-               return -EINVAL;
-
-       if (!priv->fan.pwm_set)
-               return -ENODEV;
-
-       if (percent < priv->bios_fan.min_duty)
-               percent = priv->bios_fan.min_duty;
-       if (percent > priv->bios_fan.max_duty)
-               percent = priv->bios_fan.max_duty;
-
-       ret = gpio->find(gpio, 0, DCB_GPIO_PWM_FAN, 0xff, &func);
-       if (ret == 0) {
-               divs = priv->bios_perf_fan.pwm_divisor;
-               if (priv->bios_fan.pwm_freq) {
-                       divs = 1;
-                       if (priv->fan.pwm_clock)
-                               divs = priv->fan.pwm_clock(therm);
-                       divs /= priv->bios_fan.pwm_freq;
-               }
-
-               duty = ((divs * percent) + 99) / 100;
-               if (card_type <= NV_40 || (func.log[0] & 1))
-                       duty = divs - duty;
-
-               ret = priv->fan.pwm_set(therm, func.line, divs, duty);
-               return ret;
-       }
+       return priv->fan->get(therm);
+}
 
-       return -ENODEV;
+int
+nouveau_therm_fan_set(struct nouveau_therm *therm, bool immediate, int percent)
+{
+       struct nouveau_therm_priv *priv = (void *)therm;
+       return nouveau_fan_update(priv->fan, immediate, percent);
 }
 
 int
 nouveau_therm_fan_sense(struct nouveau_therm *therm)
 {
+       struct nouveau_therm_priv *priv = (void *)therm;
        struct nouveau_timer *ptimer = nouveau_timer(therm);
        struct nouveau_gpio *gpio = nouveau_gpio(therm);
-       struct dcb_gpio_func func;
        u32 cycles, cur, prev;
        u64 start, end, tach;
 
-       if (gpio->find(gpio, 0, DCB_GPIO_FAN_SENSE, 0xff, &func))
+       if (priv->fan->tach.func == DCB_GPIO_UNUSED)
                return -ENODEV;
 
        /* Time a complete rotation and extrapolate to RPM:
@@ -118,12 +135,12 @@ nouveau_therm_fan_sense(struct nouveau_therm *therm)
         * We get 4 changes (0 -> 1 -> 0 -> 1) per complete rotation.
         */
        start = ptimer->read(ptimer);
-       prev = gpio->get(gpio, 0, func.func, func.line);
+       prev = gpio->get(gpio, 0, priv->fan->tach.func, priv->fan->tach.line);
        cycles = 0;
        do {
                usleep_range(500, 1000); /* supports 0 < rpm < 7500 */
 
-               cur = gpio->get(gpio, 0, func.func, func.line);
+               cur = gpio->get(gpio, 0, priv->fan->tach.func, priv->fan->tach.line);
                if (prev != cur) {
                        if (!start)
                                start = ptimer->read(ptimer);
@@ -141,34 +158,6 @@ nouveau_therm_fan_sense(struct nouveau_therm *therm)
                return 0;
 }
 
-int
-nouveau_therm_fan_set_mode(struct nouveau_therm *therm,
-                          enum nouveau_therm_fan_mode mode)
-{
-       struct nouveau_therm_priv *priv = (void *)therm;
-
-       if (priv->fan.mode == mode)
-               return 0;
-
-       if (mode < FAN_CONTROL_NONE || mode >= FAN_CONTROL_NR)
-               return -EINVAL;
-
-       switch (mode)
-       {
-       case FAN_CONTROL_NONE:
-               nv_info(therm, "switch fan to no-control mode\n");
-               break;
-       case FAN_CONTROL_MANUAL:
-               nv_info(therm, "switch fan to manual mode\n");
-               break;
-       case FAN_CONTROL_NR:
-               break;
-       }
-
-       priv->fan.mode = mode;
-       return 0;
-}
-
 int
 nouveau_therm_fan_user_get(struct nouveau_therm *therm)
 {
@@ -180,55 +169,86 @@ nouveau_therm_fan_user_set(struct nouveau_therm *therm, int percent)
 {
        struct nouveau_therm_priv *priv = (void *)therm;
 
-       if (priv->fan.mode != FAN_CONTROL_MANUAL)
+       if (priv->mode != NOUVEAU_THERM_CTRL_MANUAL)
                return -EINVAL;
 
-       return nouveau_therm_fan_set(therm, percent);
+       return nouveau_therm_fan_set(therm, true, percent);
 }
 
-void
+static void
 nouveau_therm_fan_set_defaults(struct nouveau_therm *therm)
 {
        struct nouveau_therm_priv *priv = (void *)therm;
 
-       priv->bios_fan.pwm_freq = 0;
-       priv->bios_fan.min_duty = 0;
-       priv->bios_fan.max_duty = 100;
+       priv->fan->bios.pwm_freq = 0;
+       priv->fan->bios.min_duty = 0;
+       priv->fan->bios.max_duty = 100;
+       priv->fan->bios.bump_period = 500;
+       priv->fan->bios.slow_down_period = 2000;
+       priv->fan->bios.linear_min_temp = 40;
+       priv->fan->bios.linear_max_temp = 85;
 }
 
-
 static void
 nouveau_therm_fan_safety_checks(struct nouveau_therm *therm)
 {
        struct nouveau_therm_priv *priv = (void *)therm;
 
-       if (priv->bios_fan.min_duty > 100)
-               priv->bios_fan.min_duty = 100;
-       if (priv->bios_fan.max_duty > 100)
-               priv->bios_fan.max_duty = 100;
+       if (priv->fan->bios.min_duty > 100)
+               priv->fan->bios.min_duty = 100;
+       if (priv->fan->bios.max_duty > 100)
+               priv->fan->bios.max_duty = 100;
 
-       if (priv->bios_fan.min_duty > priv->bios_fan.max_duty)
-               priv->bios_fan.min_duty = priv->bios_fan.max_duty;
-}
-
-int nouveau_fan_pwm_clock_dummy(struct nouveau_therm *therm)
-{
-       return 1;
+       if (priv->fan->bios.min_duty > priv->fan->bios.max_duty)
+               priv->fan->bios.min_duty = priv->fan->bios.max_duty;
 }
 
 int
 nouveau_therm_fan_ctor(struct nouveau_therm *therm)
 {
        struct nouveau_therm_priv *priv = (void *)therm;
+       struct nouveau_gpio *gpio = nouveau_gpio(therm);
        struct nouveau_bios *bios = nouveau_bios(therm);
+       struct dcb_gpio_func func;
+       int ret;
 
+       /* attempt to locate a drivable fan, and determine control method */
+       ret = gpio->find(gpio, 0, DCB_GPIO_FAN, 0xff, &func);
+       if (ret == 0) {
+               if (func.log[0] & DCB_GPIO_LOG_DIR_IN) {
+                       nv_debug(therm, "GPIO_FAN is in input mode\n");
+                       ret = -EINVAL;
+               } else {
+                       ret = nouveau_fanpwm_create(therm, &func);
+                       if (ret != 0)
+                               ret = nouveau_fantog_create(therm, &func);
+               }
+       }
+
+       /* no controllable fan found, create a dummy fan module */
+       if (ret != 0) {
+               ret = nouveau_fannil_create(therm);
+               if (ret)
+                       return ret;
+       }
+
+       nv_info(therm, "FAN control: %s\n", priv->fan->type);
+
+       /* attempt to detect a tachometer connection */
+       ret = gpio->find(gpio, 0, DCB_GPIO_FAN_SENSE, 0xff, &priv->fan->tach);
+       if (ret)
+               priv->fan->tach.func = DCB_GPIO_UNUSED;
+
+       /* initialise fan bump/slow update handling */
+       priv->fan->parent = therm;
+       nouveau_alarm_init(&priv->fan->alarm, nouveau_fan_alarm);
+       spin_lock_init(&priv->fan->lock);
+
+       /* other random init... */
        nouveau_therm_fan_set_defaults(therm);
-       nvbios_perf_fan_parse(bios, &priv->bios_perf_fan);
-       if (nvbios_therm_fan_parse(bios, &priv->bios_fan))
+       nvbios_perf_fan_parse(bios, &priv->fan->perf);
+       if (nvbios_therm_fan_parse(bios, &priv->fan->bios))
                nv_error(therm, "parsing the thermal table failed\n");
        nouveau_therm_fan_safety_checks(therm);
-
-       nouveau_therm_fan_set_mode(therm, FAN_CONTROL_NONE);
-
        return 0;
 }
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/fannil.c b/drivers/gpu/drm/nouveau/core/subdev/therm/fannil.c
new file mode 100644 (file)
index 0000000..b78c182
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "priv.h"
+
+static int
+nouveau_fannil_get(struct nouveau_therm *therm)
+{
+       return -ENODEV;
+}
+
+static int
+nouveau_fannil_set(struct nouveau_therm *therm, int percent)
+{
+       return -ENODEV;
+}
+
+int
+nouveau_fannil_create(struct nouveau_therm *therm)
+{
+       struct nouveau_therm_priv *tpriv = (void *)therm;
+       struct nouveau_fan *priv;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       tpriv->fan = priv;
+       if (!priv)
+               return -ENOMEM;
+
+       priv->type = "none / external";
+       priv->get = nouveau_fannil_get;
+       priv->set = nouveau_fannil_set;
+       return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c b/drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c
new file mode 100644 (file)
index 0000000..5f71db8
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ *         Martin Peres
+ */
+
+#include <core/option.h>
+#include <subdev/gpio.h>
+
+#include "priv.h"
+
+struct nouveau_fanpwm_priv {
+       struct nouveau_fan base;
+       struct dcb_gpio_func func;
+};
+
+static int
+nouveau_fanpwm_get(struct nouveau_therm *therm)
+{
+       struct nouveau_therm_priv *tpriv = (void *)therm;
+       struct nouveau_fanpwm_priv *priv = (void *)tpriv->fan;
+       struct nouveau_gpio *gpio = nouveau_gpio(therm);
+       int card_type = nv_device(therm)->card_type;
+       u32 divs, duty;
+       int ret;
+
+       ret = therm->pwm_get(therm, priv->func.line, &divs, &duty);
+       if (ret == 0 && divs) {
+               divs = max(divs, duty);
+               if (card_type <= NV_40 || (priv->func.log[0] & 1))
+                       duty = divs - duty;
+               return (duty * 100) / divs;
+       }
+
+       return gpio->get(gpio, 0, priv->func.func, priv->func.line) * 100;
+}
+
+static int
+nouveau_fanpwm_set(struct nouveau_therm *therm, int percent)
+{
+       struct nouveau_therm_priv *tpriv = (void *)therm;
+       struct nouveau_fanpwm_priv *priv = (void *)tpriv->fan;
+       int card_type = nv_device(therm)->card_type;
+       u32 divs, duty;
+       int ret;
+
+       divs = priv->base.perf.pwm_divisor;
+       if (priv->base.bios.pwm_freq) {
+               divs = 1;
+               if (therm->pwm_clock)
+                       divs = therm->pwm_clock(therm);
+               divs /= priv->base.bios.pwm_freq;
+       }
+
+       duty = ((divs * percent) + 99) / 100;
+       if (card_type <= NV_40 || (priv->func.log[0] & 1))
+               duty = divs - duty;
+
+       ret = therm->pwm_set(therm, priv->func.line, divs, duty);
+       if (ret == 0)
+               ret = therm->pwm_ctrl(therm, priv->func.line, true);
+       return ret;
+}
+
+int
+nouveau_fanpwm_create(struct nouveau_therm *therm, struct dcb_gpio_func *func)
+{
+       struct nouveau_device *device = nv_device(therm);
+       struct nouveau_therm_priv *tpriv = (void *)therm;
+       struct nouveau_fanpwm_priv *priv;
+       u32 divs, duty;
+
+       if (!nouveau_boolopt(device->cfgopt, "NvFanPWM", func->param) ||
+           !therm->pwm_ctrl ||
+            therm->pwm_get(therm, func->line, &divs, &duty) == -ENODEV)
+               return -ENODEV;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       tpriv->fan = &priv->base;
+       if (!priv)
+               return -ENOMEM;
+
+       priv->base.type = "PWM";
+       priv->base.get = nouveau_fanpwm_get;
+       priv->base.set = nouveau_fanpwm_set;
+       priv->func = *func;
+       return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/fantog.c b/drivers/gpu/drm/nouveau/core/subdev/therm/fantog.c
new file mode 100644 (file)
index 0000000..e601773
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2012 The Nouveau community
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Martin Peres
+ */
+
+#include "priv.h"
+
+#include <core/object.h>
+#include <core/device.h>
+
+#include <subdev/gpio.h>
+#include <subdev/timer.h>
+
+struct nouveau_fantog_priv {
+       struct nouveau_fan base;
+       struct nouveau_alarm alarm;
+       spinlock_t lock;
+       u32 period_us;
+       u32 percent;
+       struct dcb_gpio_func func;
+};
+
+static void
+nouveau_fantog_update(struct nouveau_fantog_priv *priv, int percent)
+{
+       struct nouveau_therm_priv *tpriv = (void *)priv->base.parent;
+       struct nouveau_timer *ptimer = nouveau_timer(tpriv);
+       struct nouveau_gpio *gpio = nouveau_gpio(tpriv);
+       unsigned long flags;
+       int duty;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       if (percent < 0)
+               percent = priv->percent;
+       priv->percent = percent;
+
+       duty = !gpio->get(gpio, 0, DCB_GPIO_FAN, 0xff);
+       gpio->set(gpio, 0, DCB_GPIO_FAN, 0xff, duty);
+
+       if (list_empty(&priv->alarm.head) && percent != (duty * 100)) {
+               u64 next_change = (percent * priv->period_us) / 100;
+               if (!duty)
+                       next_change = priv->period_us - next_change;
+               ptimer->alarm(ptimer, next_change * 1000, &priv->alarm);
+       }
+       spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void
+nouveau_fantog_alarm(struct nouveau_alarm *alarm)
+{
+       struct nouveau_fantog_priv *priv =
+              container_of(alarm, struct nouveau_fantog_priv, alarm);
+       nouveau_fantog_update(priv, -1);
+}
+
+static int
+nouveau_fantog_get(struct nouveau_therm *therm)
+{
+       struct nouveau_therm_priv *tpriv = (void *)therm;
+       struct nouveau_fantog_priv *priv = (void *)tpriv->fan;
+       return priv->percent;
+}
+
+static int
+nouveau_fantog_set(struct nouveau_therm *therm, int percent)
+{
+       struct nouveau_therm_priv *tpriv = (void *)therm;
+       struct nouveau_fantog_priv *priv = (void *)tpriv->fan;
+       if (therm->pwm_ctrl)
+               therm->pwm_ctrl(therm, priv->func.line, false);
+       nouveau_fantog_update(priv, percent);
+       return 0;
+}
+
+int
+nouveau_fantog_create(struct nouveau_therm *therm, struct dcb_gpio_func *func)
+{
+       struct nouveau_therm_priv *tpriv = (void *)therm;
+       struct nouveau_fantog_priv *priv;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       tpriv->fan = &priv->base;
+       if (!priv)
+               return -ENOMEM;
+
+       priv->base.type = "toggle";
+       priv->base.get = nouveau_fantog_get;
+       priv->base.set = nouveau_fantog_set;
+       nouveau_alarm_init(&priv->alarm, nouveau_fantog_alarm);
+       priv->period_us = 100000; /* 10Hz */
+       priv->percent = 100;
+       priv->func = *func;
+       spin_lock_init(&priv->lock);
+       return 0;
+}
index e512ff0aae605e7b4fd069e6654a7420e9cf2253..e24090bac195687c34f9175103b34a9e7e0cb8d5 100644 (file)
@@ -31,7 +31,7 @@ static bool
 probe_monitoring_device(struct nouveau_i2c_port *i2c,
                        struct i2c_board_info *info)
 {
-       struct nouveau_therm_priv *priv = (void *)nouveau_therm(i2c->i2c);
+       struct nouveau_therm_priv *priv = (void *)nouveau_therm(i2c);
        struct i2c_client *client;
 
        request_module("%s%s", I2C_MODULE_PREFIX, info->type);
@@ -53,6 +53,31 @@ probe_monitoring_device(struct nouveau_i2c_port *i2c,
        return true;
 }
 
+static struct i2c_board_info
+nv_board_infos[] = {
+       { I2C_BOARD_INFO("w83l785ts", 0x2d) },
+       { I2C_BOARD_INFO("w83781d", 0x2d) },
+       { I2C_BOARD_INFO("adt7473", 0x2e) },
+       { I2C_BOARD_INFO("adt7473", 0x2d) },
+       { I2C_BOARD_INFO("adt7473", 0x2c) },
+       { I2C_BOARD_INFO("f75375", 0x2e) },
+       { I2C_BOARD_INFO("lm99", 0x4c) },
+       { I2C_BOARD_INFO("lm90", 0x4c) },
+       { I2C_BOARD_INFO("lm90", 0x4d) },
+       { I2C_BOARD_INFO("adm1021", 0x18) },
+       { I2C_BOARD_INFO("adm1021", 0x19) },
+       { I2C_BOARD_INFO("adm1021", 0x1a) },
+       { I2C_BOARD_INFO("adm1021", 0x29) },
+       { I2C_BOARD_INFO("adm1021", 0x2a) },
+       { I2C_BOARD_INFO("adm1021", 0x2b) },
+       { I2C_BOARD_INFO("adm1021", 0x4c) },
+       { I2C_BOARD_INFO("adm1021", 0x4d) },
+       { I2C_BOARD_INFO("adm1021", 0x4e) },
+       { I2C_BOARD_INFO("lm63", 0x18) },
+       { I2C_BOARD_INFO("lm63", 0x4e) },
+       { }
+};
+
 void
 nouveau_therm_ic_ctor(struct nouveau_therm *therm)
 {
@@ -60,29 +85,6 @@ nouveau_therm_ic_ctor(struct nouveau_therm *therm)
        struct nouveau_bios *bios = nouveau_bios(therm);
        struct nouveau_i2c *i2c = nouveau_i2c(therm);
        struct nvbios_extdev_func extdev_entry;
-       struct i2c_board_info info[] = {
-               { I2C_BOARD_INFO("w83l785ts", 0x2d) },
-               { I2C_BOARD_INFO("w83781d", 0x2d) },
-               { I2C_BOARD_INFO("adt7473", 0x2e) },
-               { I2C_BOARD_INFO("adt7473", 0x2d) },
-               { I2C_BOARD_INFO("adt7473", 0x2c) },
-               { I2C_BOARD_INFO("f75375", 0x2e) },
-               { I2C_BOARD_INFO("lm99", 0x4c) },
-               { I2C_BOARD_INFO("lm90", 0x4c) },
-               { I2C_BOARD_INFO("lm90", 0x4d) },
-               { I2C_BOARD_INFO("adm1021", 0x18) },
-               { I2C_BOARD_INFO("adm1021", 0x19) },
-               { I2C_BOARD_INFO("adm1021", 0x1a) },
-               { I2C_BOARD_INFO("adm1021", 0x29) },
-               { I2C_BOARD_INFO("adm1021", 0x2a) },
-               { I2C_BOARD_INFO("adm1021", 0x2b) },
-               { I2C_BOARD_INFO("adm1021", 0x4c) },
-               { I2C_BOARD_INFO("adm1021", 0x4d) },
-               { I2C_BOARD_INFO("adm1021", 0x4e) },
-               { I2C_BOARD_INFO("lm63", 0x18) },
-               { I2C_BOARD_INFO("lm63", 0x4e) },
-               { }
-       };
 
        if (!nvbios_extdev_find(bios, NVBIOS_EXTDEV_LM89, &extdev_entry)) {
                struct i2c_board_info board[] = {
@@ -111,6 +113,6 @@ nouveau_therm_ic_ctor(struct nouveau_therm *therm)
        /* The vbios doesn't provide the address of an exisiting monitoring
           device. Let's try our static list.
         */
-       i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device", info,
-                     probe_monitoring_device);
+       i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device",
+                     nv_board_infos, probe_monitoring_device);
 }
index fcf2cfe731d668cf64ec5a65ef63d50bdab877a7..0f5363edb96435ec8fb992abab3479a462bcdf88 100644 (file)
 
 #include "priv.h"
 
+struct nv40_therm_priv {
+       struct nouveau_therm_priv base;
+};
+
 static int
 nv40_sensor_setup(struct nouveau_therm *therm)
 {
@@ -34,6 +38,7 @@ nv40_sensor_setup(struct nouveau_therm *therm)
        if (device->chipset >= 0x46) {
                nv_mask(therm, 0x15b8, 0x80000000, 0);
                nv_wr32(therm, 0x15b0, 0x80003fff);
+               mdelay(10); /* wait for the temperature to stabilize */
                return nv_rd32(therm, 0x15b4) & 0x3fff;
        } else {
                nv_wr32(therm, 0x15b0, 0xff);
@@ -75,7 +80,20 @@ nv40_temp_get(struct nouveau_therm *therm)
        return core_temp;
 }
 
-int
+static int
+nv40_fan_pwm_ctrl(struct nouveau_therm *therm, int line, bool enable)
+{
+       u32 mask = enable ? 0x80000000 : 0x0000000;
+       if      (line == 2) nv_mask(therm, 0x0010f0, 0x80000000, mask);
+       else if (line == 9) nv_mask(therm, 0x0015f4, 0x80000000, mask);
+       else {
+               nv_error(therm, "unknown pwm ctrl for gpio %d\n", line);
+               return -ENODEV;
+       }
+       return 0;
+}
+
+static int
 nv40_fan_pwm_get(struct nouveau_therm *therm, int line, u32 *divs, u32 *duty)
 {
        if (line == 2) {
@@ -101,15 +119,15 @@ nv40_fan_pwm_get(struct nouveau_therm *therm, int line, u32 *divs, u32 *duty)
        return -EINVAL;
 }
 
-int
+static int
 nv40_fan_pwm_set(struct nouveau_therm *therm, int line, u32 divs, u32 duty)
 {
        if (line == 2) {
-               nv_wr32(therm, 0x0010f0, 0x80000000 | (duty << 16) | divs);
+               nv_mask(therm, 0x0010f0, 0x7fff7fff, (duty << 16) | divs);
        } else
        if (line == 9) {
                nv_wr32(therm, 0x0015f8, divs);
-               nv_wr32(therm, 0x0015f4, duty | 0x80000000);
+               nv_mask(therm, 0x0015f4, 0x7fffffff, duty);
        } else {
                nv_error(therm, "unknown pwm ctrl for gpio %d\n", line);
                return -ENODEV;
@@ -118,37 +136,51 @@ nv40_fan_pwm_set(struct nouveau_therm *therm, int line, u32 divs, u32 duty)
        return 0;
 }
 
+static void
+nv40_therm_intr(struct nouveau_subdev *subdev)
+{
+       struct nouveau_therm *therm = nouveau_therm(subdev);
+       uint32_t stat = nv_rd32(therm, 0x1100);
+
+       /* traitement */
+
+       /* ack all IRQs */
+       nv_wr32(therm, 0x1100, 0x70000);
+
+       nv_error(therm, "THERM received an IRQ: stat = %x\n", stat);
+}
+
 static int
 nv40_therm_ctor(struct nouveau_object *parent,
-                  struct nouveau_object *engine,
-                  struct nouveau_oclass *oclass, void *data, u32 size,
-                  struct nouveau_object **pobject)
+               struct nouveau_object *engine,
+               struct nouveau_oclass *oclass, void *data, u32 size,
+               struct nouveau_object **pobject)
 {
-       struct nouveau_therm_priv *priv;
-       struct nouveau_therm *therm;
+       struct nv40_therm_priv *priv;
        int ret;
 
        ret = nouveau_therm_create(parent, engine, oclass, &priv);
        *pobject = nv_object(priv);
-       therm = (void *) priv;
        if (ret)
                return ret;
 
-       nouveau_therm_ic_ctor(therm);
-       nouveau_therm_sensor_ctor(therm);
-       nouveau_therm_fan_ctor(therm);
+       priv->base.base.pwm_ctrl = nv40_fan_pwm_ctrl;
+       priv->base.base.pwm_get = nv40_fan_pwm_get;
+       priv->base.base.pwm_set = nv40_fan_pwm_set;
+       priv->base.base.temp_get = nv40_temp_get;
+       priv->base.sensor.program_alarms = nouveau_therm_program_alarms_polling;
+       nv_subdev(priv)->intr = nv40_therm_intr;
+       return nouveau_therm_preinit(&priv->base.base);
+}
 
-       priv->fan.pwm_get = nv40_fan_pwm_get;
-       priv->fan.pwm_set = nv40_fan_pwm_set;
+static int
+nv40_therm_init(struct nouveau_object *object)
+{
+       struct nouveau_therm *therm = (void *)object;
 
-       therm->temp_get = nv40_temp_get;
-       therm->fan_get = nouveau_therm_fan_user_get;
-       therm->fan_set = nouveau_therm_fan_user_set;
-       therm->fan_sense = nouveau_therm_fan_sense;
-       therm->attr_get = nouveau_therm_attr_get;
-       therm->attr_set = nouveau_therm_attr_set;
+       nv40_sensor_setup(therm);
 
-       return 0;
+       return _nouveau_therm_init(object);
 }
 
 struct nouveau_oclass
@@ -157,7 +189,7 @@ nv40_therm_oclass = {
        .ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv40_therm_ctor,
                .dtor = _nouveau_therm_dtor,
-               .init = nouveau_therm_init,
-               .fini = nouveau_therm_fini,
+               .init = nv40_therm_init,
+               .fini = _nouveau_therm_fini,
        },
-};
\ No newline at end of file
+};
index 9360ddd469e727cae0f053dd1a0cd7e96c894283..86632cbd65cea4ccabff257c19838f0f82efb931 100644 (file)
 
 #include "priv.h"
 
+struct nv50_therm_priv {
+       struct nouveau_therm_priv base;
+};
+
 static int
 pwm_info(struct nouveau_therm *therm, int *line, int *ctrl, int *indx)
 {
@@ -50,6 +54,16 @@ pwm_info(struct nouveau_therm *therm, int *line, int *ctrl, int *indx)
        return 0;
 }
 
+int
+nv50_fan_pwm_ctrl(struct nouveau_therm *therm, int line, bool enable)
+{
+       u32 data = enable ? 0x00000001 : 0x00000000;
+       int ctrl, id, ret = pwm_info(therm, &line, &ctrl, &id);
+       if (ret == 0)
+               nv_mask(therm, ctrl, 0x00010001 << line, data << line);
+       return ret;
+}
+
 int
 nv50_fan_pwm_get(struct nouveau_therm *therm, int line, u32 *divs, u32 *duty)
 {
@@ -73,7 +87,6 @@ nv50_fan_pwm_set(struct nouveau_therm *therm, int line, u32 divs, u32 duty)
        if (ret)
                return ret;
 
-       nv_mask(therm, ctrl, 0x00010001 << line, 0x00000001 << line);
        nv_wr32(therm, 0x00e114 + (id * 8), divs);
        nv_wr32(therm, 0x00e118 + (id * 8), duty | 0x80000000);
        return 0;
@@ -111,38 +124,178 @@ nv50_temp_get(struct nouveau_therm *therm)
        return nv_rd32(therm, 0x20400);
 }
 
+static void
+nv50_therm_program_alarms(struct nouveau_therm *therm)
+{
+       struct nouveau_therm_priv *priv = (void *)therm;
+       struct nvbios_therm_sensor *sensor = &priv->bios_sensor;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->sensor.alarm_program_lock, flags);
+
+       /* enable RISING and FALLING IRQs for shutdown, THRS 0, 1, 2 and 4 */
+       nv_wr32(therm, 0x20000, 0x000003ff);
+
+       /* shutdown: The computer should be shutdown when reached */
+       nv_wr32(therm, 0x20484, sensor->thrs_shutdown.hysteresis);
+       nv_wr32(therm, 0x20480, sensor->thrs_shutdown.temp);
+
+       /* THRS_1 : fan boost*/
+       nv_wr32(therm, 0x204c4, sensor->thrs_fan_boost.temp);
+
+       /* THRS_2 : critical */
+       nv_wr32(therm, 0x204c0, sensor->thrs_critical.temp);
+
+       /* THRS_4 : down clock */
+       nv_wr32(therm, 0x20414, sensor->thrs_down_clock.temp);
+       spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags);
+
+       nv_info(therm,
+               "Programmed thresholds [ %d(%d), %d(%d), %d(%d), %d(%d) ]\n",
+               sensor->thrs_fan_boost.temp, sensor->thrs_fan_boost.hysteresis,
+               sensor->thrs_down_clock.temp,
+               sensor->thrs_down_clock.hysteresis,
+               sensor->thrs_critical.temp, sensor->thrs_critical.hysteresis,
+               sensor->thrs_shutdown.temp, sensor->thrs_shutdown.hysteresis);
+
+}
+
+/* must be called with alarm_program_lock taken ! */
+static void
+nv50_therm_threshold_hyst_emulation(struct nouveau_therm *therm,
+                                  uint32_t thrs_reg, u8 status_bit,
+                                  const struct nvbios_therm_threshold *thrs,
+                                  enum nouveau_therm_thrs thrs_name)
+{
+       enum nouveau_therm_thrs_direction direction;
+       enum nouveau_therm_thrs_state prev_state, new_state;
+       int temp, cur;
+
+       prev_state = nouveau_therm_sensor_get_threshold_state(therm, thrs_name);
+       temp = nv_rd32(therm, thrs_reg);
+
+       /* program the next threshold */
+       if (temp == thrs->temp) {
+               nv_wr32(therm, thrs_reg, thrs->temp - thrs->hysteresis);
+               new_state = NOUVEAU_THERM_THRS_HIGHER;
+       } else {
+               nv_wr32(therm, thrs_reg, thrs->temp);
+               new_state = NOUVEAU_THERM_THRS_LOWER;
+       }
+
+       /* fix the state (in case someone reprogrammed the alarms) */
+       cur = therm->temp_get(therm);
+       if (new_state == NOUVEAU_THERM_THRS_LOWER && cur > thrs->temp)
+               new_state = NOUVEAU_THERM_THRS_HIGHER;
+       else if (new_state == NOUVEAU_THERM_THRS_HIGHER &&
+               cur < thrs->temp - thrs->hysteresis)
+               new_state = NOUVEAU_THERM_THRS_LOWER;
+       nouveau_therm_sensor_set_threshold_state(therm, thrs_name, new_state);
+
+       /* find the direction */
+       if (prev_state < new_state)
+               direction = NOUVEAU_THERM_THRS_RISING;
+       else if (prev_state > new_state)
+               direction = NOUVEAU_THERM_THRS_FALLING;
+       else
+               return;
+
+       /* advertise a change in direction */
+       nouveau_therm_sensor_event(therm, thrs_name, direction);
+}
+
+static void
+nv50_therm_intr(struct nouveau_subdev *subdev)
+{
+       struct nouveau_therm *therm = nouveau_therm(subdev);
+       struct nouveau_therm_priv *priv = (void *)therm;
+       struct nvbios_therm_sensor *sensor = &priv->bios_sensor;
+       unsigned long flags;
+       uint32_t intr;
+
+       spin_lock_irqsave(&priv->sensor.alarm_program_lock, flags);
+
+       intr = nv_rd32(therm, 0x20100);
+
+       /* THRS_4: downclock */
+       if (intr & 0x002) {
+               nv50_therm_threshold_hyst_emulation(therm, 0x20414, 24,
+                                                 &sensor->thrs_down_clock,
+                                                 NOUVEAU_THERM_THRS_DOWNCLOCK);
+               intr &= ~0x002;
+       }
+
+       /* shutdown */
+       if (intr & 0x004) {
+               nv50_therm_threshold_hyst_emulation(therm, 0x20480, 20,
+                                                  &sensor->thrs_shutdown,
+                                                  NOUVEAU_THERM_THRS_SHUTDOWN);
+               intr &= ~0x004;
+       }
+
+       /* THRS_1 : fan boost */
+       if (intr & 0x008) {
+               nv50_therm_threshold_hyst_emulation(therm, 0x204c4, 21,
+                                                  &sensor->thrs_fan_boost,
+                                                  NOUVEAU_THERM_THRS_FANBOOST);
+               intr &= ~0x008;
+       }
+
+       /* THRS_2 : critical */
+       if (intr & 0x010) {
+               nv50_therm_threshold_hyst_emulation(therm, 0x204c0, 22,
+                                                  &sensor->thrs_critical,
+                                                  NOUVEAU_THERM_THRS_CRITICAL);
+               intr &= ~0x010;
+       }
+
+       if (intr)
+               nv_error(therm, "unhandled intr 0x%08x\n", intr);
+
+       /* ACK everything */
+       nv_wr32(therm, 0x20100, 0xffffffff);
+       nv_wr32(therm, 0x1100, 0x10000); /* PBUS */
+
+       spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags);
+}
+
 static int
 nv50_therm_ctor(struct nouveau_object *parent,
-                  struct nouveau_object *engine,
-                  struct nouveau_oclass *oclass, void *data, u32 size,
-                  struct nouveau_object **pobject)
+               struct nouveau_object *engine,
+               struct nouveau_oclass *oclass, void *data, u32 size,
+               struct nouveau_object **pobject)
 {
-       struct nouveau_therm_priv *priv;
-       struct nouveau_therm *therm;
+       struct nv50_therm_priv *priv;
        int ret;
 
        ret = nouveau_therm_create(parent, engine, oclass, &priv);
        *pobject = nv_object(priv);
-       therm = (void *) priv;
        if (ret)
                return ret;
 
-       nouveau_therm_ic_ctor(therm);
-       nouveau_therm_sensor_ctor(therm);
-       nouveau_therm_fan_ctor(therm);
+       priv->base.base.pwm_ctrl = nv50_fan_pwm_ctrl;
+       priv->base.base.pwm_get = nv50_fan_pwm_get;
+       priv->base.base.pwm_set = nv50_fan_pwm_set;
+       priv->base.base.pwm_clock = nv50_fan_pwm_clock;
+       priv->base.base.temp_get = nv50_temp_get;
+       priv->base.sensor.program_alarms = nv50_therm_program_alarms;
+       nv_subdev(priv)->intr = nv50_therm_intr;
 
-       priv->fan.pwm_get = nv50_fan_pwm_get;
-       priv->fan.pwm_set = nv50_fan_pwm_set;
-       priv->fan.pwm_clock = nv50_fan_pwm_clock;
+       /* init the thresholds */
+       nouveau_therm_sensor_set_threshold_state(&priv->base.base,
+                                                NOUVEAU_THERM_THRS_SHUTDOWN,
+                                                NOUVEAU_THERM_THRS_LOWER);
+       nouveau_therm_sensor_set_threshold_state(&priv->base.base,
+                                                NOUVEAU_THERM_THRS_FANBOOST,
+                                                NOUVEAU_THERM_THRS_LOWER);
+       nouveau_therm_sensor_set_threshold_state(&priv->base.base,
+                                                NOUVEAU_THERM_THRS_CRITICAL,
+                                                NOUVEAU_THERM_THRS_LOWER);
+       nouveau_therm_sensor_set_threshold_state(&priv->base.base,
+                                                NOUVEAU_THERM_THRS_DOWNCLOCK,
+                                                NOUVEAU_THERM_THRS_LOWER);
 
-       therm->temp_get = nv50_temp_get;
-       therm->fan_get = nouveau_therm_fan_user_get;
-       therm->fan_set = nouveau_therm_fan_user_set;
-       therm->fan_sense = nouveau_therm_fan_sense;
-       therm->attr_get = nouveau_therm_attr_get;
-       therm->attr_set = nouveau_therm_attr_set;
-
-       return 0;
+       return nouveau_therm_preinit(&priv->base.base);
 }
 
 struct nouveau_oclass
@@ -151,7 +304,7 @@ nv50_therm_oclass = {
        .ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv50_therm_ctor,
                .dtor = _nouveau_therm_dtor,
-               .init = nouveau_therm_init,
-               .fini = nouveau_therm_fini,
+               .init = _nouveau_therm_init,
+               .fini = _nouveau_therm_fini,
        },
 };
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c
new file mode 100644 (file)
index 0000000..2dcc543
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/gpio.h>
+
+#include "priv.h"
+
+struct nva3_therm_priv {
+       struct nouveau_therm_priv base;
+};
+
+int
+nva3_therm_fan_sense(struct nouveau_therm *therm)
+{
+       u32 tach = nv_rd32(therm, 0x00e728) & 0x0000ffff;
+       u32 ctrl = nv_rd32(therm, 0x00e720);
+       if (ctrl & 0x00000001)
+               return tach * 60;
+       return -ENODEV;
+}
+
+static int
+nva3_therm_init(struct nouveau_object *object)
+{
+       struct nva3_therm_priv *priv = (void *)object;
+       struct dcb_gpio_func *tach = &priv->base.fan->tach;
+       int ret;
+
+       ret = nouveau_therm_init(&priv->base.base);
+       if (ret)
+               return ret;
+
+       /* enable fan tach, count revolutions per-second */
+       nv_mask(priv, 0x00e720, 0x00000003, 0x00000002);
+       if (tach->func != DCB_GPIO_UNUSED) {
+               nv_wr32(priv, 0x00e724, nv_device(priv)->crystal * 1000);
+               nv_mask(priv, 0x00e720, 0x001f0000, tach->line << 16);
+               nv_mask(priv, 0x00e720, 0x00000001, 0x00000001);
+       }
+       nv_mask(priv, 0x00e720, 0x00000002, 0x00000000);
+
+       return 0;
+}
+
+static int
+nva3_therm_ctor(struct nouveau_object *parent,
+               struct nouveau_object *engine,
+               struct nouveau_oclass *oclass, void *data, u32 size,
+               struct nouveau_object **pobject)
+{
+       struct nva3_therm_priv *priv;
+       int ret;
+
+       ret = nouveau_therm_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       priv->base.base.pwm_ctrl = nv50_fan_pwm_ctrl;
+       priv->base.base.pwm_get = nv50_fan_pwm_get;
+       priv->base.base.pwm_set = nv50_fan_pwm_set;
+       priv->base.base.pwm_clock = nv50_fan_pwm_clock;
+       priv->base.base.temp_get = nv50_temp_get;
+       priv->base.base.fan_sense = nva3_therm_fan_sense;
+       priv->base.sensor.program_alarms = nouveau_therm_program_alarms_polling;
+       return nouveau_therm_preinit(&priv->base.base);
+}
+
+struct nouveau_oclass
+nva3_therm_oclass = {
+       .handle = NV_SUBDEV(THERM, 0xa3),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nva3_therm_ctor,
+               .dtor = _nouveau_therm_dtor,
+               .init = nva3_therm_init,
+               .fini = _nouveau_therm_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c
new file mode 100644 (file)
index 0000000..d7d30ee
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "priv.h"
+
+struct nvd0_therm_priv {
+       struct nouveau_therm_priv base;
+};
+
+static int
+pwm_info(struct nouveau_therm *therm, int line)
+{
+       u32 gpio = nv_rd32(therm, 0x00d610 + (line * 0x04));
+       switch (gpio & 0x000000c0) {
+       case 0x00000000: /* normal mode, possibly pwm forced off by us */
+       case 0x00000040: /* nvio special */
+               switch (gpio & 0x0000001f) {
+               case 0x19: return 1;
+               case 0x1c: return 0;
+               default:
+                       break;
+               }
+       default:
+               break;
+       }
+
+       nv_error(therm, "GPIO %d unknown PWM: 0x%08x\n", line, gpio);
+       return -ENODEV;
+}
+
+static int
+nvd0_fan_pwm_ctrl(struct nouveau_therm *therm, int line, bool enable)
+{
+       u32 data = enable ? 0x00000040 : 0x00000000;
+       int indx = pwm_info(therm, line);
+       if (indx < 0)
+               return indx;
+
+       nv_mask(therm, 0x00d610 + (line * 0x04), 0x000000c0, data);
+       return 0;
+}
+
+static int
+nvd0_fan_pwm_get(struct nouveau_therm *therm, int line, u32 *divs, u32 *duty)
+{
+       int indx = pwm_info(therm, line);
+       if (indx < 0)
+               return indx;
+
+       if (nv_rd32(therm, 0x00d610 + (line * 0x04)) & 0x00000040) {
+               *divs = nv_rd32(therm, 0x00e114 + (indx * 8));
+               *duty = nv_rd32(therm, 0x00e118 + (indx * 8));
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int
+nvd0_fan_pwm_set(struct nouveau_therm *therm, int line, u32 divs, u32 duty)
+{
+       int indx = pwm_info(therm, line);
+       if (indx < 0)
+               return indx;
+
+       nv_wr32(therm, 0x00e114 + (indx * 8), divs);
+       nv_wr32(therm, 0x00e118 + (indx * 8), duty | 0x80000000);
+       return 0;
+}
+
+static int
+nvd0_fan_pwm_clock(struct nouveau_therm *therm)
+{
+       return (nv_device(therm)->crystal * 1000) / 20;
+}
+
+static int
+nvd0_therm_init(struct nouveau_object *object)
+{
+       struct nvd0_therm_priv *priv = (void *)object;
+       int ret;
+
+       ret = nouveau_therm_init(&priv->base.base);
+       if (ret)
+               return ret;
+
+       /* enable fan tach, count revolutions per-second */
+       nv_mask(priv, 0x00e720, 0x00000003, 0x00000002);
+       if (priv->base.fan->tach.func != DCB_GPIO_UNUSED) {
+               nv_mask(priv, 0x00d79c, 0x000000ff, priv->base.fan->tach.line);
+               nv_wr32(priv, 0x00e724, nv_device(priv)->crystal * 1000);
+               nv_mask(priv, 0x00e720, 0x00000001, 0x00000001);
+       }
+       nv_mask(priv, 0x00e720, 0x00000002, 0x00000000);
+
+       return 0;
+}
+
+static int
+nvd0_therm_ctor(struct nouveau_object *parent,
+               struct nouveau_object *engine,
+               struct nouveau_oclass *oclass, void *data, u32 size,
+               struct nouveau_object **pobject)
+{
+       struct nvd0_therm_priv *priv;
+       int ret;
+
+       ret = nouveau_therm_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       priv->base.base.pwm_ctrl = nvd0_fan_pwm_ctrl;
+       priv->base.base.pwm_get = nvd0_fan_pwm_get;
+       priv->base.base.pwm_set = nvd0_fan_pwm_set;
+       priv->base.base.pwm_clock = nvd0_fan_pwm_clock;
+       priv->base.base.temp_get = nv50_temp_get;
+       priv->base.base.fan_sense = nva3_therm_fan_sense;
+       priv->base.sensor.program_alarms = nouveau_therm_program_alarms_polling;
+       return nouveau_therm_preinit(&priv->base.base);
+}
+
+struct nouveau_oclass
+nvd0_therm_oclass = {
+       .handle = NV_SUBDEV(THERM, 0xd0),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvd0_therm_ctor,
+               .dtor = _nouveau_therm_dtor,
+               .init = nvd0_therm_init,
+               .fini = _nouveau_therm_fini,
+       },
+};
index 1c3cd6abc36ebe9de03efd7a73ea7d478b446ca3..06b98706b3fc163460aa807af2a699ec62a8af98 100644 (file)
@@ -1,3 +1,6 @@
+#ifndef __NVTHERM_PRIV_H__
+#define __NVTHERM_PRIV_H__
+
 /*
  * Copyright 2012 The Nouveau community
  *
 #include <subdev/therm.h>
 
 #include <subdev/bios/extdev.h>
+#include <subdev/bios/gpio.h>
 #include <subdev/bios/perf.h>
 #include <subdev/bios/therm.h>
+#include <subdev/timer.h>
+
+struct nouveau_fan {
+       struct nouveau_therm *parent;
+       const char *type;
+
+       struct nvbios_therm_fan bios;
+       struct nvbios_perf_fan perf;
+
+       struct nouveau_alarm alarm;
+       spinlock_t lock;
+       int percent;
+
+       int (*get)(struct nouveau_therm *therm);
+       int (*set)(struct nouveau_therm *therm, int percent);
+
+       struct dcb_gpio_func tach;
+};
+
+enum nouveau_therm_thrs_direction {
+       NOUVEAU_THERM_THRS_FALLING = 0,
+       NOUVEAU_THERM_THRS_RISING = 1
+};
+
+enum nouveau_therm_thrs_state {
+       NOUVEAU_THERM_THRS_LOWER = 0,
+       NOUVEAU_THERM_THRS_HIGHER = 1
+};
+
+enum nouveau_therm_thrs {
+       NOUVEAU_THERM_THRS_FANBOOST = 0,
+       NOUVEAU_THERM_THRS_DOWNCLOCK = 1,
+       NOUVEAU_THERM_THRS_CRITICAL = 2,
+       NOUVEAU_THERM_THRS_SHUTDOWN = 3,
+       NOUVEAU_THERM_THRS_NR
+};
 
 struct nouveau_therm_priv {
        struct nouveau_therm base;
 
+       /* automatic thermal management */
+       struct nouveau_alarm alarm;
+       spinlock_t lock;
+       struct nouveau_therm_trip_point *last_trip;
+       int mode;
+       int suspend;
+
        /* bios */
        struct nvbios_therm_sensor bios_sensor;
-       struct nvbios_therm_fan bios_fan;
-       struct nvbios_perf_fan bios_perf_fan;
 
        /* fan priv */
+       struct nouveau_fan *fan;
+
+       /* alarms priv */
        struct {
-               enum nouveau_therm_fan_mode mode;
-               int percent;
+               spinlock_t alarm_program_lock;
+               struct nouveau_alarm therm_poll_alarm;
+               enum nouveau_therm_thrs_state alarm_state[NOUVEAU_THERM_THRS_NR];
+               void (*program_alarms)(struct nouveau_therm *);
+       } sensor;
 
-               int (*pwm_get)(struct nouveau_therm *, int line, u32*, u32*);
-               int (*pwm_set)(struct nouveau_therm *, int line, u32, u32);
-               int (*pwm_clock)(struct nouveau_therm *);
-       } fan;
+       /* what should be done if the card overheats */
+       struct {
+               void (*downclock)(struct nouveau_therm *, bool active);
+               void (*pause)(struct nouveau_therm *, bool active);
+       } emergency;
 
        /* ic */
        struct i2c_client *ic;
 };
 
-int nouveau_therm_init(struct nouveau_object *object);
-int nouveau_therm_fini(struct nouveau_object *object, bool suspend);
+int nouveau_therm_mode(struct nouveau_therm *therm, int mode);
 int nouveau_therm_attr_get(struct nouveau_therm *therm,
                       enum nouveau_therm_attr_type type);
 int nouveau_therm_attr_set(struct nouveau_therm *therm,
@@ -63,11 +114,35 @@ int nouveau_therm_sensor_ctor(struct nouveau_therm *therm);
 
 int nouveau_therm_fan_ctor(struct nouveau_therm *therm);
 int nouveau_therm_fan_get(struct nouveau_therm *therm);
-int nouveau_therm_fan_set(struct nouveau_therm *therm, int percent);
+int nouveau_therm_fan_set(struct nouveau_therm *therm, bool now, int percent);
 int nouveau_therm_fan_user_get(struct nouveau_therm *therm);
 int nouveau_therm_fan_user_set(struct nouveau_therm *therm, int percent);
-int nouveau_therm_fan_set_mode(struct nouveau_therm *therm,
-                          enum nouveau_therm_fan_mode mode);
-
 
 int nouveau_therm_fan_sense(struct nouveau_therm *therm);
+
+int nouveau_therm_preinit(struct nouveau_therm *);
+
+void nouveau_therm_sensor_set_threshold_state(struct nouveau_therm *therm,
+                                            enum nouveau_therm_thrs thrs,
+                                            enum nouveau_therm_thrs_state st);
+enum nouveau_therm_thrs_state
+nouveau_therm_sensor_get_threshold_state(struct nouveau_therm *therm,
+                                        enum nouveau_therm_thrs thrs);
+void nouveau_therm_sensor_event(struct nouveau_therm *therm,
+                               enum nouveau_therm_thrs thrs,
+                               enum nouveau_therm_thrs_direction dir);
+void nouveau_therm_program_alarms_polling(struct nouveau_therm *therm);
+
+int nv50_fan_pwm_ctrl(struct nouveau_therm *, int, bool);
+int nv50_fan_pwm_get(struct nouveau_therm *, int, u32 *, u32 *);
+int nv50_fan_pwm_set(struct nouveau_therm *, int, u32, u32);
+int nv50_fan_pwm_clock(struct nouveau_therm *);
+int nv50_temp_get(struct nouveau_therm *therm);
+
+int nva3_therm_fan_sense(struct nouveau_therm *);
+
+int nouveau_fanpwm_create(struct nouveau_therm *, struct dcb_gpio_func *);
+int nouveau_fantog_create(struct nouveau_therm *, struct dcb_gpio_func *);
+int nouveau_fannil_create(struct nouveau_therm *);
+
+#endif
index 204282301fb1c2c5e92acdecd71d66016b7ff8c2..b37624af82977543142f7035a2f2ba7b8a89cc63 100644 (file)
@@ -58,11 +58,171 @@ static void
 nouveau_therm_temp_safety_checks(struct nouveau_therm *therm)
 {
        struct nouveau_therm_priv *priv = (void *)therm;
+       struct nvbios_therm_sensor *s = &priv->bios_sensor;
 
        if (!priv->bios_sensor.slope_div)
                priv->bios_sensor.slope_div = 1;
        if (!priv->bios_sensor.offset_den)
                priv->bios_sensor.offset_den = 1;
+
+       /* enforce a minimum hysteresis on thresholds */
+       s->thrs_fan_boost.hysteresis = max_t(u8, s->thrs_fan_boost.hysteresis, 2);
+       s->thrs_down_clock.hysteresis = max_t(u8, s->thrs_down_clock.hysteresis, 2);
+       s->thrs_critical.hysteresis = max_t(u8, s->thrs_critical.hysteresis, 2);
+       s->thrs_shutdown.hysteresis = max_t(u8, s->thrs_shutdown.hysteresis, 2);
+}
+
+/* must be called with alarm_program_lock taken ! */
+void nouveau_therm_sensor_set_threshold_state(struct nouveau_therm *therm,
+                                            enum nouveau_therm_thrs thrs,
+                                            enum nouveau_therm_thrs_state st)
+{
+       struct nouveau_therm_priv *priv = (void *)therm;
+       priv->sensor.alarm_state[thrs] = st;
+}
+
+/* must be called with alarm_program_lock taken ! */
+enum nouveau_therm_thrs_state
+nouveau_therm_sensor_get_threshold_state(struct nouveau_therm *therm,
+                                        enum nouveau_therm_thrs thrs)
+{
+       struct nouveau_therm_priv *priv = (void *)therm;
+       return priv->sensor.alarm_state[thrs];
+}
+
+static void
+nv_poweroff_work(struct work_struct *work)
+{
+       orderly_poweroff(true);
+       kfree(work);
+}
+
+void nouveau_therm_sensor_event(struct nouveau_therm *therm,
+                               enum nouveau_therm_thrs thrs,
+                               enum nouveau_therm_thrs_direction dir)
+{
+       struct nouveau_therm_priv *priv = (void *)therm;
+       bool active;
+       const char *thresolds[] = {
+               "fanboost", "downclock", "critical", "shutdown"
+       };
+       uint8_t temperature = therm->temp_get(therm);
+
+       if (thrs < 0 || thrs > 3)
+               return;
+
+       if (dir == NOUVEAU_THERM_THRS_FALLING)
+               nv_info(therm, "temperature (%u C) went below the '%s' threshold\n",
+                       temperature, thresolds[thrs]);
+       else
+               nv_info(therm, "temperature (%u C) hit the '%s' threshold\n",
+                       temperature, thresolds[thrs]);
+
+       active = (dir == NOUVEAU_THERM_THRS_RISING);
+       switch (thrs) {
+       case NOUVEAU_THERM_THRS_FANBOOST:
+               if (active) {
+                       nouveau_therm_fan_set(therm, true, 100);
+                       nouveau_therm_mode(therm, NOUVEAU_THERM_CTRL_AUTO);
+               }
+               break;
+       case NOUVEAU_THERM_THRS_DOWNCLOCK:
+               if (priv->emergency.downclock)
+                       priv->emergency.downclock(therm, active);
+               break;
+       case NOUVEAU_THERM_THRS_CRITICAL:
+               if (priv->emergency.pause)
+                       priv->emergency.pause(therm, active);
+               break;
+       case NOUVEAU_THERM_THRS_SHUTDOWN:
+               if (active) {
+                       struct work_struct *work;
+
+                       work = kmalloc(sizeof(*work), GFP_ATOMIC);
+                       if (work) {
+                               INIT_WORK(work, nv_poweroff_work);
+                               schedule_work(work);
+                       }
+               }
+               break;
+       case NOUVEAU_THERM_THRS_NR:
+               break;
+       }
+
+}
+
+/* must be called with alarm_program_lock taken ! */
+static void
+nouveau_therm_threshold_hyst_polling(struct nouveau_therm *therm,
+                                  const struct nvbios_therm_threshold *thrs,
+                                  enum nouveau_therm_thrs thrs_name)
+{
+       enum nouveau_therm_thrs_direction direction;
+       enum nouveau_therm_thrs_state prev_state, new_state;
+       int temp = therm->temp_get(therm);
+
+       prev_state = nouveau_therm_sensor_get_threshold_state(therm, thrs_name);
+
+       if (temp >= thrs->temp && prev_state == NOUVEAU_THERM_THRS_LOWER) {
+               direction = NOUVEAU_THERM_THRS_RISING;
+               new_state = NOUVEAU_THERM_THRS_HIGHER;
+       } else if (temp <= thrs->temp - thrs->hysteresis &&
+                       prev_state == NOUVEAU_THERM_THRS_HIGHER) {
+               direction = NOUVEAU_THERM_THRS_FALLING;
+               new_state = NOUVEAU_THERM_THRS_LOWER;
+       } else
+               return; /* nothing to do */
+
+       nouveau_therm_sensor_set_threshold_state(therm, thrs_name, new_state);
+       nouveau_therm_sensor_event(therm, thrs_name, direction);
+}
+
+static void
+alarm_timer_callback(struct nouveau_alarm *alarm)
+{
+       struct nouveau_therm_priv *priv =
+       container_of(alarm, struct nouveau_therm_priv, sensor.therm_poll_alarm);
+       struct nvbios_therm_sensor *sensor = &priv->bios_sensor;
+       struct nouveau_timer *ptimer = nouveau_timer(priv);
+       struct nouveau_therm *therm = &priv->base;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->sensor.alarm_program_lock, flags);
+
+       nouveau_therm_threshold_hyst_polling(therm, &sensor->thrs_fan_boost,
+                                            NOUVEAU_THERM_THRS_FANBOOST);
+
+       nouveau_therm_threshold_hyst_polling(therm, &sensor->thrs_down_clock,
+                                            NOUVEAU_THERM_THRS_DOWNCLOCK);
+
+       nouveau_therm_threshold_hyst_polling(therm, &sensor->thrs_critical,
+                                            NOUVEAU_THERM_THRS_CRITICAL);
+
+       nouveau_therm_threshold_hyst_polling(therm, &sensor->thrs_shutdown,
+                                            NOUVEAU_THERM_THRS_SHUTDOWN);
+
+       /* schedule the next poll in one second */
+       if (list_empty(&alarm->head))
+               ptimer->alarm(ptimer, 1000 * 1000 * 1000, alarm);
+
+       spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags);
+}
+
+void
+nouveau_therm_program_alarms_polling(struct nouveau_therm *therm)
+{
+       struct nouveau_therm_priv *priv = (void *)therm;
+       struct nvbios_therm_sensor *sensor = &priv->bios_sensor;
+
+       nv_info(therm,
+               "programmed thresholds [ %d(%d), %d(%d), %d(%d), %d(%d) ]\n",
+               sensor->thrs_fan_boost.temp, sensor->thrs_fan_boost.hysteresis,
+               sensor->thrs_down_clock.temp,
+               sensor->thrs_down_clock.hysteresis,
+               sensor->thrs_critical.temp, sensor->thrs_critical.hysteresis,
+               sensor->thrs_shutdown.temp, sensor->thrs_shutdown.hysteresis);
+
+       alarm_timer_callback(&priv->sensor.therm_poll_alarm);
 }
 
 int
@@ -71,6 +231,8 @@ nouveau_therm_sensor_ctor(struct nouveau_therm *therm)
        struct nouveau_therm_priv *priv = (void *)therm;
        struct nouveau_bios *bios = nouveau_bios(therm);
 
+       nouveau_alarm_init(&priv->sensor.therm_poll_alarm, alarm_timer_callback);
+
        nouveau_therm_temp_set_defaults(therm);
        if (nvbios_therm_sensor_parse(bios, NVBIOS_THERM_DOMAIN_CORE,
                                      &priv->bios_sensor))
index c26ca9bef6717b18521208a2c48b2e7423d06b86..8e1bae4f12e89d9602a29ff5fb62da4e00b613f6 100644 (file)
@@ -79,7 +79,7 @@ nv04_timer_alarm_trigger(struct nouveau_timer *ptimer)
 
        /* execute any pending alarm handlers */
        list_for_each_entry_safe(alarm, atemp, &exec, head) {
-               list_del(&alarm->head);
+               list_del_init(&alarm->head);
                alarm->func(alarm);
        }
 }
index d0da230d77064bac106865d06df2ef14771cacab..74acf0f8778502208cacc24f862c412ef1feed5d 100644 (file)
@@ -3,7 +3,7 @@
 
 #define ROM_BIOS_PAGE 4096
 
-#if defined(CONFIG_ACPI)
+#if defined(CONFIG_ACPI) && defined(CONFIG_X86)
 bool nouveau_is_optimus(void);
 bool nouveau_is_v1_dsm(void);
 void nouveau_register_dsm_handler(void);
index f65b20a375f68ce1c5461a49aa5bc5cc8187e65f..5d940302d2aae5756e2b5faaa33264a92d91c1fd 100644 (file)
@@ -84,6 +84,8 @@ nv40_backlight_init(struct drm_connector *connector)
        props.max_brightness = 31;
        bd = backlight_device_register("nv_backlight", &connector->kdev, drm,
                                       &nv40_bl_ops, &props);
+       if (IS_ERR(bd))
+               return PTR_ERR(bd);
        drm->backlight = bd;
        bd->props.brightness = nv40_get_intensity(bd);
        backlight_update_status(bd);
index 865eddfa30a73faee6f985b0d7176ceefa7e48f9..50a6dd02f7c5cc45acfcb739fe233c2d56369cbd 100644 (file)
@@ -678,23 +678,6 @@ int run_tmds_table(struct drm_device *dev, struct dcb_output *dcbent, int head,
        return 0;
 }
 
-static void parse_bios_version(struct drm_device *dev, struct nvbios *bios, uint16_t offset)
-{
-       /*
-        * offset + 0  (8 bits): Micro version
-        * offset + 1  (8 bits): Minor version
-        * offset + 2  (8 bits): Chip version
-        * offset + 3  (8 bits): Major version
-        */
-       struct nouveau_drm *drm = nouveau_drm(dev);
-
-       bios->major_version = bios->data[offset + 3];
-       bios->chip_version = bios->data[offset + 2];
-       NV_INFO(drm, "Bios version %02x.%02x.%02x.%02x\n",
-                bios->data[offset + 3], bios->data[offset + 2],
-                bios->data[offset + 1], bios->data[offset]);
-}
-
 static void parse_script_table_pointers(struct nvbios *bios, uint16_t offset)
 {
        /*
@@ -710,12 +693,6 @@ static void parse_script_table_pointers(struct nvbios *bios, uint16_t offset)
         */
 
        bios->init_script_tbls_ptr = ROM16(bios->data[offset]);
-       bios->macro_index_tbl_ptr = ROM16(bios->data[offset + 2]);
-       bios->macro_tbl_ptr = ROM16(bios->data[offset + 4]);
-       bios->condition_tbl_ptr = ROM16(bios->data[offset + 6]);
-       bios->io_condition_tbl_ptr = ROM16(bios->data[offset + 8]);
-       bios->io_flag_condition_tbl_ptr = ROM16(bios->data[offset + 10]);
-       bios->init_function_tbl_ptr = ROM16(bios->data[offset + 12]);
 }
 
 static int parse_bit_A_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
@@ -765,25 +742,6 @@ static int parse_bit_A_tbl_entry(struct drm_device *dev, struct nvbios *bios, st
        return 0;
 }
 
-static int parse_bit_C_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
-{
-       /*
-        * offset + 8  (16 bits): PLL limits table pointer
-        *
-        * There's more in here, but that's unknown.
-        */
-       struct nouveau_drm *drm = nouveau_drm(dev);
-
-       if (bitentry->length < 10) {
-               NV_ERROR(drm, "Do not understand BIT C table\n");
-               return -EINVAL;
-       }
-
-       bios->pll_limit_tbl_ptr = ROM16(bios->data[bitentry->offset + 8]);
-
-       return 0;
-}
-
 static int parse_bit_display_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
 {
        /*
@@ -821,12 +779,6 @@ static int parse_bit_init_tbl_entry(struct drm_device *dev, struct nvbios *bios,
        }
 
        parse_script_table_pointers(bios, bitentry->offset);
-
-       if (bitentry->length >= 16)
-               bios->some_script_ptr = ROM16(bios->data[bitentry->offset + 14]);
-       if (bitentry->length >= 18)
-               bios->init96_tbl_ptr = ROM16(bios->data[bitentry->offset + 16]);
-
        return 0;
 }
 
@@ -852,8 +804,6 @@ static int parse_bit_i_tbl_entry(struct drm_device *dev, struct nvbios *bios, st
                return -EINVAL;
        }
 
-       parse_bios_version(dev, bios, bitentry->offset);
-
        /*
         * bit 4 seems to indicate a mobile bios (doesn't suffer from BMP's
         * Quadro identity crisis), other bits possibly as for BMP feature byte
@@ -1078,9 +1028,6 @@ parse_bit_structure(struct nvbios *bios, const uint16_t bitoffset)
                return ret;
        if (bios->major_version >= 0x60) /* g80+ */
                parse_bit_table(bios, bitoffset, &BIT_TABLE('A', A));
-       ret = parse_bit_table(bios, bitoffset, &BIT_TABLE('C', C));
-       if (ret)
-               return ret;
        parse_bit_table(bios, bitoffset, &BIT_TABLE('D', display));
        ret = parse_bit_table(bios, bitoffset, &BIT_TABLE('I', init));
        if (ret)
@@ -1228,8 +1175,6 @@ static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsi
         */
        bios->feature_byte = bmp[9];
 
-       parse_bios_version(dev, bios, offset + 10);
-
        if (bmp_version_major < 5 || bmp_version_minor < 0x10)
                bios->old_style_init = true;
        legacy_scripts_offset = 18;
@@ -1276,8 +1221,10 @@ static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsi
                bios->fp.lvdsmanufacturerpointer = ROM16(bmp[117]);
                bios->fp.fpxlatemanufacturertableptr = ROM16(bmp[119]);
        }
+#if 0
        if (bmplength > 143)
                bios->pll_limit_tbl_ptr = ROM16(bmp[142]);
+#endif
 
        if (bmplength > 157)
                bios->fp.duallink_transition_clk = ROM16(bmp[156]) * 10;
@@ -1522,6 +1469,7 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
        }
        case DCB_OUTPUT_DP:
                entry->dpconf.sor.link = (conf & 0x00000030) >> 4;
+               entry->extdev = (conf & 0x0000ff00) >> 8;
                switch ((conf & 0x00e00000) >> 21) {
                case 0:
                        entry->dpconf.link_bw = 162000;
@@ -1543,8 +1491,10 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
                }
                break;
        case DCB_OUTPUT_TMDS:
-               if (dcb->version >= 0x40)
+               if (dcb->version >= 0x40) {
                        entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4;
+                       entry->extdev = (conf & 0x0000ff00) >> 8;
+               }
                else if (dcb->version >= 0x30)
                        entry->tmdsconf.slave_addr = (conf & 0x00000700) >> 8;
                else if (dcb->version >= 0x22)
@@ -1937,9 +1887,9 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios)
                if (conn[0] != 0xff) {
                        NV_INFO(drm, "DCB conn %02d: ", idx);
                        if (olddcb_conntab(dev)[3] < 4)
-                               printk("%04x\n", ROM16(conn[0]));
+                               pr_cont("%04x\n", ROM16(conn[0]));
                        else
-                               printk("%08x\n", ROM32(conn[0]));
+                               pr_cont("%08x\n", ROM32(conn[0]));
                }
        }
        dcb_fake_connectors(bios);
@@ -2052,45 +2002,29 @@ uint8_t *nouveau_bios_embedded_edid(struct drm_device *dev)
 static bool NVInitVBIOS(struct drm_device *dev)
 {
        struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nvbios *bios = &drm->vbios;
-
-       memset(bios, 0, sizeof(struct nvbios));
-       spin_lock_init(&bios->lock);
-       bios->dev = dev;
-
-       bios->data = nouveau_bios(drm->device)->data;
-       bios->length = nouveau_bios(drm->device)->size;
-       return true;
-}
+       struct nouveau_bios *bios = nouveau_bios(drm->device);
+       struct nvbios *legacy = &drm->vbios;
+
+       memset(legacy, 0, sizeof(struct nvbios));
+       spin_lock_init(&legacy->lock);
+       legacy->dev = dev;
+
+       legacy->data = bios->data;
+       legacy->length = bios->size;
+       legacy->major_version = bios->version.major;
+       legacy->chip_version = bios->version.chip;
+       if (bios->bit_offset) {
+               legacy->type = NVBIOS_BIT;
+               legacy->offset = bios->bit_offset;
+               return !parse_bit_structure(legacy, legacy->offset + 6);
+       } else
+       if (bios->bmp_offset) {
+               legacy->type = NVBIOS_BMP;
+               legacy->offset = bios->bmp_offset;
+               return !parse_bmp_structure(dev, legacy, legacy->offset);
+       }
 
-static int nouveau_parse_vbios_struct(struct drm_device *dev)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nvbios *bios = &drm->vbios;
-       const uint8_t bit_signature[] = { 0xff, 0xb8, 'B', 'I', 'T' };
-       const uint8_t bmp_signature[] = { 0xff, 0x7f, 'N', 'V', 0x0 };
-       int offset;
-
-       offset = findstr(bios->data, bios->length,
-                                       bit_signature, sizeof(bit_signature));
-       if (offset) {
-               NV_INFO(drm, "BIT BIOS found\n");
-               bios->type = NVBIOS_BIT;
-               bios->offset = offset;
-               return parse_bit_structure(bios, offset + 6);
-       }
-
-       offset = findstr(bios->data, bios->length,
-                                       bmp_signature, sizeof(bmp_signature));
-       if (offset) {
-               NV_INFO(drm, "BMP BIOS found\n");
-               bios->type = NVBIOS_BMP;
-               bios->offset = offset;
-               return parse_bmp_structure(dev, bios, offset);
-       }
-
-       NV_ERROR(drm, "No known BIOS signature found\n");
-       return -ENODEV;
+       return false;
 }
 
 int
@@ -2146,10 +2080,6 @@ nouveau_bios_init(struct drm_device *dev)
        if (!NVInitVBIOS(dev))
                return -ENODEV;
 
-       ret = nouveau_parse_vbios_struct(dev);
-       if (ret)
-               return ret;
-
        ret = parse_dcb_table(dev, bios);
        if (ret)
                return ret;
index f68c54ca422ff7e541283de8e2aaa53d873258f0..7ccd28f11adf0405bb8bedfa3a465ba212a4c818 100644 (file)
@@ -107,20 +107,10 @@ struct nvbios {
        bool old_style_init;
        uint16_t init_script_tbls_ptr;
        uint16_t extra_init_script_tbl_ptr;
-       uint16_t macro_index_tbl_ptr;
-       uint16_t macro_tbl_ptr;
-       uint16_t condition_tbl_ptr;
-       uint16_t io_condition_tbl_ptr;
-       uint16_t io_flag_condition_tbl_ptr;
-       uint16_t init_function_tbl_ptr;
-
-       uint16_t pll_limit_tbl_ptr;
+
        uint16_t ram_restrict_tbl_ptr;
        uint8_t ram_restrict_group_count;
 
-       uint16_t some_script_ptr; /* BIT I + 14 */
-       uint16_t init96_tbl_ptr; /* BIT I + 16 */
-
        struct dcb_table dcb;
 
        struct {
index 69d7b1d0b9d69e2b24f27be89cc7f869669bd4a3..11ca82148edcb13505d9c7d06a303ad25053b04b 100644 (file)
@@ -28,6 +28,7 @@
  */
 
 #include <core/engine.h>
+#include <linux/swiotlb.h>
 
 #include <subdev/fb.h>
 #include <subdev/vm.h>
@@ -300,17 +301,18 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype)
        struct ttm_buffer_object *bo = &nvbo->bo;
        int ret;
 
+       ret = ttm_bo_reserve(bo, false, false, false, 0);
+       if (ret)
+               goto out;
+
        if (nvbo->pin_refcnt && !(memtype & (1 << bo->mem.mem_type))) {
                NV_ERROR(drm, "bo %p pinned elsewhere: 0x%08x vs 0x%08x\n", bo,
                         1 << bo->mem.mem_type, memtype);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
 
        if (nvbo->pin_refcnt++)
-               return 0;
-
-       ret = ttm_bo_reserve(bo, false, false, false, 0);
-       if (ret)
                goto out;
 
        nouveau_bo_placement_set(nvbo, memtype, 0);
@@ -328,10 +330,8 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype)
                        break;
                }
        }
-       ttm_bo_unreserve(bo);
 out:
-       if (unlikely(ret))
-               nvbo->pin_refcnt--;
+       ttm_bo_unreserve(bo);
        return ret;
 }
 
@@ -342,13 +342,13 @@ nouveau_bo_unpin(struct nouveau_bo *nvbo)
        struct ttm_buffer_object *bo = &nvbo->bo;
        int ret;
 
-       if (--nvbo->pin_refcnt)
-               return 0;
-
        ret = ttm_bo_reserve(bo, false, false, false, 0);
        if (ret)
                return ret;
 
+       if (--nvbo->pin_refcnt)
+               goto out;
+
        nouveau_bo_placement_set(nvbo, bo->mem.placement, 0);
 
        ret = nouveau_bo_validate(nvbo, false, false);
@@ -365,6 +365,7 @@ nouveau_bo_unpin(struct nouveau_bo *nvbo)
                }
        }
 
+out:
        ttm_bo_unreserve(bo);
        return ret;
 }
@@ -561,7 +562,7 @@ nouveau_bo_move_accel_cleanup(struct nouveau_channel *chan,
        struct nouveau_fence *fence = NULL;
        int ret;
 
-       ret = nouveau_fence_new(chan, &fence);
+       ret = nouveau_fence_new(chan, false, &fence);
        if (ret)
                return ret;
 
index 25ca37989d2cb24f128060453818b178d02551cb..653dbbbd4fa1d48ca43092bc79ad0970f90c7665 100644 (file)
@@ -28,10 +28,11 @@ struct nouveau_bo {
        struct nouveau_drm_tile *tile;
 
        struct drm_gem_object *gem;
+
+       /* protect by the ttm reservation lock */
        int pin_refcnt;
 
        struct ttm_bo_kmap_obj dma_buf_vmap;
-       int vmapping_count;
 };
 
 static inline struct nouveau_bo *
index 174300b6a02e0d0654d825b69b2c7194c6ffec3a..eaa80a2b81ee9db2bbb89d699d69f4f0a6a19094 100644 (file)
@@ -51,14 +51,15 @@ nouveau_channel_idle(struct nouveau_channel *chan)
        struct nouveau_fence *fence = NULL;
        int ret;
 
-       ret = nouveau_fence_new(chan, &fence);
+       ret = nouveau_fence_new(chan, false, &fence);
        if (!ret) {
                ret = nouveau_fence_wait(fence, false, false);
                nouveau_fence_unref(&fence);
        }
 
        if (ret)
-               NV_ERROR(cli, "failed to idle channel 0x%08x\n", chan->handle);
+               NV_ERROR(cli, "failed to idle channel 0x%08x [%s]\n",
+                        chan->handle, cli->base.name);
        return ret;
 }
 
index e620ba8271b4bc4e5d71edbd8f787a2c14eecf0b..4dd7ae2ac6c692b61e4f63a813bae7b49abcd808 100644 (file)
@@ -55,8 +55,6 @@ MODULE_PARM_DESC(duallink, "Allow dual-link TMDS (default: enabled)");
 static int nouveau_duallink = 1;
 module_param_named(duallink, nouveau_duallink, int, 0400);
 
-static void nouveau_connector_hotplug(void *, int);
-
 struct nouveau_encoder *
 find_encoder(struct drm_connector *connector, int type)
 {
@@ -100,22 +98,6 @@ static void
 nouveau_connector_destroy(struct drm_connector *connector)
 {
        struct nouveau_connector *nv_connector = nouveau_connector(connector);
-       struct nouveau_gpio *gpio;
-       struct nouveau_drm *drm;
-       struct drm_device *dev;
-
-       if (!nv_connector)
-               return;
-
-       dev  = nv_connector->base.dev;
-       drm  = nouveau_drm(dev);
-       gpio = nouveau_gpio(drm->device);
-
-       if (gpio && nv_connector->hpd != DCB_GPIO_UNUSED) {
-               gpio->isr_del(gpio, 0, nv_connector->hpd, 0xff,
-                             nouveau_connector_hotplug, connector);
-       }
-
        kfree(nv_connector->edid);
        drm_sysfs_connector_remove(connector);
        drm_connector_cleanup(connector);
@@ -130,7 +112,6 @@ nouveau_connector_ddc_detect(struct drm_connector *connector,
        struct nouveau_connector *nv_connector = nouveau_connector(connector);
        struct nouveau_drm *drm = nouveau_drm(dev);
        struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
-       struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
        struct nouveau_i2c_port *port = NULL;
        int i, panel = -ENODEV;
 
@@ -160,8 +141,7 @@ nouveau_connector_ddc_detect(struct drm_connector *connector,
                        continue;
                nv_encoder = nouveau_encoder(obj_to_encoder(obj));
 
-               if (nv_encoder->dcb->i2c_index < 0xf)
-                       port = i2c->find(i2c, nv_encoder->dcb->i2c_index);
+               port = nv_encoder->i2c;
                if (port && nv_probe_i2c(port, 0x50)) {
                        *pnv_encoder = nv_encoder;
                        break;
@@ -399,9 +379,10 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force)
                struct edid *edid =
                        (struct edid *)nouveau_bios_embedded_edid(dev);
                if (edid) {
-                       nv_connector->edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
-                       *(nv_connector->edid) = *edid;
-                       status = connector_status_connected;
+                       nv_connector->edid =
+                                       kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
+                       if (nv_connector->edid)
+                               status = connector_status_connected;
                }
        }
 
@@ -911,6 +892,37 @@ nouveau_connector_funcs_lvds = {
        .force = nouveau_connector_force
 };
 
+static void
+nouveau_connector_hotplug_work(struct work_struct *work)
+{
+       struct nouveau_connector *nv_connector =
+               container_of(work, struct nouveau_connector, hpd_work);
+       struct drm_connector *connector = &nv_connector->base;
+       struct drm_device *dev = connector->dev;
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
+       bool plugged = gpio->get(gpio, 0, nv_connector->hpd.func, 0xff);
+
+       NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un",
+                drm_get_connector_name(connector));
+
+       if (plugged)
+               drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
+       else
+               drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+
+       drm_helper_hpd_irq_event(dev);
+}
+
+static int
+nouveau_connector_hotplug(struct nouveau_eventh *event, int index)
+{
+       struct nouveau_connector *nv_connector =
+               container_of(event, struct nouveau_connector, hpd_func);
+       schedule_work(&nv_connector->hpd_work);
+       return NVKM_EVENT_KEEP;
+}
+
 static int
 drm_conntype_from_dcb(enum dcb_connector_type dcb)
 {
@@ -961,6 +973,7 @@ nouveau_connector_create(struct drm_device *dev, int index)
                return ERR_PTR(-ENOMEM);
 
        connector = &nv_connector->base;
+       INIT_WORK(&nv_connector->hpd_work, nouveau_connector_hotplug_work);
        nv_connector->index = index;
 
        /* attempt to parse vbios connector type and hotplug gpio */
@@ -975,8 +988,11 @@ nouveau_connector_create(struct drm_device *dev, int index)
                if (olddcb_conntab(dev)[3] >= 4)
                        entry |= (u32)ROM16(nv_connector->dcb[2]) << 16;
 
-               nv_connector->hpd = ffs((entry & 0x07033000) >> 12);
-               nv_connector->hpd = hpd[nv_connector->hpd];
+               ret = gpio->find(gpio, 0, hpd[ffs((entry & 0x07033000) >> 12)],
+                                DCB_GPIO_UNUSED, &nv_connector->hpd);
+               nv_connector->hpd_func.func = nouveau_connector_hotplug;
+               if (ret)
+                       nv_connector->hpd.func = DCB_GPIO_UNUSED;
 
                nv_connector->type = nv_connector->dcb[0];
                if (drm_conntype_from_dcb(nv_connector->type) ==
@@ -999,7 +1015,7 @@ nouveau_connector_create(struct drm_device *dev, int index)
                }
        } else {
                nv_connector->type = DCB_CONNECTOR_NONE;
-               nv_connector->hpd = DCB_GPIO_UNUSED;
+               nv_connector->hpd.func = DCB_GPIO_UNUSED;
        }
 
        /* no vbios data, or an unknown dcb connector type - attempt to
@@ -1126,31 +1142,9 @@ nouveau_connector_create(struct drm_device *dev, int index)
        }
 
        connector->polled = DRM_CONNECTOR_POLL_CONNECT;
-       if (gpio && nv_connector->hpd != DCB_GPIO_UNUSED) {
-               ret = gpio->isr_add(gpio, 0, nv_connector->hpd, 0xff,
-                                   nouveau_connector_hotplug, connector);
-               if (ret == 0)
-                       connector->polled = DRM_CONNECTOR_POLL_HPD;
-       }
+       if (nv_connector->hpd.func != DCB_GPIO_UNUSED)
+               connector->polled = DRM_CONNECTOR_POLL_HPD;
 
        drm_sysfs_connector_add(connector);
        return connector;
 }
-
-static void
-nouveau_connector_hotplug(void *data, int plugged)
-{
-       struct drm_connector *connector = data;
-       struct drm_device *dev = connector->dev;
-       struct nouveau_drm *drm = nouveau_drm(dev);
-
-       NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un",
-                drm_get_connector_name(connector));
-
-       if (plugged)
-               drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
-       else
-               drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
-
-       drm_helper_hpd_irq_event(dev);
-}
index 20eb84cce9e6a3fe4228146d35129967b606b306..6e399aad491a0b284a059d2292c9092716a489d1 100644 (file)
 #include <drm/drm_edid.h>
 #include "nouveau_crtc.h"
 
+#include <core/event.h>
+
+#include <subdev/bios.h>
+#include <subdev/bios/gpio.h>
+
 struct nouveau_i2c_port;
 
 enum nouveau_underscan_type {
@@ -61,7 +66,10 @@ struct nouveau_connector {
        enum dcb_connector_type type;
        u8 index;
        u8 *dcb;
-       u8 hpd;
+
+       struct dcb_gpio_func hpd;
+       struct work_struct hpd_work;
+       struct nouveau_eventh hpd_func;
 
        int dithering_mode;
        int dithering_depth;
diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.c b/drivers/gpu/drm/nouveau/nouveau_debugfs.c
new file mode 100644 (file)
index 0000000..5392e07
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2009 Red Hat <bskeggs@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/*
+ * Authors:
+ *  Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include "nouveau_debugfs.h"
+#include "nouveau_drm.h"
+
+static int
+nouveau_debugfs_vbios_image(struct seq_file *m, void *data)
+{
+       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct nouveau_drm *drm = nouveau_drm(node->minor->dev);
+       int i;
+
+       for (i = 0; i < drm->vbios.length; i++)
+               seq_printf(m, "%c", drm->vbios.data[i]);
+       return 0;
+}
+
+static struct drm_info_list nouveau_debugfs_list[] = {
+       { "vbios.rom", nouveau_debugfs_vbios_image, 0, NULL },
+};
+#define NOUVEAU_DEBUGFS_ENTRIES ARRAY_SIZE(nouveau_debugfs_list)
+
+int
+nouveau_debugfs_init(struct drm_minor *minor)
+{
+       drm_debugfs_create_files(nouveau_debugfs_list, NOUVEAU_DEBUGFS_ENTRIES,
+                                minor->debugfs_root, minor);
+       return 0;
+}
+
+void
+nouveau_debugfs_takedown(struct drm_minor *minor)
+{
+       drm_debugfs_remove_files(nouveau_debugfs_list, NOUVEAU_DEBUGFS_ENTRIES,
+                                minor);
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.h b/drivers/gpu/drm/nouveau/nouveau_debugfs.h
new file mode 100644 (file)
index 0000000..a62af6f
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef __NOUVEAU_DEBUGFS_H__
+#define __NOUVEAU_DEBUGFS_H__
+
+#include <drm/drmP.h>
+
+#if defined(CONFIG_DEBUG_FS)
+extern int  nouveau_debugfs_init(struct drm_minor *);
+extern void nouveau_debugfs_takedown(struct drm_minor *);
+#else
+static inline int
+nouveau_debugfs_init(struct drm_minor *minor)
+{
+       return 0;
+}
+
+static inline void nouveau_debugfs_takedown(struct drm_minor *minor)
+{
+}
+
+#endif
+
+#endif
index 508b00a2ce0de6d48b80e33d1ef1bc515598b3d2..4610c3a29bbef4d626e5643960cd785950c7056b 100644 (file)
@@ -41,6 +41,8 @@
 #include <subdev/gpio.h>
 #include <engine/disp.h>
 
+#include <core/class.h>
+
 static void
 nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb)
 {
@@ -78,11 +80,6 @@ nouveau_framebuffer_init(struct drm_device *dev,
        struct drm_framebuffer *fb = &nv_fb->base;
        int ret;
 
-       ret = drm_framebuffer_init(dev, fb, &nouveau_framebuffer_funcs);
-       if (ret) {
-               return ret;
-       }
-
        drm_helper_mode_fill_fb_struct(fb, mode_cmd);
        nv_fb->nvbo = nvbo;
 
@@ -125,6 +122,11 @@ nouveau_framebuffer_init(struct drm_device *dev,
                }
        }
 
+       ret = drm_framebuffer_init(dev, fb, &nouveau_framebuffer_funcs);
+       if (ret) {
+               return ret;
+       }
+
        return 0;
 }
 
@@ -231,8 +233,10 @@ nouveau_display_init(struct drm_device *dev)
        /* enable hotplug interrupts */
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                struct nouveau_connector *conn = nouveau_connector(connector);
-               if (gpio)
-                       gpio->irq(gpio, 0, conn->hpd, 0xff, true);
+               if (gpio && conn->hpd.func != DCB_GPIO_UNUSED) {
+                       nouveau_event_get(gpio->events, conn->hpd.line,
+                                        &conn->hpd_func);
+               }
        }
 
        return ret;
@@ -249,37 +253,20 @@ nouveau_display_fini(struct drm_device *dev)
        /* disable hotplug interrupts */
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                struct nouveau_connector *conn = nouveau_connector(connector);
-               if (gpio)
-                       gpio->irq(gpio, 0, conn->hpd, 0xff, false);
+               if (gpio && conn->hpd.func != DCB_GPIO_UNUSED) {
+                       nouveau_event_put(gpio->events, conn->hpd.line,
+                                        &conn->hpd_func);
+               }
        }
 
        drm_kms_helper_poll_disable(dev);
        disp->fini(dev);
 }
 
-static void
-nouveau_display_vblank_notify(void *data, int crtc)
-{
-       drm_handle_vblank(data, crtc);
-}
-
-static void
-nouveau_display_vblank_get(void *data, int crtc)
-{
-       drm_vblank_get(data, crtc);
-}
-
-static void
-nouveau_display_vblank_put(void *data, int crtc)
-{
-       drm_vblank_put(data, crtc);
-}
-
 int
 nouveau_display_create(struct drm_device *dev)
 {
        struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nouveau_disp *pdisp = nouveau_disp(drm->device);
        struct nouveau_display *disp;
        u32 pclass = dev->pdev->class >> 8;
        int ret, gen;
@@ -288,11 +275,6 @@ nouveau_display_create(struct drm_device *dev)
        if (!disp)
                return -ENOMEM;
 
-       pdisp->vblank.data = dev;
-       pdisp->vblank.notify = nouveau_display_vblank_notify;
-       pdisp->vblank.get = nouveau_display_vblank_get;
-       pdisp->vblank.put = nouveau_display_vblank_put;
-
        drm_mode_config_init(dev);
        drm_mode_create_scaling_mode_property(dev);
        drm_mode_create_dvi_i_properties(dev);
@@ -316,17 +298,13 @@ nouveau_display_create(struct drm_device *dev)
                drm_property_create_range(dev, 0, "underscan vborder", 0, 128);
 
        if (gen >= 1) {
+               /* -90..+90 */
                disp->vibrant_hue_property =
-                       drm_property_create(dev, DRM_MODE_PROP_RANGE,
-                                           "vibrant hue", 2);
-               disp->vibrant_hue_property->values[0] = 0;
-               disp->vibrant_hue_property->values[1] = 180; /* -90..+90 */
+                       drm_property_create_range(dev, 0, "vibrant hue", 0, 180);
 
+               /* -100..+100 */
                disp->color_vibrance_property =
-                       drm_property_create(dev, DRM_MODE_PROP_RANGE,
-                                           "color vibrance", 2);
-               disp->color_vibrance_property->values[0] = 0;
-               disp->color_vibrance_property->values[1] = 200; /* -100..+100 */
+                       drm_property_create_range(dev, 0, "color vibrance", 0, 200);
        }
 
        dev->mode_config.funcs = &nouveau_mode_config_funcs;
@@ -478,39 +456,6 @@ nouveau_display_resume(struct drm_device *dev)
        }
 }
 
-int
-nouveau_vblank_enable(struct drm_device *dev, int crtc)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-
-       if (device->card_type >= NV_D0)
-               nv_mask(device, 0x6100c0 + (crtc * 0x800), 1, 1);
-       else
-       if (device->card_type >= NV_50)
-               nv_mask(device, NV50_PDISPLAY_INTR_EN_1, 0,
-                       NV50_PDISPLAY_INTR_EN_1_VBLANK_CRTC_(crtc));
-       else
-               NVWriteCRTC(dev, crtc, NV_PCRTC_INTR_EN_0,
-                           NV_PCRTC_INTR_0_VBLANK);
-
-       return 0;
-}
-
-void
-nouveau_vblank_disable(struct drm_device *dev, int crtc)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-
-       if (device->card_type >= NV_D0)
-               nv_mask(device, 0x6100c0 + (crtc * 0x800), 1, 0);
-       else
-       if (device->card_type >= NV_50)
-               nv_mask(device, NV50_PDISPLAY_INTR_EN_1,
-                       NV50_PDISPLAY_INTR_EN_1_VBLANK_CRTC_(crtc), 0);
-       else
-               NVWriteCRTC(dev, crtc, NV_PCRTC_INTR_EN_0, 0);
-}
-
 static int
 nouveau_page_flip_reserve(struct nouveau_bo *old_bo,
                          struct nouveau_bo *new_bo)
@@ -595,7 +540,7 @@ nouveau_page_flip_emit(struct nouveau_channel *chan,
        }
        FIRE_RING (chan);
 
-       ret = nouveau_fence_new(chan, pfence);
+       ret = nouveau_fence_new(chan, false, pfence);
        if (ret)
                goto fail;
 
index 722548bb3bd3c23da77d1d2e25aeb36817a816d2..1ea3e4734b621f6e103a034f69696ea9b35529df 100644 (file)
@@ -59,9 +59,6 @@ void nouveau_display_fini(struct drm_device *dev);
 int  nouveau_display_suspend(struct drm_device *dev);
 void nouveau_display_resume(struct drm_device *dev);
 
-int  nouveau_vblank_enable(struct drm_device *dev, int crtc);
-void nouveau_vblank_disable(struct drm_device *dev, int crtc);
-
 int  nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
                            struct drm_pending_vblank_event *event);
 int  nouveau_finish_page_flip(struct nouveau_channel *,
index 5c2e22932d1c3cd3537dc4fa3508d8dd1f4963e2..690d5930ce32225a382a472ebc90ac4b0fdb972b 100644 (file)
@@ -191,7 +191,7 @@ WIND_RING(struct nouveau_channel *chan)
 #define NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG                    0x00000002
 #define NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_GEQUAL                0x00000004
 #define NVC0_SUBCHAN_SEMAPHORE_TRIGGER_YIELD                         0x00001000
-#define NV84_SUBCHAN_NOTIFY_INTR                                     0x00000020
+#define NV84_SUBCHAN_UEVENT                                          0x00000020
 #define NV84_SUBCHAN_WRCACHE_FLUSH                                   0x00000024
 #define NV10_SUBCHAN_REF_CNT                                         0x00000050
 #define NVSW_SUBCHAN_PAGE_FLIP                                       0x00000054
index 59838651ee8f9be4536ac0ff29242951d40e3b64..36fd22500569541505870dc1e185eddc138627db 100644 (file)
 #include <subdev/gpio.h>
 #include <subdev/i2c.h>
 
-/******************************************************************************
- * link training
- *****************************************************************************/
-struct dp_state {
-       struct nouveau_i2c_port *auxch;
-       struct nouveau_object *core;
-       struct dcb_output *dcb;
-       int crtc;
-       u8 *dpcd;
-       int link_nr;
-       u32 link_bw;
-       u8  stat[6];
-       u8  conf[4];
-};
-
-static void
-dp_set_link_config(struct drm_device *dev, struct dp_state *dp)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct dcb_output *dcb = dp->dcb;
-       const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
-       const u32 moff = (dp->crtc << 3) | (link << 2) | or;
-       u8 sink[2];
-       u32 data;
-
-       NV_DEBUG(drm, "%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw);
-
-       /* set desired link configuration on the source */
-       data = ((dp->link_bw / 27000) << 8) | dp->link_nr;
-       if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP)
-               data |= NV94_DISP_SOR_DP_LNKCTL_FRAME_ENH;
-
-       nv_call(dp->core, NV94_DISP_SOR_DP_LNKCTL + moff, data);
-
-       /* inform the sink of the new configuration */
-       sink[0] = dp->link_bw / 27000;
-       sink[1] = dp->link_nr;
-       if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP)
-               sink[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
-
-       nv_wraux(dp->auxch, DP_LINK_BW_SET, sink, 2);
-}
-
-static void
-dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 pattern)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct dcb_output *dcb = dp->dcb;
-       const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
-       const u32 moff = (dp->crtc << 3) | (link << 2) | or;
-       u8 sink_tp;
-
-       NV_DEBUG(drm, "training pattern %d\n", pattern);
-
-       nv_call(dp->core, NV94_DISP_SOR_DP_TRAIN + moff, pattern);
-
-       nv_rdaux(dp->auxch, DP_TRAINING_PATTERN_SET, &sink_tp, 1);
-       sink_tp &= ~DP_TRAINING_PATTERN_MASK;
-       sink_tp |= pattern;
-       nv_wraux(dp->auxch, DP_TRAINING_PATTERN_SET, &sink_tp, 1);
-}
-
-static int
-dp_link_train_commit(struct drm_device *dev, struct dp_state *dp)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct dcb_output *dcb = dp->dcb;
-       const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
-       const u32 moff = (dp->crtc << 3) | (link << 2) | or;
-       int i;
-
-       for (i = 0; i < dp->link_nr; i++) {
-               u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf;
-               u8 lpre = (lane & 0x0c) >> 2;
-               u8 lvsw = (lane & 0x03) >> 0;
-
-               dp->conf[i] = (lpre << 3) | lvsw;
-               if (lvsw == DP_TRAIN_VOLTAGE_SWING_1200)
-                       dp->conf[i] |= DP_TRAIN_MAX_SWING_REACHED;
-               if ((lpre << 3) == DP_TRAIN_PRE_EMPHASIS_9_5)
-                       dp->conf[i] |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
-
-               NV_DEBUG(drm, "config lane %d %02x\n", i, dp->conf[i]);
-
-               nv_call(dp->core, NV94_DISP_SOR_DP_DRVCTL(i) + moff, (lvsw << 8) | lpre);
-       }
-
-       return nv_wraux(dp->auxch, DP_TRAINING_LANE0_SET, dp->conf, 4);
-}
-
-static int
-dp_link_train_update(struct drm_device *dev, struct dp_state *dp, u32 delay)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       int ret;
-
-       udelay(delay);
-
-       ret = nv_rdaux(dp->auxch, DP_LANE0_1_STATUS, dp->stat, 6);
-       if (ret)
-               return ret;
-
-       NV_DEBUG(drm, "status %*ph\n", 6, dp->stat);
-       return 0;
-}
-
-static int
-dp_link_train_cr(struct drm_device *dev, struct dp_state *dp)
-{
-       bool cr_done = false, abort = false;
-       int voltage = dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
-       int tries = 0, i;
-
-       dp_set_training_pattern(dev, dp, DP_TRAINING_PATTERN_1);
-
-       do {
-               if (dp_link_train_commit(dev, dp) ||
-                   dp_link_train_update(dev, dp, 100))
-                       break;
-
-               cr_done = true;
-               for (i = 0; i < dp->link_nr; i++) {
-                       u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf;
-                       if (!(lane & DP_LANE_CR_DONE)) {
-                               cr_done = false;
-                               if (dp->conf[i] & DP_TRAIN_MAX_SWING_REACHED)
-                                       abort = true;
-                               break;
-                       }
-               }
-
-               if ((dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK) != voltage) {
-                       voltage = dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
-                       tries = 0;
-               }
-       } while (!cr_done && !abort && ++tries < 5);
-
-       return cr_done ? 0 : -1;
-}
-
-static int
-dp_link_train_eq(struct drm_device *dev, struct dp_state *dp)
-{
-       bool eq_done, cr_done = true;
-       int tries = 0, i;
-
-       dp_set_training_pattern(dev, dp, DP_TRAINING_PATTERN_2);
-
-       do {
-               if (dp_link_train_update(dev, dp, 400))
-                       break;
-
-               eq_done = !!(dp->stat[2] & DP_INTERLANE_ALIGN_DONE);
-               for (i = 0; i < dp->link_nr && eq_done; i++) {
-                       u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf;
-                       if (!(lane & DP_LANE_CR_DONE))
-                               cr_done = false;
-                       if (!(lane & DP_LANE_CHANNEL_EQ_DONE) ||
-                           !(lane & DP_LANE_SYMBOL_LOCKED))
-                               eq_done = false;
-               }
-
-               if (dp_link_train_commit(dev, dp))
-                       break;
-       } while (!eq_done && cr_done && ++tries <= 5);
-
-       return eq_done ? 0 : -1;
-}
-
-static void
-dp_link_train_init(struct drm_device *dev, struct dp_state *dp, bool spread)
-{
-       struct dcb_output *dcb = dp->dcb;
-       const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
-       const u32 moff = (dp->crtc << 3) | (link << 2) | or;
-
-       nv_call(dp->core, NV94_DISP_SOR_DP_TRAIN + moff, (spread ?
-                         NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_ON :
-                         NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_OFF) |
-                         NV94_DISP_SOR_DP_TRAIN_OP_INIT);
-}
-
-static void
-dp_link_train_fini(struct drm_device *dev, struct dp_state *dp)
-{
-       struct dcb_output *dcb = dp->dcb;
-       const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
-       const u32 moff = (dp->crtc << 3) | (link << 2) | or;
-
-       nv_call(dp->core, NV94_DISP_SOR_DP_TRAIN + moff,
-                         NV94_DISP_SOR_DP_TRAIN_OP_FINI);
-}
-
-static bool
-nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate,
-                     struct nouveau_object *core)
-{
-       struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-       struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
-       struct nouveau_connector *nv_connector =
-               nouveau_encoder_connector_get(nv_encoder);
-       struct drm_device *dev = encoder->dev;
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
-       struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
-       const u32 bw_list[] = { 270000, 162000, 0 };
-       const u32 *link_bw = bw_list;
-       struct dp_state dp;
-
-       dp.auxch = i2c->find(i2c, nv_encoder->dcb->i2c_index);
-       if (!dp.auxch)
-               return false;
-
-       dp.core = core;
-       dp.dcb = nv_encoder->dcb;
-       dp.crtc = nv_crtc->index;
-       dp.dpcd = nv_encoder->dp.dpcd;
-
-       /* adjust required bandwidth for 8B/10B coding overhead */
-       datarate = (datarate / 8) * 10;
-
-       /* some sinks toggle hotplug in response to some of the actions
-        * we take during link training (DP_SET_POWER is one), we need
-        * to ignore them for the moment to avoid races.
-        */
-       gpio->irq(gpio, 0, nv_connector->hpd, 0xff, false);
-
-       /* enable down-spreading and execute pre-train script from vbios */
-       dp_link_train_init(dev, &dp, nv_encoder->dp.dpcd[3] & 1);
-
-       /* start off at highest link rate supported by encoder and display */
-       while (*link_bw > nv_encoder->dp.link_bw)
-               link_bw++;
-
-       while (link_bw[0]) {
-               /* find minimum required lane count at this link rate */
-               dp.link_nr = nv_encoder->dp.link_nr;
-               while ((dp.link_nr >> 1) * link_bw[0] > datarate)
-                       dp.link_nr >>= 1;
-
-               /* drop link rate to minimum with this lane count */
-               while ((link_bw[1] * dp.link_nr) > datarate)
-                       link_bw++;
-               dp.link_bw = link_bw[0];
-
-               /* program selected link configuration */
-               dp_set_link_config(dev, &dp);
-
-               /* attempt to train the link at this configuration */
-               memset(dp.stat, 0x00, sizeof(dp.stat));
-               if (!dp_link_train_cr(dev, &dp) &&
-                   !dp_link_train_eq(dev, &dp))
-                       break;
-
-               /* retry at lower rate */
-               link_bw++;
-       }
-
-       /* finish link training */
-       dp_set_training_pattern(dev, &dp, DP_TRAINING_PATTERN_DISABLE);
-
-       /* execute post-train script from vbios */
-       dp_link_train_fini(dev, &dp);
-
-       /* re-enable hotplug detect */
-       gpio->irq(gpio, 0, nv_connector->hpd, 0xff, true);
-       return true;
-}
-
-void
-nouveau_dp_dpms(struct drm_encoder *encoder, int mode, u32 datarate,
-               struct nouveau_object *core)
-{
-       struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-       struct nouveau_drm *drm = nouveau_drm(encoder->dev);
-       struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
-       struct nouveau_i2c_port *auxch;
-       u8 status;
-
-       auxch = i2c->find(i2c, nv_encoder->dcb->i2c_index);
-       if (!auxch)
-               return;
-
-       if (mode == DRM_MODE_DPMS_ON)
-               status = DP_SET_POWER_D0;
-       else
-               status = DP_SET_POWER_D3;
-
-       nv_wraux(auxch, DP_SET_POWER, &status, 1);
-
-       if (mode == DRM_MODE_DPMS_ON)
-               nouveau_dp_link_train(encoder, datarate, core);
-}
-
 static void
 nouveau_dp_probe_oui(struct drm_device *dev, struct nouveau_i2c_port *auxch,
                     u8 *dpcd)
@@ -355,12 +61,11 @@ nouveau_dp_detect(struct drm_encoder *encoder)
        struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
        struct drm_device *dev = encoder->dev;
        struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
        struct nouveau_i2c_port *auxch;
        u8 *dpcd = nv_encoder->dp.dpcd;
        int ret;
 
-       auxch = i2c->find(i2c, nv_encoder->dcb->i2c_index);
+       auxch = nv_encoder->i2c;
        if (!auxch)
                return false;
 
index 8b090f1eb51d7f3ebf284814709ef1f0c472a774..8a03c58ae9882a0c435097d969e1d0678362c64d 100644 (file)
@@ -34,6 +34,8 @@
 #include <subdev/device.h>
 #include <subdev/vm.h>
 
+#include <engine/disp.h>
+
 #include "nouveau_drm.h"
 #include "nouveau_irq.h"
 #include "nouveau_dma.h"
@@ -48,6 +50,7 @@
 #include "nouveau_abi16.h"
 #include "nouveau_fbcon.h"
 #include "nouveau_fence.h"
+#include "nouveau_debugfs.h"
 
 MODULE_PARM_DESC(config, "option string to pass to driver core");
 static char *nouveau_config;
@@ -68,6 +71,32 @@ module_param_named(modeset, nouveau_modeset, int, 0400);
 
 static struct drm_driver driver;
 
+static int
+nouveau_drm_vblank_enable(struct drm_device *dev, int head)
+{
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_disp *pdisp = nouveau_disp(drm->device);
+       nouveau_event_get(pdisp->vblank, head, &drm->vblank);
+       return 0;
+}
+
+static void
+nouveau_drm_vblank_disable(struct drm_device *dev, int head)
+{
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_disp *pdisp = nouveau_disp(drm->device);
+       nouveau_event_put(pdisp->vblank, head, &drm->vblank);
+}
+
+static int
+nouveau_drm_vblank_handler(struct nouveau_eventh *event, int head)
+{
+       struct nouveau_drm *drm =
+               container_of(event, struct nouveau_drm, vblank);
+       drm_handle_vblank(drm->dev, head);
+       return NVKM_EVENT_KEEP;
+}
+
 static u64
 nouveau_name(struct pci_dev *pdev)
 {
@@ -132,7 +161,8 @@ nouveau_accel_init(struct nouveau_drm *drm)
 
        /* initialise synchronisation routines */
        if      (device->card_type < NV_10) ret = nv04_fence_create(drm);
-       else if (device->card_type < NV_50) ret = nv10_fence_create(drm);
+       else if (device->chipset   <  0x17) ret = nv10_fence_create(drm);
+       else if (device->card_type < NV_50) ret = nv17_fence_create(drm);
        else if (device->chipset   <  0x84) ret = nv50_fence_create(drm);
        else if (device->card_type < NV_C0) ret = nv84_fence_create(drm);
        else                                ret = nvc0_fence_create(drm);
@@ -259,6 +289,7 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
 
        dev->dev_private = drm;
        drm->dev = dev;
+       drm->vblank.func = nouveau_drm_vblank_handler;
 
        INIT_LIST_HEAD(&drm->clients);
        spin_lock_init(&drm->tile.lock);
@@ -398,7 +429,7 @@ nouveau_drm_remove(struct pci_dev *pdev)
        nouveau_object_debug();
 }
 
-int
+static int
 nouveau_do_suspend(struct drm_device *dev)
 {
        struct nouveau_drm *drm = nouveau_drm(dev);
@@ -469,7 +500,7 @@ int nouveau_pmops_suspend(struct device *dev)
        return 0;
 }
 
-int
+static int
 nouveau_do_resume(struct drm_device *dev)
 {
        struct nouveau_drm *drm = nouveau_drm(dev);
@@ -543,10 +574,11 @@ nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv)
        struct pci_dev *pdev = dev->pdev;
        struct nouveau_drm *drm = nouveau_drm(dev);
        struct nouveau_cli *cli;
-       char name[16];
+       char name[32], tmpname[TASK_COMM_LEN];
        int ret;
 
-       snprintf(name, sizeof(name), "%d", pid_nr(fpriv->pid));
+       get_task_comm(tmpname, current);
+       snprintf(name, sizeof(name), "%s[%d]", tmpname, pid_nr(fpriv->pid));
 
        ret = nouveau_cli_create(pdev, name, sizeof(*cli), (void **)&cli);
        if (ret)
@@ -636,22 +668,32 @@ driver = {
        .postclose = nouveau_drm_postclose,
        .lastclose = nouveau_vga_lastclose,
 
+#if defined(CONFIG_DEBUG_FS)
+       .debugfs_init = nouveau_debugfs_init,
+       .debugfs_cleanup = nouveau_debugfs_takedown,
+#endif
+
        .irq_preinstall = nouveau_irq_preinstall,
        .irq_postinstall = nouveau_irq_postinstall,
        .irq_uninstall = nouveau_irq_uninstall,
        .irq_handler = nouveau_irq_handler,
 
        .get_vblank_counter = drm_vblank_count,
-       .enable_vblank = nouveau_vblank_enable,
-       .disable_vblank = nouveau_vblank_disable,
+       .enable_vblank = nouveau_drm_vblank_enable,
+       .disable_vblank = nouveau_drm_vblank_disable,
 
        .ioctls = nouveau_ioctls,
        .fops = &nouveau_driver_fops,
 
        .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
        .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
-       .gem_prime_export = nouveau_gem_prime_export,
-       .gem_prime_import = nouveau_gem_prime_import,
+       .gem_prime_export = drm_gem_prime_export,
+       .gem_prime_import = drm_gem_prime_import,
+       .gem_prime_pin = nouveau_gem_prime_pin,
+       .gem_prime_get_sg_table = nouveau_gem_prime_get_sg_table,
+       .gem_prime_import_sg_table = nouveau_gem_prime_import_sg_table,
+       .gem_prime_vmap = nouveau_gem_prime_vmap,
+       .gem_prime_vunmap = nouveau_gem_prime_vunmap,
 
        .gem_init_object = nouveau_gem_object_new,
        .gem_free_object = nouveau_gem_object_del,
index aa89eb938b47441049034117790ec614cc771fbc..b25df374c901df36f1c6e9408225ef9a2ed4b813 100644 (file)
@@ -13,6 +13,7 @@
 #define DRIVER_PATCHLEVEL      0
 
 #include <core/client.h>
+#include <core/event.h>
 
 #include <subdev/vm.h>
 
@@ -112,6 +113,7 @@ struct nouveau_drm {
        struct nvbios vbios;
        struct nouveau_display *display;
        struct backlight_device *backlight;
+       struct nouveau_eventh vblank;
 
        /* power management */
        struct nouveau_pm *pm;
index d0d95bd511ab6947d779e281ecaec43714e70f3a..e24341229d5e0e6d3ed330a5895a478d32ccdf69 100644 (file)
 
 struct nouveau_i2c_port;
 
-struct dp_train_func {
-       void (*link_set)(struct drm_device *, struct dcb_output *, int crtc,
-                        int nr, u32 bw, bool enhframe);
-       void (*train_set)(struct drm_device *, struct dcb_output *, u8 pattern);
-       void (*train_adj)(struct drm_device *, struct dcb_output *,
-                         u8 lane, u8 swing, u8 preem);
-};
-
 struct nouveau_encoder {
        struct drm_encoder_slave base;
 
        struct dcb_output *dcb;
        int or;
+       struct nouveau_i2c_port *i2c;
 
        /* different to drm_encoder.crtc, this reflects what's
         * actually programmed on the hw, not the proposed crtc */
index 67a1a069de28453e8401897cc3102d29847cbff2..b035317815803259c26aa28ba20d63d0930107a3 100644 (file)
@@ -251,9 +251,10 @@ nouveau_fbcon_zfill(struct drm_device *dev, struct nouveau_fbdev *fbcon)
 }
 
 static int
-nouveau_fbcon_create(struct nouveau_fbdev *fbcon,
+nouveau_fbcon_create(struct drm_fb_helper *helper,
                     struct drm_fb_helper_surface_size *sizes)
 {
+       struct nouveau_fbdev *fbcon = (struct nouveau_fbdev *)helper;
        struct drm_device *dev = fbcon->dev;
        struct nouveau_drm *drm = nouveau_drm(dev);
        struct nouveau_device *device = nv_device(drm->device);
@@ -388,23 +389,6 @@ out:
        return ret;
 }
 
-static int
-nouveau_fbcon_find_or_create_single(struct drm_fb_helper *helper,
-                                   struct drm_fb_helper_surface_size *sizes)
-{
-       struct nouveau_fbdev *fbcon = (struct nouveau_fbdev *)helper;
-       int new_fb = 0;
-       int ret;
-
-       if (!helper->fb) {
-               ret = nouveau_fbcon_create(fbcon, sizes);
-               if (ret)
-                       return ret;
-               new_fb = 1;
-       }
-       return new_fb;
-}
-
 void
 nouveau_fbcon_output_poll_changed(struct drm_device *dev)
 {
@@ -433,6 +417,7 @@ nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *fbcon)
                nouveau_fb->nvbo = NULL;
        }
        drm_fb_helper_fini(&fbcon->helper);
+       drm_framebuffer_unregister_private(&nouveau_fb->base);
        drm_framebuffer_cleanup(&nouveau_fb->base);
        return 0;
 }
@@ -449,7 +434,7 @@ void nouveau_fbcon_gpu_lockup(struct fb_info *info)
 static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
        .gamma_set = nouveau_fbcon_gamma_set,
        .gamma_get = nouveau_fbcon_gamma_get,
-       .fb_probe = nouveau_fbcon_find_or_create_single,
+       .fb_probe = nouveau_fbcon_create,
 };
 
 
@@ -490,6 +475,9 @@ nouveau_fbcon_init(struct drm_device *dev)
        else
                preferred_bpp = 32;
 
+       /* disable all the possible outputs/crtcs before entering KMS mode */
+       drm_helper_disable_unused_functions(dev);
+
        drm_fb_helper_initial_config(&fbcon->helper, preferred_bpp);
        return 0;
 }
index 1d049be79f745f7ddb658fd9099c4d9882a0badb..6c946837a0aa347ea12105801c6d54e0f1f7a9ea 100644 (file)
 #include "nouveau_dma.h"
 #include "nouveau_fence.h"
 
+#include <engine/fifo.h>
+
 void
 nouveau_fence_context_del(struct nouveau_fence_chan *fctx)
 {
        struct nouveau_fence *fence, *fnext;
        spin_lock(&fctx->lock);
        list_for_each_entry_safe(fence, fnext, &fctx->pending, head) {
-               if (fence->work)
-                       fence->work(fence->priv, false);
                fence->channel = NULL;
                list_del(&fence->head);
                nouveau_fence_unref(&fence);
@@ -59,17 +59,14 @@ nouveau_fence_context_new(struct nouveau_fence_chan *fctx)
 static void
 nouveau_fence_update(struct nouveau_channel *chan)
 {
-       struct nouveau_fence_priv *priv = chan->drm->fence;
        struct nouveau_fence_chan *fctx = chan->fence;
        struct nouveau_fence *fence, *fnext;
 
        spin_lock(&fctx->lock);
        list_for_each_entry_safe(fence, fnext, &fctx->pending, head) {
-               if (priv->read(chan) < fence->sequence)
+               if (fctx->read(chan) < fence->sequence)
                        break;
 
-               if (fence->work)
-                       fence->work(fence->priv, true);
                fence->channel = NULL;
                list_del(&fence->head);
                nouveau_fence_unref(&fence);
@@ -80,7 +77,6 @@ nouveau_fence_update(struct nouveau_channel *chan)
 int
 nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan)
 {
-       struct nouveau_fence_priv *priv = chan->drm->fence;
        struct nouveau_fence_chan *fctx = chan->fence;
        int ret;
 
@@ -88,7 +84,7 @@ nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan)
        fence->timeout  = jiffies + (3 * DRM_HZ);
        fence->sequence = ++fctx->sequence;
 
-       ret = priv->emit(fence);
+       ret = fctx->emit(fence);
        if (!ret) {
                kref_get(&fence->kref);
                spin_lock(&fctx->lock);
@@ -107,13 +103,87 @@ nouveau_fence_done(struct nouveau_fence *fence)
        return !fence->channel;
 }
 
+struct nouveau_fence_uevent {
+       struct nouveau_eventh handler;
+       struct nouveau_fence_priv *priv;
+};
+
+static int
+nouveau_fence_wait_uevent_handler(struct nouveau_eventh *event, int index)
+{
+       struct nouveau_fence_uevent *uevent =
+               container_of(event, struct nouveau_fence_uevent, handler);
+       wake_up_all(&uevent->priv->waiting);
+       return NVKM_EVENT_KEEP;
+}
+
+static int
+nouveau_fence_wait_uevent(struct nouveau_fence *fence, bool intr)
+
+{
+       struct nouveau_channel *chan = fence->channel;
+       struct nouveau_fifo *pfifo = nouveau_fifo(chan->drm->device);
+       struct nouveau_fence_priv *priv = chan->drm->fence;
+       struct nouveau_fence_uevent uevent = {
+               .handler.func = nouveau_fence_wait_uevent_handler,
+               .priv = priv,
+       };
+       int ret = 0;
+
+       nouveau_event_get(pfifo->uevent, 0, &uevent.handler);
+
+       if (fence->timeout) {
+               unsigned long timeout = fence->timeout - jiffies;
+
+               if (time_before(jiffies, fence->timeout)) {
+                       if (intr) {
+                               ret = wait_event_interruptible_timeout(
+                                               priv->waiting,
+                                               nouveau_fence_done(fence),
+                                               timeout);
+                       } else {
+                               ret = wait_event_timeout(priv->waiting,
+                                               nouveau_fence_done(fence),
+                                               timeout);
+                       }
+               }
+
+               if (ret >= 0) {
+                       fence->timeout = jiffies + ret;
+                       if (time_after_eq(jiffies, fence->timeout))
+                               ret = -EBUSY;
+               }
+       } else {
+               if (intr) {
+                       ret = wait_event_interruptible(priv->waiting,
+                                       nouveau_fence_done(fence));
+               } else {
+                       wait_event(priv->waiting, nouveau_fence_done(fence));
+               }
+       }
+
+       nouveau_event_put(pfifo->uevent, 0, &uevent.handler);
+       if (unlikely(ret < 0))
+               return ret;
+
+       return 0;
+}
+
 int
 nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr)
 {
+       struct nouveau_channel *chan = fence->channel;
+       struct nouveau_fence_priv *priv = chan ? chan->drm->fence : NULL;
        unsigned long sleep_time = NSEC_PER_MSEC / 1000;
        ktime_t t;
        int ret = 0;
 
+       while (priv && priv->uevent && lazy && !nouveau_fence_done(fence)) {
+               ret = nouveau_fence_wait_uevent(fence, intr);
+               if (ret < 0)
+                       return ret;
+       }
+
        while (!nouveau_fence_done(fence)) {
                if (fence->timeout && time_after_eq(jiffies, fence->timeout)) {
                        ret = -EBUSY;
@@ -143,14 +213,14 @@ nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr)
 int
 nouveau_fence_sync(struct nouveau_fence *fence, struct nouveau_channel *chan)
 {
-       struct nouveau_fence_priv *priv = chan->drm->fence;
+       struct nouveau_fence_chan *fctx = chan->fence;
        struct nouveau_channel *prev;
        int ret = 0;
 
        prev = fence ? fence->channel : NULL;
        if (prev) {
                if (unlikely(prev != chan && !nouveau_fence_done(fence))) {
-                       ret = priv->sync(fence, prev, chan);
+                       ret = fctx->sync(fence, prev, chan);
                        if (unlikely(ret))
                                ret = nouveau_fence_wait(fence, true, false);
                }
@@ -182,7 +252,8 @@ nouveau_fence_ref(struct nouveau_fence *fence)
 }
 
 int
-nouveau_fence_new(struct nouveau_channel *chan, struct nouveau_fence **pfence)
+nouveau_fence_new(struct nouveau_channel *chan, bool sysmem,
+                 struct nouveau_fence **pfence)
 {
        struct nouveau_fence *fence;
        int ret = 0;
@@ -193,13 +264,13 @@ nouveau_fence_new(struct nouveau_channel *chan, struct nouveau_fence **pfence)
        fence = kzalloc(sizeof(*fence), GFP_KERNEL);
        if (!fence)
                return -ENOMEM;
+
+       fence->sysmem = sysmem;
        kref_init(&fence->kref);
 
-       if (chan) {
-               ret = nouveau_fence_emit(fence, chan);
-               if (ret)
-                       nouveau_fence_unref(&fence);
-       }
+       ret = nouveau_fence_emit(fence, chan);
+       if (ret)
+               nouveau_fence_unref(&fence);
 
        *pfence = fence;
        return ret;
index cdb83acdffe264b2144e615f96bc55d346384116..c89943407b527984ab2b6628ad926f48f46e1b6d 100644 (file)
@@ -7,15 +7,15 @@ struct nouveau_fence {
        struct list_head head;
        struct kref kref;
 
+       bool sysmem;
+
        struct nouveau_channel *channel;
        unsigned long timeout;
        u32 sequence;
-
-       void (*work)(void *priv, bool signalled);
-       void *priv;
 };
 
-int  nouveau_fence_new(struct nouveau_channel *, struct nouveau_fence **);
+int  nouveau_fence_new(struct nouveau_channel *, bool sysmem,
+                      struct nouveau_fence **);
 struct nouveau_fence *
 nouveau_fence_ref(struct nouveau_fence *);
 void nouveau_fence_unref(struct nouveau_fence **);
@@ -29,6 +29,13 @@ struct nouveau_fence_chan {
        struct list_head pending;
        struct list_head flip;
 
+       int  (*emit)(struct nouveau_fence *);
+       int  (*sync)(struct nouveau_fence *, struct nouveau_channel *,
+                    struct nouveau_channel *);
+       u32  (*read)(struct nouveau_channel *);
+       int  (*emit32)(struct nouveau_channel *, u64, u32);
+       int  (*sync32)(struct nouveau_channel *, u64, u32);
+
        spinlock_t lock;
        u32 sequence;
 };
@@ -39,10 +46,9 @@ struct nouveau_fence_priv {
        void (*resume)(struct nouveau_drm *);
        int  (*context_new)(struct nouveau_channel *);
        void (*context_del)(struct nouveau_channel *);
-       int  (*emit)(struct nouveau_fence *);
-       int  (*sync)(struct nouveau_fence *, struct nouveau_channel *,
-                    struct nouveau_channel *);
-       u32  (*read)(struct nouveau_channel *);
+
+       wait_queue_head_t waiting;
+       bool uevent;
 };
 
 #define nouveau_fence(drm) ((struct nouveau_fence_priv *)(drm)->fence)
@@ -60,13 +66,31 @@ u32  nv10_fence_read(struct nouveau_channel *);
 void nv10_fence_context_del(struct nouveau_channel *);
 void nv10_fence_destroy(struct nouveau_drm *);
 int  nv10_fence_create(struct nouveau_drm *);
+
+int  nv17_fence_create(struct nouveau_drm *);
 void nv17_fence_resume(struct nouveau_drm *drm);
 
 int nv50_fence_create(struct nouveau_drm *);
 int nv84_fence_create(struct nouveau_drm *);
 int nvc0_fence_create(struct nouveau_drm *);
-u64 nvc0_fence_crtc(struct nouveau_channel *, int crtc);
 
 int nouveau_flip_complete(void *chan);
 
+struct nv84_fence_chan {
+       struct nouveau_fence_chan base;
+       struct nouveau_vma vma;
+       struct nouveau_vma vma_gart;
+       struct nouveau_vma dispc_vma[4];
+};
+
+struct nv84_fence_priv {
+       struct nouveau_fence_priv base;
+       struct nouveau_bo *bo;
+       struct nouveau_bo *bo_gart;
+       u32 *suspend;
+};
+
+u64  nv84_fence_crtc(struct nouveau_channel *, int);
+int  nv84_fence_context_new(struct nouveau_channel *);
+
 #endif
index 8bf695c52f95227fd6f8f3be407efffc558bdf24..b4b4d0c1f4aff944f2a260fe9ba2754b4ac58723 100644 (file)
@@ -24,8 +24,6 @@
  *
  */
 
-#include <linux/dma-buf.h>
-
 #include <subdev/fb.h>
 
 #include "nouveau_drm.h"
@@ -205,6 +203,7 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data,
                      struct drm_file *file_priv)
 {
        struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_cli *cli = nouveau_cli(file_priv);
        struct nouveau_fb *pfb = nouveau_fb(drm->device);
        struct drm_nouveau_gem_new *req = data;
        struct nouveau_bo *nvbo = NULL;
@@ -213,7 +212,7 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data,
        drm->ttm.bdev.dev_mapping = drm->dev->dev_mapping;
 
        if (!pfb->memtype_valid(pfb, req->info.tile_flags)) {
-               NV_ERROR(drm, "bad page flags: 0x%08x\n", req->info.tile_flags);
+               NV_ERROR(cli, "bad page flags: 0x%08x\n", req->info.tile_flags);
                return -EINVAL;
        }
 
@@ -315,16 +314,18 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv,
              struct drm_nouveau_gem_pushbuf_bo *pbbo,
              int nr_buffers, struct validate_op *op)
 {
+       struct nouveau_cli *cli = nouveau_cli(file_priv);
        struct drm_device *dev = chan->drm->dev;
        struct nouveau_drm *drm = nouveau_drm(dev);
        uint32_t sequence;
        int trycnt = 0;
        int ret, i;
+       struct nouveau_bo *res_bo = NULL;
 
        sequence = atomic_add_return(1, &drm->ttm.validate_sequence);
 retry:
        if (++trycnt > 100000) {
-               NV_ERROR(drm, "%s failed and gave up.\n", __func__);
+               NV_ERROR(cli, "%s failed and gave up.\n", __func__);
                return -EINVAL;
        }
 
@@ -335,14 +336,19 @@ retry:
 
                gem = drm_gem_object_lookup(dev, file_priv, b->handle);
                if (!gem) {
-                       NV_ERROR(drm, "Unknown handle 0x%08x\n", b->handle);
+                       NV_ERROR(cli, "Unknown handle 0x%08x\n", b->handle);
                        validate_fini(op, NULL);
                        return -ENOENT;
                }
                nvbo = gem->driver_private;
+               if (nvbo == res_bo) {
+                       res_bo = NULL;
+                       drm_gem_object_unreference_unlocked(gem);
+                       continue;
+               }
 
                if (nvbo->reserved_by && nvbo->reserved_by == file_priv) {
-                       NV_ERROR(drm, "multiple instances of buffer %d on "
+                       NV_ERROR(cli, "multiple instances of buffer %d on "
                                      "validation list\n", b->handle);
                        drm_gem_object_unreference_unlocked(gem);
                        validate_fini(op, NULL);
@@ -352,15 +358,19 @@ retry:
                ret = ttm_bo_reserve(&nvbo->bo, true, false, true, sequence);
                if (ret) {
                        validate_fini(op, NULL);
-                       if (unlikely(ret == -EAGAIN))
-                               ret = ttm_bo_wait_unreserved(&nvbo->bo, true);
-                       drm_gem_object_unreference_unlocked(gem);
+                       if (unlikely(ret == -EAGAIN)) {
+                               sequence = atomic_add_return(1, &drm->ttm.validate_sequence);
+                               ret = ttm_bo_reserve_slowpath(&nvbo->bo, true,
+                                                             sequence);
+                               if (!ret)
+                                       res_bo = nvbo;
+                       }
                        if (unlikely(ret)) {
+                               drm_gem_object_unreference_unlocked(gem);
                                if (ret != -ERESTARTSYS)
-                                       NV_ERROR(drm, "fail reserve\n");
+                                       NV_ERROR(cli, "fail reserve\n");
                                return ret;
                        }
-                       goto retry;
                }
 
                b->user_priv = (uint64_t)(unsigned long)nvbo;
@@ -376,12 +386,14 @@ retry:
                if (b->valid_domains & NOUVEAU_GEM_DOMAIN_GART)
                        list_add_tail(&nvbo->entry, &op->gart_list);
                else {
-                       NV_ERROR(drm, "invalid valid domains: 0x%08x\n",
+                       NV_ERROR(cli, "invalid valid domains: 0x%08x\n",
                                 b->valid_domains);
                        list_add_tail(&nvbo->entry, &op->both_list);
                        validate_fini(op, NULL);
                        return -EINVAL;
                }
+               if (nvbo == res_bo)
+                       goto retry;
        }
 
        return 0;
@@ -407,8 +419,9 @@ validate_sync(struct nouveau_channel *chan, struct nouveau_bo *nvbo)
 }
 
 static int
-validate_list(struct nouveau_channel *chan, struct list_head *list,
-             struct drm_nouveau_gem_pushbuf_bo *pbbo, uint64_t user_pbbo_ptr)
+validate_list(struct nouveau_channel *chan, struct nouveau_cli *cli,
+             struct list_head *list, struct drm_nouveau_gem_pushbuf_bo *pbbo,
+             uint64_t user_pbbo_ptr)
 {
        struct nouveau_drm *drm = chan->drm;
        struct drm_nouveau_gem_pushbuf_bo __user *upbbo =
@@ -421,7 +434,7 @@ validate_list(struct nouveau_channel *chan, struct list_head *list,
 
                ret = validate_sync(chan, nvbo);
                if (unlikely(ret)) {
-                       NV_ERROR(drm, "fail pre-validate sync\n");
+                       NV_ERROR(cli, "fail pre-validate sync\n");
                        return ret;
                }
 
@@ -429,20 +442,20 @@ validate_list(struct nouveau_channel *chan, struct list_head *list,
                                             b->write_domains,
                                             b->valid_domains);
                if (unlikely(ret)) {
-                       NV_ERROR(drm, "fail set_domain\n");
+                       NV_ERROR(cli, "fail set_domain\n");
                        return ret;
                }
 
                ret = nouveau_bo_validate(nvbo, true, false);
                if (unlikely(ret)) {
                        if (ret != -ERESTARTSYS)
-                               NV_ERROR(drm, "fail ttm_validate\n");
+                               NV_ERROR(cli, "fail ttm_validate\n");
                        return ret;
                }
 
                ret = validate_sync(chan, nvbo);
                if (unlikely(ret)) {
-                       NV_ERROR(drm, "fail post-validate sync\n");
+                       NV_ERROR(cli, "fail post-validate sync\n");
                        return ret;
                }
 
@@ -478,7 +491,7 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan,
                             uint64_t user_buffers, int nr_buffers,
                             struct validate_op *op, int *apply_relocs)
 {
-       struct nouveau_drm *drm = chan->drm;
+       struct nouveau_cli *cli = nouveau_cli(file_priv);
        int ret, relocs = 0;
 
        INIT_LIST_HEAD(&op->vram_list);
@@ -491,32 +504,32 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan,
        ret = validate_init(chan, file_priv, pbbo, nr_buffers, op);
        if (unlikely(ret)) {
                if (ret != -ERESTARTSYS)
-                       NV_ERROR(drm, "validate_init\n");
+                       NV_ERROR(cli, "validate_init\n");
                return ret;
        }
 
-       ret = validate_list(chan, &op->vram_list, pbbo, user_buffers);
+       ret = validate_list(chan, cli, &op->vram_list, pbbo, user_buffers);
        if (unlikely(ret < 0)) {
                if (ret != -ERESTARTSYS)
-                       NV_ERROR(drm, "validate vram_list\n");
+                       NV_ERROR(cli, "validate vram_list\n");
                validate_fini(op, NULL);
                return ret;
        }
        relocs += ret;
 
-       ret = validate_list(chan, &op->gart_list, pbbo, user_buffers);
+       ret = validate_list(chan, cli, &op->gart_list, pbbo, user_buffers);
        if (unlikely(ret < 0)) {
                if (ret != -ERESTARTSYS)
-                       NV_ERROR(drm, "validate gart_list\n");
+                       NV_ERROR(cli, "validate gart_list\n");
                validate_fini(op, NULL);
                return ret;
        }
        relocs += ret;
 
-       ret = validate_list(chan, &op->both_list, pbbo, user_buffers);
+       ret = validate_list(chan, cli, &op->both_list, pbbo, user_buffers);
        if (unlikely(ret < 0)) {
                if (ret != -ERESTARTSYS)
-                       NV_ERROR(drm, "validate both_list\n");
+                       NV_ERROR(cli, "validate both_list\n");
                validate_fini(op, NULL);
                return ret;
        }
@@ -545,11 +558,10 @@ u_memcpya(uint64_t user, unsigned nmemb, unsigned size)
 }
 
 static int
-nouveau_gem_pushbuf_reloc_apply(struct drm_device *dev,
+nouveau_gem_pushbuf_reloc_apply(struct nouveau_cli *cli,
                                struct drm_nouveau_gem_pushbuf *req,
                                struct drm_nouveau_gem_pushbuf_bo *bo)
 {
-       struct nouveau_drm *drm = nouveau_drm(dev);
        struct drm_nouveau_gem_pushbuf_reloc *reloc = NULL;
        int ret = 0;
        unsigned i;
@@ -565,7 +577,7 @@ nouveau_gem_pushbuf_reloc_apply(struct drm_device *dev,
                uint32_t data;
 
                if (unlikely(r->bo_index > req->nr_buffers)) {
-                       NV_ERROR(drm, "reloc bo index invalid\n");
+                       NV_ERROR(cli, "reloc bo index invalid\n");
                        ret = -EINVAL;
                        break;
                }
@@ -575,7 +587,7 @@ nouveau_gem_pushbuf_reloc_apply(struct drm_device *dev,
                        continue;
 
                if (unlikely(r->reloc_bo_index > req->nr_buffers)) {
-                       NV_ERROR(drm, "reloc container bo index invalid\n");
+                       NV_ERROR(cli, "reloc container bo index invalid\n");
                        ret = -EINVAL;
                        break;
                }
@@ -583,7 +595,7 @@ nouveau_gem_pushbuf_reloc_apply(struct drm_device *dev,
 
                if (unlikely(r->reloc_bo_offset + 4 >
                             nvbo->bo.mem.num_pages << PAGE_SHIFT)) {
-                       NV_ERROR(drm, "reloc outside of bo\n");
+                       NV_ERROR(cli, "reloc outside of bo\n");
                        ret = -EINVAL;
                        break;
                }
@@ -592,7 +604,7 @@ nouveau_gem_pushbuf_reloc_apply(struct drm_device *dev,
                        ret = ttm_bo_kmap(&nvbo->bo, 0, nvbo->bo.mem.num_pages,
                                          &nvbo->kmap);
                        if (ret) {
-                               NV_ERROR(drm, "failed kmap for reloc\n");
+                               NV_ERROR(cli, "failed kmap for reloc\n");
                                break;
                        }
                        nvbo->validate_mapped = true;
@@ -617,7 +629,7 @@ nouveau_gem_pushbuf_reloc_apply(struct drm_device *dev,
                ret = ttm_bo_wait(&nvbo->bo, false, false, false);
                spin_unlock(&nvbo->bo.bdev->fence_lock);
                if (ret) {
-                       NV_ERROR(drm, "reloc wait_idle failed: %d\n", ret);
+                       NV_ERROR(cli, "reloc wait_idle failed: %d\n", ret);
                        break;
                }
 
@@ -633,6 +645,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
                          struct drm_file *file_priv)
 {
        struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev);
+       struct nouveau_cli *cli = nouveau_cli(file_priv);
        struct nouveau_abi16_chan *temp;
        struct nouveau_drm *drm = nouveau_drm(dev);
        struct drm_nouveau_gem_pushbuf *req = data;
@@ -662,19 +675,19 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
                goto out_next;
 
        if (unlikely(req->nr_push > NOUVEAU_GEM_MAX_PUSH)) {
-               NV_ERROR(drm, "pushbuf push count exceeds limit: %d max %d\n",
+               NV_ERROR(cli, "pushbuf push count exceeds limit: %d max %d\n",
                         req->nr_push, NOUVEAU_GEM_MAX_PUSH);
                return nouveau_abi16_put(abi16, -EINVAL);
        }
 
        if (unlikely(req->nr_buffers > NOUVEAU_GEM_MAX_BUFFERS)) {
-               NV_ERROR(drm, "pushbuf bo count exceeds limit: %d max %d\n",
+               NV_ERROR(cli, "pushbuf bo count exceeds limit: %d max %d\n",
                         req->nr_buffers, NOUVEAU_GEM_MAX_BUFFERS);
                return nouveau_abi16_put(abi16, -EINVAL);
        }
 
        if (unlikely(req->nr_relocs > NOUVEAU_GEM_MAX_RELOCS)) {
-               NV_ERROR(drm, "pushbuf reloc count exceeds limit: %d max %d\n",
+               NV_ERROR(cli, "pushbuf reloc count exceeds limit: %d max %d\n",
                         req->nr_relocs, NOUVEAU_GEM_MAX_RELOCS);
                return nouveau_abi16_put(abi16, -EINVAL);
        }
@@ -692,7 +705,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
        /* Ensure all push buffers are on validate list */
        for (i = 0; i < req->nr_push; i++) {
                if (push[i].bo_index >= req->nr_buffers) {
-                       NV_ERROR(drm, "push %d buffer not in list\n", i);
+                       NV_ERROR(cli, "push %d buffer not in list\n", i);
                        ret = -EINVAL;
                        goto out_prevalid;
                }
@@ -703,15 +716,15 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
                                           req->nr_buffers, &op, &do_reloc);
        if (ret) {
                if (ret != -ERESTARTSYS)
-                       NV_ERROR(drm, "validate: %d\n", ret);
+                       NV_ERROR(cli, "validate: %d\n", ret);
                goto out_prevalid;
        }
 
        /* Apply any relocations that are required */
        if (do_reloc) {
-               ret = nouveau_gem_pushbuf_reloc_apply(dev, req, bo);
+               ret = nouveau_gem_pushbuf_reloc_apply(cli, req, bo);
                if (ret) {
-                       NV_ERROR(drm, "reloc apply: %d\n", ret);
+                       NV_ERROR(cli, "reloc apply: %d\n", ret);
                        goto out;
                }
        }
@@ -719,7 +732,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
        if (chan->dma.ib_max) {
                ret = nouveau_dma_wait(chan, req->nr_push + 1, 16);
                if (ret) {
-                       NV_ERROR(drm, "nv50cal_space: %d\n", ret);
+                       NV_ERROR(cli, "nv50cal_space: %d\n", ret);
                        goto out;
                }
 
@@ -734,7 +747,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
        if (nv_device(drm->device)->chipset >= 0x25) {
                ret = RING_SPACE(chan, req->nr_push * 2);
                if (ret) {
-                       NV_ERROR(drm, "cal_space: %d\n", ret);
+                       NV_ERROR(cli, "cal_space: %d\n", ret);
                        goto out;
                }
 
@@ -748,7 +761,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
        } else {
                ret = RING_SPACE(chan, req->nr_push * (2 + NOUVEAU_DMA_SKIPS));
                if (ret) {
-                       NV_ERROR(drm, "jmp_space: %d\n", ret);
+                       NV_ERROR(cli, "jmp_space: %d\n", ret);
                        goto out;
                }
 
@@ -784,9 +797,9 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
                }
        }
 
-       ret = nouveau_fence_new(chan, &fence);
+       ret = nouveau_fence_new(chan, false, &fence);
        if (ret) {
-               NV_ERROR(drm, "error fencing pushbuf: %d\n", ret);
+               NV_ERROR(cli, "error fencing pushbuf: %d\n", ret);
                WIND_RING(chan);
                goto out;
        }
index 5c1049236d221622fda2733696e08832f880d7d1..8d7a3f0aeb8660a28d5593acbee3b715199d0365 100644 (file)
@@ -35,9 +35,11 @@ extern int nouveau_gem_ioctl_cpu_fini(struct drm_device *, void *,
 extern int nouveau_gem_ioctl_info(struct drm_device *, void *,
                                  struct drm_file *);
 
-extern struct dma_buf *nouveau_gem_prime_export(struct drm_device *dev,
-                               struct drm_gem_object *obj, int flags);
-extern struct drm_gem_object *nouveau_gem_prime_import(struct drm_device *dev,
-                               struct dma_buf *dma_buf);
+extern int nouveau_gem_prime_pin(struct drm_gem_object *);
+extern struct sg_table *nouveau_gem_prime_get_sg_table(struct drm_gem_object *);
+extern struct drm_gem_object *nouveau_gem_prime_import_sg_table(
+       struct drm_device *, size_t size, struct sg_table *);
+extern void *nouveau_gem_prime_vmap(struct drm_gem_object *);
+extern void nouveau_gem_prime_vunmap(struct drm_gem_object *, void *);
 
 #endif
index a701ff5ffa5b8fdb63497eac6782523814a2cdf7..bb54098c6d970bd4c0b45582220490fb11b15426 100644 (file)
@@ -408,6 +408,81 @@ nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf)
 static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp,
                                                  NULL, 0);
 
+static ssize_t
+nouveau_hwmon_show_temp1_auto_point1_pwm(struct device *d,
+                                        struct device_attribute *a, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", 100);
+}
+static SENSOR_DEVICE_ATTR(temp1_auto_point1_pwm, S_IRUGO,
+                         nouveau_hwmon_show_temp1_auto_point1_pwm, NULL, 0);
+
+static ssize_t
+nouveau_hwmon_temp1_auto_point1_temp(struct device *d,
+                                    struct device_attribute *a, char *buf)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+             therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST) * 1000);
+}
+static ssize_t
+nouveau_hwmon_set_temp1_auto_point1_temp(struct device *d,
+                                        struct device_attribute *a,
+                                        const char *buf, size_t count)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+       long value;
+
+       if (kstrtol(buf, 10, &value) == -EINVAL)
+               return count;
+
+       therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST,
+                       value / 1000);
+
+       return count;
+}
+static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp, S_IRUGO | S_IWUSR,
+                         nouveau_hwmon_temp1_auto_point1_temp,
+                         nouveau_hwmon_set_temp1_auto_point1_temp, 0);
+
+static ssize_t
+nouveau_hwmon_temp1_auto_point1_temp_hyst(struct device *d,
+                                         struct device_attribute *a, char *buf)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+        therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST) * 1000);
+}
+static ssize_t
+nouveau_hwmon_set_temp1_auto_point1_temp_hyst(struct device *d,
+                                             struct device_attribute *a,
+                                             const char *buf, size_t count)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+       long value;
+
+       if (kstrtol(buf, 10, &value) == -EINVAL)
+               return count;
+
+       therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST,
+                       value / 1000);
+
+       return count;
+}
+static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp_hyst, S_IRUGO | S_IWUSR,
+                         nouveau_hwmon_temp1_auto_point1_temp_hyst,
+                         nouveau_hwmon_set_temp1_auto_point1_temp_hyst, 0);
+
 static ssize_t
 nouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf)
 {
@@ -438,6 +513,38 @@ static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp,
                                                  nouveau_hwmon_set_max_temp,
                                                  0);
 
+static ssize_t
+nouveau_hwmon_max_temp_hyst(struct device *d, struct device_attribute *a,
+                           char *buf)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+         therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_DOWN_CLK_HYST) * 1000);
+}
+static ssize_t
+nouveau_hwmon_set_max_temp_hyst(struct device *d, struct device_attribute *a,
+                                               const char *buf, size_t count)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+       long value;
+
+       if (kstrtol(buf, 10, &value) == -EINVAL)
+               return count;
+
+       therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_DOWN_CLK_HYST,
+                       value / 1000);
+
+       return count;
+}
+static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
+                         nouveau_hwmon_max_temp_hyst,
+                         nouveau_hwmon_set_max_temp_hyst, 0);
+
 static ssize_t
 nouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a,
                                                        char *buf)
@@ -471,6 +578,107 @@ static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR,
                                                nouveau_hwmon_set_critical_temp,
                                                0);
 
+static ssize_t
+nouveau_hwmon_critical_temp_hyst(struct device *d, struct device_attribute *a,
+                                                       char *buf)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+         therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_CRITICAL_HYST) * 1000);
+}
+static ssize_t
+nouveau_hwmon_set_critical_temp_hyst(struct device *d,
+                                    struct device_attribute *a,
+                                    const char *buf,
+                                    size_t count)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+       long value;
+
+       if (kstrtol(buf, 10, &value) == -EINVAL)
+               return count;
+
+       therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_CRITICAL_HYST,
+                       value / 1000);
+
+       return count;
+}
+static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO | S_IWUSR,
+                         nouveau_hwmon_critical_temp_hyst,
+                         nouveau_hwmon_set_critical_temp_hyst, 0);
+static ssize_t
+nouveau_hwmon_emergency_temp(struct device *d, struct device_attribute *a,
+                                                       char *buf)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+              therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN) * 1000);
+}
+static ssize_t
+nouveau_hwmon_set_emergency_temp(struct device *d, struct device_attribute *a,
+                                                           const char *buf,
+                                                               size_t count)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+       long value;
+
+       if (kstrtol(buf, 10, &value) == -EINVAL)
+               return count;
+
+       therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN, value / 1000);
+
+       return count;
+}
+static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO | S_IWUSR,
+                                       nouveau_hwmon_emergency_temp,
+                                       nouveau_hwmon_set_emergency_temp,
+                                       0);
+
+static ssize_t
+nouveau_hwmon_emergency_temp_hyst(struct device *d, struct device_attribute *a,
+                                                       char *buf)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+         therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN_HYST) * 1000);
+}
+static ssize_t
+nouveau_hwmon_set_emergency_temp_hyst(struct device *d,
+                                     struct device_attribute *a,
+                                     const char *buf,
+                                     size_t count)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+       long value;
+
+       if (kstrtol(buf, 10, &value) == -EINVAL)
+               return count;
+
+       therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN_HYST,
+                       value / 1000);
+
+       return count;
+}
+static SENSOR_DEVICE_ATTR(temp1_emergency_hyst, S_IRUGO | S_IWUSR,
+                                       nouveau_hwmon_emergency_temp_hyst,
+                                       nouveau_hwmon_set_emergency_temp_hyst,
+                                       0);
+
 static ssize_t nouveau_hwmon_show_name(struct device *dev,
                                      struct device_attribute *attr,
                                      char *buf)
@@ -490,7 +698,7 @@ static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO,
                                                NULL, 0);
 
 static ssize_t
-nouveau_hwmon_show_fan0_input(struct device *d, struct device_attribute *attr,
+nouveau_hwmon_show_fan1_input(struct device *d, struct device_attribute *attr,
                              char *buf)
 {
        struct drm_device *dev = dev_get_drvdata(d);
@@ -499,7 +707,7 @@ nouveau_hwmon_show_fan0_input(struct device *d, struct device_attribute *attr,
 
        return snprintf(buf, PAGE_SIZE, "%d\n", therm->fan_sense(therm));
 }
-static SENSOR_DEVICE_ATTR(fan0_input, S_IRUGO, nouveau_hwmon_show_fan0_input,
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, nouveau_hwmon_show_fan1_input,
                          NULL, 0);
 
  static ssize_t
@@ -665,14 +873,21 @@ static SENSOR_DEVICE_ATTR(pwm1_max, S_IRUGO | S_IWUSR,
 
 static struct attribute *hwmon_attributes[] = {
        &sensor_dev_attr_temp1_input.dev_attr.attr,
+       &sensor_dev_attr_temp1_auto_point1_pwm.dev_attr.attr,
+       &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr,
+       &sensor_dev_attr_temp1_auto_point1_temp_hyst.dev_attr.attr,
        &sensor_dev_attr_temp1_max.dev_attr.attr,
+       &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
        &sensor_dev_attr_temp1_crit.dev_attr.attr,
+       &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp1_emergency.dev_attr.attr,
+       &sensor_dev_attr_temp1_emergency_hyst.dev_attr.attr,
        &sensor_dev_attr_name.dev_attr.attr,
        &sensor_dev_attr_update_rate.dev_attr.attr,
        NULL
 };
 static struct attribute *hwmon_fan_rpm_attributes[] = {
-       &sensor_dev_attr_fan0_input.dev_attr.attr,
+       &sensor_dev_attr_fan1_input.dev_attr.attr,
        NULL
 };
 static struct attribute *hwmon_pwm_fan_attributes[] = {
@@ -717,7 +932,7 @@ nouveau_hwmon_init(struct drm_device *dev)
        dev_set_drvdata(hwmon_dev, dev);
 
        /* default sysfs entries */
-       ret = sysfs_create_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
+       ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_attrgroup);
        if (ret) {
                if (ret)
                        goto error;
@@ -728,7 +943,7 @@ nouveau_hwmon_init(struct drm_device *dev)
         *     the gpio entries for pwm fan control even when there's no
         *     actual fan connected to it... therm table? */
        if (therm->fan_get && therm->fan_get(therm) >= 0) {
-               ret = sysfs_create_group(&dev->pdev->dev.kobj,
+               ret = sysfs_create_group(&hwmon_dev->kobj,
                                         &hwmon_pwm_fan_attrgroup);
                if (ret)
                        goto error;
@@ -736,7 +951,7 @@ nouveau_hwmon_init(struct drm_device *dev)
 
        /* if the card can read the fan rpm */
        if (therm->fan_sense(therm) >= 0) {
-               ret = sysfs_create_group(&dev->pdev->dev.kobj,
+               ret = sysfs_create_group(&hwmon_dev->kobj,
                                         &hwmon_fan_rpm_attrgroup);
                if (ret)
                        goto error;
@@ -764,10 +979,10 @@ nouveau_hwmon_fini(struct drm_device *dev)
        struct nouveau_pm *pm = nouveau_pm(dev);
 
        if (pm->hwmon) {
-               sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
-               sysfs_remove_group(&dev->pdev->dev.kobj,
+               sysfs_remove_group(&pm->hwmon->kobj, &hwmon_attrgroup);
+               sysfs_remove_group(&pm->hwmon->kobj,
                                   &hwmon_pwm_fan_attrgroup);
-               sysfs_remove_group(&dev->pdev->dev.kobj,
+               sysfs_remove_group(&pm->hwmon->kobj,
                                   &hwmon_fan_rpm_attrgroup);
 
                hwmon_device_unregister(pm->hwmon);
index b8e05ae38212db2b8912f874dc4fe5fb627e7102..f53e10874caee8ba71852ebdde6eece32ecab415 100644 (file)
  * Authors: Dave Airlie
  */
 
-#include <linux/dma-buf.h>
-
 #include <drm/drmP.h>
 
 #include "nouveau_drm.h"
 #include "nouveau_gem.h"
 
-static struct sg_table *nouveau_gem_map_dma_buf(struct dma_buf_attachment *attachment,
-                                         enum dma_data_direction dir)
+struct sg_table *nouveau_gem_prime_get_sg_table(struct drm_gem_object *obj)
 {
-       struct nouveau_bo *nvbo = attachment->dmabuf->priv;
-       struct drm_device *dev = nvbo->gem->dev;
+       struct nouveau_bo *nvbo = nouveau_gem_object(obj);
        int npages = nvbo->bo.num_pages;
-       struct sg_table *sg;
-       int nents;
-
-       mutex_lock(&dev->struct_mutex);
-       sg = drm_prime_pages_to_sg(nvbo->bo.ttm->pages, npages);
-       nents = dma_map_sg(attachment->dev, sg->sgl, sg->nents, dir);
-       mutex_unlock(&dev->struct_mutex);
-       return sg;
-}
-
-static void nouveau_gem_unmap_dma_buf(struct dma_buf_attachment *attachment,
-                                     struct sg_table *sg, enum dma_data_direction dir)
-{
-       dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, dir);
-       sg_free_table(sg);
-       kfree(sg);
-}
-
-static void nouveau_gem_dmabuf_release(struct dma_buf *dma_buf)
-{
-       struct nouveau_bo *nvbo = dma_buf->priv;
-
-       if (nvbo->gem->export_dma_buf == dma_buf) {
-               nvbo->gem->export_dma_buf = NULL;
-               drm_gem_object_unreference_unlocked(nvbo->gem);
-       }
-}
-
-static void *nouveau_gem_kmap_atomic(struct dma_buf *dma_buf, unsigned long page_num)
-{
-       return NULL;
-}
-
-static void nouveau_gem_kunmap_atomic(struct dma_buf *dma_buf, unsigned long page_num, void *addr)
-{
 
-}
-static void *nouveau_gem_kmap(struct dma_buf *dma_buf, unsigned long page_num)
-{
-       return NULL;
+       return drm_prime_pages_to_sg(nvbo->bo.ttm->pages, npages);
 }
 
-static void nouveau_gem_kunmap(struct dma_buf *dma_buf, unsigned long page_num, void *addr)
+void *nouveau_gem_prime_vmap(struct drm_gem_object *obj)
 {
-
-}
-
-static int nouveau_gem_prime_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma)
-{
-       return -EINVAL;
-}
-
-static void *nouveau_gem_prime_vmap(struct dma_buf *dma_buf)
-{
-       struct nouveau_bo *nvbo = dma_buf->priv;
-       struct drm_device *dev = nvbo->gem->dev;
+       struct nouveau_bo *nvbo = nouveau_gem_object(obj);
        int ret;
 
-       mutex_lock(&dev->struct_mutex);
-       if (nvbo->vmapping_count) {
-               nvbo->vmapping_count++;
-               goto out_unlock;
-       }
-
        ret = ttm_bo_kmap(&nvbo->bo, 0, nvbo->bo.num_pages,
                          &nvbo->dma_buf_vmap);
-       if (ret) {
-               mutex_unlock(&dev->struct_mutex);
+       if (ret)
                return ERR_PTR(ret);
-       }
-       nvbo->vmapping_count = 1;
-out_unlock:
-       mutex_unlock(&dev->struct_mutex);
+
        return nvbo->dma_buf_vmap.virtual;
 }
 
-static void nouveau_gem_prime_vunmap(struct dma_buf *dma_buf, void *vaddr)
+void nouveau_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
 {
-       struct nouveau_bo *nvbo = dma_buf->priv;
-       struct drm_device *dev = nvbo->gem->dev;
+       struct nouveau_bo *nvbo = nouveau_gem_object(obj);
 
-       mutex_lock(&dev->struct_mutex);
-       nvbo->vmapping_count--;
-       if (nvbo->vmapping_count == 0) {
-               ttm_bo_kunmap(&nvbo->dma_buf_vmap);
-       }
-       mutex_unlock(&dev->struct_mutex);
+       ttm_bo_kunmap(&nvbo->dma_buf_vmap);
 }
 
-static const struct dma_buf_ops nouveau_dmabuf_ops =  {
-       .map_dma_buf = nouveau_gem_map_dma_buf,
-       .unmap_dma_buf = nouveau_gem_unmap_dma_buf,
-       .release = nouveau_gem_dmabuf_release,
-       .kmap = nouveau_gem_kmap,
-       .kmap_atomic = nouveau_gem_kmap_atomic,
-       .kunmap = nouveau_gem_kunmap,
-       .kunmap_atomic = nouveau_gem_kunmap_atomic,
-       .mmap = nouveau_gem_prime_mmap,
-       .vmap = nouveau_gem_prime_vmap,
-       .vunmap = nouveau_gem_prime_vunmap,
-};
-
-static int
-nouveau_prime_new(struct drm_device *dev,
-                 size_t size,
-                 struct sg_table *sg,
-                 struct nouveau_bo **pnvbo)
+struct drm_gem_object *nouveau_gem_prime_import_sg_table(struct drm_device *dev,
+                                                        size_t size,
+                                                        struct sg_table *sg)
 {
        struct nouveau_bo *nvbo;
        u32 flags = 0;
@@ -150,24 +66,22 @@ nouveau_prime_new(struct drm_device *dev,
        flags = TTM_PL_FLAG_TT;
 
        ret = nouveau_bo_new(dev, size, 0, flags, 0, 0,
-                            sg, pnvbo);
+                            sg, &nvbo);
        if (ret)
-               return ret;
-       nvbo = *pnvbo;
+               return ERR_PTR(ret);
 
        nvbo->valid_domains = NOUVEAU_GEM_DOMAIN_GART;
        nvbo->gem = drm_gem_object_alloc(dev, nvbo->bo.mem.size);
        if (!nvbo->gem) {
-               nouveau_bo_ref(NULL, pnvbo);
-               return -ENOMEM;
+               nouveau_bo_ref(NULL, &nvbo);
+               return ERR_PTR(-ENOMEM);
        }
 
        nvbo->gem->driver_private = nvbo;
-       return 0;
+       return nvbo->gem;
 }
 
-struct dma_buf *nouveau_gem_prime_export(struct drm_device *dev,
-                               struct drm_gem_object *obj, int flags)
+int nouveau_gem_prime_pin(struct drm_gem_object *obj)
 {
        struct nouveau_bo *nvbo = nouveau_gem_object(obj);
        int ret = 0;
@@ -175,52 +89,7 @@ struct dma_buf *nouveau_gem_prime_export(struct drm_device *dev,
        /* pin buffer into GTT */
        ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_TT);
        if (ret)
-               return ERR_PTR(-EINVAL);
-
-       return dma_buf_export(nvbo, &nouveau_dmabuf_ops, obj->size, flags);
-}
-
-struct drm_gem_object *nouveau_gem_prime_import(struct drm_device *dev,
-                               struct dma_buf *dma_buf)
-{
-       struct dma_buf_attachment *attach;
-       struct sg_table *sg;
-       struct nouveau_bo *nvbo;
-       int ret;
-
-       if (dma_buf->ops == &nouveau_dmabuf_ops) {
-               nvbo = dma_buf->priv;
-               if (nvbo->gem) {
-                       if (nvbo->gem->dev == dev) {
-                               drm_gem_object_reference(nvbo->gem);
-                               dma_buf_put(dma_buf);
-                               return nvbo->gem;
-                       }
-               }
-       }
-       /* need to attach */
-       attach = dma_buf_attach(dma_buf, dev->dev);
-       if (IS_ERR(attach))
-               return ERR_PTR(PTR_ERR(attach));
-
-       sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
-       if (IS_ERR(sg)) {
-               ret = PTR_ERR(sg);
-               goto fail_detach;
-       }
-
-       ret = nouveau_prime_new(dev, dma_buf->size, sg, &nvbo);
-       if (ret)
-               goto fail_unmap;
-
-       nvbo->gem->import_attach = attach;
-
-       return nvbo->gem;
+               return -EINVAL;
 
-fail_unmap:
-       dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL);
-fail_detach:
-       dma_buf_detach(dma_buf, attach);
-       return ERR_PTR(ret);
+       return 0;
 }
-
index 39ffc07f906b8f57fcaf2427e3a5be3bbfd76bb5..7e24cdf1cb3944d1b343829182cf321b026d2869 100644 (file)
@@ -490,8 +490,8 @@ static void nv04_dfp_update_backlight(struct drm_encoder *encoder, int mode)
        /* BIOS scripts usually take care of the backlight, thanks
         * Apple for your consistency.
         */
-       if (dev->pci_device == 0x0179 || dev->pci_device == 0x0189 ||
-           dev->pci_device == 0x0329) {
+       if (dev->pci_device == 0x0174 || dev->pci_device == 0x0179 ||
+           dev->pci_device == 0x0189 || dev->pci_device == 0x0329) {
                if (mode == DRM_MODE_DPMS_ON) {
                        nv_mask(device, NV_PBUS_DEBUG_DUALHEAD_CTL, 0, 1 << 31);
                        nv_mask(device, NV_PCRTC_GPIO_EXT, 3, 1);
index 2cd6fb8c548e830e8fc629cf9fbe8bc60c3b3f63..ad48444c385c1781aca7e86ced7515214807d74d 100644 (file)
@@ -22,6 +22,9 @@
  * Author: Ben Skeggs
  */
 
+#include <core/object.h>
+#include <core/class.h>
+
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 
@@ -31,6 +34,8 @@
 #include "nouveau_encoder.h"
 #include "nouveau_connector.h"
 
+#include <subdev/i2c.h>
+
 int
 nv04_display_early_init(struct drm_device *dev)
 {
@@ -53,6 +58,7 @@ int
 nv04_display_create(struct drm_device *dev)
 {
        struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
        struct dcb_table *dcb = &drm->vbios.dcb;
        struct drm_connector *connector, *ct;
        struct drm_encoder *encoder;
@@ -71,6 +77,11 @@ nv04_display_create(struct drm_device *dev)
 
        nouveau_hw_save_vga_fonts(dev, 1);
 
+       ret = nouveau_object_new(nv_object(drm), NVDRM_DEVICE, 0xd1500000,
+                                NV04_DISP_CLASS, NULL, 0, &disp->core);
+       if (ret)
+               return ret;
+
        nv04_crtc_create(dev, 0);
        if (nv_two_heads(dev))
                nv04_crtc_create(dev, 1);
@@ -114,6 +125,11 @@ nv04_display_create(struct drm_device *dev)
                }
        }
 
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+               nv_encoder->i2c = i2c->find(i2c, nv_encoder->dcb->i2c_index);
+       }
+
        /* Save previous state */
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
                crtc->funcs->save(crtc);
@@ -140,7 +156,7 @@ nv04_display_destroy(struct drm_device *dev)
                        .crtc = crtc,
                };
 
-               crtc->funcs->set_config(&modeset);
+               drm_mode_set_config_internal(&modeset);
        }
 
        /* Restore state */
index 45322802e37d5576084646b565fbaace947691dd..a0a031dad13f366a9b69510afaa55200337fba3b 100644 (file)
@@ -80,6 +80,7 @@ struct nv04_display {
        struct nv04_mode_state saved_reg;
        uint32_t saved_vga_font[4][16384];
        uint32_t dac_users[4];
+       struct nouveau_object *core;
 };
 
 static inline struct nv04_display *
index a220b94ba9f2dfe3904ea38137818e972efc181f..94eadd1dd10a40441736381ac35a18335d68bb8d 100644 (file)
@@ -78,6 +78,9 @@ nv04_fence_context_new(struct nouveau_channel *chan)
        struct nv04_fence_chan *fctx = kzalloc(sizeof(*fctx), GFP_KERNEL);
        if (fctx) {
                nouveau_fence_context_new(&fctx->base);
+               fctx->base.emit = nv04_fence_emit;
+               fctx->base.sync = nv04_fence_sync;
+               fctx->base.read = nv04_fence_read;
                chan->fence = fctx;
                return 0;
        }
@@ -104,8 +107,5 @@ nv04_fence_create(struct nouveau_drm *drm)
        priv->base.dtor = nv04_fence_destroy;
        priv->base.context_new = nv04_fence_context_new;
        priv->base.context_del = nv04_fence_context_del;
-       priv->base.emit = nv04_fence_emit;
-       priv->base.sync = nv04_fence_sync;
-       priv->base.read = nv04_fence_read;
        return 0;
 }
index 03017f24d593fa84b569a6c3f370b310b7ab09c2..06f434f03fbace2583cb9ed3c3c3400a807b48f8 100644 (file)
 
 #include "nouveau_drm.h"
 #include "nouveau_dma.h"
-#include "nouveau_fence.h"
-
-struct nv10_fence_chan {
-       struct nouveau_fence_chan base;
-};
-
-struct nv10_fence_priv {
-       struct nouveau_fence_priv base;
-       struct nouveau_bo *bo;
-       spinlock_t lock;
-       u32 sequence;
-};
+#include "nv10_fence.h"
 
 int
 nv10_fence_emit(struct nouveau_fence *fence)
@@ -61,45 +50,6 @@ nv10_fence_sync(struct nouveau_fence *fence,
        return -ENODEV;
 }
 
-int
-nv17_fence_sync(struct nouveau_fence *fence,
-               struct nouveau_channel *prev, struct nouveau_channel *chan)
-{
-       struct nv10_fence_priv *priv = chan->drm->fence;
-       u32 value;
-       int ret;
-
-       if (!mutex_trylock(&prev->cli->mutex))
-               return -EBUSY;
-
-       spin_lock(&priv->lock);
-       value = priv->sequence;
-       priv->sequence += 2;
-       spin_unlock(&priv->lock);
-
-       ret = RING_SPACE(prev, 5);
-       if (!ret) {
-               BEGIN_NV04(prev, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 4);
-               OUT_RING  (prev, NvSema);
-               OUT_RING  (prev, 0);
-               OUT_RING  (prev, value + 0);
-               OUT_RING  (prev, value + 1);
-               FIRE_RING (prev);
-       }
-
-       if (!ret && !(ret = RING_SPACE(chan, 5))) {
-               BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 4);
-               OUT_RING  (chan, NvSema);
-               OUT_RING  (chan, 0);
-               OUT_RING  (chan, value + 1);
-               OUT_RING  (chan, value + 2);
-               FIRE_RING (chan);
-       }
-
-       mutex_unlock(&prev->cli->mutex);
-       return 0;
-}
-
 u32
 nv10_fence_read(struct nouveau_channel *chan)
 {
@@ -115,39 +65,20 @@ nv10_fence_context_del(struct nouveau_channel *chan)
        kfree(fctx);
 }
 
-static int
+int
 nv10_fence_context_new(struct nouveau_channel *chan)
 {
-       struct nv10_fence_priv *priv = chan->drm->fence;
        struct nv10_fence_chan *fctx;
-       int ret = 0;
 
        fctx = chan->fence = kzalloc(sizeof(*fctx), GFP_KERNEL);
        if (!fctx)
                return -ENOMEM;
 
        nouveau_fence_context_new(&fctx->base);
-
-       if (priv->bo) {
-               struct ttm_mem_reg *mem = &priv->bo->bo.mem;
-               struct nouveau_object *object;
-               u32 start = mem->start * PAGE_SIZE;
-               u32 limit = mem->start + mem->size - 1;
-
-               ret = nouveau_object_new(nv_object(chan->cli), chan->handle,
-                                        NvSema, 0x0002,
-                                        &(struct nv_dma_class) {
-                                               .flags = NV_DMA_TARGET_VRAM |
-                                                        NV_DMA_ACCESS_RDWR,
-                                               .start = start,
-                                               .limit = limit,
-                                        }, sizeof(struct nv_dma_class),
-                                        &object);
-       }
-
-       if (ret)
-               nv10_fence_context_del(chan);
-       return ret;
+       fctx->base.emit = nv10_fence_emit;
+       fctx->base.read = nv10_fence_read;
+       fctx->base.sync = nv10_fence_sync;
+       return 0;
 }
 
 void
@@ -162,18 +93,10 @@ nv10_fence_destroy(struct nouveau_drm *drm)
        kfree(priv);
 }
 
-void nv17_fence_resume(struct nouveau_drm *drm)
-{
-       struct nv10_fence_priv *priv = drm->fence;
-
-       nouveau_bo_wr32(priv->bo, 0, priv->sequence);
-}
-
 int
 nv10_fence_create(struct nouveau_drm *drm)
 {
        struct nv10_fence_priv *priv;
-       int ret = 0;
 
        priv = drm->fence = kzalloc(sizeof(*priv), GFP_KERNEL);
        if (!priv)
@@ -182,33 +105,6 @@ nv10_fence_create(struct nouveau_drm *drm)
        priv->base.dtor = nv10_fence_destroy;
        priv->base.context_new = nv10_fence_context_new;
        priv->base.context_del = nv10_fence_context_del;
-       priv->base.emit = nv10_fence_emit;
-       priv->base.read = nv10_fence_read;
-       priv->base.sync = nv10_fence_sync;
        spin_lock_init(&priv->lock);
-
-       if (nv_device(drm->device)->chipset >= 0x17) {
-               ret = nouveau_bo_new(drm->dev, 4096, 0x1000, TTM_PL_FLAG_VRAM,
-                                    0, 0x0000, NULL, &priv->bo);
-               if (!ret) {
-                       ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM);
-                       if (!ret) {
-                               ret = nouveau_bo_map(priv->bo);
-                               if (ret)
-                                       nouveau_bo_unpin(priv->bo);
-                       }
-                       if (ret)
-                               nouveau_bo_ref(NULL, &priv->bo);
-               }
-
-               if (ret == 0) {
-                       nouveau_bo_wr32(priv->bo, 0x000, 0x00000000);
-                       priv->base.sync = nv17_fence_sync;
-                       priv->base.resume = nv17_fence_resume;
-               }
-       }
-
-       if (ret)
-               nv10_fence_destroy(drm);
-       return ret;
+       return 0;
 }
diff --git a/drivers/gpu/drm/nouveau/nv10_fence.h b/drivers/gpu/drm/nouveau/nv10_fence.h
new file mode 100644 (file)
index 0000000..e5d9204
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef __NV10_FENCE_H_
+#define __NV10_FENCE_H_
+
+#include <core/os.h>
+#include "nouveau_fence.h"
+#include "nouveau_bo.h"
+
+struct nv10_fence_chan {
+       struct nouveau_fence_chan base;
+};
+
+struct nv10_fence_priv {
+       struct nouveau_fence_priv base;
+       struct nouveau_bo *bo;
+       spinlock_t lock;
+       u32 sequence;
+};
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nv17_fence.c b/drivers/gpu/drm/nouveau/nv17_fence.c
new file mode 100644 (file)
index 0000000..8e47a9b
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include <core/object.h>
+#include <core/class.h>
+
+#include "nouveau_drm.h"
+#include "nouveau_dma.h"
+#include "nv10_fence.h"
+
+int
+nv17_fence_sync(struct nouveau_fence *fence,
+               struct nouveau_channel *prev, struct nouveau_channel *chan)
+{
+       struct nv10_fence_priv *priv = chan->drm->fence;
+       u32 value;
+       int ret;
+
+       if (!mutex_trylock(&prev->cli->mutex))
+               return -EBUSY;
+
+       spin_lock(&priv->lock);
+       value = priv->sequence;
+       priv->sequence += 2;
+       spin_unlock(&priv->lock);
+
+       ret = RING_SPACE(prev, 5);
+       if (!ret) {
+               BEGIN_NV04(prev, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 4);
+               OUT_RING  (prev, NvSema);
+               OUT_RING  (prev, 0);
+               OUT_RING  (prev, value + 0);
+               OUT_RING  (prev, value + 1);
+               FIRE_RING (prev);
+       }
+
+       if (!ret && !(ret = RING_SPACE(chan, 5))) {
+               BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 4);
+               OUT_RING  (chan, NvSema);
+               OUT_RING  (chan, 0);
+               OUT_RING  (chan, value + 1);
+               OUT_RING  (chan, value + 2);
+               FIRE_RING (chan);
+       }
+
+       mutex_unlock(&prev->cli->mutex);
+       return 0;
+}
+
+static int
+nv17_fence_context_new(struct nouveau_channel *chan)
+{
+       struct nv10_fence_priv *priv = chan->drm->fence;
+       struct nv10_fence_chan *fctx;
+       struct ttm_mem_reg *mem = &priv->bo->bo.mem;
+       struct nouveau_object *object;
+       u32 start = mem->start * PAGE_SIZE;
+       u32 limit = mem->start + mem->size - 1;
+       int ret = 0;
+
+       fctx = chan->fence = kzalloc(sizeof(*fctx), GFP_KERNEL);
+       if (!fctx)
+               return -ENOMEM;
+
+       nouveau_fence_context_new(&fctx->base);
+       fctx->base.emit = nv10_fence_emit;
+       fctx->base.read = nv10_fence_read;
+       fctx->base.sync = nv17_fence_sync;
+
+       ret = nouveau_object_new(nv_object(chan->cli), chan->handle,
+                                NvSema, 0x0002,
+                                &(struct nv_dma_class) {
+                                       .flags = NV_DMA_TARGET_VRAM |
+                                                NV_DMA_ACCESS_RDWR,
+                                       .start = start,
+                                       .limit = limit,
+                                }, sizeof(struct nv_dma_class),
+                                &object);
+       if (ret)
+               nv10_fence_context_del(chan);
+       return ret;
+}
+
+void
+nv17_fence_resume(struct nouveau_drm *drm)
+{
+       struct nv10_fence_priv *priv = drm->fence;
+
+       nouveau_bo_wr32(priv->bo, 0, priv->sequence);
+}
+
+int
+nv17_fence_create(struct nouveau_drm *drm)
+{
+       struct nv10_fence_priv *priv;
+       int ret = 0;
+
+       priv = drm->fence = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->base.dtor = nv10_fence_destroy;
+       priv->base.resume = nv17_fence_resume;
+       priv->base.context_new = nv17_fence_context_new;
+       priv->base.context_del = nv10_fence_context_del;
+       spin_lock_init(&priv->lock);
+
+       ret = nouveau_bo_new(drm->dev, 4096, 0x1000, TTM_PL_FLAG_VRAM,
+                            0, 0x0000, NULL, &priv->bo);
+       if (!ret) {
+               ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM);
+               if (!ret) {
+                       ret = nouveau_bo_map(priv->bo);
+                       if (ret)
+                               nouveau_bo_unpin(priv->bo);
+               }
+               if (ret)
+                       nouveau_bo_ref(NULL, &priv->bo);
+       }
+
+       if (ret) {
+               nv10_fence_destroy(drm);
+               return ret;
+       }
+
+       nouveau_bo_wr32(priv->bo, 0x000, 0x00000000);
+       return ret;
+}
index 2ca276ada507774ce27b8f03d5d8139c8ddb202b..977e42be20500d1b8f42b748f08c8691f74c9a01 100644 (file)
@@ -768,7 +768,7 @@ static int nv17_tv_set_property(struct drm_encoder *encoder,
                                .crtc = crtc,
                        };
 
-                       crtc->funcs->set_config(&modeset);
+                       drm_mode_set_config_internal(&modeset);
                }
        }
 
index 35874085a61e62920f8e584c51e86de8d5112216..a6237c9cbbc30c5cacfc246733fdbf1c67e68588 100644 (file)
@@ -43,6 +43,7 @@
 #include <subdev/timer.h>
 #include <subdev/bar.h>
 #include <subdev/fb.h>
+#include <subdev/i2c.h>
 
 #define EVO_DMA_NR 9
 
@@ -128,6 +129,11 @@ struct nv50_dmac {
        struct nv50_chan base;
        dma_addr_t handle;
        u32 *ptr;
+
+       /* Protects against concurrent pushbuf access to this channel, lock is
+        * grabbed by evo_wait (if the pushbuf reservation is successful) and
+        * dropped again by evo_kick. */
+       struct mutex lock;
 };
 
 static void
@@ -271,6 +277,8 @@ nv50_dmac_create(struct nouveau_object *core, u32 bclass, u8 head,
        u32 pushbuf = *(u32 *)data;
        int ret;
 
+       mutex_init(&dmac->lock);
+
        dmac->ptr = pci_alloc_consistent(nv_device(core)->pdev, PAGE_SIZE,
                                        &dmac->handle);
        if (!dmac->ptr)
@@ -395,11 +403,13 @@ evo_wait(void *evoc, int nr)
        struct nv50_dmac *dmac = evoc;
        u32 put = nv_ro32(dmac->base.user, 0x0000) / 4;
 
+       mutex_lock(&dmac->lock);
        if (put + nr >= (PAGE_SIZE / 4) - 8) {
                dmac->ptr[put] = 0x20000000;
 
                nv_wo32(dmac->base.user, 0x0000, 0x00000000);
                if (!nv_wait(dmac->base.user, 0x0004, ~0, 0x00000000)) {
+                       mutex_unlock(&dmac->lock);
                        NV_ERROR(dmac->base.user, "channel stalled\n");
                        return NULL;
                }
@@ -415,6 +425,7 @@ evo_kick(u32 *push, void *evoc)
 {
        struct nv50_dmac *dmac = evoc;
        nv_wo32(dmac->base.user, 0x0000, (push - dmac->ptr) << 2);
+       mutex_unlock(&dmac->lock);
 }
 
 #define evo_mthd(p,m,s) *((p)++) = (((s) << 18) | (m))
@@ -423,7 +434,10 @@ evo_kick(u32 *push, void *evoc)
 static bool
 evo_sync_wait(void *data)
 {
-       return nouveau_bo_rd32(data, EVO_MAST_NTFY) != 0x00000000;
+       if (nouveau_bo_rd32(data, EVO_MAST_NTFY) != 0x00000000)
+               return true;
+       usleep_range(1, 2);
+       return false;
 }
 
 static int
@@ -502,7 +516,7 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
                if (ret)
                        return ret;
 
-               if (nv_mclass(chan->object) < NVC0_CHANNEL_IND_CLASS) {
+               if (nv_mclass(chan->object) < NV84_CHANNEL_IND_CLASS) {
                        BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 2);
                        OUT_RING  (chan, NvEvoSema0 + nv_crtc->index);
                        OUT_RING  (chan, sync->sem.offset);
@@ -512,24 +526,36 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
                        OUT_RING  (chan, sync->sem.offset ^ 0x10);
                        OUT_RING  (chan, 0x74b1e000);
                        BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1);
-                       if (nv_mclass(chan->object) < NV84_CHANNEL_DMA_CLASS)
-                               OUT_RING  (chan, NvSema);
-                       else
-                               OUT_RING  (chan, chan->vram);
+                       OUT_RING  (chan, NvSema);
+               } else
+               if (nv_mclass(chan->object) < NVC0_CHANNEL_IND_CLASS) {
+                       u64 offset = nv84_fence_crtc(chan, nv_crtc->index);
+                       offset += sync->sem.offset;
+
+                       BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
+                       OUT_RING  (chan, upper_32_bits(offset));
+                       OUT_RING  (chan, lower_32_bits(offset));
+                       OUT_RING  (chan, 0xf00d0000 | sync->sem.value);
+                       OUT_RING  (chan, 0x00000002);
+                       BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
+                       OUT_RING  (chan, upper_32_bits(offset));
+                       OUT_RING  (chan, lower_32_bits(offset ^ 0x10));
+                       OUT_RING  (chan, 0x74b1e000);
+                       OUT_RING  (chan, 0x00000001);
                } else {
-                       u64 offset = nvc0_fence_crtc(chan, nv_crtc->index);
+                       u64 offset = nv84_fence_crtc(chan, nv_crtc->index);
                        offset += sync->sem.offset;
 
                        BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
                        OUT_RING  (chan, upper_32_bits(offset));
                        OUT_RING  (chan, lower_32_bits(offset));
                        OUT_RING  (chan, 0xf00d0000 | sync->sem.value);
-                       OUT_RING  (chan, 0x1002);
+                       OUT_RING  (chan, 0x00001002);
                        BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
                        OUT_RING  (chan, upper_32_bits(offset));
                        OUT_RING  (chan, lower_32_bits(offset ^ 0x10));
                        OUT_RING  (chan, 0x74b1e000);
-                       OUT_RING  (chan, 0x1001);
+                       OUT_RING  (chan, 0x00001001);
                }
 
                FIRE_RING (chan);
@@ -1493,9 +1519,6 @@ nv50_dac_disconnect(struct drm_encoder *encoder)
                                evo_mthd(push, 0x0180 + (or * 0x020), 1);
                                evo_data(push, 0x00000000);
                        }
-
-                       evo_mthd(push, 0x0080, 1);
-                       evo_data(push, 0x00000000);
                        evo_kick(push, mast);
                }
        }
@@ -1542,20 +1565,23 @@ static const struct drm_encoder_funcs nv50_dac_func = {
 static int
 nv50_dac_create(struct drm_connector *connector, struct dcb_output *dcbe)
 {
-       struct drm_device *dev = connector->dev;
+       struct nouveau_drm *drm = nouveau_drm(connector->dev);
+       struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
        struct nouveau_encoder *nv_encoder;
        struct drm_encoder *encoder;
+       int type = DRM_MODE_ENCODER_DAC;
 
        nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
        if (!nv_encoder)
                return -ENOMEM;
        nv_encoder->dcb = dcbe;
        nv_encoder->or = ffs(dcbe->or) - 1;
+       nv_encoder->i2c = i2c->find(i2c, dcbe->i2c_index);
 
        encoder = to_drm_encoder(nv_encoder);
        encoder->possible_crtcs = dcbe->heads;
        encoder->possible_clones = 0;
-       drm_encoder_init(dev, encoder, &nv50_dac_func, DRM_MODE_ENCODER_DAC);
+       drm_encoder_init(connector->dev, encoder, &nv50_dac_func, type);
        drm_encoder_helper_add(encoder, &nv50_dac_hfunc);
 
        drm_mode_connector_attach_encoder(connector, encoder);
@@ -1664,9 +1690,6 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode)
        }
 
        nv_call(disp->core, NV50_DISP_SOR_PWR + or, (mode == DRM_MODE_DPMS_ON));
-
-       if (nv_encoder->dcb->type == DCB_OUTPUT_DP)
-               nouveau_dp_dpms(encoder, mode, nv_encoder->dp.datarate, disp->core);
 }
 
 static bool
@@ -1709,9 +1732,6 @@ nv50_sor_disconnect(struct drm_encoder *encoder)
                                evo_mthd(push, 0x0200 + (or * 0x20), 1);
                                evo_data(push, 0x00000000);
                        }
-
-                       evo_mthd(push, 0x0080, 1);
-                       evo_data(push, 0x00000000);
                        evo_kick(push, mast);
                }
 
@@ -1722,14 +1742,6 @@ nv50_sor_disconnect(struct drm_encoder *encoder)
        nv_encoder->crtc = NULL;
 }
 
-static void
-nv50_sor_prepare(struct drm_encoder *encoder)
-{
-       nv50_sor_disconnect(encoder);
-       if (nouveau_encoder(encoder)->dcb->type == DCB_OUTPUT_DP)
-               evo_sync(encoder->dev);
-}
-
 static void
 nv50_sor_commit(struct drm_encoder *encoder)
 {
@@ -1825,8 +1837,13 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
        push = evo_wait(nv50_mast(dev), 8);
        if (push) {
                if (nv50_vers(mast) < NVD0_DISP_CLASS) {
+                       u32 ctrl = (depth << 16) | (proto << 8) | owner;
+                       if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+                               ctrl |= 0x00001000;
+                       if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+                               ctrl |= 0x00002000;
                        evo_mthd(push, 0x0600 + (nv_encoder->or * 0x040), 1);
-                       evo_data(push, (depth << 16) | (proto << 8) | owner);
+                       evo_data(push, ctrl);
                } else {
                        u32 magic = 0x31ec6000 | (nv_crtc->index << 25);
                        u32 syncs = 0x00000001;
@@ -1862,7 +1879,7 @@ nv50_sor_destroy(struct drm_encoder *encoder)
 static const struct drm_encoder_helper_funcs nv50_sor_hfunc = {
        .dpms = nv50_sor_dpms,
        .mode_fixup = nv50_sor_mode_fixup,
-       .prepare = nv50_sor_prepare,
+       .prepare = nv50_sor_disconnect,
        .commit = nv50_sor_commit,
        .mode_set = nv50_sor_mode_set,
        .disable = nv50_sor_disconnect,
@@ -1876,27 +1893,214 @@ static const struct drm_encoder_funcs nv50_sor_func = {
 static int
 nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe)
 {
-       struct drm_device *dev = connector->dev;
+       struct nouveau_drm *drm = nouveau_drm(connector->dev);
+       struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
        struct nouveau_encoder *nv_encoder;
        struct drm_encoder *encoder;
+       int type;
+
+       switch (dcbe->type) {
+       case DCB_OUTPUT_LVDS: type = DRM_MODE_ENCODER_LVDS; break;
+       case DCB_OUTPUT_TMDS:
+       case DCB_OUTPUT_DP:
+       default:
+               type = DRM_MODE_ENCODER_TMDS;
+               break;
+       }
 
        nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
        if (!nv_encoder)
                return -ENOMEM;
        nv_encoder->dcb = dcbe;
        nv_encoder->or = ffs(dcbe->or) - 1;
+       nv_encoder->i2c = i2c->find(i2c, dcbe->i2c_index);
        nv_encoder->last_dpms = DRM_MODE_DPMS_OFF;
 
        encoder = to_drm_encoder(nv_encoder);
        encoder->possible_crtcs = dcbe->heads;
        encoder->possible_clones = 0;
-       drm_encoder_init(dev, encoder, &nv50_sor_func, DRM_MODE_ENCODER_TMDS);
+       drm_encoder_init(connector->dev, encoder, &nv50_sor_func, type);
        drm_encoder_helper_add(encoder, &nv50_sor_hfunc);
 
        drm_mode_connector_attach_encoder(connector, encoder);
        return 0;
 }
 
+/******************************************************************************
+ * PIOR
+ *****************************************************************************/
+
+static void
+nv50_pior_dpms(struct drm_encoder *encoder, int mode)
+{
+       struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+       struct nv50_disp *disp = nv50_disp(encoder->dev);
+       u32 mthd = (nv_encoder->dcb->type << 12) | nv_encoder->or;
+       u32 ctrl = (mode == DRM_MODE_DPMS_ON);
+       nv_call(disp->core, NV50_DISP_PIOR_PWR + mthd, ctrl);
+}
+
+static bool
+nv50_pior_mode_fixup(struct drm_encoder *encoder,
+                    const struct drm_display_mode *mode,
+                    struct drm_display_mode *adjusted_mode)
+{
+       struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+       struct nouveau_connector *nv_connector;
+
+       nv_connector = nouveau_encoder_connector_get(nv_encoder);
+       if (nv_connector && nv_connector->native_mode) {
+               if (nv_connector->scaling_mode != DRM_MODE_SCALE_NONE) {
+                       int id = adjusted_mode->base.id;
+                       *adjusted_mode = *nv_connector->native_mode;
+                       adjusted_mode->base.id = id;
+               }
+       }
+
+       adjusted_mode->clock *= 2;
+       return true;
+}
+
+static void
+nv50_pior_commit(struct drm_encoder *encoder)
+{
+}
+
+static void
+nv50_pior_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
+                  struct drm_display_mode *adjusted_mode)
+{
+       struct nv50_mast *mast = nv50_mast(encoder->dev);
+       struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+       struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
+       struct nouveau_connector *nv_connector;
+       u8 owner = 1 << nv_crtc->index;
+       u8 proto, depth;
+       u32 *push;
+
+       nv_connector = nouveau_encoder_connector_get(nv_encoder);
+       switch (nv_connector->base.display_info.bpc) {
+       case 10: depth = 0x6; break;
+       case  8: depth = 0x5; break;
+       case  6: depth = 0x2; break;
+       default: depth = 0x0; break;
+       }
+
+       switch (nv_encoder->dcb->type) {
+       case DCB_OUTPUT_TMDS:
+       case DCB_OUTPUT_DP:
+               proto = 0x0;
+               break;
+       default:
+               BUG_ON(1);
+               break;
+       }
+
+       nv50_pior_dpms(encoder, DRM_MODE_DPMS_ON);
+
+       push = evo_wait(mast, 8);
+       if (push) {
+               if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) {
+                       u32 ctrl = (depth << 16) | (proto << 8) | owner;
+                       if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+                               ctrl |= 0x00001000;
+                       if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+                               ctrl |= 0x00002000;
+                       evo_mthd(push, 0x0700 + (nv_encoder->or * 0x040), 1);
+                       evo_data(push, ctrl);
+               }
+
+               evo_kick(push, mast);
+       }
+
+       nv_encoder->crtc = encoder->crtc;
+}
+
+static void
+nv50_pior_disconnect(struct drm_encoder *encoder)
+{
+       struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+       struct nv50_mast *mast = nv50_mast(encoder->dev);
+       const int or = nv_encoder->or;
+       u32 *push;
+
+       if (nv_encoder->crtc) {
+               nv50_crtc_prepare(nv_encoder->crtc);
+
+               push = evo_wait(mast, 4);
+               if (push) {
+                       if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) {
+                               evo_mthd(push, 0x0700 + (or * 0x040), 1);
+                               evo_data(push, 0x00000000);
+                       }
+                       evo_kick(push, mast);
+               }
+       }
+
+       nv_encoder->crtc = NULL;
+}
+
+static void
+nv50_pior_destroy(struct drm_encoder *encoder)
+{
+       drm_encoder_cleanup(encoder);
+       kfree(encoder);
+}
+
+static const struct drm_encoder_helper_funcs nv50_pior_hfunc = {
+       .dpms = nv50_pior_dpms,
+       .mode_fixup = nv50_pior_mode_fixup,
+       .prepare = nv50_pior_disconnect,
+       .commit = nv50_pior_commit,
+       .mode_set = nv50_pior_mode_set,
+       .disable = nv50_pior_disconnect,
+       .get_crtc = nv50_display_crtc_get,
+};
+
+static const struct drm_encoder_funcs nv50_pior_func = {
+       .destroy = nv50_pior_destroy,
+};
+
+static int
+nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe)
+{
+       struct nouveau_drm *drm = nouveau_drm(connector->dev);
+       struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
+       struct nouveau_i2c_port *ddc = NULL;
+       struct nouveau_encoder *nv_encoder;
+       struct drm_encoder *encoder;
+       int type;
+
+       switch (dcbe->type) {
+       case DCB_OUTPUT_TMDS:
+               ddc  = i2c->find_type(i2c, NV_I2C_TYPE_EXTDDC(dcbe->extdev));
+               type = DRM_MODE_ENCODER_TMDS;
+               break;
+       case DCB_OUTPUT_DP:
+               ddc  = i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(dcbe->extdev));
+               type = DRM_MODE_ENCODER_TMDS;
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
+       if (!nv_encoder)
+               return -ENOMEM;
+       nv_encoder->dcb = dcbe;
+       nv_encoder->or = ffs(dcbe->or) - 1;
+       nv_encoder->i2c = ddc;
+
+       encoder = to_drm_encoder(nv_encoder);
+       encoder->possible_crtcs = dcbe->heads;
+       encoder->possible_clones = 0;
+       drm_encoder_init(connector->dev, encoder, &nv50_pior_func, type);
+       drm_encoder_helper_add(encoder, &nv50_pior_hfunc);
+
+       drm_mode_connector_attach_encoder(connector, encoder);
+       return 0;
+}
+
 /******************************************************************************
  * Init
  *****************************************************************************/
@@ -1913,7 +2117,7 @@ nv50_display_init(struct drm_device *dev)
                evo_mthd(push, 0x0088, 1);
                evo_data(push, NvEvoSync);
                evo_kick(push, nv50_mast(dev));
-               return evo_sync(dev);
+               return 0;
        }
 
        return -EBUSY;
@@ -2019,25 +2223,28 @@ nv50_display_create(struct drm_device *dev)
                if (IS_ERR(connector))
                        continue;
 
-               if (dcbe->location != DCB_LOC_ON_CHIP) {
-                       NV_WARN(drm, "skipping off-chip encoder %d/%d\n",
-                               dcbe->type, ffs(dcbe->or) - 1);
-                       continue;
+               if (dcbe->location == DCB_LOC_ON_CHIP) {
+                       switch (dcbe->type) {
+                       case DCB_OUTPUT_TMDS:
+                       case DCB_OUTPUT_LVDS:
+                       case DCB_OUTPUT_DP:
+                               ret = nv50_sor_create(connector, dcbe);
+                               break;
+                       case DCB_OUTPUT_ANALOG:
+                               ret = nv50_dac_create(connector, dcbe);
+                               break;
+                       default:
+                               ret = -ENODEV;
+                               break;
+                       }
+               } else {
+                       ret = nv50_pior_create(connector, dcbe);
                }
 
-               switch (dcbe->type) {
-               case DCB_OUTPUT_TMDS:
-               case DCB_OUTPUT_LVDS:
-               case DCB_OUTPUT_DP:
-                       nv50_sor_create(connector, dcbe);
-                       break;
-               case DCB_OUTPUT_ANALOG:
-                       nv50_dac_create(connector, dcbe);
-                       break;
-               default:
-                       NV_WARN(drm, "skipping unsupported encoder %d/%d\n",
-                               dcbe->type, ffs(dcbe->or) - 1);
-                       continue;
+               if (ret) {
+                       NV_WARN(drm, "failed to create encoder %d/%d/%d: %d\n",
+                                    dcbe->location, dcbe->type,
+                                    ffs(dcbe->or) - 1, ret);
                }
        }
 
index d889f3ac0d412d4affac8c1a17428270b6f0a448..f9701e567db89793136c52e02bf11d59baba4ce4 100644 (file)
 
 #include "nouveau_drm.h"
 #include "nouveau_dma.h"
-#include "nouveau_fence.h"
+#include "nv10_fence.h"
 
 #include "nv50_display.h"
 
-struct nv50_fence_chan {
-       struct nouveau_fence_chan base;
-};
-
-struct nv50_fence_priv {
-       struct nouveau_fence_priv base;
-       struct nouveau_bo *bo;
-       spinlock_t lock;
-       u32 sequence;
-};
-
 static int
 nv50_fence_context_new(struct nouveau_channel *chan)
 {
        struct drm_device *dev = chan->drm->dev;
-       struct nv50_fence_priv *priv = chan->drm->fence;
-       struct nv50_fence_chan *fctx;
+       struct nv10_fence_priv *priv = chan->drm->fence;
+       struct nv10_fence_chan *fctx;
        struct ttm_mem_reg *mem = &priv->bo->bo.mem;
        struct nouveau_object *object;
        int ret, i;
@@ -57,6 +46,9 @@ nv50_fence_context_new(struct nouveau_channel *chan)
                return -ENOMEM;
 
        nouveau_fence_context_new(&fctx->base);
+       fctx->base.emit = nv10_fence_emit;
+       fctx->base.read = nv10_fence_read;
+       fctx->base.sync = nv17_fence_sync;
 
        ret = nouveau_object_new(nv_object(chan->cli), chan->handle,
                                 NvSema, 0x0002,
@@ -91,7 +83,7 @@ nv50_fence_context_new(struct nouveau_channel *chan)
 int
 nv50_fence_create(struct nouveau_drm *drm)
 {
-       struct nv50_fence_priv *priv;
+       struct nv10_fence_priv *priv;
        int ret = 0;
 
        priv = drm->fence = kzalloc(sizeof(*priv), GFP_KERNEL);
@@ -99,11 +91,9 @@ nv50_fence_create(struct nouveau_drm *drm)
                return -ENOMEM;
 
        priv->base.dtor = nv10_fence_destroy;
+       priv->base.resume = nv17_fence_resume;
        priv->base.context_new = nv50_fence_context_new;
        priv->base.context_del = nv10_fence_context_del;
-       priv->base.emit = nv10_fence_emit;
-       priv->base.read = nv10_fence_read;
-       priv->base.sync = nv17_fence_sync;
        spin_lock_init(&priv->lock);
 
        ret = nouveau_bo_new(drm->dev, 4096, 0x1000, TTM_PL_FLAG_VRAM,
@@ -119,13 +109,11 @@ nv50_fence_create(struct nouveau_drm *drm)
                        nouveau_bo_ref(NULL, &priv->bo);
        }
 
-       if (ret == 0) {
-               nouveau_bo_wr32(priv->bo, 0x000, 0x00000000);
-               priv->base.sync = nv17_fence_sync;
-               priv->base.resume = nv17_fence_resume;
+       if (ret) {
+               nv10_fence_destroy(drm);
+               return ret;
        }
 
-       if (ret)
-               nv10_fence_destroy(drm);
+       nouveau_bo_wr32(priv->bo, 0x000, 0x00000000);
        return ret;
 }
index c686650584b6d33ad203ed3b4ed4cd62dd38bb73..9fd475c89820f640fbcdf4af42de62ac0d75c755 100644 (file)
@@ -23,6 +23,7 @@
  */
 
 #include <core/object.h>
+#include <core/client.h>
 #include <core/class.h>
 
 #include <engine/fifo.h>
 
 #include "nv50_display.h"
 
-struct nv84_fence_chan {
-       struct nouveau_fence_chan base;
-};
-
-struct nv84_fence_priv {
-       struct nouveau_fence_priv base;
-       struct nouveau_gpuobj *mem;
-};
+u64
+nv84_fence_crtc(struct nouveau_channel *chan, int crtc)
+{
+       struct nv84_fence_chan *fctx = chan->fence;
+       return fctx->dispc_vma[crtc].offset;
+}
 
 static int
-nv84_fence_emit(struct nouveau_fence *fence)
+nv84_fence_emit32(struct nouveau_channel *chan, u64 virtual, u32 sequence)
 {
-       struct nouveau_channel *chan = fence->channel;
-       struct nouveau_fifo_chan *fifo = (void *)chan->object;
-       int ret = RING_SPACE(chan, 7);
+       int ret = RING_SPACE(chan, 8);
        if (ret == 0) {
                BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1);
-               OUT_RING  (chan, NvSema);
-               BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
-               OUT_RING  (chan, upper_32_bits(fifo->chid * 16));
-               OUT_RING  (chan, lower_32_bits(fifo->chid * 16));
-               OUT_RING  (chan, fence->sequence);
+               OUT_RING  (chan, chan->vram);
+               BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 5);
+               OUT_RING  (chan, upper_32_bits(virtual));
+               OUT_RING  (chan, lower_32_bits(virtual));
+               OUT_RING  (chan, sequence);
                OUT_RING  (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG);
+               OUT_RING  (chan, 0x00000000);
                FIRE_RING (chan);
        }
        return ret;
 }
 
-
 static int
-nv84_fence_sync(struct nouveau_fence *fence,
-               struct nouveau_channel *prev, struct nouveau_channel *chan)
+nv84_fence_sync32(struct nouveau_channel *chan, u64 virtual, u32 sequence)
 {
-       struct nouveau_fifo_chan *fifo = (void *)prev->object;
        int ret = RING_SPACE(chan, 7);
        if (ret == 0) {
                BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1);
-               OUT_RING  (chan, NvSema);
+               OUT_RING  (chan, chan->vram);
                BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
-               OUT_RING  (chan, upper_32_bits(fifo->chid * 16));
-               OUT_RING  (chan, lower_32_bits(fifo->chid * 16));
-               OUT_RING  (chan, fence->sequence);
+               OUT_RING  (chan, upper_32_bits(virtual));
+               OUT_RING  (chan, lower_32_bits(virtual));
+               OUT_RING  (chan, sequence);
                OUT_RING  (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_GEQUAL);
                FIRE_RING (chan);
        }
        return ret;
 }
 
+static int
+nv84_fence_emit(struct nouveau_fence *fence)
+{
+       struct nouveau_channel *chan = fence->channel;
+       struct nv84_fence_chan *fctx = chan->fence;
+       struct nouveau_fifo_chan *fifo = (void *)chan->object;
+       u64 addr = fifo->chid * 16;
+
+       if (fence->sysmem)
+               addr += fctx->vma_gart.offset;
+       else
+               addr += fctx->vma.offset;
+
+       return fctx->base.emit32(chan, addr, fence->sequence);
+}
+
+static int
+nv84_fence_sync(struct nouveau_fence *fence,
+               struct nouveau_channel *prev, struct nouveau_channel *chan)
+{
+       struct nv84_fence_chan *fctx = chan->fence;
+       struct nouveau_fifo_chan *fifo = (void *)prev->object;
+       u64 addr = fifo->chid * 16;
+
+       if (fence->sysmem)
+               addr += fctx->vma_gart.offset;
+       else
+               addr += fctx->vma.offset;
+
+       return fctx->base.sync32(chan, addr, fence->sequence);
+}
+
 static u32
 nv84_fence_read(struct nouveau_channel *chan)
 {
        struct nouveau_fifo_chan *fifo = (void *)chan->object;
        struct nv84_fence_priv *priv = chan->drm->fence;
-       return nv_ro32(priv->mem, fifo->chid * 16);
+       return nouveau_bo_rd32(priv->bo, fifo->chid * 16/4);
 }
 
 static void
 nv84_fence_context_del(struct nouveau_channel *chan)
 {
+       struct drm_device *dev = chan->drm->dev;
+       struct nv84_fence_priv *priv = chan->drm->fence;
        struct nv84_fence_chan *fctx = chan->fence;
+       int i;
+
+       for (i = 0; i < dev->mode_config.num_crtc; i++) {
+               struct nouveau_bo *bo = nv50_display_crtc_sema(dev, i);
+               nouveau_bo_vma_del(bo, &fctx->dispc_vma[i]);
+       }
+
+       nouveau_bo_vma_del(priv->bo, &fctx->vma_gart);
+       nouveau_bo_vma_del(priv->bo, &fctx->vma);
        nouveau_fence_context_del(&fctx->base);
        chan->fence = NULL;
        kfree(fctx);
 }
 
-static int
+int
 nv84_fence_context_new(struct nouveau_channel *chan)
 {
-       struct drm_device *dev = chan->drm->dev;
        struct nouveau_fifo_chan *fifo = (void *)chan->object;
+       struct nouveau_client *client = nouveau_client(fifo);
        struct nv84_fence_priv *priv = chan->drm->fence;
        struct nv84_fence_chan *fctx;
-       struct nouveau_object *object;
        int ret, i;
 
        fctx = chan->fence = kzalloc(sizeof(*fctx), GFP_KERNEL);
@@ -113,44 +150,74 @@ nv84_fence_context_new(struct nouveau_channel *chan)
                return -ENOMEM;
 
        nouveau_fence_context_new(&fctx->base);
+       fctx->base.emit = nv84_fence_emit;
+       fctx->base.sync = nv84_fence_sync;
+       fctx->base.read = nv84_fence_read;
+       fctx->base.emit32 = nv84_fence_emit32;
+       fctx->base.sync32 = nv84_fence_sync32;
 
-       ret = nouveau_object_new(nv_object(chan->cli), chan->handle,
-                                NvSema, 0x0002,
-                                &(struct nv_dma_class) {
-                                       .flags = NV_DMA_TARGET_VRAM |
-                                                NV_DMA_ACCESS_RDWR,
-                                       .start = priv->mem->addr,
-                                       .limit = priv->mem->addr +
-                                                priv->mem->size - 1,
-                                }, sizeof(struct nv_dma_class),
-                                &object);
-
-       /* dma objects for display sync channel semaphore blocks */
-       for (i = 0; !ret && i < dev->mode_config.num_crtc; i++) {
-               struct nouveau_bo *bo = nv50_display_crtc_sema(dev, i);
+       ret = nouveau_bo_vma_add(priv->bo, client->vm, &fctx->vma);
+       if (ret == 0) {
+               ret = nouveau_bo_vma_add(priv->bo_gart, client->vm,
+                                       &fctx->vma_gart);
+       }
 
-               ret = nouveau_object_new(nv_object(chan->cli), chan->handle,
-                                        NvEvoSema0 + i, 0x003d,
-                                        &(struct nv_dma_class) {
-                                               .flags = NV_DMA_TARGET_VRAM |
-                                                        NV_DMA_ACCESS_RDWR,
-                                               .start = bo->bo.offset,
-                                               .limit = bo->bo.offset + 0xfff,
-                                        }, sizeof(struct nv_dma_class),
-                                        &object);
+       /* map display semaphore buffers into channel's vm */
+       for (i = 0; !ret && i < chan->drm->dev->mode_config.num_crtc; i++) {
+               struct nouveau_bo *bo = nv50_display_crtc_sema(chan->drm->dev, i);
+               ret = nouveau_bo_vma_add(bo, client->vm, &fctx->dispc_vma[i]);
        }
 
+       nouveau_bo_wr32(priv->bo, fifo->chid * 16/4, 0x00000000);
+
        if (ret)
                nv84_fence_context_del(chan);
-       nv_wo32(priv->mem, fifo->chid * 16, 0x00000000);
        return ret;
 }
 
+static bool
+nv84_fence_suspend(struct nouveau_drm *drm)
+{
+       struct nouveau_fifo *pfifo = nouveau_fifo(drm->device);
+       struct nv84_fence_priv *priv = drm->fence;
+       int i;
+
+       priv->suspend = vmalloc((pfifo->max + 1) * sizeof(u32));
+       if (priv->suspend) {
+               for (i = 0; i <= pfifo->max; i++)
+                       priv->suspend[i] = nouveau_bo_rd32(priv->bo, i*4);
+       }
+
+       return priv->suspend != NULL;
+}
+
+static void
+nv84_fence_resume(struct nouveau_drm *drm)
+{
+       struct nouveau_fifo *pfifo = nouveau_fifo(drm->device);
+       struct nv84_fence_priv *priv = drm->fence;
+       int i;
+
+       if (priv->suspend) {
+               for (i = 0; i <= pfifo->max; i++)
+                       nouveau_bo_wr32(priv->bo, i*4, priv->suspend[i]);
+               vfree(priv->suspend);
+               priv->suspend = NULL;
+       }
+}
+
 static void
 nv84_fence_destroy(struct nouveau_drm *drm)
 {
        struct nv84_fence_priv *priv = drm->fence;
-       nouveau_gpuobj_ref(NULL, &priv->mem);
+       nouveau_bo_unmap(priv->bo_gart);
+       if (priv->bo_gart)
+               nouveau_bo_unpin(priv->bo_gart);
+       nouveau_bo_ref(NULL, &priv->bo_gart);
+       nouveau_bo_unmap(priv->bo);
+       if (priv->bo)
+               nouveau_bo_unpin(priv->bo);
+       nouveau_bo_ref(NULL, &priv->bo);
        drm->fence = NULL;
        kfree(priv);
 }
@@ -160,7 +227,6 @@ nv84_fence_create(struct nouveau_drm *drm)
 {
        struct nouveau_fifo *pfifo = nouveau_fifo(drm->device);
        struct nv84_fence_priv *priv;
-       u32 chan = pfifo->max + 1;
        int ret;
 
        priv = drm->fence = kzalloc(sizeof(*priv), GFP_KERNEL);
@@ -168,14 +234,42 @@ nv84_fence_create(struct nouveau_drm *drm)
                return -ENOMEM;
 
        priv->base.dtor = nv84_fence_destroy;
+       priv->base.suspend = nv84_fence_suspend;
+       priv->base.resume = nv84_fence_resume;
        priv->base.context_new = nv84_fence_context_new;
        priv->base.context_del = nv84_fence_context_del;
-       priv->base.emit = nv84_fence_emit;
-       priv->base.sync = nv84_fence_sync;
-       priv->base.read = nv84_fence_read;
 
-       ret = nouveau_gpuobj_new(drm->device, NULL, chan * 16, 0x1000, 0,
-                               &priv->mem);
+       init_waitqueue_head(&priv->base.waiting);
+       priv->base.uevent = true;
+
+       ret = nouveau_bo_new(drm->dev, 16 * (pfifo->max + 1), 0,
+                            TTM_PL_FLAG_VRAM, 0, 0, NULL, &priv->bo);
+       if (ret == 0) {
+               ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM);
+               if (ret == 0) {
+                       ret = nouveau_bo_map(priv->bo);
+                       if (ret)
+                               nouveau_bo_unpin(priv->bo);
+               }
+               if (ret)
+                       nouveau_bo_ref(NULL, &priv->bo);
+       }
+
+       if (ret == 0)
+               ret = nouveau_bo_new(drm->dev, 16 * (pfifo->max + 1), 0,
+                                    TTM_PL_FLAG_TT, 0, 0, NULL,
+                                    &priv->bo_gart);
+       if (ret == 0) {
+               ret = nouveau_bo_pin(priv->bo_gart, TTM_PL_FLAG_TT);
+               if (ret == 0) {
+                       ret = nouveau_bo_map(priv->bo_gart);
+                       if (ret)
+                               nouveau_bo_unpin(priv->bo_gart);
+               }
+               if (ret)
+                       nouveau_bo_ref(NULL, &priv->bo_gart);
+       }
+
        if (ret)
                nv84_fence_destroy(drm);
        return ret;
index 2a56b1b551cb85f4a4df7ee506c19f5a3d1c8e37..9566267fbc421adae678b26ec1d4a302b67ec9d9 100644 (file)
 
 #include "nv50_display.h"
 
-struct nvc0_fence_priv {
-       struct nouveau_fence_priv base;
-       struct nouveau_bo *bo;
-       u32 *suspend;
-};
-
-struct nvc0_fence_chan {
-       struct nouveau_fence_chan base;
-       struct nouveau_vma vma;
-       struct nouveau_vma dispc_vma[4];
-};
-
-u64
-nvc0_fence_crtc(struct nouveau_channel *chan, int crtc)
-{
-       struct nvc0_fence_chan *fctx = chan->fence;
-       return fctx->dispc_vma[crtc].offset;
-}
-
 static int
-nvc0_fence_emit(struct nouveau_fence *fence)
+nvc0_fence_emit32(struct nouveau_channel *chan, u64 virtual, u32 sequence)
 {
-       struct nouveau_channel *chan = fence->channel;
-       struct nvc0_fence_chan *fctx = chan->fence;
-       struct nouveau_fifo_chan *fifo = (void *)chan->object;
-       u64 addr = fctx->vma.offset + fifo->chid * 16;
-       int ret;
-
-       ret = RING_SPACE(chan, 5);
+       int ret = RING_SPACE(chan, 6);
        if (ret == 0) {
-               BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
-               OUT_RING  (chan, upper_32_bits(addr));
-               OUT_RING  (chan, lower_32_bits(addr));
-               OUT_RING  (chan, fence->sequence);
+               BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 5);
+               OUT_RING  (chan, upper_32_bits(virtual));
+               OUT_RING  (chan, lower_32_bits(virtual));
+               OUT_RING  (chan, sequence);
                OUT_RING  (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG);
+               OUT_RING  (chan, 0x00000000);
                FIRE_RING (chan);
        }
-
        return ret;
 }
 
 static int
-nvc0_fence_sync(struct nouveau_fence *fence,
-               struct nouveau_channel *prev, struct nouveau_channel *chan)
+nvc0_fence_sync32(struct nouveau_channel *chan, u64 virtual, u32 sequence)
 {
-       struct nvc0_fence_chan *fctx = chan->fence;
-       struct nouveau_fifo_chan *fifo = (void *)prev->object;
-       u64 addr = fctx->vma.offset + fifo->chid * 16;
-       int ret;
-
-       ret = RING_SPACE(chan, 5);
+       int ret = RING_SPACE(chan, 5);
        if (ret == 0) {
                BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
-               OUT_RING  (chan, upper_32_bits(addr));
-               OUT_RING  (chan, lower_32_bits(addr));
-               OUT_RING  (chan, fence->sequence);
+               OUT_RING  (chan, upper_32_bits(virtual));
+               OUT_RING  (chan, lower_32_bits(virtual));
+               OUT_RING  (chan, sequence);
                OUT_RING  (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_GEQUAL |
                                 NVC0_SUBCHAN_SEMAPHORE_TRIGGER_YIELD);
                FIRE_RING (chan);
        }
-
        return ret;
 }
 
-static u32
-nvc0_fence_read(struct nouveau_channel *chan)
-{
-       struct nouveau_fifo_chan *fifo = (void *)chan->object;
-       struct nvc0_fence_priv *priv = chan->drm->fence;
-       return nouveau_bo_rd32(priv->bo, fifo->chid * 16/4);
-}
-
-static void
-nvc0_fence_context_del(struct nouveau_channel *chan)
-{
-       struct drm_device *dev = chan->drm->dev;
-       struct nvc0_fence_priv *priv = chan->drm->fence;
-       struct nvc0_fence_chan *fctx = chan->fence;
-       int i;
-
-       for (i = 0; i < dev->mode_config.num_crtc; i++) {
-               struct nouveau_bo *bo = nv50_display_crtc_sema(dev, i);
-               nouveau_bo_vma_del(bo, &fctx->dispc_vma[i]);
-       }
-
-       nouveau_bo_vma_del(priv->bo, &fctx->vma);
-       nouveau_fence_context_del(&fctx->base);
-       chan->fence = NULL;
-       kfree(fctx);
-}
-
 static int
 nvc0_fence_context_new(struct nouveau_channel *chan)
 {
-       struct nouveau_fifo_chan *fifo = (void *)chan->object;
-       struct nouveau_client *client = nouveau_client(fifo);
-       struct nvc0_fence_priv *priv = chan->drm->fence;
-       struct nvc0_fence_chan *fctx;
-       int ret, i;
-
-       fctx = chan->fence = kzalloc(sizeof(*fctx), GFP_KERNEL);
-       if (!fctx)
-               return -ENOMEM;
-
-       nouveau_fence_context_new(&fctx->base);
-
-       ret = nouveau_bo_vma_add(priv->bo, client->vm, &fctx->vma);
-       if (ret)
-               nvc0_fence_context_del(chan);
-
-       /* map display semaphore buffers into channel's vm */
-       for (i = 0; !ret && i < chan->drm->dev->mode_config.num_crtc; i++) {
-               struct nouveau_bo *bo = nv50_display_crtc_sema(chan->drm->dev, i);
-               ret = nouveau_bo_vma_add(bo, client->vm, &fctx->dispc_vma[i]);
+       int ret = nv84_fence_context_new(chan);
+       if (ret == 0) {
+               struct nv84_fence_chan *fctx = chan->fence;
+               fctx->base.emit32 = nvc0_fence_emit32;
+               fctx->base.sync32 = nvc0_fence_sync32;
        }
-
-       nouveau_bo_wr32(priv->bo, fifo->chid * 16/4, 0x00000000);
        return ret;
 }
 
-static bool
-nvc0_fence_suspend(struct nouveau_drm *drm)
-{
-       struct nouveau_fifo *pfifo = nouveau_fifo(drm->device);
-       struct nvc0_fence_priv *priv = drm->fence;
-       int i;
-
-       priv->suspend = vmalloc((pfifo->max + 1) * sizeof(u32));
-       if (priv->suspend) {
-               for (i = 0; i <= pfifo->max; i++)
-                       priv->suspend[i] = nouveau_bo_rd32(priv->bo, i);
-       }
-
-       return priv->suspend != NULL;
-}
-
-static void
-nvc0_fence_resume(struct nouveau_drm *drm)
-{
-       struct nouveau_fifo *pfifo = nouveau_fifo(drm->device);
-       struct nvc0_fence_priv *priv = drm->fence;
-       int i;
-
-       if (priv->suspend) {
-               for (i = 0; i <= pfifo->max; i++)
-                       nouveau_bo_wr32(priv->bo, i, priv->suspend[i]);
-               vfree(priv->suspend);
-               priv->suspend = NULL;
-       }
-}
-
-static void
-nvc0_fence_destroy(struct nouveau_drm *drm)
-{
-       struct nvc0_fence_priv *priv = drm->fence;
-       nouveau_bo_unmap(priv->bo);
-       if (priv->bo)
-               nouveau_bo_unpin(priv->bo);
-       nouveau_bo_ref(NULL, &priv->bo);
-       drm->fence = NULL;
-       kfree(priv);
-}
-
 int
 nvc0_fence_create(struct nouveau_drm *drm)
 {
-       struct nouveau_fifo *pfifo = nouveau_fifo(drm->device);
-       struct nvc0_fence_priv *priv;
-       int ret;
-
-       priv = drm->fence = kzalloc(sizeof(*priv), GFP_KERNEL);
-       if (!priv)
-               return -ENOMEM;
-
-       priv->base.dtor = nvc0_fence_destroy;
-       priv->base.suspend = nvc0_fence_suspend;
-       priv->base.resume = nvc0_fence_resume;
-       priv->base.context_new = nvc0_fence_context_new;
-       priv->base.context_del = nvc0_fence_context_del;
-       priv->base.emit = nvc0_fence_emit;
-       priv->base.sync = nvc0_fence_sync;
-       priv->base.read = nvc0_fence_read;
-
-       ret = nouveau_bo_new(drm->dev, 16 * (pfifo->max + 1), 0,
-                            TTM_PL_FLAG_VRAM, 0, 0, NULL, &priv->bo);
+       int ret = nv84_fence_create(drm);
        if (ret == 0) {
-               ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM);
-               if (ret == 0) {
-                       ret = nouveau_bo_map(priv->bo);
-                       if (ret)
-                               nouveau_bo_unpin(priv->bo);
-               }
-               if (ret)
-                       nouveau_bo_ref(NULL, &priv->bo);
+               struct nv84_fence_priv *priv = drm->fence;
+               priv->base.context_new = nvc0_fence_context_new;
        }
-
-       if (ret)
-               nvc0_fence_destroy(drm);
        return ret;
 }
similarity index 96%
rename from drivers/staging/omapdrm/Makefile
rename to drivers/gpu/drm/omapdrm/Makefile
index 1ca0e0016de424b249219b178dcb9ee71654fd06..d85e058f2845a014c0216cabeda52d70cc7bf0b6 100644 (file)
@@ -5,6 +5,7 @@
 
 ccflags-y := -Iinclude/drm -Werror
 omapdrm-y := omap_drv.o \
+       omap_irq.o \
        omap_debugfs.o \
        omap_crtc.o \
        omap_plane.o \
diff --git a/drivers/gpu/drm/omapdrm/TODO b/drivers/gpu/drm/omapdrm/TODO
new file mode 100644 (file)
index 0000000..4d8c18a
--- /dev/null
@@ -0,0 +1,23 @@
+TODO
+. Where should we do eviction (detatch_pages())?  We aren't necessarily
+  accessing the pages via a GART, so maybe we need some other threshold
+  to put a cap on the # of pages that can be pin'd.
+  . Use mm_shrinker to trigger unpinning pages.
+  . This is mainly theoretical since most of these devices don't actually
+    have swap or harddrive.
+. GEM/shmem backed pages can have existing mappings (kernel linear map,
+  etc..), which isn't really ideal.
+. Revisit GEM sync object infrastructure.. TTM has some framework for this
+  already.  Possibly this could be refactored out and made more common?
+  There should be some way to do this with less wheel-reinvention.
+  . This can be handled by the dma-buf fence/reservation stuff when it
+    lands
+
+Userspace:
+. git://anongit.freedesktop.org/xorg/driver/xf86-video-omap
+
+Currently tested on
+. OMAP3530 beagleboard
+. OMAP4430 pandaboard
+. OMAP4460 pandaboard
+. OMAP5432 uEVM
similarity index 73%
rename from drivers/staging/omapdrm/omap_connector.c
rename to drivers/gpu/drm/omapdrm/omap_connector.c
index 91edb3f9697292961883ca5876f4ab520b27ed6a..44284fd981fc0824b3bd5c6b6e0d583c03d5f72a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * drivers/staging/omapdrm/omap_connector.c
+ * drivers/gpu/drm/omapdrm/omap_connector.c
  *
  * Copyright (C) 2011 Texas Instruments
  * Author: Rob Clark <rob@ti.com>
 struct omap_connector {
        struct drm_connector base;
        struct omap_dss_device *dssdev;
+       struct drm_encoder *encoder;
 };
 
-static inline void copy_timings_omap_to_drm(struct drm_display_mode *mode,
+void copy_timings_omap_to_drm(struct drm_display_mode *mode,
                struct omap_video_timings *timings)
 {
        mode->clock = timings->pixel_clock;
@@ -64,7 +65,7 @@ static inline void copy_timings_omap_to_drm(struct drm_display_mode *mode,
                mode->flags |= DRM_MODE_FLAG_NVSYNC;
 }
 
-static inline void copy_timings_drm_to_omap(struct omap_video_timings *timings,
+void copy_timings_drm_to_omap(struct omap_video_timings *timings,
                struct drm_display_mode *mode)
 {
        timings->pixel_clock = mode->clock;
@@ -96,48 +97,7 @@ static inline void copy_timings_drm_to_omap(struct omap_video_timings *timings,
        timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES;
 }
 
-static void omap_connector_dpms(struct drm_connector *connector, int mode)
-{
-       struct omap_connector *omap_connector = to_omap_connector(connector);
-       struct omap_dss_device *dssdev = omap_connector->dssdev;
-       int old_dpms;
-
-       DBG("%s: %d", dssdev->name, mode);
-
-       old_dpms = connector->dpms;
-
-       /* from off to on, do from crtc to connector */
-       if (mode < old_dpms)
-               drm_helper_connector_dpms(connector, mode);
-
-       if (mode == DRM_MODE_DPMS_ON) {
-               /* store resume info for suspended displays */
-               switch (dssdev->state) {
-               case OMAP_DSS_DISPLAY_SUSPENDED:
-                       dssdev->activate_after_resume = true;
-                       break;
-               case OMAP_DSS_DISPLAY_DISABLED: {
-                       int ret = dssdev->driver->enable(dssdev);
-                       if (ret) {
-                               DBG("%s: failed to enable: %d",
-                                               dssdev->name, ret);
-                               dssdev->driver->disable(dssdev);
-                       }
-                       break;
-               }
-               default:
-                       break;
-               }
-       } else {
-               /* TODO */
-       }
-
-       /* from on to off, do from connector to crtc */
-       if (mode > old_dpms)
-               drm_helper_connector_dpms(connector, mode);
-}
-
-enum drm_connector_status omap_connector_detect(
+static enum drm_connector_status omap_connector_detect(
                struct drm_connector *connector, bool force)
 {
        struct omap_connector *omap_connector = to_omap_connector(connector);
@@ -164,8 +124,6 @@ static void omap_connector_destroy(struct drm_connector *connector)
        struct omap_connector *omap_connector = to_omap_connector(connector);
        struct omap_dss_device *dssdev = omap_connector->dssdev;
 
-       dssdev->driver->disable(dssdev);
-
        DBG("%s", omap_connector->dssdev->name);
        drm_sysfs_connector_remove(connector);
        drm_connector_cleanup(connector);
@@ -261,36 +219,12 @@ static int omap_connector_mode_valid(struct drm_connector *connector,
 struct drm_encoder *omap_connector_attached_encoder(
                struct drm_connector *connector)
 {
-       int i;
        struct omap_connector *omap_connector = to_omap_connector(connector);
-
-       for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
-               struct drm_mode_object *obj;
-
-               if (connector->encoder_ids[i] == 0)
-                       break;
-
-               obj = drm_mode_object_find(connector->dev,
-                               connector->encoder_ids[i],
-                               DRM_MODE_OBJECT_ENCODER);
-
-               if (obj) {
-                       struct drm_encoder *encoder = obj_to_encoder(obj);
-                       struct omap_overlay_manager *mgr =
-                                       omap_encoder_get_manager(encoder);
-                       DBG("%s: found %s", omap_connector->dssdev->name,
-                                       mgr->name);
-                       return encoder;
-               }
-       }
-
-       DBG("%s: no encoder", omap_connector->dssdev->name);
-
-       return NULL;
+       return omap_connector->encoder;
 }
 
 static const struct drm_connector_funcs omap_connector_funcs = {
-       .dpms = omap_connector_dpms,
+       .dpms = drm_helper_connector_dpms,
        .detect = omap_connector_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .destroy = omap_connector_destroy,
@@ -302,34 +236,6 @@ static const struct drm_connector_helper_funcs omap_connector_helper_funcs = {
        .best_encoder = omap_connector_attached_encoder,
 };
 
-/* called from encoder when mode is set, to propagate settings to the dssdev */
-void omap_connector_mode_set(struct drm_connector *connector,
-               struct drm_display_mode *mode)
-{
-       struct drm_device *dev = connector->dev;
-       struct omap_connector *omap_connector = to_omap_connector(connector);
-       struct omap_dss_device *dssdev = omap_connector->dssdev;
-       struct omap_dss_driver *dssdrv = dssdev->driver;
-       struct omap_video_timings timings = {0};
-
-       copy_timings_drm_to_omap(&timings, mode);
-
-       DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
-                       omap_connector->dssdev->name,
-                       mode->base.id, mode->name, mode->vrefresh, mode->clock,
-                       mode->hdisplay, mode->hsync_start,
-                       mode->hsync_end, mode->htotal,
-                       mode->vdisplay, mode->vsync_start,
-                       mode->vsync_end, mode->vtotal, mode->type, mode->flags);
-
-       if (dssdrv->check_timings(dssdev, &timings)) {
-               dev_err(dev->dev, "could not set timings\n");
-               return;
-       }
-
-       dssdrv->set_timings(dssdev, &timings);
-}
-
 /* flush an area of the framebuffer (in case of manual update display that
  * is not automatically flushed)
  */
@@ -344,7 +250,8 @@ void omap_connector_flush(struct drm_connector *connector,
 
 /* initialize connector */
 struct drm_connector *omap_connector_init(struct drm_device *dev,
-               int connector_type, struct omap_dss_device *dssdev)
+               int connector_type, struct omap_dss_device *dssdev,
+               struct drm_encoder *encoder)
 {
        struct drm_connector *connector = NULL;
        struct omap_connector *omap_connector;
@@ -360,6 +267,8 @@ struct drm_connector *omap_connector_init(struct drm_device *dev,
        }
 
        omap_connector->dssdev = dssdev;
+       omap_connector->encoder = encoder;
+
        connector = &omap_connector->base;
 
        drm_connector_init(dev, connector, &omap_connector_funcs,
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
new file mode 100644 (file)
index 0000000..ac2258f
--- /dev/null
@@ -0,0 +1,657 @@
+/*
+ * drivers/gpu/drm/omapdrm/omap_crtc.c
+ *
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob@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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 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 "omap_drv.h"
+
+#include <drm/drm_mode.h>
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+#define to_omap_crtc(x) container_of(x, struct omap_crtc, base)
+
+struct omap_crtc {
+       struct drm_crtc base;
+       struct drm_plane *plane;
+
+       const char *name;
+       int pipe;
+       enum omap_channel channel;
+       struct omap_overlay_manager_info info;
+
+       /*
+        * Temporary: eventually this will go away, but it is needed
+        * for now to keep the output's happy.  (They only need
+        * mgr->id.)  Eventually this will be replaced w/ something
+        * more common-panel-framework-y
+        */
+       struct omap_overlay_manager mgr;
+
+       struct omap_video_timings timings;
+       bool enabled;
+       bool full_update;
+
+       struct omap_drm_apply apply;
+
+       struct omap_drm_irq apply_irq;
+       struct omap_drm_irq error_irq;
+
+       /* list of in-progress apply's: */
+       struct list_head pending_applies;
+
+       /* list of queued apply's: */
+       struct list_head queued_applies;
+
+       /* for handling queued and in-progress applies: */
+       struct work_struct apply_work;
+
+       /* if there is a pending flip, these will be non-null: */
+       struct drm_pending_vblank_event *event;
+       struct drm_framebuffer *old_fb;
+
+       /* for handling page flips without caring about what
+        * the callback is called from.  Possibly we should just
+        * make omap_gem always call the cb from the worker so
+        * we don't have to care about this..
+        *
+        * XXX maybe fold into apply_work??
+        */
+       struct work_struct page_flip_work;
+};
+
+/*
+ * Manager-ops, callbacks from output when they need to configure
+ * the upstream part of the video pipe.
+ *
+ * Most of these we can ignore until we add support for command-mode
+ * panels.. for video-mode the crtc-helpers already do an adequate
+ * job of sequencing the setup of the video pipe in the proper order
+ */
+
+/* we can probably ignore these until we support command-mode panels: */
+static void omap_crtc_start_update(struct omap_overlay_manager *mgr)
+{
+}
+
+static int omap_crtc_enable(struct omap_overlay_manager *mgr)
+{
+       return 0;
+}
+
+static void omap_crtc_disable(struct omap_overlay_manager *mgr)
+{
+}
+
+static void omap_crtc_set_timings(struct omap_overlay_manager *mgr,
+               const struct omap_video_timings *timings)
+{
+       struct omap_crtc *omap_crtc = container_of(mgr, struct omap_crtc, mgr);
+       DBG("%s", omap_crtc->name);
+       omap_crtc->timings = *timings;
+       omap_crtc->full_update = true;
+}
+
+static void omap_crtc_set_lcd_config(struct omap_overlay_manager *mgr,
+               const struct dss_lcd_mgr_config *config)
+{
+       struct omap_crtc *omap_crtc = container_of(mgr, struct omap_crtc, mgr);
+       DBG("%s", omap_crtc->name);
+       dispc_mgr_set_lcd_config(omap_crtc->channel, config);
+}
+
+static int omap_crtc_register_framedone_handler(
+               struct omap_overlay_manager *mgr,
+               void (*handler)(void *), void *data)
+{
+       return 0;
+}
+
+static void omap_crtc_unregister_framedone_handler(
+               struct omap_overlay_manager *mgr,
+               void (*handler)(void *), void *data)
+{
+}
+
+static const struct dss_mgr_ops mgr_ops = {
+               .start_update = omap_crtc_start_update,
+               .enable = omap_crtc_enable,
+               .disable = omap_crtc_disable,
+               .set_timings = omap_crtc_set_timings,
+               .set_lcd_config = omap_crtc_set_lcd_config,
+               .register_framedone_handler = omap_crtc_register_framedone_handler,
+               .unregister_framedone_handler = omap_crtc_unregister_framedone_handler,
+};
+
+/*
+ * CRTC funcs:
+ */
+
+static void omap_crtc_destroy(struct drm_crtc *crtc)
+{
+       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+
+       DBG("%s", omap_crtc->name);
+
+       WARN_ON(omap_crtc->apply_irq.registered);
+       omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
+
+       omap_crtc->plane->funcs->destroy(omap_crtc->plane);
+       drm_crtc_cleanup(crtc);
+
+       kfree(omap_crtc);
+}
+
+static void omap_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+       struct omap_drm_private *priv = crtc->dev->dev_private;
+       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+       bool enabled = (mode == DRM_MODE_DPMS_ON);
+       int i;
+
+       DBG("%s: %d", omap_crtc->name, mode);
+
+       if (enabled != omap_crtc->enabled) {
+               omap_crtc->enabled = enabled;
+               omap_crtc->full_update = true;
+               omap_crtc_apply(crtc, &omap_crtc->apply);
+
+               /* also enable our private plane: */
+               WARN_ON(omap_plane_dpms(omap_crtc->plane, mode));
+
+               /* and any attached overlay planes: */
+               for (i = 0; i < priv->num_planes; i++) {
+                       struct drm_plane *plane = priv->planes[i];
+                       if (plane->crtc == crtc)
+                               WARN_ON(omap_plane_dpms(plane, mode));
+               }
+       }
+}
+
+static bool omap_crtc_mode_fixup(struct drm_crtc *crtc,
+               const struct drm_display_mode *mode,
+               struct drm_display_mode *adjusted_mode)
+{
+       return true;
+}
+
+static int omap_crtc_mode_set(struct drm_crtc *crtc,
+               struct drm_display_mode *mode,
+               struct drm_display_mode *adjusted_mode,
+               int x, int y,
+               struct drm_framebuffer *old_fb)
+{
+       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+
+       mode = adjusted_mode;
+
+       DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
+                       omap_crtc->name, mode->base.id, mode->name,
+                       mode->vrefresh, mode->clock,
+                       mode->hdisplay, mode->hsync_start,
+                       mode->hsync_end, mode->htotal,
+                       mode->vdisplay, mode->vsync_start,
+                       mode->vsync_end, mode->vtotal,
+                       mode->type, mode->flags);
+
+       copy_timings_drm_to_omap(&omap_crtc->timings, mode);
+       omap_crtc->full_update = true;
+
+       return omap_plane_mode_set(omap_crtc->plane, crtc, crtc->fb,
+                       0, 0, mode->hdisplay, mode->vdisplay,
+                       x << 16, y << 16,
+                       mode->hdisplay << 16, mode->vdisplay << 16,
+                       NULL, NULL);
+}
+
+static void omap_crtc_prepare(struct drm_crtc *crtc)
+{
+       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+       DBG("%s", omap_crtc->name);
+       omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+}
+
+static void omap_crtc_commit(struct drm_crtc *crtc)
+{
+       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+       DBG("%s", omap_crtc->name);
+       omap_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+               struct drm_framebuffer *old_fb)
+{
+       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+       struct drm_plane *plane = omap_crtc->plane;
+       struct drm_display_mode *mode = &crtc->mode;
+
+       return omap_plane_mode_set(plane, crtc, crtc->fb,
+                       0, 0, mode->hdisplay, mode->vdisplay,
+                       x << 16, y << 16,
+                       mode->hdisplay << 16, mode->vdisplay << 16,
+                       NULL, NULL);
+}
+
+static void omap_crtc_load_lut(struct drm_crtc *crtc)
+{
+}
+
+static void vblank_cb(void *arg)
+{
+       struct drm_crtc *crtc = arg;
+       struct drm_device *dev = crtc->dev;
+       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+
+       /* wakeup userspace */
+       if (omap_crtc->event)
+               drm_send_vblank_event(dev, omap_crtc->pipe, omap_crtc->event);
+
+       omap_crtc->event = NULL;
+       omap_crtc->old_fb = NULL;
+
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static void page_flip_worker(struct work_struct *work)
+{
+       struct omap_crtc *omap_crtc =
+                       container_of(work, struct omap_crtc, page_flip_work);
+       struct drm_crtc *crtc = &omap_crtc->base;
+       struct drm_display_mode *mode = &crtc->mode;
+       struct drm_gem_object *bo;
+
+       mutex_lock(&crtc->mutex);
+       omap_plane_mode_set(omap_crtc->plane, crtc, crtc->fb,
+                       0, 0, mode->hdisplay, mode->vdisplay,
+                       crtc->x << 16, crtc->y << 16,
+                       mode->hdisplay << 16, mode->vdisplay << 16,
+                       vblank_cb, crtc);
+       mutex_unlock(&crtc->mutex);
+
+       bo = omap_framebuffer_bo(crtc->fb, 0);
+       drm_gem_object_unreference_unlocked(bo);
+}
+
+static void page_flip_cb(void *arg)
+{
+       struct drm_crtc *crtc = arg;
+       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+       struct omap_drm_private *priv = crtc->dev->dev_private;
+
+       /* avoid assumptions about what ctxt we are called from: */
+       queue_work(priv->wq, &omap_crtc->page_flip_work);
+}
+
+static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
+                struct drm_framebuffer *fb,
+                struct drm_pending_vblank_event *event)
+{
+       struct drm_device *dev = crtc->dev;
+       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+       struct drm_gem_object *bo;
+
+       DBG("%d -> %d (event=%p)", crtc->fb ? crtc->fb->base.id : -1,
+                       fb->base.id, event);
+
+       if (omap_crtc->old_fb) {
+               dev_err(dev->dev, "already a pending flip\n");
+               return -EINVAL;
+       }
+
+       omap_crtc->event = event;
+       crtc->fb = fb;
+
+       /*
+        * Hold a reference temporarily until the crtc is updated
+        * and takes the reference to the bo.  This avoids it
+        * getting freed from under us:
+        */
+       bo = omap_framebuffer_bo(fb, 0);
+       drm_gem_object_reference(bo);
+
+       omap_gem_op_async(bo, OMAP_GEM_READ, page_flip_cb, crtc);
+
+       return 0;
+}
+
+static int omap_crtc_set_property(struct drm_crtc *crtc,
+               struct drm_property *property, uint64_t val)
+{
+       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+       struct omap_drm_private *priv = crtc->dev->dev_private;
+
+       if (property == priv->rotation_prop) {
+               crtc->invert_dimensions =
+                               !!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270)));
+       }
+
+       return omap_plane_set_property(omap_crtc->plane, property, val);
+}
+
+static const struct drm_crtc_funcs omap_crtc_funcs = {
+       .set_config = drm_crtc_helper_set_config,
+       .destroy = omap_crtc_destroy,
+       .page_flip = omap_crtc_page_flip_locked,
+       .set_property = omap_crtc_set_property,
+};
+
+static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = {
+       .dpms = omap_crtc_dpms,
+       .mode_fixup = omap_crtc_mode_fixup,
+       .mode_set = omap_crtc_mode_set,
+       .prepare = omap_crtc_prepare,
+       .commit = omap_crtc_commit,
+       .mode_set_base = omap_crtc_mode_set_base,
+       .load_lut = omap_crtc_load_lut,
+};
+
+const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc)
+{
+       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+       return &omap_crtc->timings;
+}
+
+enum omap_channel omap_crtc_channel(struct drm_crtc *crtc)
+{
+       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+       return omap_crtc->channel;
+}
+
+static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
+{
+       struct omap_crtc *omap_crtc =
+                       container_of(irq, struct omap_crtc, error_irq);
+       struct drm_crtc *crtc = &omap_crtc->base;
+       DRM_ERROR("%s: errors: %08x\n", omap_crtc->name, irqstatus);
+       /* avoid getting in a flood, unregister the irq until next vblank */
+       omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
+}
+
+static void omap_crtc_apply_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
+{
+       struct omap_crtc *omap_crtc =
+                       container_of(irq, struct omap_crtc, apply_irq);
+       struct drm_crtc *crtc = &omap_crtc->base;
+
+       if (!omap_crtc->error_irq.registered)
+               omap_irq_register(crtc->dev, &omap_crtc->error_irq);
+
+       if (!dispc_mgr_go_busy(omap_crtc->channel)) {
+               struct omap_drm_private *priv =
+                               crtc->dev->dev_private;
+               DBG("%s: apply done", omap_crtc->name);
+               omap_irq_unregister(crtc->dev, &omap_crtc->apply_irq);
+               queue_work(priv->wq, &omap_crtc->apply_work);
+       }
+}
+
+static void apply_worker(struct work_struct *work)
+{
+       struct omap_crtc *omap_crtc =
+                       container_of(work, struct omap_crtc, apply_work);
+       struct drm_crtc *crtc = &omap_crtc->base;
+       struct drm_device *dev = crtc->dev;
+       struct omap_drm_apply *apply, *n;
+       bool need_apply;
+
+       /*
+        * Synchronize everything on mode_config.mutex, to keep
+        * the callbacks and list modification all serialized
+        * with respect to modesetting ioctls from userspace.
+        */
+       mutex_lock(&crtc->mutex);
+       dispc_runtime_get();
+
+       /*
+        * If we are still pending a previous update, wait.. when the
+        * pending update completes, we get kicked again.
+        */
+       if (omap_crtc->apply_irq.registered)
+               goto out;
+
+       /* finish up previous apply's: */
+       list_for_each_entry_safe(apply, n,
+                       &omap_crtc->pending_applies, pending_node) {
+               apply->post_apply(apply);
+               list_del(&apply->pending_node);
+       }
+
+       need_apply = !list_empty(&omap_crtc->queued_applies);
+
+       /* then handle the next round of of queued apply's: */
+       list_for_each_entry_safe(apply, n,
+                       &omap_crtc->queued_applies, queued_node) {
+               apply->pre_apply(apply);
+               list_del(&apply->queued_node);
+               apply->queued = false;
+               list_add_tail(&apply->pending_node,
+                               &omap_crtc->pending_applies);
+       }
+
+       if (need_apply) {
+               enum omap_channel channel = omap_crtc->channel;
+
+               DBG("%s: GO", omap_crtc->name);
+
+               if (dispc_mgr_is_enabled(channel)) {
+                       omap_irq_register(dev, &omap_crtc->apply_irq);
+                       dispc_mgr_go(channel);
+               } else {
+                       struct omap_drm_private *priv = dev->dev_private;
+                       queue_work(priv->wq, &omap_crtc->apply_work);
+               }
+       }
+
+out:
+       dispc_runtime_put();
+       mutex_unlock(&crtc->mutex);
+}
+
+int omap_crtc_apply(struct drm_crtc *crtc,
+               struct omap_drm_apply *apply)
+{
+       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+
+       WARN_ON(!mutex_is_locked(&crtc->mutex));
+
+       /* no need to queue it again if it is already queued: */
+       if (apply->queued)
+               return 0;
+
+       apply->queued = true;
+       list_add_tail(&apply->queued_node, &omap_crtc->queued_applies);
+
+       /*
+        * If there are no currently pending updates, then go ahead and
+        * kick the worker immediately, otherwise it will run again when
+        * the current update finishes.
+        */
+       if (list_empty(&omap_crtc->pending_applies)) {
+               struct omap_drm_private *priv = crtc->dev->dev_private;
+               queue_work(priv->wq, &omap_crtc->apply_work);
+       }
+
+       return 0;
+}
+
+/* called only from apply */
+static void set_enabled(struct drm_crtc *crtc, bool enable)
+{
+       struct drm_device *dev = crtc->dev;
+       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+       enum omap_channel channel = omap_crtc->channel;
+       struct omap_irq_wait *wait = NULL;
+
+       if (dispc_mgr_is_enabled(channel) == enable)
+               return;
+
+       /* ignore sync-lost irqs during enable/disable */
+       omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
+
+       if (dispc_mgr_get_framedone_irq(channel)) {
+               if (!enable) {
+                       wait = omap_irq_wait_init(dev,
+                                       dispc_mgr_get_framedone_irq(channel), 1);
+               }
+       } else {
+               /*
+                * When we disable digit output, we need to wait until fields
+                * are done.  Otherwise the DSS is still working, and turning
+                * off the clocks prevents DSS from going to OFF mode. And when
+                * enabling, we need to wait for the extra sync losts
+                */
+               wait = omap_irq_wait_init(dev,
+                               dispc_mgr_get_vsync_irq(channel), 2);
+       }
+
+       dispc_mgr_enable(channel, enable);
+
+       if (wait) {
+               int ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100));
+               if (ret) {
+                       dev_err(dev->dev, "%s: timeout waiting for %s\n",
+                                       omap_crtc->name, enable ? "enable" : "disable");
+               }
+       }
+
+       omap_irq_register(crtc->dev, &omap_crtc->error_irq);
+}
+
+static void omap_crtc_pre_apply(struct omap_drm_apply *apply)
+{
+       struct omap_crtc *omap_crtc =
+                       container_of(apply, struct omap_crtc, apply);
+       struct drm_crtc *crtc = &omap_crtc->base;
+       struct drm_encoder *encoder = NULL;
+
+       DBG("%s: enabled=%d, full=%d", omap_crtc->name,
+                       omap_crtc->enabled, omap_crtc->full_update);
+
+       if (omap_crtc->full_update) {
+               struct omap_drm_private *priv = crtc->dev->dev_private;
+               int i;
+               for (i = 0; i < priv->num_encoders; i++) {
+                       if (priv->encoders[i]->crtc == crtc) {
+                               encoder = priv->encoders[i];
+                               break;
+                       }
+               }
+       }
+
+       if (!omap_crtc->enabled) {
+               set_enabled(&omap_crtc->base, false);
+               if (encoder)
+                       omap_encoder_set_enabled(encoder, false);
+       } else {
+               if (encoder) {
+                       omap_encoder_set_enabled(encoder, false);
+                       omap_encoder_update(encoder, &omap_crtc->mgr,
+                                       &omap_crtc->timings);
+                       omap_encoder_set_enabled(encoder, true);
+                       omap_crtc->full_update = false;
+               }
+
+               dispc_mgr_setup(omap_crtc->channel, &omap_crtc->info);
+               dispc_mgr_set_timings(omap_crtc->channel,
+                               &omap_crtc->timings);
+               set_enabled(&omap_crtc->base, true);
+       }
+
+       omap_crtc->full_update = false;
+}
+
+static void omap_crtc_post_apply(struct omap_drm_apply *apply)
+{
+       /* nothing needed for post-apply */
+}
+
+static const char *channel_names[] = {
+               [OMAP_DSS_CHANNEL_LCD] = "lcd",
+               [OMAP_DSS_CHANNEL_DIGIT] = "tv",
+               [OMAP_DSS_CHANNEL_LCD2] = "lcd2",
+};
+
+/* initialize crtc */
+struct drm_crtc *omap_crtc_init(struct drm_device *dev,
+               struct drm_plane *plane, enum omap_channel channel, int id)
+{
+       struct drm_crtc *crtc = NULL;
+       struct omap_crtc *omap_crtc;
+       struct omap_overlay_manager_info *info;
+
+       DBG("%s", channel_names[channel]);
+
+       omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL);
+
+       if (!omap_crtc) {
+               dev_err(dev->dev, "could not allocate CRTC\n");
+               goto fail;
+       }
+
+       crtc = &omap_crtc->base;
+
+       INIT_WORK(&omap_crtc->page_flip_work, page_flip_worker);
+       INIT_WORK(&omap_crtc->apply_work, apply_worker);
+
+       INIT_LIST_HEAD(&omap_crtc->pending_applies);
+       INIT_LIST_HEAD(&omap_crtc->queued_applies);
+
+       omap_crtc->apply.pre_apply  = omap_crtc_pre_apply;
+       omap_crtc->apply.post_apply = omap_crtc_post_apply;
+
+       omap_crtc->apply_irq.irqmask = pipe2vbl(id);
+       omap_crtc->apply_irq.irq = omap_crtc_apply_irq;
+
+       omap_crtc->error_irq.irqmask =
+                       dispc_mgr_get_sync_lost_irq(channel);
+       omap_crtc->error_irq.irq = omap_crtc_error_irq;
+       omap_irq_register(dev, &omap_crtc->error_irq);
+
+       omap_crtc->channel = channel;
+       omap_crtc->plane = plane;
+       omap_crtc->plane->crtc = crtc;
+       omap_crtc->name = channel_names[channel];
+       omap_crtc->pipe = id;
+
+       /* temporary: */
+       omap_crtc->mgr.id = channel;
+
+       dss_install_mgr_ops(&mgr_ops);
+
+       /* TODO: fix hard-coded setup.. add properties! */
+       info = &omap_crtc->info;
+       info->default_color = 0x00000000;
+       info->trans_key = 0x00000000;
+       info->trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
+       info->trans_enabled = false;
+
+       drm_crtc_init(dev, crtc, &omap_crtc_funcs);
+       drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs);
+
+       omap_plane_install_properties(omap_crtc->plane, &crtc->base);
+
+       return crtc;
+
+fail:
+       if (crtc)
+               omap_crtc_destroy(crtc);
+
+       return NULL;
+}
similarity index 90%
rename from drivers/staging/omapdrm/omap_debugfs.c
rename to drivers/gpu/drm/omapdrm/omap_debugfs.c
index 2f122e00b51da07ad7c29e7c4f868ad2bb4ce7b7..c27f59da7f2935f0fe3a1908b9a73c9a4c6b7529 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * drivers/staging/omapdrm/omap_debugfs.c
+ * drivers/gpu/drm/omapdrm/omap_debugfs.c
  *
  * Copyright (C) 2011 Texas Instruments
  * Author: Rob Clark <rob.clark@linaro.org>
@@ -57,21 +57,11 @@ static int fb_show(struct seq_file *m, void *arg)
        struct drm_device *dev = node->minor->dev;
        struct omap_drm_private *priv = dev->dev_private;
        struct drm_framebuffer *fb;
-       int ret;
-
-       ret = mutex_lock_interruptible(&dev->mode_config.mutex);
-       if (ret)
-               return ret;
-
-       ret = mutex_lock_interruptible(&dev->struct_mutex);
-       if (ret) {
-               mutex_unlock(&dev->mode_config.mutex);
-               return ret;
-       }
 
        seq_printf(m, "fbcon ");
        omap_framebuffer_describe(priv->fbdev->fb, m);
 
+       mutex_lock(&dev->mode_config.fb_lock);
        list_for_each_entry(fb, &dev->mode_config.fb_list, head) {
                if (fb == priv->fbdev->fb)
                        continue;
@@ -79,9 +69,7 @@ static int fb_show(struct seq_file *m, void *arg)
                seq_printf(m, "user ");
                omap_framebuffer_describe(fb, m);
        }
-
-       mutex_unlock(&dev->struct_mutex);
-       mutex_unlock(&dev->mode_config.mutex);
+       mutex_unlock(&dev->mode_config.fb_lock);
 
        return 0;
 }
similarity index 96%
rename from drivers/staging/omapdrm/omap_dmm_priv.h
rename to drivers/gpu/drm/omapdrm/omap_dmm_priv.h
index 273ec12c028abd13f3257cbdc7bb24628b156bf1..58bcd6ae0255dace4ae845c366af35cf62be4f20 100644 (file)
@@ -118,6 +118,11 @@ struct pat {
 #define DESCR_SIZE 128
 #define REFILL_BUFFER_SIZE ((4 * 128 * 256) + (3 * DESCR_SIZE))
 
+/* For OMAP5, a fixed offset is added to all Y coordinates for 1D buffers.
+ * This is used in programming to address the upper portion of the LUT
+*/
+#define OMAP5_LUT_OFFSET       128
+
 struct dmm;
 
 struct dmm_txn {
similarity index 86%
rename from drivers/staging/omapdrm/omap_dmm_tiler.c
rename to drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
index 59bf43899fc09d20ac5c01a0c1894f4b166bc189..3910215371053037aff61580939cd8596298e9eb 100644 (file)
@@ -213,6 +213,11 @@ static void dmm_txn_append(struct dmm_txn *txn, struct pat_area *area,
                txn->last_pat->next_pa = (uint32_t)pat_pa;
 
        pat->area = *area;
+
+       /* adjust Y coordinates based off of container parameters */
+       pat->area.y0 += engine->tcm->y_offset;
+       pat->area.y1 += engine->tcm->y_offset;
+
        pat->ctrl = (struct pat_ctrl){
                        .start = 1,
                        .lut_id = engine->tcm->lut_id,
@@ -622,6 +627,11 @@ static int omap_dmm_probe(struct platform_device *dev)
        omap_dmm->lut_width = ((pat_geom >> 16) & 0xF) << 5;
        omap_dmm->lut_height = ((pat_geom >> 24) & 0xF) << 5;
 
+       /* increment LUT by one if on OMAP5 */
+       /* LUT has twice the height, and is split into a separate container */
+       if (omap_dmm->lut_height != omap_dmm->container_height)
+               omap_dmm->num_lut++;
+
        /* initialize DMM registers */
        writel(0x88888888, omap_dmm->base + DMM_PAT_VIEW__0);
        writel(0x88888888, omap_dmm->base + DMM_PAT_VIEW__1);
@@ -701,6 +711,9 @@ static int omap_dmm_probe(struct platform_device *dev)
        }
 
        /* init containers */
+       /* Each LUT is associated with a TCM (container manager).  We use the
+          lut_id to denote the lut_id used to identify the correct LUT for
+          programming during reill operations */
        for (i = 0; i < omap_dmm->num_lut; i++) {
                omap_dmm->tcm[i] = sita_init(omap_dmm->container_width,
                                                omap_dmm->container_height,
@@ -717,13 +730,23 @@ static int omap_dmm_probe(struct platform_device *dev)
 
        /* assign access mode containers to applicable tcm container */
        /* OMAP 4 has 1 container for all 4 views */
+       /* OMAP 5 has 2 containers, 1 for 2D and 1 for 1D */
        containers[TILFMT_8BIT] = omap_dmm->tcm[0];
        containers[TILFMT_16BIT] = omap_dmm->tcm[0];
        containers[TILFMT_32BIT] = omap_dmm->tcm[0];
-       containers[TILFMT_PAGE] = omap_dmm->tcm[0];
+
+       if (omap_dmm->container_height != omap_dmm->lut_height) {
+               /* second LUT is used for PAGE mode.  Programming must use
+                  y offset that is added to all y coordinates.  LUT id is still
+                  0, because it is the same LUT, just the upper 128 lines */
+               containers[TILFMT_PAGE] = omap_dmm->tcm[1];
+               omap_dmm->tcm[1]->y_offset = OMAP5_LUT_OFFSET;
+               omap_dmm->tcm[1]->lut_id = 0;
+       } else {
+               containers[TILFMT_PAGE] = omap_dmm->tcm[0];
+       }
 
        area = (struct tcm_area) {
-               .is2d = true,
                .tcm = NULL,
                .p1.x = omap_dmm->container_width - 1,
                .p1.y = omap_dmm->container_height - 1,
@@ -835,64 +858,81 @@ int tiler_map_show(struct seq_file *s, void *arg)
        int h_adj;
        int w_adj;
        unsigned long flags;
+       int lut_idx;
+
 
        if (!omap_dmm) {
                /* early return if dmm/tiler device is not initialized */
                return 0;
        }
 
-       h_adj = omap_dmm->lut_height / ydiv;
-       w_adj = omap_dmm->lut_width / xdiv;
+       h_adj = omap_dmm->container_height / ydiv;
+       w_adj = omap_dmm->container_width / xdiv;
 
-       map = kzalloc(h_adj * sizeof(*map), GFP_KERNEL);
-       global_map = kzalloc((w_adj + 1) * h_adj, GFP_KERNEL);
+       map = kmalloc(h_adj * sizeof(*map), GFP_KERNEL);
+       global_map = kmalloc((w_adj + 1) * h_adj, GFP_KERNEL);
 
        if (!map || !global_map)
                goto error;
 
-       memset(global_map, ' ', (w_adj + 1) * h_adj);
-       for (i = 0; i < omap_dmm->lut_height; i++) {
-               map[i] = global_map + i * (w_adj + 1);
-               map[i][w_adj] = 0;
-       }
-       spin_lock_irqsave(&list_lock, flags);
+       for (lut_idx = 0; lut_idx < omap_dmm->num_lut; lut_idx++) {
+               memset(map, 0, sizeof(h_adj * sizeof(*map)));
+               memset(global_map, ' ', (w_adj + 1) * h_adj);
 
-       list_for_each_entry(block, &omap_dmm->alloc_head, alloc_node) {
-               if (block->fmt != TILFMT_PAGE) {
-                       fill_map(map, xdiv, ydiv, &block->area, *m2dp, true);
-                       if (!*++a2dp)
-                               a2dp = a2d;
-                       if (!*++m2dp)
-                               m2dp = m2d;
-                       map_2d_info(map, xdiv, ydiv, nice, &block->area);
-               } else {
-                       bool start = read_map_pt(map, xdiv, ydiv,
-                                                       &block->area.p0)
-                                                                       == ' ';
-                       bool end = read_map_pt(map, xdiv, ydiv, &block->area.p1)
-                                                                       == ' ';
-                       tcm_for_each_slice(a, block->area, p)
-                               fill_map(map, xdiv, ydiv, &a, '=', true);
-                       fill_map_pt(map, xdiv, ydiv, &block->area.p0,
+               for (i = 0; i < omap_dmm->container_height; i++) {
+                       map[i] = global_map + i * (w_adj + 1);
+                       map[i][w_adj] = 0;
+               }
+
+               spin_lock_irqsave(&list_lock, flags);
+
+               list_for_each_entry(block, &omap_dmm->alloc_head, alloc_node) {
+                       if (block->area.tcm == omap_dmm->tcm[lut_idx]) {
+                               if (block->fmt != TILFMT_PAGE) {
+                                       fill_map(map, xdiv, ydiv, &block->area,
+                                               *m2dp, true);
+                                       if (!*++a2dp)
+                                               a2dp = a2d;
+                                       if (!*++m2dp)
+                                               m2dp = m2d;
+                                       map_2d_info(map, xdiv, ydiv, nice,
+                                                       &block->area);
+                               } else {
+                                       bool start = read_map_pt(map, xdiv,
+                                               ydiv, &block->area.p0) == ' ';
+                                       bool end = read_map_pt(map, xdiv, ydiv,
+                                                       &block->area.p1) == ' ';
+
+                                       tcm_for_each_slice(a, block->area, p)
+                                               fill_map(map, xdiv, ydiv, &a,
+                                                       '=', true);
+                                       fill_map_pt(map, xdiv, ydiv,
+                                                       &block->area.p0,
                                                        start ? '<' : 'X');
-                       fill_map_pt(map, xdiv, ydiv, &block->area.p1,
+                                       fill_map_pt(map, xdiv, ydiv,
+                                                       &block->area.p1,
                                                        end ? '>' : 'X');
-                       map_1d_info(map, xdiv, ydiv, nice, &block->area);
+                                       map_1d_info(map, xdiv, ydiv, nice,
+                                                       &block->area);
+                               }
+                       }
                }
-       }
 
-       spin_unlock_irqrestore(&list_lock, flags);
+               spin_unlock_irqrestore(&list_lock, flags);
 
-       if (s) {
-               seq_printf(s, "BEGIN DMM TILER MAP\n");
-               for (i = 0; i < 128; i++)
-                       seq_printf(s, "%03d:%s\n", i, map[i]);
-               seq_printf(s, "END TILER MAP\n");
-       } else {
-               dev_dbg(omap_dmm->dev, "BEGIN DMM TILER MAP\n");
-               for (i = 0; i < 128; i++)
-                       dev_dbg(omap_dmm->dev, "%03d:%s\n", i, map[i]);
-               dev_dbg(omap_dmm->dev, "END TILER MAP\n");
+               if (s) {
+                       seq_printf(s, "CONTAINER %d DUMP BEGIN\n", lut_idx);
+                       for (i = 0; i < 128; i++)
+                               seq_printf(s, "%03d:%s\n", i, map[i]);
+                       seq_printf(s, "CONTAINER %d DUMP END\n", lut_idx);
+               } else {
+                       dev_dbg(omap_dmm->dev, "CONTAINER %d DUMP BEGIN\n",
+                               lut_idx);
+                       for (i = 0; i < 128; i++)
+                               dev_dbg(omap_dmm->dev, "%03d:%s\n", i, map[i]);
+                       dev_dbg(omap_dmm->dev, "CONTAINER %d DUMP END\n",
+                               lut_idx);
+               }
        }
 
 error:
@@ -903,12 +943,45 @@ error:
 }
 #endif
 
+#ifdef CONFIG_PM
+static int omap_dmm_resume(struct device *dev)
+{
+       struct tcm_area area;
+       int i;
+
+       if (!omap_dmm)
+               return -ENODEV;
+
+       area = (struct tcm_area) {
+               .tcm = NULL,
+               .p1.x = omap_dmm->container_width - 1,
+               .p1.y = omap_dmm->container_height - 1,
+       };
+
+       /* initialize all LUTs to dummy page entries */
+       for (i = 0; i < omap_dmm->num_lut; i++) {
+               area.tcm = omap_dmm->tcm[i];
+               if (fill(&area, NULL, 0, 0, true))
+                       dev_err(dev, "refill failed");
+       }
+
+       return 0;
+}
+
+static const struct dev_pm_ops omap_dmm_pm_ops = {
+       .resume = omap_dmm_resume,
+};
+#endif
+
 struct platform_driver omap_dmm_driver = {
        .probe = omap_dmm_probe,
        .remove = omap_dmm_remove,
        .driver = {
                .owner = THIS_MODULE,
                .name = DMM_DRIVER_NAME,
+#ifdef CONFIG_PM
+               .pm = &omap_dmm_pm_ops,
+#endif
        },
 };
 
similarity index 55%
rename from drivers/staging/omapdrm/omap_drv.c
rename to drivers/gpu/drm/omapdrm/omap_drv.c
index 84943e5ba1d6563f49877ada93e022a1d67b5385..9083538bd16a8d0017fed2ad1fce4df5356acd62 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * drivers/staging/omapdrm/omap_drv.c
+ * drivers/gpu/drm/omapdrm/omap_drv.c
  *
  * Copyright (C) 2011 Texas Instruments
  * Author: Rob Clark <rob@ti.com>
@@ -74,320 +74,99 @@ static int get_connector_type(struct omap_dss_device *dssdev)
        }
 }
 
-#if 0 /* enable when dss2 supports hotplug */
-static int omap_drm_notifier(struct notifier_block *nb,
-               unsigned long evt, void *arg)
-{
-       switch (evt) {
-       case OMAP_DSS_SIZE_CHANGE:
-       case OMAP_DSS_HOTPLUG_CONNECT:
-       case OMAP_DSS_HOTPLUG_DISCONNECT: {
-               struct drm_device *dev = drm_device;
-               DBG("hotplug event: evt=%d, dev=%p", evt, dev);
-               if (dev)
-                       drm_sysfs_hotplug_event(dev);
-
-               return NOTIFY_OK;
-       }
-       default:  /* don't care about other events for now */
-               return NOTIFY_DONE;
-       }
-}
-#endif
-
-static void dump_video_chains(void)
-{
-       int i;
-
-       DBG("dumping video chains: ");
-       for (i = 0; i < omap_dss_get_num_overlays(); i++) {
-               struct omap_overlay *ovl = omap_dss_get_overlay(i);
-               struct omap_overlay_manager *mgr = ovl->manager;
-               struct omap_dss_device *dssdev = mgr ?
-                                       mgr->get_device(mgr) : NULL;
-               if (dssdev) {
-                       DBG("%d: %s -> %s -> %s", i, ovl->name, mgr->name,
-                                               dssdev->name);
-               } else if (mgr) {
-                       DBG("%d: %s -> %s", i, ovl->name, mgr->name);
-               } else {
-                       DBG("%d: %s", i, ovl->name);
-               }
-       }
-}
-
-/* create encoders for each manager */
-static int create_encoder(struct drm_device *dev,
-               struct omap_overlay_manager *mgr)
-{
-       struct omap_drm_private *priv = dev->dev_private;
-       struct drm_encoder *encoder = omap_encoder_init(dev, mgr);
-
-       if (!encoder) {
-               dev_err(dev->dev, "could not create encoder: %s\n",
-                               mgr->name);
-               return -ENOMEM;
-       }
-
-       BUG_ON(priv->num_encoders >= ARRAY_SIZE(priv->encoders));
-
-       priv->encoders[priv->num_encoders++] = encoder;
-
-       return 0;
-}
-
-/* create connectors for each display device */
-static int create_connector(struct drm_device *dev,
-               struct omap_dss_device *dssdev)
+static int omap_modeset_init(struct drm_device *dev)
 {
        struct omap_drm_private *priv = dev->dev_private;
-       static struct notifier_block *notifier;
-       struct drm_connector *connector;
-       int j;
-
-       if (!dssdev->driver) {
-               dev_warn(dev->dev, "%s has no driver.. skipping it\n",
-                               dssdev->name);
-               return 0;
-       }
-
-       if (!(dssdev->driver->get_timings ||
-                               dssdev->driver->read_edid)) {
-               dev_warn(dev->dev, "%s driver does not support "
-                       "get_timings or read_edid.. skipping it!\n",
-                       dssdev->name);
-               return 0;
-       }
+       struct omap_dss_device *dssdev = NULL;
+       int num_ovls = dss_feat_get_num_ovls();
+       int id;
 
-       connector = omap_connector_init(dev,
-                       get_connector_type(dssdev), dssdev);
+       drm_mode_config_init(dev);
 
-       if (!connector) {
-               dev_err(dev->dev, "could not create connector: %s\n",
-                               dssdev->name);
-               return -ENOMEM;
-       }
+       omap_drm_irq_install(dev);
 
-       BUG_ON(priv->num_connectors >= ARRAY_SIZE(priv->connectors));
+       /*
+        * Create private planes and CRTCs for the last NUM_CRTCs overlay
+        * plus manager:
+        */
+       for (id = 0; id < min(num_crtc, num_ovls); id++) {
+               struct drm_plane *plane;
+               struct drm_crtc *crtc;
 
-       priv->connectors[priv->num_connectors++] = connector;
+               plane = omap_plane_init(dev, id, true);
+               crtc = omap_crtc_init(dev, plane, pipe2chan(id), id);
 
-#if 0 /* enable when dss2 supports hotplug */
-       notifier = kzalloc(sizeof(struct notifier_block), GFP_KERNEL);
-       notifier->notifier_call = omap_drm_notifier;
-       omap_dss_add_notify(dssdev, notifier);
-#else
-       notifier = NULL;
-#endif
+               BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs));
+               priv->crtcs[id] = crtc;
+               priv->num_crtcs++;
 
-       for (j = 0; j < priv->num_encoders; j++) {
-               struct omap_overlay_manager *mgr =
-                       omap_encoder_get_manager(priv->encoders[j]);
-               if (mgr->get_device(mgr) == dssdev) {
-                       drm_mode_connector_attach_encoder(connector,
-                                       priv->encoders[j]);
-               }
+               priv->planes[id] = plane;
+               priv->num_planes++;
        }
 
-       return 0;
-}
-
-/* create up to max_overlays CRTCs mapping to overlays.. by default,
- * connect the overlays to different managers/encoders, giving priority
- * to encoders connected to connectors with a detected connection
- */
-static int create_crtc(struct drm_device *dev, struct omap_overlay *ovl,
-               int *j, unsigned int connected_connectors)
-{
-       struct omap_drm_private *priv = dev->dev_private;
-       struct omap_overlay_manager *mgr = NULL;
-       struct drm_crtc *crtc;
-
-       /* find next best connector, ones with detected connection first
+       /*
+        * Create normal planes for the remaining overlays:
         */
-       while (*j < priv->num_connectors && !mgr) {
-               if (connected_connectors & (1 << *j)) {
-                       struct drm_encoder *encoder =
-                               omap_connector_attached_encoder(
-                                               priv->connectors[*j]);
-                       if (encoder)
-                               mgr = omap_encoder_get_manager(encoder);
+       for (; id < num_ovls; id++) {
+               struct drm_plane *plane = omap_plane_init(dev, id, false);
 
-               }
-               (*j)++;
+               BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes));
+               priv->planes[priv->num_planes++] = plane;
        }
 
-       /* if we couldn't find another connected connector, lets start
-        * looking at the unconnected connectors:
-        *
-        * note: it might not be immediately apparent, but thanks to
-        * the !mgr check in both this loop and the one above, the only
-        * way to enter this loop is with *j == priv->num_connectors,
-        * so idx can never go negative.
-        */
-       while (*j < 2 * priv->num_connectors && !mgr) {
-               int idx = *j - priv->num_connectors;
-               if (!(connected_connectors & (1 << idx))) {
-                       struct drm_encoder *encoder =
-                               omap_connector_attached_encoder(
-                                               priv->connectors[idx]);
-                       if (encoder)
-                               mgr = omap_encoder_get_manager(encoder);
+       for_each_dss_dev(dssdev) {
+               struct drm_connector *connector;
+               struct drm_encoder *encoder;
 
+               if (!dssdev->driver) {
+                       dev_warn(dev->dev, "%s has no driver.. skipping it\n",
+                                       dssdev->name);
+                       return 0;
                }
-               (*j)++;
-       }
-
-       crtc = omap_crtc_init(dev, ovl, priv->num_crtcs);
-
-       if (!crtc) {
-               dev_err(dev->dev, "could not create CRTC: %s\n",
-                               ovl->name);
-               return -ENOMEM;
-       }
-
-       BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs));
-
-       priv->crtcs[priv->num_crtcs++] = crtc;
-
-       return 0;
-}
-
-static int create_plane(struct drm_device *dev, struct omap_overlay *ovl,
-               unsigned int possible_crtcs)
-{
-       struct omap_drm_private *priv = dev->dev_private;
-       struct drm_plane *plane =
-                       omap_plane_init(dev, ovl, possible_crtcs, false);
-
-       if (!plane) {
-               dev_err(dev->dev, "could not create plane: %s\n",
-                               ovl->name);
-               return -ENOMEM;
-       }
-
-       BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes));
-
-       priv->planes[priv->num_planes++] = plane;
-
-       return 0;
-}
 
-static int match_dev_name(struct omap_dss_device *dssdev, void *data)
-{
-       return !strcmp(dssdev->name, data);
-}
-
-static unsigned int detect_connectors(struct drm_device *dev)
-{
-       struct omap_drm_private *priv = dev->dev_private;
-       unsigned int connected_connectors = 0;
-       int i;
-
-       for (i = 0; i < priv->num_connectors; i++) {
-               struct drm_connector *connector = priv->connectors[i];
-               if (omap_connector_detect(connector, true) ==
-                               connector_status_connected) {
-                       connected_connectors |= (1 << i);
+               if (!(dssdev->driver->get_timings ||
+                                       dssdev->driver->read_edid)) {
+                       dev_warn(dev->dev, "%s driver does not support "
+                               "get_timings or read_edid.. skipping it!\n",
+                               dssdev->name);
+                       return 0;
                }
-       }
 
-       return connected_connectors;
-}
-
-static int omap_modeset_init(struct drm_device *dev)
-{
-       const struct omap_drm_platform_data *pdata = dev->dev->platform_data;
-       struct omap_kms_platform_data *kms_pdata = NULL;
-       struct omap_drm_private *priv = dev->dev_private;
-       struct omap_dss_device *dssdev = NULL;
-       int i, j;
-       unsigned int connected_connectors = 0;
-
-       drm_mode_config_init(dev);
+               encoder = omap_encoder_init(dev, dssdev);
 
-       if (pdata && pdata->kms_pdata) {
-               kms_pdata = pdata->kms_pdata;
-
-               /* if platform data is provided by the board file, use it to
-                * control which overlays, managers, and devices we own.
-                */
-               for (i = 0; i < kms_pdata->mgr_cnt; i++) {
-                       struct omap_overlay_manager *mgr =
-                               omap_dss_get_overlay_manager(
-                                               kms_pdata->mgr_ids[i]);
-                       create_encoder(dev, mgr);
+               if (!encoder) {
+                       dev_err(dev->dev, "could not create encoder: %s\n",
+                                       dssdev->name);
+                       return -ENOMEM;
                }
 
-               for (i = 0; i < kms_pdata->dev_cnt; i++) {
-                       struct omap_dss_device *dssdev =
-                               omap_dss_find_device(
-                                       (void *)kms_pdata->dev_names[i],
-                                       match_dev_name);
-                       if (!dssdev) {
-                               dev_warn(dev->dev, "no such dssdev: %s\n",
-                                               kms_pdata->dev_names[i]);
-                               continue;
-                       }
-                       create_connector(dev, dssdev);
-               }
-
-               connected_connectors = detect_connectors(dev);
+               connector = omap_connector_init(dev,
+                               get_connector_type(dssdev), dssdev, encoder);
 
-               j = 0;
-               for (i = 0; i < kms_pdata->ovl_cnt; i++) {
-                       struct omap_overlay *ovl =
-                               omap_dss_get_overlay(kms_pdata->ovl_ids[i]);
-                       create_crtc(dev, ovl, &j, connected_connectors);
+               if (!connector) {
+                       dev_err(dev->dev, "could not create connector: %s\n",
+                                       dssdev->name);
+                       return -ENOMEM;
                }
 
-               for (i = 0; i < kms_pdata->pln_cnt; i++) {
-                       struct omap_overlay *ovl =
-                               omap_dss_get_overlay(kms_pdata->pln_ids[i]);
-                       create_plane(dev, ovl, (1 << priv->num_crtcs) - 1);
-               }
-       } else {
-               /* otherwise just grab up to CONFIG_DRM_OMAP_NUM_CRTCS and try
-                * to make educated guesses about everything else
-                */
-               int max_overlays = min(omap_dss_get_num_overlays(), num_crtc);
+               BUG_ON(priv->num_encoders >= ARRAY_SIZE(priv->encoders));
+               BUG_ON(priv->num_connectors >= ARRAY_SIZE(priv->connectors));
 
-               for (i = 0; i < omap_dss_get_num_overlay_managers(); i++)
-                       create_encoder(dev, omap_dss_get_overlay_manager(i));
+               priv->encoders[priv->num_encoders++] = encoder;
+               priv->connectors[priv->num_connectors++] = connector;
 
-               for_each_dss_dev(dssdev) {
-                       create_connector(dev, dssdev);
-               }
+               drm_mode_connector_attach_encoder(connector, encoder);
 
-               connected_connectors = detect_connectors(dev);
-
-               j = 0;
-               for (i = 0; i < max_overlays; i++) {
-                       create_crtc(dev, omap_dss_get_overlay(i),
-                                       &j, connected_connectors);
-               }
-
-               /* use any remaining overlays as drm planes */
-               for (; i < omap_dss_get_num_overlays(); i++) {
-                       struct omap_overlay *ovl = omap_dss_get_overlay(i);
-                       create_plane(dev, ovl, (1 << priv->num_crtcs) - 1);
+               /* figure out which crtc's we can connect the encoder to: */
+               encoder->possible_crtcs = 0;
+               for (id = 0; id < priv->num_crtcs; id++) {
+                       enum omap_dss_output_id supported_outputs =
+                                       dss_feat_get_supported_outputs(pipe2chan(id));
+                       if (supported_outputs & dssdev->output->id)
+                               encoder->possible_crtcs |= (1 << id);
                }
        }
 
-       /* for now keep the mapping of CRTCs and encoders static.. */
-       for (i = 0; i < priv->num_encoders; i++) {
-               struct drm_encoder *encoder = priv->encoders[i];
-               struct omap_overlay_manager *mgr =
-                               omap_encoder_get_manager(encoder);
-
-               encoder->possible_crtcs = (1 << priv->num_crtcs) - 1;
-
-               DBG("%s: possible_crtcs=%08x", mgr->name,
-                                       encoder->possible_crtcs);
-       }
-
-       dump_video_chains();
-
        dev->mode_config.min_width = 32;
        dev->mode_config.min_height = 32;
 
@@ -450,7 +229,7 @@ static int ioctl_gem_new(struct drm_device *dev, void *data,
                struct drm_file *file_priv)
 {
        struct drm_omap_gem_new *args = data;
-       DBG("%p:%p: size=0x%08x, flags=%08x", dev, file_priv,
+       VERB("%p:%p: size=0x%08x, flags=%08x", dev, file_priv,
                        args->size.bytes, args->flags);
        return omap_gem_new_handle(dev, file_priv, args->size,
                        args->flags, &args->handle);
@@ -510,7 +289,7 @@ static int ioctl_gem_info(struct drm_device *dev, void *data,
        struct drm_gem_object *obj;
        int ret = 0;
 
-       DBG("%p:%p: handle=%d", dev, file_priv, args->handle);
+       VERB("%p:%p: handle=%d", dev, file_priv, args->handle);
 
        obj = drm_gem_object_lookup(dev, file_priv, args->handle);
        if (!obj)
@@ -565,14 +344,6 @@ static int dev_load(struct drm_device *dev, unsigned long flags)
 
        dev->dev_private = priv;
 
-       ret = omapdss_compat_init();
-       if (ret) {
-               dev_err(dev->dev, "coult not init omapdss\n");
-               dev->dev_private = NULL;
-               kfree(priv);
-               return ret;
-       }
-
        priv->wq = alloc_ordered_workqueue("omapdrm", 0);
 
        INIT_LIST_HEAD(&priv->obj_list);
@@ -584,21 +355,23 @@ static int dev_load(struct drm_device *dev, unsigned long flags)
                dev_err(dev->dev, "omap_modeset_init failed: ret=%d\n", ret);
                dev->dev_private = NULL;
                kfree(priv);
-               omapdss_compat_uninit();
                return ret;
        }
 
+       ret = drm_vblank_init(dev, priv->num_crtcs);
+       if (ret)
+               dev_warn(dev->dev, "could not init vblank\n");
+
        priv->fbdev = omap_fbdev_init(dev);
        if (!priv->fbdev) {
                dev_warn(dev->dev, "omap_fbdev_init failed\n");
                /* well, limp along without an fbdev.. maybe X11 will work? */
        }
 
-       drm_kms_helper_poll_init(dev);
+       /* store off drm_device for use in pm ops */
+       dev_set_drvdata(dev->dev, dev);
 
-       ret = drm_vblank_init(dev, priv->num_crtcs);
-       if (ret)
-               dev_warn(dev->dev, "could not init vblank\n");
+       drm_kms_helper_poll_init(dev);
 
        return 0;
 }
@@ -609,8 +382,9 @@ static int dev_unload(struct drm_device *dev)
 
        DBG("unload: dev=%p", dev);
 
-       drm_vblank_cleanup(dev);
        drm_kms_helper_poll_fini(dev);
+       drm_vblank_cleanup(dev);
+       omap_drm_irq_uninstall(dev);
 
        omap_fbdev_free(dev);
        omap_modeset_free(dev);
@@ -619,11 +393,11 @@ static int dev_unload(struct drm_device *dev)
        flush_workqueue(priv->wq);
        destroy_workqueue(priv->wq);
 
-       omapdss_compat_uninit();
-
        kfree(dev->dev_private);
        dev->dev_private = NULL;
 
+       dev_set_drvdata(dev->dev, NULL);
+
        return 0;
 }
 
@@ -680,7 +454,9 @@ static void dev_lastclose(struct drm_device *dev)
                }
        }
 
+       drm_modeset_lock_all(dev);
        ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev);
+       drm_modeset_unlock_all(dev);
        if (ret)
                DBG("failed to restore crtc mode");
 }
@@ -695,60 +471,6 @@ static void dev_postclose(struct drm_device *dev, struct drm_file *file)
        DBG("postclose: dev=%p, file=%p", dev, file);
 }
 
-/**
- * enable_vblank - enable vblank interrupt events
- * @dev: DRM device
- * @crtc: which irq to enable
- *
- * Enable vblank interrupts for @crtc.  If the device doesn't have
- * a hardware vblank counter, this routine should be a no-op, since
- * interrupts will have to stay on to keep the count accurate.
- *
- * RETURNS
- * Zero on success, appropriate errno if the given @crtc's vblank
- * interrupt cannot be enabled.
- */
-static int dev_enable_vblank(struct drm_device *dev, int crtc)
-{
-       DBG("enable_vblank: dev=%p, crtc=%d", dev, crtc);
-       return 0;
-}
-
-/**
- * disable_vblank - disable vblank interrupt events
- * @dev: DRM device
- * @crtc: which irq to enable
- *
- * Disable vblank interrupts for @crtc.  If the device doesn't have
- * a hardware vblank counter, this routine should be a no-op, since
- * interrupts will have to stay on to keep the count accurate.
- */
-static void dev_disable_vblank(struct drm_device *dev, int crtc)
-{
-       DBG("disable_vblank: dev=%p, crtc=%d", dev, crtc);
-}
-
-static irqreturn_t dev_irq_handler(DRM_IRQ_ARGS)
-{
-       return IRQ_HANDLED;
-}
-
-static void dev_irq_preinstall(struct drm_device *dev)
-{
-       DBG("irq_preinstall: dev=%p", dev);
-}
-
-static int dev_irq_postinstall(struct drm_device *dev)
-{
-       DBG("irq_postinstall: dev=%p", dev);
-       return 0;
-}
-
-static void dev_irq_uninstall(struct drm_device *dev)
-{
-       DBG("irq_uninstall: dev=%p", dev);
-}
-
 static const struct vm_operations_struct omap_gem_vm_ops = {
        .fault = omap_gem_fault,
        .open = drm_gem_vm_open,
@@ -778,12 +500,12 @@ static struct drm_driver omap_drm_driver = {
                .preclose = dev_preclose,
                .postclose = dev_postclose,
                .get_vblank_counter = drm_vblank_count,
-               .enable_vblank = dev_enable_vblank,
-               .disable_vblank = dev_disable_vblank,
-               .irq_preinstall = dev_irq_preinstall,
-               .irq_postinstall = dev_irq_postinstall,
-               .irq_uninstall = dev_irq_uninstall,
-               .irq_handler = dev_irq_handler,
+               .enable_vblank = omap_irq_enable_vblank,
+               .disable_vblank = omap_irq_disable_vblank,
+               .irq_preinstall = omap_irq_preinstall,
+               .irq_postinstall = omap_irq_postinstall,
+               .irq_uninstall = omap_irq_uninstall,
+               .irq_handler = omap_irq_handler,
 #ifdef CONFIG_DEBUG_FS
                .debugfs_init = omap_debugfs_init,
                .debugfs_cleanup = omap_debugfs_cleanup,
@@ -841,10 +563,19 @@ static int pdev_remove(struct platform_device *device)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static const struct dev_pm_ops omapdrm_pm_ops = {
+       .resume = omap_gem_resume,
+};
+#endif
+
 struct platform_driver pdev = {
                .driver = {
                        .name = DRIVER_NAME,
                        .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+                       .pm = &omapdrm_pm_ops,
+#endif
                },
                .probe = pdev_probe,
                .remove = pdev_remove,
similarity index 66%
rename from drivers/staging/omapdrm/omap_drv.h
rename to drivers/gpu/drm/omapdrm/omap_drv.h
index 1d4aea53b75da20e44be90b4a32e589a4a067785..d4f997bb4ac0eaab02f6419d4c832246959bbb45 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * drivers/staging/omapdrm/omap_drv.h
+ * drivers/gpu/drm/omapdrm/omap_drv.h
  *
  * Copyright (C) 2011 Texas Instruments
  * Author: Rob Clark <rob@ti.com>
@@ -25,8 +25,9 @@
 #include <linux/types.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/omap_drm.h>
 #include <linux/platform_data/omap_drm.h>
-#include "omap_drm.h"
+
 
 #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
 #define VERB(fmt, ...) if (0) DRM_DEBUG(fmt, ##__VA_ARGS__) /* verbose debug */
  */
 #define MAX_MAPPERS 2
 
+/* parameters which describe (unrotated) coordinates of scanout within a fb: */
+struct omap_drm_window {
+       uint32_t rotation;
+       int32_t  crtc_x, crtc_y;                /* signed because can be offscreen */
+       uint32_t crtc_w, crtc_h;
+       uint32_t src_x, src_y;
+       uint32_t src_w, src_h;
+};
+
+/* Once GO bit is set, we can't make further updates to shadowed registers
+ * until the GO bit is cleared.  So various parts in the kms code that need
+ * to update shadowed registers queue up a pair of callbacks, pre_apply
+ * which is called before setting GO bit, and post_apply that is called
+ * after GO bit is cleared.  The crtc manages the queuing, and everyone
+ * else goes thru omap_crtc_apply() using these callbacks so that the
+ * code which has to deal w/ GO bit state is centralized.
+ */
+struct omap_drm_apply {
+       struct list_head pending_node, queued_node;
+       bool queued;
+       void (*pre_apply)(struct omap_drm_apply *apply);
+       void (*post_apply)(struct omap_drm_apply *apply);
+};
+
+/* For transiently registering for different DSS irqs that various parts
+ * of the KMS code need during setup/configuration.  We these are not
+ * necessarily the same as what drm_vblank_get/put() are requesting, and
+ * the hysteresis in drm_vblank_put() is not necessarily desirable for
+ * internal housekeeping related irq usage.
+ */
+struct omap_drm_irq {
+       struct list_head node;
+       uint32_t irqmask;
+       bool registered;
+       void (*irq)(struct omap_drm_irq *irq, uint32_t irqstatus);
+};
+
+/* For KMS code that needs to wait for a certain # of IRQs:
+ */
+struct omap_irq_wait;
+struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev,
+               uint32_t irqmask, int count);
+int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait,
+               unsigned long timeout);
+
 struct omap_drm_private {
        uint32_t omaprev;
 
@@ -58,6 +104,7 @@ struct omap_drm_private {
 
        struct workqueue_struct *wq;
 
+       /* list of GEM objects: */
        struct list_head obj_list;
 
        bool has_dmm;
@@ -65,6 +112,11 @@ struct omap_drm_private {
        /* properties: */
        struct drm_property *rotation_prop;
        struct drm_property *zorder_prop;
+
+       /* irq handling: */
+       struct list_head irq_list;    /* list of omap_drm_irq */
+       uint32_t vblank_mask;         /* irq bits set for userspace vblank */
+       struct omap_drm_irq error_handler;
 };
 
 /* this should probably be in drm-core to standardize amongst drivers */
@@ -75,15 +127,6 @@ struct omap_drm_private {
 #define DRM_REFLECT_X  4
 #define DRM_REFLECT_Y  5
 
-/* parameters which describe (unrotated) coordinates of scanout within a fb: */
-struct omap_drm_window {
-       uint32_t rotation;
-       int32_t  crtc_x, crtc_y;                /* signed because can be offscreen */
-       uint32_t crtc_w, crtc_h;
-       uint32_t src_x, src_y;
-       uint32_t src_w, src_h;
-};
-
 #ifdef CONFIG_DEBUG_FS
 int omap_debugfs_init(struct drm_minor *minor);
 void omap_debugfs_cleanup(struct drm_minor *minor);
@@ -92,23 +135,40 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m);
 void omap_gem_describe_objects(struct list_head *list, struct seq_file *m);
 #endif
 
+#ifdef CONFIG_PM
+int omap_gem_resume(struct device *dev);
+#endif
+
+int omap_irq_enable_vblank(struct drm_device *dev, int crtc);
+void omap_irq_disable_vblank(struct drm_device *dev, int crtc);
+irqreturn_t omap_irq_handler(DRM_IRQ_ARGS);
+void omap_irq_preinstall(struct drm_device *dev);
+int omap_irq_postinstall(struct drm_device *dev);
+void omap_irq_uninstall(struct drm_device *dev);
+void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq);
+void omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq);
+int omap_drm_irq_uninstall(struct drm_device *dev);
+int omap_drm_irq_install(struct drm_device *dev);
+
 struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev);
 void omap_fbdev_free(struct drm_device *dev);
 
+const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc);
+enum omap_channel omap_crtc_channel(struct drm_crtc *crtc);
+int omap_crtc_apply(struct drm_crtc *crtc,
+               struct omap_drm_apply *apply);
 struct drm_crtc *omap_crtc_init(struct drm_device *dev,
-               struct omap_overlay *ovl, int id);
+               struct drm_plane *plane, enum omap_channel channel, int id);
 
 struct drm_plane *omap_plane_init(struct drm_device *dev,
-               struct omap_overlay *ovl, unsigned int possible_crtcs,
-               bool priv);
+               int plane_id, bool private_plane);
 int omap_plane_dpms(struct drm_plane *plane, int mode);
 int omap_plane_mode_set(struct drm_plane *plane,
                struct drm_crtc *crtc, struct drm_framebuffer *fb,
                int crtc_x, int crtc_y,
                unsigned int crtc_w, unsigned int crtc_h,
                uint32_t src_x, uint32_t src_y,
-               uint32_t src_w, uint32_t src_h);
-void omap_plane_on_endwin(struct drm_plane *plane,
+               uint32_t src_w, uint32_t src_h,
                void (*fxn)(void *), void *arg);
 void omap_plane_install_properties(struct drm_plane *plane,
                struct drm_mode_object *obj);
@@ -116,21 +176,25 @@ int omap_plane_set_property(struct drm_plane *plane,
                struct drm_property *property, uint64_t val);
 
 struct drm_encoder *omap_encoder_init(struct drm_device *dev,
-               struct omap_overlay_manager *mgr);
-struct omap_overlay_manager *omap_encoder_get_manager(
+               struct omap_dss_device *dssdev);
+int omap_encoder_set_enabled(struct drm_encoder *encoder, bool enabled);
+int omap_encoder_update(struct drm_encoder *encoder,
+               struct omap_overlay_manager *mgr,
+               struct omap_video_timings *timings);
+
+struct drm_connector *omap_connector_init(struct drm_device *dev,
+               int connector_type, struct omap_dss_device *dssdev,
                struct drm_encoder *encoder);
 struct drm_encoder *omap_connector_attached_encoder(
                struct drm_connector *connector);
-enum drm_connector_status omap_connector_detect(
-               struct drm_connector *connector, bool force);
-
-struct drm_connector *omap_connector_init(struct drm_device *dev,
-               int connector_type, struct omap_dss_device *dssdev);
-void omap_connector_mode_set(struct drm_connector *connector,
-               struct drm_display_mode *mode);
 void omap_connector_flush(struct drm_connector *connector,
                int x, int y, int w, int h);
 
+void copy_timings_omap_to_drm(struct drm_display_mode *mode,
+               struct omap_video_timings *timings);
+void copy_timings_drm_to_omap(struct omap_video_timings *timings,
+               struct drm_display_mode *mode);
+
 uint32_t omap_framebuffer_get_formats(uint32_t *pixel_formats,
                uint32_t max_formats, enum omap_color_mode supported_modes);
 struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev,
@@ -207,6 +271,40 @@ static inline int align_pitch(int pitch, int width, int bpp)
        return ALIGN(pitch, 8 * bytespp);
 }
 
+static inline enum omap_channel pipe2chan(int pipe)
+{
+       int num_mgrs = dss_feat_get_num_mgrs();
+
+       /*
+        * We usually don't want to create a CRTC for each manager,
+        * at least not until we have a way to expose private planes
+        * to userspace.  Otherwise there would not be enough video
+        * pipes left for drm planes.  The higher #'d managers tend
+        * to have more features so start in reverse order.
+        */
+       return num_mgrs - pipe - 1;
+}
+
+/* map crtc to vblank mask */
+static inline uint32_t pipe2vbl(int crtc)
+{
+       enum omap_channel channel = pipe2chan(crtc);
+       return dispc_mgr_get_vsync_irq(channel);
+}
+
+static inline int crtc2pipe(struct drm_device *dev, struct drm_crtc *crtc)
+{
+       struct omap_drm_private *priv = dev->dev_private;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(priv->crtcs); i++)
+               if (priv->crtcs[i] == crtc)
+                       return i;
+
+       BUG();  /* bogus CRTC ptr */
+       return -1;
+}
+
 /* should these be made into common util helpers?
  */
 
similarity index 59%
rename from drivers/staging/omapdrm/omap_encoder.c
rename to drivers/gpu/drm/omapdrm/omap_encoder.c
index 5341d5e3e3179e30b2bbe8540b12cc935843a2b7..7e1f2ab653729edc653438d43eca1619e8c49011 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * drivers/staging/omapdrm/omap_encoder.c
+ * drivers/gpu/drm/omapdrm/omap_encoder.c
  *
  * Copyright (C) 2011 Texas Instruments
  * Author: Rob Clark <rob@ti.com>
 #include "drm_crtc.h"
 #include "drm_crtc_helper.h"
 
+#include <linux/list.h>
+
+
 /*
  * encoder funcs
  */
 
 #define to_omap_encoder(x) container_of(x, struct omap_encoder, base)
 
+/* The encoder and connector both map to same dssdev.. the encoder
+ * handles the 'active' parts, ie. anything the modifies the state
+ * of the hw, and the connector handles the 'read-only' parts, like
+ * detecting connection and reading edid.
+ */
 struct omap_encoder {
        struct drm_encoder base;
-       struct omap_overlay_manager *mgr;
+       struct omap_dss_device *dssdev;
 };
 
 static void omap_encoder_destroy(struct drm_encoder *encoder)
 {
        struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
-       DBG("%s", omap_encoder->mgr->name);
        drm_encoder_cleanup(encoder);
        kfree(omap_encoder);
 }
 
+static const struct drm_encoder_funcs omap_encoder_funcs = {
+       .destroy = omap_encoder_destroy,
+};
+
+/*
+ * The CRTC drm_crtc_helper_set_mode() doesn't really give us the right
+ * order.. the easiest way to work around this for now is to make all
+ * the encoder-helper's no-op's and have the omap_crtc code take care
+ * of the sequencing and call us in the right points.
+ *
+ * Eventually to handle connecting CRTCs to different encoders properly,
+ * either the CRTC helpers need to change or we need to replace
+ * drm_crtc_helper_set_mode(), but lets wait until atomic-modeset for
+ * that.
+ */
+
 static void omap_encoder_dpms(struct drm_encoder *encoder, int mode)
 {
-       struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
-       DBG("%s: %d", omap_encoder->mgr->name, mode);
 }
 
 static bool omap_encoder_mode_fixup(struct drm_encoder *encoder,
                                  const struct drm_display_mode *mode,
                                  struct drm_display_mode *adjusted_mode)
 {
-       struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
-       DBG("%s", omap_encoder->mgr->name);
        return true;
 }
 
@@ -60,47 +79,16 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder,
                                struct drm_display_mode *mode,
                                struct drm_display_mode *adjusted_mode)
 {
-       struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
-       struct drm_device *dev = encoder->dev;
-       struct omap_drm_private *priv = dev->dev_private;
-       int i;
-
-       mode = adjusted_mode;
-
-       DBG("%s: set mode: %dx%d", omap_encoder->mgr->name,
-                       mode->hdisplay, mode->vdisplay);
-
-       for (i = 0; i < priv->num_connectors; i++) {
-               struct drm_connector *connector = priv->connectors[i];
-               if (connector->encoder == encoder)
-                       omap_connector_mode_set(connector, mode);
-
-       }
 }
 
 static void omap_encoder_prepare(struct drm_encoder *encoder)
 {
-       struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
-       struct drm_encoder_helper_funcs *encoder_funcs =
-                               encoder->helper_private;
-       DBG("%s", omap_encoder->mgr->name);
-       encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
 }
 
 static void omap_encoder_commit(struct drm_encoder *encoder)
 {
-       struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
-       struct drm_encoder_helper_funcs *encoder_funcs =
-                               encoder->helper_private;
-       DBG("%s", omap_encoder->mgr->name);
-       omap_encoder->mgr->apply(omap_encoder->mgr);
-       encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
 }
 
-static const struct drm_encoder_funcs omap_encoder_funcs = {
-       .destroy = omap_encoder_destroy,
-};
-
 static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = {
        .dpms = omap_encoder_dpms,
        .mode_fixup = omap_encoder_mode_fixup,
@@ -109,23 +97,54 @@ static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = {
        .commit = omap_encoder_commit,
 };
 
-struct omap_overlay_manager *omap_encoder_get_manager(
-               struct drm_encoder *encoder)
+/*
+ * Instead of relying on the helpers for modeset, the omap_crtc code
+ * calls these functions in the proper sequence.
+ */
+
+int omap_encoder_set_enabled(struct drm_encoder *encoder, bool enabled)
 {
        struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
-       return omap_encoder->mgr;
+       struct omap_dss_device *dssdev = omap_encoder->dssdev;
+       struct omap_dss_driver *dssdrv = dssdev->driver;
+
+       if (enabled) {
+               return dssdrv->enable(dssdev);
+       } else {
+               dssdrv->disable(dssdev);
+               return 0;
+       }
+}
+
+int omap_encoder_update(struct drm_encoder *encoder,
+               struct omap_overlay_manager *mgr,
+               struct omap_video_timings *timings)
+{
+       struct drm_device *dev = encoder->dev;
+       struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+       struct omap_dss_device *dssdev = omap_encoder->dssdev;
+       struct omap_dss_driver *dssdrv = dssdev->driver;
+       int ret;
+
+       dssdev->output->manager = mgr;
+
+       ret = dssdrv->check_timings(dssdev, timings);
+       if (ret) {
+               dev_err(dev->dev, "could not set timings: %d\n", ret);
+               return ret;
+       }
+
+       dssdrv->set_timings(dssdev, timings);
+
+       return 0;
 }
 
 /* initialize encoder */
 struct drm_encoder *omap_encoder_init(struct drm_device *dev,
-               struct omap_overlay_manager *mgr)
+               struct omap_dss_device *dssdev)
 {
        struct drm_encoder *encoder = NULL;
        struct omap_encoder *omap_encoder;
-       struct omap_overlay_manager_info info;
-       int ret;
-
-       DBG("%s", mgr->name);
 
        omap_encoder = kzalloc(sizeof(*omap_encoder), GFP_KERNEL);
        if (!omap_encoder) {
@@ -133,33 +152,14 @@ struct drm_encoder *omap_encoder_init(struct drm_device *dev,
                goto fail;
        }
 
-       omap_encoder->mgr = mgr;
+       omap_encoder->dssdev = dssdev;
+
        encoder = &omap_encoder->base;
 
        drm_encoder_init(dev, encoder, &omap_encoder_funcs,
                         DRM_MODE_ENCODER_TMDS);
        drm_encoder_helper_add(encoder, &omap_encoder_helper_funcs);
 
-       mgr->get_manager_info(mgr, &info);
-
-       /* TODO: fix hard-coded setup.. */
-       info.default_color = 0x00000000;
-       info.trans_key = 0x00000000;
-       info.trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
-       info.trans_enabled = false;
-
-       ret = mgr->set_manager_info(mgr, &info);
-       if (ret) {
-               dev_err(dev->dev, "could not set manager info\n");
-               goto fail;
-       }
-
-       ret = mgr->apply(mgr);
-       if (ret) {
-               dev_err(dev->dev, "could not apply\n");
-               goto fail;
-       }
-
        return encoder;
 
 fail:
similarity index 99%
rename from drivers/staging/omapdrm/omap_fb.c
rename to drivers/gpu/drm/omapdrm/omap_fb.c
index 09028e9c1093e247476d4be7fa7684395bede40f..9d5f6f696c7275457abf6a34ddc1ec12b76fe04e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * drivers/staging/omapdrm/omap_fb.c
+ * drivers/gpu/drm/omapdrm/omap_fb.c
  *
  * Copyright (C) 2011 Texas Instruments
  * Author: Rob Clark <rob@ti.com>
@@ -424,14 +424,6 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
        }
 
        fb = &omap_fb->base;
-       ret = drm_framebuffer_init(dev, fb, &omap_framebuffer_funcs);
-       if (ret) {
-               dev_err(dev->dev, "framebuffer init failed: %d\n", ret);
-               goto fail;
-       }
-
-       DBG("create: FB ID: %d (%p)", fb->base.id, fb);
-
        omap_fb->format = format;
 
        for (i = 0; i < n; i++) {
@@ -462,6 +454,14 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
 
        drm_helper_mode_fill_fb_struct(fb, mode_cmd);
 
+       ret = drm_framebuffer_init(dev, fb, &omap_framebuffer_funcs);
+       if (ret) {
+               dev_err(dev->dev, "framebuffer init failed: %d\n", ret);
+               goto fail;
+       }
+
+       DBG("create: FB ID: %d (%p)", fb->base.id, fb);
+
        return fb;
 
 fail:
similarity index 95%
rename from drivers/staging/omapdrm/omap_fbdev.c
rename to drivers/gpu/drm/omapdrm/omap_fbdev.c
index 8a027bb77d97c8c3fe04a6e0398c8e74aced86ae..f0033bd3e4ae3b46249e9ee171326736481a24cf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * drivers/staging/omapdrm/omap_fbdev.c
+ * drivers/gpu/drm/omapdrm/omap_fbdev.c
  *
  * Copyright (C) 2011 Texas Instruments
  * Author: Rob Clark <rob@ti.com>
@@ -131,9 +131,6 @@ static struct fb_ops omap_fb_ops = {
        .fb_pan_display = omap_fbdev_pan_display,
        .fb_blank = drm_fb_helper_blank,
        .fb_setcmap = drm_fb_helper_setcmap,
-
-       .fb_debug_enter = drm_fb_helper_debug_enter,
-       .fb_debug_leave = drm_fb_helper_debug_leave,
 };
 
 static int omap_fbdev_create(struct drm_fb_helper *helper,
@@ -275,8 +272,10 @@ fail:
        if (ret) {
                if (fbi)
                        framebuffer_release(fbi);
-               if (fb)
+               if (fb) {
+                       drm_framebuffer_unregister_private(fb);
                        drm_framebuffer_remove(fb);
+               }
        }
 
        return ret;
@@ -294,25 +293,10 @@ static void omap_crtc_fb_gamma_get(struct drm_crtc *crtc,
        DBG("fbdev: get gamma");
 }
 
-static int omap_fbdev_probe(struct drm_fb_helper *helper,
-               struct drm_fb_helper_surface_size *sizes)
-{
-       int new_fb = 0;
-       int ret;
-
-       if (!helper->fb) {
-               ret = omap_fbdev_create(helper, sizes);
-               if (ret)
-                       return ret;
-               new_fb = 1;
-       }
-       return new_fb;
-}
-
 static struct drm_fb_helper_funcs omap_fb_helper_funcs = {
        .gamma_set = omap_crtc_fb_gamma_set,
        .gamma_get = omap_crtc_fb_gamma_get,
-       .fb_probe = omap_fbdev_probe,
+       .fb_probe = omap_fbdev_create,
 };
 
 static struct drm_fb_helper *get_fb(struct fb_info *fbi)
@@ -367,6 +351,10 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev)
        }
 
        drm_fb_helper_single_add_all_connectors(helper);
+
+       /* disable all the possible outputs/crtcs before entering KMS mode */
+       drm_helper_disable_unused_functions(dev);
+
        drm_fb_helper_initial_config(helper, 32);
 
        priv->fbdev = helper;
@@ -400,8 +388,10 @@ void omap_fbdev_free(struct drm_device *dev)
        fbdev = to_omap_fbdev(priv->fbdev);
 
        /* this will free the backing object */
-       if (fbdev->fb)
+       if (fbdev->fb) {
+               drm_framebuffer_unregister_private(fbdev->fb);
                drm_framebuffer_remove(fbdev->fb);
+       }
 
        kfree(fbdev);
 
similarity index 97%
rename from drivers/staging/omapdrm/omap_gem.c
rename to drivers/gpu/drm/omapdrm/omap_gem.c
index c38992b76fc9f86bf9a4664dfc2e90516041b422..e8302b02691d5d0a5c0fa345e49f4000aa293611 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * drivers/staging/omapdrm/omap_gem.c
+ * drivers/gpu/drm/omapdrm/omap_gem.c
  *
  * Copyright (C) 2011 Texas Instruments
  * Author: Rob Clark <rob.clark@linaro.org>
@@ -964,6 +964,34 @@ void *omap_gem_vaddr(struct drm_gem_object *obj)
        return omap_obj->vaddr;
 }
 
+#ifdef CONFIG_PM
+/* re-pin objects in DMM in resume path: */
+int omap_gem_resume(struct device *dev)
+{
+       struct drm_device *drm_dev = dev_get_drvdata(dev);
+       struct omap_drm_private *priv = drm_dev->dev_private;
+       struct omap_gem_object *omap_obj;
+       int ret = 0;
+
+       list_for_each_entry(omap_obj, &priv->obj_list, mm_list) {
+               if (omap_obj->block) {
+                       struct drm_gem_object *obj = &omap_obj->base;
+                       uint32_t npages = obj->size >> PAGE_SHIFT;
+                       WARN_ON(!omap_obj->pages);  /* this can't happen */
+                       ret = tiler_pin(omap_obj->block,
+                                       omap_obj->pages, npages,
+                                       omap_obj->roll, true);
+                       if (ret) {
+                               dev_err(dev, "could not repin: %d\n", ret);
+                               return ret;
+                       }
+               }
+       }
+
+       return 0;
+}
+#endif
+
 #ifdef CONFIG_DEBUG_FS
 void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
 {
@@ -1239,12 +1267,12 @@ int omap_gem_set_sync_object(struct drm_gem_object *obj, void *syncobj)
 
        if ((omap_obj->flags & OMAP_BO_EXT_SYNC) && !syncobj) {
                /* clearing a previously set syncobj */
-               syncobj = kzalloc(sizeof(*omap_obj->sync), GFP_ATOMIC);
+               syncobj = kmemdup(omap_obj->sync, sizeof(*omap_obj->sync),
+                                 GFP_ATOMIC);
                if (!syncobj) {
                        ret = -ENOMEM;
                        goto unlock;
                }
-               memcpy(syncobj, omap_obj->sync, sizeof(*omap_obj->sync));
                omap_obj->flags &= ~OMAP_BO_EXT_SYNC;
                omap_obj->sync = syncobj;
        } else if (syncobj && !(omap_obj->flags & OMAP_BO_EXT_SYNC)) {
similarity index 97%
rename from drivers/staging/omapdrm/omap_gem_dmabuf.c
rename to drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c
index ea3840038250391bb3e3e0c897fc07a246b67732..ac74d1bc67bfc885e740e6adc809a0bd5c8d2b1d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * drivers/staging/omapdrm/omap_gem_dmabuf.c
+ * drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c
  *
  * Copyright (C) 2011 Texas Instruments
  * Author: Rob Clark <rob.clark@linaro.org>
@@ -53,10 +53,10 @@ static struct sg_table *omap_gem_map_dma_buf(
        /* this should be after _get_paddr() to ensure we have pages attached */
        omap_gem_dma_sync(obj, dir);
 
-out:
-       if (ret)
-               return ERR_PTR(ret);
        return sg;
+out:
+       kfree(sg);
+       return ERR_PTR(ret);
 }
 
 static void omap_gem_unmap_dma_buf(struct dma_buf_attachment *attachment,
@@ -194,7 +194,7 @@ struct dma_buf_ops omap_dmabuf_ops = {
 struct dma_buf *omap_gem_prime_export(struct drm_device *dev,
                struct drm_gem_object *obj, int flags)
 {
-       return dma_buf_export(obj, &omap_dmabuf_ops, obj->size, 0600);
+       return dma_buf_export(obj, &omap_dmabuf_ops, obj->size, flags);
 }
 
 struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev,
similarity index 98%
rename from drivers/staging/omapdrm/omap_gem_helpers.c
rename to drivers/gpu/drm/omapdrm/omap_gem_helpers.c
index ffb8cceaeb465d0c3d3d4ba60606328a353ba152..e4a66a35fc6a9315bdd1bdbb64f77871d0ff58d4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * drivers/staging/omapdrm/omap_gem_helpers.c
+ * drivers/gpu/drm/omapdrm/omap_gem_helpers.c
  *
  * Copyright (C) 2011 Texas Instruments
  * Author: Rob Clark <rob.clark@linaro.org>
diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c
new file mode 100644 (file)
index 0000000..e01303e
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ * drivers/gpu/drm/omapdrm/omap_irq.c
+ *
+ * Copyright (C) 2012 Texas Instruments
+ * Author: Rob Clark <rob.clark@linaro.org>
+ *
+ * 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 "omap_drv.h"
+
+static DEFINE_SPINLOCK(list_lock);
+
+static void omap_irq_error_handler(struct omap_drm_irq *irq,
+               uint32_t irqstatus)
+{
+       DRM_ERROR("errors: %08x\n", irqstatus);
+}
+
+/* call with list_lock and dispc runtime held */
+static void omap_irq_update(struct drm_device *dev)
+{
+       struct omap_drm_private *priv = dev->dev_private;
+       struct omap_drm_irq *irq;
+       uint32_t irqmask = priv->vblank_mask;
+
+       BUG_ON(!spin_is_locked(&list_lock));
+
+       list_for_each_entry(irq, &priv->irq_list, node)
+               irqmask |= irq->irqmask;
+
+       DBG("irqmask=%08x", irqmask);
+
+       dispc_write_irqenable(irqmask);
+       dispc_read_irqenable();        /* flush posted write */
+}
+
+void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq)
+{
+       struct omap_drm_private *priv = dev->dev_private;
+       unsigned long flags;
+
+       dispc_runtime_get();
+       spin_lock_irqsave(&list_lock, flags);
+
+       if (!WARN_ON(irq->registered)) {
+               irq->registered = true;
+               list_add(&irq->node, &priv->irq_list);
+               omap_irq_update(dev);
+       }
+
+       spin_unlock_irqrestore(&list_lock, flags);
+       dispc_runtime_put();
+}
+
+void omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq)
+{
+       unsigned long flags;
+
+       dispc_runtime_get();
+       spin_lock_irqsave(&list_lock, flags);
+
+       if (!WARN_ON(!irq->registered)) {
+               irq->registered = false;
+               list_del(&irq->node);
+               omap_irq_update(dev);
+       }
+
+       spin_unlock_irqrestore(&list_lock, flags);
+       dispc_runtime_put();
+}
+
+struct omap_irq_wait {
+       struct omap_drm_irq irq;
+       int count;
+};
+
+static DECLARE_WAIT_QUEUE_HEAD(wait_event);
+
+static void wait_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
+{
+       struct omap_irq_wait *wait =
+                       container_of(irq, struct omap_irq_wait, irq);
+       wait->count--;
+       wake_up_all(&wait_event);
+}
+
+struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev,
+               uint32_t irqmask, int count)
+{
+       struct omap_irq_wait *wait = kzalloc(sizeof(*wait), GFP_KERNEL);
+       wait->irq.irq = wait_irq;
+       wait->irq.irqmask = irqmask;
+       wait->count = count;
+       omap_irq_register(dev, &wait->irq);
+       return wait;
+}
+
+int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait,
+               unsigned long timeout)
+{
+       int ret = wait_event_timeout(wait_event, (wait->count <= 0), timeout);
+       omap_irq_unregister(dev, &wait->irq);
+       kfree(wait);
+       if (ret == 0)
+               return -1;
+       return 0;
+}
+
+/**
+ * enable_vblank - enable vblank interrupt events
+ * @dev: DRM device
+ * @crtc: which irq to enable
+ *
+ * Enable vblank interrupts for @crtc.  If the device doesn't have
+ * a hardware vblank counter, this routine should be a no-op, since
+ * interrupts will have to stay on to keep the count accurate.
+ *
+ * RETURNS
+ * Zero on success, appropriate errno if the given @crtc's vblank
+ * interrupt cannot be enabled.
+ */
+int omap_irq_enable_vblank(struct drm_device *dev, int crtc)
+{
+       struct omap_drm_private *priv = dev->dev_private;
+       unsigned long flags;
+
+       DBG("dev=%p, crtc=%d", dev, crtc);
+
+       dispc_runtime_get();
+       spin_lock_irqsave(&list_lock, flags);
+       priv->vblank_mask |= pipe2vbl(crtc);
+       omap_irq_update(dev);
+       spin_unlock_irqrestore(&list_lock, flags);
+       dispc_runtime_put();
+
+       return 0;
+}
+
+/**
+ * disable_vblank - disable vblank interrupt events
+ * @dev: DRM device
+ * @crtc: which irq to enable
+ *
+ * Disable vblank interrupts for @crtc.  If the device doesn't have
+ * a hardware vblank counter, this routine should be a no-op, since
+ * interrupts will have to stay on to keep the count accurate.
+ */
+void omap_irq_disable_vblank(struct drm_device *dev, int crtc)
+{
+       struct omap_drm_private *priv = dev->dev_private;
+       unsigned long flags;
+
+       DBG("dev=%p, crtc=%d", dev, crtc);
+
+       dispc_runtime_get();
+       spin_lock_irqsave(&list_lock, flags);
+       priv->vblank_mask &= ~pipe2vbl(crtc);
+       omap_irq_update(dev);
+       spin_unlock_irqrestore(&list_lock, flags);
+       dispc_runtime_put();
+}
+
+irqreturn_t omap_irq_handler(DRM_IRQ_ARGS)
+{
+       struct drm_device *dev = (struct drm_device *) arg;
+       struct omap_drm_private *priv = dev->dev_private;
+       struct omap_drm_irq *handler, *n;
+       unsigned long flags;
+       unsigned int id;
+       u32 irqstatus;
+
+       irqstatus = dispc_read_irqstatus();
+       dispc_clear_irqstatus(irqstatus);
+       dispc_read_irqstatus();        /* flush posted write */
+
+       VERB("irqs: %08x", irqstatus);
+
+       for (id = 0; id < priv->num_crtcs; id++)
+               if (irqstatus & pipe2vbl(id))
+                       drm_handle_vblank(dev, id);
+
+       spin_lock_irqsave(&list_lock, flags);
+       list_for_each_entry_safe(handler, n, &priv->irq_list, node) {
+               if (handler->irqmask & irqstatus) {
+                       spin_unlock_irqrestore(&list_lock, flags);
+                       handler->irq(handler, handler->irqmask & irqstatus);
+                       spin_lock_irqsave(&list_lock, flags);
+               }
+       }
+       spin_unlock_irqrestore(&list_lock, flags);
+
+       return IRQ_HANDLED;
+}
+
+void omap_irq_preinstall(struct drm_device *dev)
+{
+       DBG("dev=%p", dev);
+       dispc_runtime_get();
+       dispc_clear_irqstatus(0xffffffff);
+       dispc_runtime_put();
+}
+
+int omap_irq_postinstall(struct drm_device *dev)
+{
+       struct omap_drm_private *priv = dev->dev_private;
+       struct omap_drm_irq *error_handler = &priv->error_handler;
+
+       DBG("dev=%p", dev);
+
+       INIT_LIST_HEAD(&priv->irq_list);
+
+       error_handler->irq = omap_irq_error_handler;
+       error_handler->irqmask = DISPC_IRQ_OCP_ERR;
+
+       /* for now ignore DISPC_IRQ_SYNC_LOST_DIGIT.. really I think
+        * we just need to ignore it while enabling tv-out
+        */
+       error_handler->irqmask &= ~DISPC_IRQ_SYNC_LOST_DIGIT;
+
+       omap_irq_register(dev, error_handler);
+
+       return 0;
+}
+
+void omap_irq_uninstall(struct drm_device *dev)
+{
+       DBG("dev=%p", dev);
+       // TODO prolly need to call drm_irq_uninstall() somewhere too
+}
+
+/*
+ * We need a special version, instead of just using drm_irq_install(),
+ * because we need to register the irq via omapdss.  Once omapdss and
+ * omapdrm are merged together we can assign the dispc hwmod data to
+ * ourselves and drop these and just use drm_irq_{install,uninstall}()
+ */
+
+int omap_drm_irq_install(struct drm_device *dev)
+{
+       int ret;
+
+       mutex_lock(&dev->struct_mutex);
+
+       if (dev->irq_enabled) {
+               mutex_unlock(&dev->struct_mutex);
+               return -EBUSY;
+       }
+       dev->irq_enabled = 1;
+       mutex_unlock(&dev->struct_mutex);
+
+       /* Before installing handler */
+       if (dev->driver->irq_preinstall)
+               dev->driver->irq_preinstall(dev);
+
+       ret = dispc_request_irq(dev->driver->irq_handler, dev);
+
+       if (ret < 0) {
+               mutex_lock(&dev->struct_mutex);
+               dev->irq_enabled = 0;
+               mutex_unlock(&dev->struct_mutex);
+               return ret;
+       }
+
+       /* After installing handler */
+       if (dev->driver->irq_postinstall)
+               ret = dev->driver->irq_postinstall(dev);
+
+       if (ret < 0) {
+               mutex_lock(&dev->struct_mutex);
+               dev->irq_enabled = 0;
+               mutex_unlock(&dev->struct_mutex);
+               dispc_free_irq(dev);
+       }
+
+       return ret;
+}
+
+int omap_drm_irq_uninstall(struct drm_device *dev)
+{
+       unsigned long irqflags;
+       int irq_enabled, i;
+
+       mutex_lock(&dev->struct_mutex);
+       irq_enabled = dev->irq_enabled;
+       dev->irq_enabled = 0;
+       mutex_unlock(&dev->struct_mutex);
+
+       /*
+        * Wake up any waiters so they don't hang.
+        */
+       if (dev->num_crtcs) {
+               spin_lock_irqsave(&dev->vbl_lock, irqflags);
+               for (i = 0; i < dev->num_crtcs; i++) {
+                       DRM_WAKEUP(&dev->vbl_queue[i]);
+                       dev->vblank_enabled[i] = 0;
+                       dev->last_vblank[i] =
+                               dev->driver->get_vblank_counter(dev, i);
+               }
+               spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+       }
+
+       if (!irq_enabled)
+               return -EINVAL;
+
+       if (dev->driver->irq_uninstall)
+               dev->driver->irq_uninstall(dev);
+
+       dispc_free_irq(dev);
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c
new file mode 100644 (file)
index 0000000..dd68d14
--- /dev/null
@@ -0,0 +1,450 @@
+/*
+ * drivers/gpu/drm/omapdrm/omap_plane.c
+ *
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob.clark@linaro.org>
+ *
+ * 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/kfifo.h>
+
+#include "omap_drv.h"
+#include "omap_dmm_tiler.h"
+
+/* some hackery because omapdss has an 'enum omap_plane' (which would be
+ * better named omap_plane_id).. and compiler seems unhappy about having
+ * both a 'struct omap_plane' and 'enum omap_plane'
+ */
+#define omap_plane _omap_plane
+
+/*
+ * plane funcs
+ */
+
+struct callback {
+       void (*fxn)(void *);
+       void *arg;
+};
+
+#define to_omap_plane(x) container_of(x, struct omap_plane, base)
+
+struct omap_plane {
+       struct drm_plane base;
+       int id;  /* TODO rename omap_plane -> omap_plane_id in omapdss so I can use the enum */
+       const char *name;
+       struct omap_overlay_info info;
+       struct omap_drm_apply apply;
+
+       /* position/orientation of scanout within the fb: */
+       struct omap_drm_window win;
+       bool enabled;
+
+       /* last fb that we pinned: */
+       struct drm_framebuffer *pinned_fb;
+
+       uint32_t nformats;
+       uint32_t formats[32];
+
+       struct omap_drm_irq error_irq;
+
+       /* set of bo's pending unpin until next post_apply() */
+       DECLARE_KFIFO_PTR(unpin_fifo, struct drm_gem_object *);
+
+       // XXX maybe get rid of this and handle vblank in crtc too?
+       struct callback apply_done_cb;
+};
+
+static void unpin(void *arg, struct drm_gem_object *bo)
+{
+       struct drm_plane *plane = arg;
+       struct omap_plane *omap_plane = to_omap_plane(plane);
+
+       if (kfifo_put(&omap_plane->unpin_fifo,
+                       (const struct drm_gem_object **)&bo)) {
+               /* also hold a ref so it isn't free'd while pinned */
+               drm_gem_object_reference(bo);
+       } else {
+               dev_err(plane->dev->dev, "unpin fifo full!\n");
+               omap_gem_put_paddr(bo);
+       }
+}
+
+/* update which fb (if any) is pinned for scanout */
+static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb)
+{
+       struct omap_plane *omap_plane = to_omap_plane(plane);
+       struct drm_framebuffer *pinned_fb = omap_plane->pinned_fb;
+
+       if (pinned_fb != fb) {
+               int ret;
+
+               DBG("%p -> %p", pinned_fb, fb);
+
+               if (fb)
+                       drm_framebuffer_reference(fb);
+
+               ret = omap_framebuffer_replace(pinned_fb, fb, plane, unpin);
+
+               if (pinned_fb)
+                       drm_framebuffer_unreference(pinned_fb);
+
+               if (ret) {
+                       dev_err(plane->dev->dev, "could not swap %p -> %p\n",
+                                       omap_plane->pinned_fb, fb);
+                       if (fb)
+                               drm_framebuffer_unreference(fb);
+                       omap_plane->pinned_fb = NULL;
+                       return ret;
+               }
+
+               omap_plane->pinned_fb = fb;
+       }
+
+       return 0;
+}
+
+static void omap_plane_pre_apply(struct omap_drm_apply *apply)
+{
+       struct omap_plane *omap_plane =
+                       container_of(apply, struct omap_plane, apply);
+       struct omap_drm_window *win = &omap_plane->win;
+       struct drm_plane *plane = &omap_plane->base;
+       struct drm_device *dev = plane->dev;
+       struct omap_overlay_info *info = &omap_plane->info;
+       struct drm_crtc *crtc = plane->crtc;
+       enum omap_channel channel;
+       bool enabled = omap_plane->enabled && crtc;
+       bool ilace, replication;
+       int ret;
+
+       DBG("%s, enabled=%d", omap_plane->name, enabled);
+
+       /* if fb has changed, pin new fb: */
+       update_pin(plane, enabled ? plane->fb : NULL);
+
+       if (!enabled) {
+               dispc_ovl_enable(omap_plane->id, false);
+               return;
+       }
+
+       channel = omap_crtc_channel(crtc);
+
+       /* update scanout: */
+       omap_framebuffer_update_scanout(plane->fb, win, info);
+
+       DBG("%dx%d -> %dx%d (%d)", info->width, info->height,
+                       info->out_width, info->out_height,
+                       info->screen_width);
+       DBG("%d,%d %08x %08x", info->pos_x, info->pos_y,
+                       info->paddr, info->p_uv_addr);
+
+       /* TODO: */
+       ilace = false;
+       replication = false;
+
+       /* and finally, update omapdss: */
+       ret = dispc_ovl_setup(omap_plane->id, info,
+                       replication, omap_crtc_timings(crtc), false);
+       if (ret) {
+               dev_err(dev->dev, "dispc_ovl_setup failed: %d\n", ret);
+               return;
+       }
+
+       dispc_ovl_enable(omap_plane->id, true);
+       dispc_ovl_set_channel_out(omap_plane->id, channel);
+}
+
+static void omap_plane_post_apply(struct omap_drm_apply *apply)
+{
+       struct omap_plane *omap_plane =
+                       container_of(apply, struct omap_plane, apply);
+       struct drm_plane *plane = &omap_plane->base;
+       struct omap_overlay_info *info = &omap_plane->info;
+       struct drm_gem_object *bo = NULL;
+       struct callback cb;
+
+       cb = omap_plane->apply_done_cb;
+       omap_plane->apply_done_cb.fxn = NULL;
+
+       while (kfifo_get(&omap_plane->unpin_fifo, &bo)) {
+               omap_gem_put_paddr(bo);
+               drm_gem_object_unreference_unlocked(bo);
+       }
+
+       if (cb.fxn)
+               cb.fxn(cb.arg);
+
+       if (omap_plane->enabled) {
+               omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y,
+                               info->out_width, info->out_height);
+       }
+}
+
+static int apply(struct drm_plane *plane)
+{
+       if (plane->crtc) {
+               struct omap_plane *omap_plane = to_omap_plane(plane);
+               return omap_crtc_apply(plane->crtc, &omap_plane->apply);
+       }
+       return 0;
+}
+
+int omap_plane_mode_set(struct drm_plane *plane,
+               struct drm_crtc *crtc, struct drm_framebuffer *fb,
+               int crtc_x, int crtc_y,
+               unsigned int crtc_w, unsigned int crtc_h,
+               uint32_t src_x, uint32_t src_y,
+               uint32_t src_w, uint32_t src_h,
+               void (*fxn)(void *), void *arg)
+{
+       struct omap_plane *omap_plane = to_omap_plane(plane);
+       struct omap_drm_window *win = &omap_plane->win;
+
+       win->crtc_x = crtc_x;
+       win->crtc_y = crtc_y;
+       win->crtc_w = crtc_w;
+       win->crtc_h = crtc_h;
+
+       /* src values are in Q16 fixed point, convert to integer: */
+       win->src_x = src_x >> 16;
+       win->src_y = src_y >> 16;
+       win->src_w = src_w >> 16;
+       win->src_h = src_h >> 16;
+
+       if (fxn) {
+               /* omap_crtc should ensure that a new page flip
+                * isn't permitted while there is one pending:
+                */
+               BUG_ON(omap_plane->apply_done_cb.fxn);
+
+               omap_plane->apply_done_cb.fxn = fxn;
+               omap_plane->apply_done_cb.arg = arg;
+       }
+
+       plane->fb = fb;
+       plane->crtc = crtc;
+
+       return apply(plane);
+}
+
+static int omap_plane_update(struct drm_plane *plane,
+               struct drm_crtc *crtc, struct drm_framebuffer *fb,
+               int crtc_x, int crtc_y,
+               unsigned int crtc_w, unsigned int crtc_h,
+               uint32_t src_x, uint32_t src_y,
+               uint32_t src_w, uint32_t src_h)
+{
+       struct omap_plane *omap_plane = to_omap_plane(plane);
+       omap_plane->enabled = true;
+       return omap_plane_mode_set(plane, crtc, fb,
+                       crtc_x, crtc_y, crtc_w, crtc_h,
+                       src_x, src_y, src_w, src_h,
+                       NULL, NULL);
+}
+
+static int omap_plane_disable(struct drm_plane *plane)
+{
+       struct omap_plane *omap_plane = to_omap_plane(plane);
+       omap_plane->win.rotation = BIT(DRM_ROTATE_0);
+       return omap_plane_dpms(plane, DRM_MODE_DPMS_OFF);
+}
+
+static void omap_plane_destroy(struct drm_plane *plane)
+{
+       struct omap_plane *omap_plane = to_omap_plane(plane);
+
+       DBG("%s", omap_plane->name);
+
+       omap_irq_unregister(plane->dev, &omap_plane->error_irq);
+
+       omap_plane_disable(plane);
+       drm_plane_cleanup(plane);
+
+       WARN_ON(!kfifo_is_empty(&omap_plane->unpin_fifo));
+       kfifo_free(&omap_plane->unpin_fifo);
+
+       kfree(omap_plane);
+}
+
+int omap_plane_dpms(struct drm_plane *plane, int mode)
+{
+       struct omap_plane *omap_plane = to_omap_plane(plane);
+       bool enabled = (mode == DRM_MODE_DPMS_ON);
+       int ret = 0;
+
+       if (enabled != omap_plane->enabled) {
+               omap_plane->enabled = enabled;
+               ret = apply(plane);
+       }
+
+       return ret;
+}
+
+/* helper to install properties which are common to planes and crtcs */
+void omap_plane_install_properties(struct drm_plane *plane,
+               struct drm_mode_object *obj)
+{
+       struct drm_device *dev = plane->dev;
+       struct omap_drm_private *priv = dev->dev_private;
+       struct drm_property *prop;
+
+       if (priv->has_dmm) {
+               prop = priv->rotation_prop;
+               if (!prop) {
+                       const struct drm_prop_enum_list props[] = {
+                                       { DRM_ROTATE_0,   "rotate-0" },
+                                       { DRM_ROTATE_90,  "rotate-90" },
+                                       { DRM_ROTATE_180, "rotate-180" },
+                                       { DRM_ROTATE_270, "rotate-270" },
+                                       { DRM_REFLECT_X,  "reflect-x" },
+                                       { DRM_REFLECT_Y,  "reflect-y" },
+                       };
+                       prop = drm_property_create_bitmask(dev, 0, "rotation",
+                                       props, ARRAY_SIZE(props));
+                       if (prop == NULL)
+                               return;
+                       priv->rotation_prop = prop;
+               }
+               drm_object_attach_property(obj, prop, 0);
+       }
+
+       prop = priv->zorder_prop;
+       if (!prop) {
+               prop = drm_property_create_range(dev, 0, "zorder", 0, 3);
+               if (prop == NULL)
+                       return;
+               priv->zorder_prop = prop;
+       }
+       drm_object_attach_property(obj, prop, 0);
+}
+
+int omap_plane_set_property(struct drm_plane *plane,
+               struct drm_property *property, uint64_t val)
+{
+       struct omap_plane *omap_plane = to_omap_plane(plane);
+       struct omap_drm_private *priv = plane->dev->dev_private;
+       int ret = -EINVAL;
+
+       if (property == priv->rotation_prop) {
+               DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val);
+               omap_plane->win.rotation = val;
+               ret = apply(plane);
+       } else if (property == priv->zorder_prop) {
+               DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val);
+               omap_plane->info.zorder = val;
+               ret = apply(plane);
+       }
+
+       return ret;
+}
+
+static const struct drm_plane_funcs omap_plane_funcs = {
+               .update_plane = omap_plane_update,
+               .disable_plane = omap_plane_disable,
+               .destroy = omap_plane_destroy,
+               .set_property = omap_plane_set_property,
+};
+
+static void omap_plane_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
+{
+       struct omap_plane *omap_plane =
+                       container_of(irq, struct omap_plane, error_irq);
+       DRM_ERROR("%s: errors: %08x\n", omap_plane->name, irqstatus);
+}
+
+static const char *plane_names[] = {
+               [OMAP_DSS_GFX] = "gfx",
+               [OMAP_DSS_VIDEO1] = "vid1",
+               [OMAP_DSS_VIDEO2] = "vid2",
+               [OMAP_DSS_VIDEO3] = "vid3",
+};
+
+static const uint32_t error_irqs[] = {
+               [OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW,
+               [OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW,
+               [OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW,
+               [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW,
+};
+
+/* initialize plane */
+struct drm_plane *omap_plane_init(struct drm_device *dev,
+               int id, bool private_plane)
+{
+       struct omap_drm_private *priv = dev->dev_private;
+       struct drm_plane *plane = NULL;
+       struct omap_plane *omap_plane;
+       struct omap_overlay_info *info;
+       int ret;
+
+       DBG("%s: priv=%d", plane_names[id], private_plane);
+
+       omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
+       if (!omap_plane) {
+               dev_err(dev->dev, "could not allocate plane\n");
+               goto fail;
+       }
+
+       ret = kfifo_alloc(&omap_plane->unpin_fifo, 16, GFP_KERNEL);
+       if (ret) {
+               dev_err(dev->dev, "could not allocate unpin FIFO\n");
+               goto fail;
+       }
+
+       omap_plane->nformats = omap_framebuffer_get_formats(
+                       omap_plane->formats, ARRAY_SIZE(omap_plane->formats),
+                       dss_feat_get_supported_color_modes(id));
+       omap_plane->id = id;
+       omap_plane->name = plane_names[id];
+
+       plane = &omap_plane->base;
+
+       omap_plane->apply.pre_apply  = omap_plane_pre_apply;
+       omap_plane->apply.post_apply = omap_plane_post_apply;
+
+       omap_plane->error_irq.irqmask = error_irqs[id];
+       omap_plane->error_irq.irq = omap_plane_error_irq;
+       omap_irq_register(dev, &omap_plane->error_irq);
+
+       drm_plane_init(dev, plane, (1 << priv->num_crtcs) - 1, &omap_plane_funcs,
+                       omap_plane->formats, omap_plane->nformats, private_plane);
+
+       omap_plane_install_properties(plane, &plane->base);
+
+       /* get our starting configuration, set defaults for parameters
+        * we don't currently use, etc:
+        */
+       info = &omap_plane->info;
+       info->rotation_type = OMAP_DSS_ROT_DMA;
+       info->rotation = OMAP_DSS_ROT_0;
+       info->global_alpha = 0xff;
+       info->mirror = 0;
+
+       /* Set defaults depending on whether we are a CRTC or overlay
+        * layer.
+        * TODO add ioctl to give userspace an API to change this.. this
+        * will come in a subsequent patch.
+        */
+       if (private_plane)
+               omap_plane->info.zorder = 0;
+       else
+               omap_plane->info.zorder = id;
+
+       return plane;
+
+fail:
+       if (plane)
+               omap_plane_destroy(plane);
+
+       return NULL;
+}
similarity index 99%
rename from drivers/staging/omapdrm/tcm.h
rename to drivers/gpu/drm/omapdrm/tcm.h
index d273e3ee0b4c7d64737b1e52fbbcb068a0be089a..a8d5ce47686f791981b96b401d701ae83abab585 100644 (file)
@@ -59,6 +59,8 @@ struct tcm {
        u16 width, height;      /* container dimensions */
        int lut_id;             /* Lookup table identifier */
 
+       unsigned int y_offset;  /* offset to use for y coordinates */
+
        /* 'pvt' structure shall contain any tcm details (attr) along with
        linked list of allocated areas and mutex for mutually exclusive access
        to the list.  It may also contain copies of width and height to notice
index 081e55f66c409d5b3417153fa613bad3cf653430..8263af3fd832b336f4c58ed5c33878343df8e997 100644 (file)
@@ -350,7 +350,6 @@ struct radeon_bo {
        struct drm_gem_object           gem_base;
 
        struct ttm_bo_kmap_obj dma_buf_vmap;
-       int vmapping_count;
 };
 #define gem_to_radeon_bo(gobj) container_of((gobj), struct radeon_bo, gem_base)
 
index 0d67674b64b13e9a4618548fb1ca3d2d2211f797..b097d5b4ff393daaa735d158a89aaea69f421a9f 100644 (file)
@@ -246,8 +246,14 @@ int radeon_crtc_cursor_move(struct drm_crtc *crtc,
                int i = 0;
                struct drm_crtc *crtc_p;
 
-               /* avivo cursor image can't end on 128 pixel boundary or
+               /*
+                * avivo cursor image can't end on 128 pixel boundary or
                 * go past the end of the frame if both crtcs are enabled
+                *
+                * NOTE: It is safe to access crtc->enabled of other crtcs
+                * without holding either the mode_config lock or the other
+                * crtc's lock as long as write access to this flag _always_
+                * grabs all locks.
                 */
                list_for_each_entry(crtc_p, &crtc->dev->mode_config.crtc_list, head) {
                        if (crtc_p->enabled)
index 9f02fdd68f517fe716b279c58c047b7e449c855c..44b8034a400d297dcba86012c8dd797f72a6af28 100644 (file)
@@ -430,7 +430,8 @@ bool radeon_card_posted(struct radeon_device *rdev)
 {
        uint32_t reg;
 
-       if (efi_enabled && rdev->pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE)
+       if (efi_enabled(EFI_BOOT) &&
+           rdev->pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE)
                return false;
 
        /* first check CRTCs */
index 05c96fa0b0515a9eab3a89b6bcab97fd1af888dd..e38fd559f1abb843b0b1733cfc92f4ddc301802b 100644 (file)
@@ -1089,12 +1089,12 @@ radeon_framebuffer_init(struct drm_device *dev,
 {
        int ret;
        rfb->obj = obj;
+       drm_helper_mode_fill_fb_struct(&rfb->base, mode_cmd);
        ret = drm_framebuffer_init(dev, &rfb->base, &radeon_fb_funcs);
        if (ret) {
                rfb->obj = NULL;
                return ret;
        }
-       drm_helper_mode_fill_fb_struct(&rfb->base, mode_cmd);
        return 0;
 }
 
index 833484da12d97206d6d47bf72aed846d1aed3ace..167758488ed6fd6781cbf3fa5d8a5b5a1e3c10a1 100644 (file)
@@ -118,11 +118,13 @@ int radeon_mode_dumb_create(struct drm_file *file_priv,
 int radeon_mode_dumb_destroy(struct drm_file *file_priv,
                             struct drm_device *dev,
                             uint32_t handle);
-struct dma_buf *radeon_gem_prime_export(struct drm_device *dev,
-                                       struct drm_gem_object *obj,
-                                       int flags);
-struct drm_gem_object *radeon_gem_prime_import(struct drm_device *dev,
-                                              struct dma_buf *dma_buf);
+struct sg_table *radeon_gem_prime_get_sg_table(struct drm_gem_object *obj);
+struct drm_gem_object *radeon_gem_prime_import_sg_table(struct drm_device *dev,
+                                                       size_t size,
+                                                       struct sg_table *sg);
+int radeon_gem_prime_pin(struct drm_gem_object *obj);
+void *radeon_gem_prime_vmap(struct drm_gem_object *obj);
+void radeon_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
 extern long radeon_kms_compat_ioctl(struct file *filp, unsigned int cmd,
                                    unsigned long arg);
 
@@ -409,8 +411,13 @@ static struct drm_driver kms_driver = {
 
        .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
        .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
-       .gem_prime_export = radeon_gem_prime_export,
-       .gem_prime_import = radeon_gem_prime_import,
+       .gem_prime_export = drm_gem_prime_export,
+       .gem_prime_import = drm_gem_prime_import,
+       .gem_prime_pin = radeon_gem_prime_pin,
+       .gem_prime_get_sg_table = radeon_gem_prime_get_sg_table,
+       .gem_prime_import_sg_table = radeon_gem_prime_import_sg_table,
+       .gem_prime_vmap = radeon_gem_prime_vmap,
+       .gem_prime_vunmap = radeon_gem_prime_vunmap,
 
        .name = DRIVER_NAME,
        .desc = DRIVER_DESC,
index cc8489d8c6d1263c7e5049ea1fcd0ae2dcf5a95a..b1746741bc591267f246815adcadc0725dcf42cc 100644 (file)
@@ -187,9 +187,10 @@ out_unref:
        return ret;
 }
 
-static int radeonfb_create(struct radeon_fbdev *rfbdev,
+static int radeonfb_create(struct drm_fb_helper *helper,
                           struct drm_fb_helper_surface_size *sizes)
 {
+       struct radeon_fbdev *rfbdev = (struct radeon_fbdev *)helper;
        struct radeon_device *rdev = rfbdev->rdev;
        struct fb_info *info;
        struct drm_framebuffer *fb = NULL;
@@ -293,28 +294,13 @@ out_unref:
        }
        if (fb && ret) {
                drm_gem_object_unreference(gobj);
+               drm_framebuffer_unregister_private(fb);
                drm_framebuffer_cleanup(fb);
                kfree(fb);
        }
        return ret;
 }
 
-static int radeon_fb_find_or_create_single(struct drm_fb_helper *helper,
-                                          struct drm_fb_helper_surface_size *sizes)
-{
-       struct radeon_fbdev *rfbdev = (struct radeon_fbdev *)helper;
-       int new_fb = 0;
-       int ret;
-
-       if (!helper->fb) {
-               ret = radeonfb_create(rfbdev, sizes);
-               if (ret)
-                       return ret;
-               new_fb = 1;
-       }
-       return new_fb;
-}
-
 void radeon_fb_output_poll_changed(struct radeon_device *rdev)
 {
        drm_fb_helper_hotplug_event(&rdev->mode_info.rfbdev->helper);
@@ -339,6 +325,7 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb
                rfb->obj = NULL;
        }
        drm_fb_helper_fini(&rfbdev->helper);
+       drm_framebuffer_unregister_private(&rfb->base);
        drm_framebuffer_cleanup(&rfb->base);
 
        return 0;
@@ -347,7 +334,7 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb
 static struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
        .gamma_set = radeon_crtc_fb_gamma_set,
        .gamma_get = radeon_crtc_fb_gamma_get,
-       .fb_probe = radeon_fb_find_or_create_single,
+       .fb_probe = radeonfb_create,
 };
 
 int radeon_fbdev_init(struct radeon_device *rdev)
@@ -377,6 +364,10 @@ int radeon_fbdev_init(struct radeon_device *rdev)
        }
 
        drm_fb_helper_single_add_all_connectors(&rfbdev->helper);
+
+       /* disable all the possible outputs/crtcs before entering KMS mode */
+       drm_helper_disable_unused_functions(rdev->ddev);
+
        drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel);
        return 0;
 }
index 26c23bb651c6b53835a2061096bdecd4f9d47492..4940af7e75e6dd180de3ebd6d80e70897ea325d5 100644 (file)
 #include "radeon.h"
 #include <drm/radeon_drm.h>
 
-#include <linux/dma-buf.h>
-
-static struct sg_table *radeon_gem_map_dma_buf(struct dma_buf_attachment *attachment,
-                                              enum dma_data_direction dir)
+struct sg_table *radeon_gem_prime_get_sg_table(struct drm_gem_object *obj)
 {
-       struct radeon_bo *bo = attachment->dmabuf->priv;
-       struct drm_device *dev = bo->rdev->ddev;
+       struct radeon_bo *bo = gem_to_radeon_bo(obj);
        int npages = bo->tbo.num_pages;
-       struct sg_table *sg;
-       int nents;
-
-       mutex_lock(&dev->struct_mutex);
-       sg = drm_prime_pages_to_sg(bo->tbo.ttm->pages, npages);
-       nents = dma_map_sg(attachment->dev, sg->sgl, sg->nents, dir);
-       mutex_unlock(&dev->struct_mutex);
-       return sg;
-}
-
-static void radeon_gem_unmap_dma_buf(struct dma_buf_attachment *attachment,
-                                    struct sg_table *sg, enum dma_data_direction dir)
-{
-       dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, dir);
-       sg_free_table(sg);
-       kfree(sg);
-}
-
-static void radeon_gem_dmabuf_release(struct dma_buf *dma_buf)
-{
-       struct radeon_bo *bo = dma_buf->priv;
-
-       if (bo->gem_base.export_dma_buf == dma_buf) {
-               DRM_ERROR("unreference dmabuf %p\n", &bo->gem_base);
-               bo->gem_base.export_dma_buf = NULL;
-               drm_gem_object_unreference_unlocked(&bo->gem_base);
-       }
-}
-
-static void *radeon_gem_kmap_atomic(struct dma_buf *dma_buf, unsigned long page_num)
-{
-       return NULL;
-}
-
-static void radeon_gem_kunmap_atomic(struct dma_buf *dma_buf, unsigned long page_num, void *addr)
-{
-
-}
-static void *radeon_gem_kmap(struct dma_buf *dma_buf, unsigned long page_num)
-{
-       return NULL;
-}
-
-static void radeon_gem_kunmap(struct dma_buf *dma_buf, unsigned long page_num, void *addr)
-{
 
+       return drm_prime_pages_to_sg(bo->tbo.ttm->pages, npages);
 }
 
-static int radeon_gem_prime_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma)
+void *radeon_gem_prime_vmap(struct drm_gem_object *obj)
 {
-       return -EINVAL;
-}
-
-static void *radeon_gem_prime_vmap(struct dma_buf *dma_buf)
-{
-       struct radeon_bo *bo = dma_buf->priv;
-       struct drm_device *dev = bo->rdev->ddev;
+       struct radeon_bo *bo = gem_to_radeon_bo(obj);
        int ret;
 
-       mutex_lock(&dev->struct_mutex);
-       if (bo->vmapping_count) {
-               bo->vmapping_count++;
-               goto out_unlock;
-       }
-
        ret = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.num_pages,
                          &bo->dma_buf_vmap);
-       if (ret) {
-               mutex_unlock(&dev->struct_mutex);
+       if (ret)
                return ERR_PTR(ret);
-       }
-       bo->vmapping_count = 1;
-out_unlock:
-       mutex_unlock(&dev->struct_mutex);
+
        return bo->dma_buf_vmap.virtual;
 }
 
-static void radeon_gem_prime_vunmap(struct dma_buf *dma_buf, void *vaddr)
+void radeon_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
 {
-       struct radeon_bo *bo = dma_buf->priv;
-       struct drm_device *dev = bo->rdev->ddev;
+       struct radeon_bo *bo = gem_to_radeon_bo(obj);
 
-       mutex_lock(&dev->struct_mutex);
-       bo->vmapping_count--;
-       if (bo->vmapping_count == 0) {
-               ttm_bo_kunmap(&bo->dma_buf_vmap);
-       }
-       mutex_unlock(&dev->struct_mutex);
+       ttm_bo_kunmap(&bo->dma_buf_vmap);
 }
-const static struct dma_buf_ops radeon_dmabuf_ops =  {
-       .map_dma_buf = radeon_gem_map_dma_buf,
-       .unmap_dma_buf = radeon_gem_unmap_dma_buf,
-       .release = radeon_gem_dmabuf_release,
-       .kmap = radeon_gem_kmap,
-       .kmap_atomic = radeon_gem_kmap_atomic,
-       .kunmap = radeon_gem_kunmap,
-       .kunmap_atomic = radeon_gem_kunmap_atomic,
-       .mmap = radeon_gem_prime_mmap,
-       .vmap = radeon_gem_prime_vmap,
-       .vunmap = radeon_gem_prime_vunmap,
-};
-
-static int radeon_prime_create(struct drm_device *dev,
-                              size_t size,
-                              struct sg_table *sg,
-                              struct radeon_bo **pbo)
+
+struct drm_gem_object *radeon_gem_prime_import_sg_table(struct drm_device *dev,
+                                                       size_t size,
+                                                       struct sg_table *sg)
 {
        struct radeon_device *rdev = dev->dev_private;
        struct radeon_bo *bo;
        int ret;
 
        ret = radeon_bo_create(rdev, size, PAGE_SIZE, false,
-                              RADEON_GEM_DOMAIN_GTT, sg, pbo);
+                              RADEON_GEM_DOMAIN_GTT, sg, &bo);
        if (ret)
-               return ret;
-       bo = *pbo;
+               return ERR_PTR(ret);
        bo->gem_base.driver_private = bo;
 
        mutex_lock(&rdev->gem.mutex);
        list_add_tail(&bo->list, &rdev->gem.objects);
        mutex_unlock(&rdev->gem.mutex);
 
-       return 0;
+       return &bo->gem_base;
 }
 
-struct dma_buf *radeon_gem_prime_export(struct drm_device *dev,
-                                       struct drm_gem_object *obj,
-                                       int flags)
+int radeon_gem_prime_pin(struct drm_gem_object *obj)
 {
        struct radeon_bo *bo = gem_to_radeon_bo(obj);
        int ret = 0;
 
        ret = radeon_bo_reserve(bo, false);
        if (unlikely(ret != 0))
-               return ERR_PTR(ret);
+               return ret;
 
        /* pin buffer into GTT */
        ret = radeon_bo_pin(bo, RADEON_GEM_DOMAIN_GTT, NULL);
        if (ret) {
                radeon_bo_unreserve(bo);
-               return ERR_PTR(ret);
+               return ret;
        }
        radeon_bo_unreserve(bo);
-       return dma_buf_export(bo, &radeon_dmabuf_ops, obj->size, flags);
-}
 
-struct drm_gem_object *radeon_gem_prime_import(struct drm_device *dev,
-                                              struct dma_buf *dma_buf)
-{
-       struct dma_buf_attachment *attach;
-       struct sg_table *sg;
-       struct radeon_bo *bo;
-       int ret;
-
-       if (dma_buf->ops == &radeon_dmabuf_ops) {
-               bo = dma_buf->priv;
-               if (bo->gem_base.dev == dev) {
-                       drm_gem_object_reference(&bo->gem_base);
-                       dma_buf_put(dma_buf);
-                       return &bo->gem_base;
-               }
-       }
-
-       /* need to attach */
-       attach = dma_buf_attach(dma_buf, dev->dev);
-       if (IS_ERR(attach))
-               return ERR_CAST(attach);
-
-       sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
-       if (IS_ERR(sg)) {
-               ret = PTR_ERR(sg);
-               goto fail_detach;
-       }
-
-       ret = radeon_prime_create(dev, dma_buf->size, sg, &bo);
-       if (ret)
-               goto fail_unmap;
-
-       bo->gem_base.import_attach = attach;
-
-       return &bo->gem_base;
-
-fail_unmap:
-       dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL);
-fail_detach:
-       dma_buf_detach(dma_buf, attach);
-       return ERR_PTR(ret);
+       return 0;
 }
index 1d8ff2f850ba0d6d4a4a47c68913bc4cbb4392e7..93f760e27a9200a81b94dfee8ce6d14ac935b49a 100644 (file)
@@ -38,6 +38,7 @@
 #include <drm/radeon_drm.h>
 #include <linux/seq_file.h>
 #include <linux/slab.h>
+#include <linux/swiotlb.h>
 #include "radeon_reg.h"
 #include "radeon.h"
 
index d1d5306ebf24a6a6f563cdc7a7c3e0af375b72e4..f6e0b53950518a14165b067dc06a1c042cc311e4 100644 (file)
@@ -313,9 +313,9 @@ static int shmob_drm_pm_resume(struct device *dev)
 {
        struct shmob_drm_device *sdev = dev_get_drvdata(dev);
 
-       mutex_lock(&sdev->ddev->mode_config.mutex);
+       drm_modeset_lock_all(sdev->ddev);
        shmob_drm_crtc_resume(&sdev->crtc);
-       mutex_unlock(&sdev->ddev->mode_config.mutex);
+       drm_modeset_unlock_all(sdev->ddev);
 
        drm_kms_helper_poll_enable(sdev->ddev);
        return 0;
index 97993c6835fdd01207bf052306036bbbb0e69594..03914953cb1c059bcbe8f810564d4c5a10f28224 100644 (file)
@@ -39,10 +39,6 @@ int tegra_drm_fb_init(struct drm_device *drm)
        if (IS_ERR(fbdev))
                return PTR_ERR(fbdev);
 
-#ifndef CONFIG_FRAMEBUFFER_CONSOLE
-       drm_fbdev_cma_restore_mode(fbdev);
-#endif
-
        host1x->fbdev = fbdev;
 
        return 0;
index 52b20b12c83a3859da1b5fbbc1449fd3d4e907a4..9b07b7d44a58b318eb05727a0545a8325a1a5efd 100644 (file)
@@ -158,7 +158,8 @@ static void ttm_bo_release_list(struct kref *list_kref)
        ttm_mem_global_free(bdev->glob->mem_glob, acc_size);
 }
 
-int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo, bool interruptible)
+static int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo,
+                                 bool interruptible)
 {
        if (interruptible) {
                return wait_event_interruptible(bo->event_queue,
@@ -168,7 +169,6 @@ int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo, bool interruptible)
                return 0;
        }
 }
-EXPORT_SYMBOL(ttm_bo_wait_unreserved);
 
 void ttm_bo_add_to_lru(struct ttm_buffer_object *bo)
 {
@@ -213,14 +213,13 @@ int ttm_bo_del_from_lru(struct ttm_buffer_object *bo)
        return put_count;
 }
 
-int ttm_bo_reserve_locked(struct ttm_buffer_object *bo,
+int ttm_bo_reserve_nolru(struct ttm_buffer_object *bo,
                          bool interruptible,
                          bool no_wait, bool use_sequence, uint32_t sequence)
 {
-       struct ttm_bo_global *glob = bo->glob;
        int ret;
 
-       while (unlikely(atomic_read(&bo->reserved) != 0)) {
+       while (unlikely(atomic_xchg(&bo->reserved, 1) != 0)) {
                /**
                 * Deadlock avoidance for multi-bo reserving.
                 */
@@ -241,26 +240,36 @@ int ttm_bo_reserve_locked(struct ttm_buffer_object *bo,
                if (no_wait)
                        return -EBUSY;
 
-               spin_unlock(&glob->lru_lock);
                ret = ttm_bo_wait_unreserved(bo, interruptible);
-               spin_lock(&glob->lru_lock);
 
                if (unlikely(ret))
                        return ret;
        }
 
-       atomic_set(&bo->reserved, 1);
        if (use_sequence) {
+               bool wake_up = false;
                /**
                 * Wake up waiters that may need to recheck for deadlock,
                 * if we decreased the sequence number.
                 */
                if (unlikely((bo->val_seq - sequence < (1 << 31))
                             || !bo->seq_valid))
-                       wake_up_all(&bo->event_queue);
+                       wake_up = true;
 
+               /*
+                * In the worst case with memory ordering these values can be
+                * seen in the wrong order. However since we call wake_up_all
+                * in that case, this will hopefully not pose a problem,
+                * and the worst case would only cause someone to accidentally
+                * hit -EAGAIN in ttm_bo_reserve when they see old value of
+                * val_seq. However this would only happen if seq_valid was
+                * written before val_seq was, and just means some slightly
+                * increased cpu usage
+                */
                bo->val_seq = sequence;
                bo->seq_valid = true;
+               if (wake_up)
+                       wake_up_all(&bo->event_queue);
        } else {
                bo->seq_valid = false;
        }
@@ -289,17 +298,64 @@ int ttm_bo_reserve(struct ttm_buffer_object *bo,
        int put_count = 0;
        int ret;
 
-       spin_lock(&glob->lru_lock);
-       ret = ttm_bo_reserve_locked(bo, interruptible, no_wait, use_sequence,
-                                   sequence);
-       if (likely(ret == 0))
+       ret = ttm_bo_reserve_nolru(bo, interruptible, no_wait, use_sequence,
+                                  sequence);
+       if (likely(ret == 0)) {
+               spin_lock(&glob->lru_lock);
                put_count = ttm_bo_del_from_lru(bo);
-       spin_unlock(&glob->lru_lock);
+               spin_unlock(&glob->lru_lock);
+               ttm_bo_list_ref_sub(bo, put_count, true);
+       }
 
-       ttm_bo_list_ref_sub(bo, put_count, true);
+       return ret;
+}
+
+int ttm_bo_reserve_slowpath_nolru(struct ttm_buffer_object *bo,
+                                 bool interruptible, uint32_t sequence)
+{
+       bool wake_up = false;
+       int ret;
+
+       while (unlikely(atomic_xchg(&bo->reserved, 1) != 0)) {
+               WARN_ON(bo->seq_valid && sequence == bo->val_seq);
+
+               ret = ttm_bo_wait_unreserved(bo, interruptible);
 
+               if (unlikely(ret))
+                       return ret;
+       }
+
+       if ((bo->val_seq - sequence < (1 << 31)) || !bo->seq_valid)
+               wake_up = true;
+
+       /**
+        * Wake up waiters that may need to recheck for deadlock,
+        * if we decreased the sequence number.
+        */
+       bo->val_seq = sequence;
+       bo->seq_valid = true;
+       if (wake_up)
+               wake_up_all(&bo->event_queue);
+
+       return 0;
+}
+
+int ttm_bo_reserve_slowpath(struct ttm_buffer_object *bo,
+                           bool interruptible, uint32_t sequence)
+{
+       struct ttm_bo_global *glob = bo->glob;
+       int put_count, ret;
+
+       ret = ttm_bo_reserve_slowpath_nolru(bo, interruptible, sequence);
+       if (likely(!ret)) {
+               spin_lock(&glob->lru_lock);
+               put_count = ttm_bo_del_from_lru(bo);
+               spin_unlock(&glob->lru_lock);
+               ttm_bo_list_ref_sub(bo, put_count, true);
+       }
        return ret;
 }
+EXPORT_SYMBOL(ttm_bo_reserve_slowpath);
 
 void ttm_bo_unreserve_locked(struct ttm_buffer_object *bo)
 {
@@ -511,7 +567,7 @@ static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo)
        int ret;
 
        spin_lock(&glob->lru_lock);
-       ret = ttm_bo_reserve_locked(bo, false, true, false, 0);
+       ret = ttm_bo_reserve_nolru(bo, false, true, false, 0);
 
        spin_lock(&bdev->fence_lock);
        (void) ttm_bo_wait(bo, false, false, true);
@@ -604,7 +660,7 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo,
                        return ret;
 
                spin_lock(&glob->lru_lock);
-               ret = ttm_bo_reserve_locked(bo, false, true, false, 0);
+               ret = ttm_bo_reserve_nolru(bo, false, true, false, 0);
 
                /*
                 * We raced, and lost, someone else holds the reservation now,
@@ -668,7 +724,14 @@ static int ttm_bo_delayed_delete(struct ttm_bo_device *bdev, bool remove_all)
                        kref_get(&nentry->list_kref);
                }
 
-               ret = ttm_bo_reserve_locked(entry, false, !remove_all, false, 0);
+               ret = ttm_bo_reserve_nolru(entry, false, true, false, 0);
+               if (remove_all && ret) {
+                       spin_unlock(&glob->lru_lock);
+                       ret = ttm_bo_reserve_nolru(entry, false, false,
+                                                  false, 0);
+                       spin_lock(&glob->lru_lock);
+               }
+
                if (!ret)
                        ret = ttm_bo_cleanup_refs_and_unlock(entry, false,
                                                             !remove_all);
@@ -816,7 +879,7 @@ static int ttm_mem_evict_first(struct ttm_bo_device *bdev,
 
        spin_lock(&glob->lru_lock);
        list_for_each_entry(bo, &man->lru, lru) {
-               ret = ttm_bo_reserve_locked(bo, false, true, false, 0);
+               ret = ttm_bo_reserve_nolru(bo, false, true, false, 0);
                if (!ret)
                        break;
        }
@@ -1797,7 +1860,7 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink)
 
        spin_lock(&glob->lru_lock);
        list_for_each_entry(bo, &glob->swap_lru, swap) {
-               ret = ttm_bo_reserve_locked(bo, false, true, false, 0);
+               ret = ttm_bo_reserve_nolru(bo, false, true, false, 0);
                if (!ret)
                        break;
        }
index cd9e4523dc561efaf0208689a90a4790b4dab986..7b90def15674d8e9b99c11e2011287b3f992a742 100644 (file)
@@ -82,22 +82,6 @@ static void ttm_eu_list_ref_sub(struct list_head *list)
        }
 }
 
-static int ttm_eu_wait_unreserved_locked(struct list_head *list,
-                                        struct ttm_buffer_object *bo)
-{
-       struct ttm_bo_global *glob = bo->glob;
-       int ret;
-
-       ttm_eu_del_from_lru_locked(list);
-       spin_unlock(&glob->lru_lock);
-       ret = ttm_bo_wait_unreserved(bo, true);
-       spin_lock(&glob->lru_lock);
-       if (unlikely(ret != 0))
-               ttm_eu_backoff_reservation_locked(list);
-       return ret;
-}
-
-
 void ttm_eu_backoff_reservation(struct list_head *list)
 {
        struct ttm_validate_buffer *entry;
@@ -145,47 +129,65 @@ int ttm_eu_reserve_buffers(struct list_head *list)
        entry = list_first_entry(list, struct ttm_validate_buffer, head);
        glob = entry->bo->glob;
 
-retry:
        spin_lock(&glob->lru_lock);
        val_seq = entry->bo->bdev->val_seq++;
 
+retry:
        list_for_each_entry(entry, list, head) {
                struct ttm_buffer_object *bo = entry->bo;
 
-retry_this_bo:
-               ret = ttm_bo_reserve_locked(bo, true, true, true, val_seq);
+               /* already slowpath reserved? */
+               if (entry->reserved)
+                       continue;
+
+               ret = ttm_bo_reserve_nolru(bo, true, true, true, val_seq);
                switch (ret) {
                case 0:
                        break;
                case -EBUSY:
-                       ret = ttm_eu_wait_unreserved_locked(list, bo);
-                       if (unlikely(ret != 0)) {
-                               spin_unlock(&glob->lru_lock);
-                               ttm_eu_list_ref_sub(list);
-                               return ret;
-                       }
-                       goto retry_this_bo;
+                       ttm_eu_del_from_lru_locked(list);
+                       spin_unlock(&glob->lru_lock);
+                       ret = ttm_bo_reserve_nolru(bo, true, false,
+                                                  true, val_seq);
+                       spin_lock(&glob->lru_lock);
+                       if (!ret)
+                               break;
+
+                       if (unlikely(ret != -EAGAIN))
+                               goto err;
+
+                       /* fallthrough */
                case -EAGAIN:
                        ttm_eu_backoff_reservation_locked(list);
+
+                       /*
+                        * temporarily increase sequence number every retry,
+                        * to prevent us from seeing our old reservation
+                        * sequence when someone else reserved the buffer,
+                        * but hasn't updated the seq_valid/seqno members yet.
+                        */
+                       val_seq = entry->bo->bdev->val_seq++;
+
                        spin_unlock(&glob->lru_lock);
                        ttm_eu_list_ref_sub(list);
-                       ret = ttm_bo_wait_unreserved(bo, true);
+                       ret = ttm_bo_reserve_slowpath_nolru(bo, true, val_seq);
                        if (unlikely(ret != 0))
                                return ret;
+                       spin_lock(&glob->lru_lock);
+                       entry->reserved = true;
+                       if (unlikely(atomic_read(&bo->cpu_writers) > 0)) {
+                               ret = -EBUSY;
+                               goto err;
+                       }
                        goto retry;
                default:
-                       ttm_eu_backoff_reservation_locked(list);
-                       spin_unlock(&glob->lru_lock);
-                       ttm_eu_list_ref_sub(list);
-                       return ret;
+                       goto err;
                }
 
                entry->reserved = true;
                if (unlikely(atomic_read(&bo->cpu_writers) > 0)) {
-                       ttm_eu_backoff_reservation_locked(list);
-                       spin_unlock(&glob->lru_lock);
-                       ttm_eu_list_ref_sub(list);
-                       return -EBUSY;
+                       ret = -EBUSY;
+                       goto err;
                }
        }
 
@@ -194,6 +196,12 @@ retry_this_bo:
        ttm_eu_list_ref_sub(list);
 
        return 0;
+
+err:
+       ttm_eu_backoff_reservation_locked(list);
+       spin_unlock(&glob->lru_lock);
+       ttm_eu_list_ref_sub(list);
+       return ret;
 }
 EXPORT_SYMBOL(ttm_eu_reserve_buffers);
 
index 87aa5f5d3c88657b8c55055608a65baa271c4c94..cc6d90f28c71a7c2958cbd652e26552f3a23923a 100644 (file)
@@ -75,6 +75,8 @@ struct udl_framebuffer {
        struct drm_framebuffer base;
        struct udl_gem_object *obj;
        bool active_16; /* active on the 16-bit channel */
+       int x1, y1, x2, y2; /* dirty rect */
+       spinlock_t dirty_lock;
 };
 
 #define to_udl_fb(x) container_of(x, struct udl_framebuffer, base)
index d4ab3beaada027825d6182a1330b79bd75d220d4..9f4be3d4a02e5df3396f2b5bef8b5a7fc8c6c22f 100644 (file)
@@ -22,9 +22,9 @@
 
 #include <drm/drm_fb_helper.h>
 
-#define DL_DEFIO_WRITE_DELAY    5 /* fb_deferred_io.delay in jiffies */
+#define DL_DEFIO_WRITE_DELAY    (HZ/20) /* fb_deferred_io.delay in jiffies */
 
-static int fb_defio = 1;  /* Optionally enable experimental fb_defio mmap support */
+static int fb_defio = 0;  /* Optionally enable experimental fb_defio mmap support */
 static int fb_bpp = 16;
 
 module_param(fb_bpp, int, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
@@ -153,6 +153,9 @@ int udl_handle_damage(struct udl_framebuffer *fb, int x, int y,
        struct urb *urb;
        int aligned_x;
        int bpp = (fb->base.bits_per_pixel / 8);
+       int x2, y2;
+       bool store_for_later = false;
+       unsigned long flags;
 
        if (!fb->active_16)
                return 0;
@@ -169,8 +172,6 @@ int udl_handle_damage(struct udl_framebuffer *fb, int x, int y,
                }
        }
 
-       start_cycles = get_cycles();
-
        aligned_x = DL_ALIGN_DOWN(x, sizeof(unsigned long));
        width = DL_ALIGN_UP(width + (x-aligned_x), sizeof(unsigned long));
        x = aligned_x;
@@ -180,19 +181,53 @@ int udl_handle_damage(struct udl_framebuffer *fb, int x, int y,
            (y + height > fb->base.height))
                return -EINVAL;
 
+       /* if we are in atomic just store the info
+          can't test inside spin lock */
+       if (in_atomic())
+               store_for_later = true;
+
+       x2 = x + width - 1;
+       y2 = y + height - 1;
+
+       spin_lock_irqsave(&fb->dirty_lock, flags);
+
+       if (fb->y1 < y)
+               y = fb->y1;
+       if (fb->y2 > y2)
+               y2 = fb->y2;
+       if (fb->x1 < x)
+               x = fb->x1;
+       if (fb->x2 > x2)
+               x2 = fb->x2;
+
+       if (store_for_later) {
+               fb->x1 = x;
+               fb->x2 = x2;
+               fb->y1 = y;
+               fb->y2 = y2;
+               spin_unlock_irqrestore(&fb->dirty_lock, flags);
+               return 0;
+       }
+
+       fb->x1 = fb->y1 = INT_MAX;
+       fb->x2 = fb->y2 = 0;
+
+       spin_unlock_irqrestore(&fb->dirty_lock, flags);
+       start_cycles = get_cycles();
+
        urb = udl_get_urb(dev);
        if (!urb)
                return 0;
        cmd = urb->transfer_buffer;
 
-       for (i = y; i < y + height ; i++) {
+       for (i = y; i <= y2 ; i++) {
                const int line_offset = fb->base.pitches[0] * i;
                const int byte_offset = line_offset + (x * bpp);
                const int dev_byte_offset = (fb->base.width * bpp * i) + (x * bpp);
                if (udl_render_hline(dev, bpp, &urb,
                                     (char *) fb->obj->vmapping,
                                     &cmd, byte_offset, dev_byte_offset,
-                                    width * bpp,
+                                    (x2 - x + 1) * bpp,
                                     &bytes_identical, &bytes_sent))
                        goto error;
        }
@@ -422,7 +457,6 @@ static void udl_user_framebuffer_destroy(struct drm_framebuffer *fb)
 static const struct drm_framebuffer_funcs udlfb_funcs = {
        .destroy = udl_user_framebuffer_destroy,
        .dirty = udl_user_framebuffer_dirty,
-       .create_handle = NULL,
 };
 
 
@@ -434,16 +468,18 @@ udl_framebuffer_init(struct drm_device *dev,
 {
        int ret;
 
+       spin_lock_init(&ufb->dirty_lock);
        ufb->obj = obj;
-       ret = drm_framebuffer_init(dev, &ufb->base, &udlfb_funcs);
        drm_helper_mode_fill_fb_struct(&ufb->base, mode_cmd);
+       ret = drm_framebuffer_init(dev, &ufb->base, &udlfb_funcs);
        return ret;
 }
 
 
-static int udlfb_create(struct udl_fbdev *ufbdev,
+static int udlfb_create(struct drm_fb_helper *helper,
                        struct drm_fb_helper_surface_size *sizes)
 {
+       struct udl_fbdev *ufbdev = (struct udl_fbdev *)helper;
        struct drm_device *dev = ufbdev->helper.dev;
        struct fb_info *info;
        struct device *device = &dev->usbdev->dev;
@@ -521,27 +557,10 @@ out:
        return ret;
 }
 
-static int udl_fb_find_or_create_single(struct drm_fb_helper *helper,
-                                       struct drm_fb_helper_surface_size *sizes)
-{
-       struct udl_fbdev *ufbdev = (struct udl_fbdev *)helper;
-       int new_fb = 0;
-       int ret;
-
-       if (!helper->fb) {
-               ret = udlfb_create(ufbdev, sizes);
-               if (ret)
-                       return ret;
-
-               new_fb = 1;
-       }
-       return new_fb;
-}
-
 static struct drm_fb_helper_funcs udl_fb_helper_funcs = {
        .gamma_set = udl_crtc_fb_gamma_set,
        .gamma_get = udl_crtc_fb_gamma_get,
-       .fb_probe = udl_fb_find_or_create_single,
+       .fb_probe = udlfb_create,
 };
 
 static void udl_fbdev_destroy(struct drm_device *dev,
@@ -556,6 +575,7 @@ static void udl_fbdev_destroy(struct drm_device *dev,
                framebuffer_release(info);
        }
        drm_fb_helper_fini(&ufbdev->helper);
+       drm_framebuffer_unregister_private(&ufbdev->ufb.base);
        drm_framebuffer_cleanup(&ufbdev->ufb.base);
        drm_gem_object_unreference_unlocked(&ufbdev->ufb.obj->base);
 }
@@ -583,6 +603,10 @@ int udl_fbdev_init(struct drm_device *dev)
        }
 
        drm_fb_helper_single_add_all_connectors(&ufbdev->helper);
+
+       /* disable all the possible outputs/crtcs before entering KMS mode */
+       drm_helper_disable_unused_functions(dev);
+
        drm_fb_helper_initial_config(&ufbdev->helper, bpp_sel);
        return 0;
 }
index 142fee5f983f9aa36c8fff99b3ddaf4d4eba78cd..f343db73e0952ae453e25882b5a49509db788172 100644 (file)
@@ -75,15 +75,19 @@ static int udl_trim_hline(const u8 *bback, const u8 **bfront, int *width_bytes)
 }
 #endif
 
-static inline u16 pixel32_to_be16p(const uint8_t *pixel)
+static inline u16 pixel32_to_be16(const uint32_t pixel)
 {
-       uint32_t pix = *(uint32_t *)pixel;
-       u16 retval;
+       return (((pixel >> 3) & 0x001f) |
+               ((pixel >> 5) & 0x07e0) |
+               ((pixel >> 8) & 0xf800));
+}
 
-       retval =  (((pix >> 3) & 0x001f) |
-                  ((pix >> 5) & 0x07e0) |
-                  ((pix >> 8) & 0xf800));
-       return retval;
+static bool pixel_repeats(const void *pixel, const uint32_t repeat, int bpp)
+{
+       if (bpp == 2)
+               return *(const uint16_t *)pixel == repeat;
+       else
+               return *(const uint32_t *)pixel == repeat;
 }
 
 /*
@@ -152,29 +156,33 @@ static void udl_compress_hline16(
                prefetch_range((void *) pixel, (cmd_pixel_end - pixel) * bpp);
 
                while (pixel < cmd_pixel_end) {
-                       const u8 * const repeating_pixel = pixel;
-
-                       if (bpp == 2)
-                               *(uint16_t *)cmd = cpu_to_be16p((uint16_t *)pixel);
-                       else if (bpp == 4)
-                               *(uint16_t *)cmd = cpu_to_be16(pixel32_to_be16p(pixel));
+                       const u8 *const start = pixel;
+                       u32 repeating_pixel;
+
+                       if (bpp == 2) {
+                               repeating_pixel = *(uint16_t *)pixel;
+                               *(uint16_t *)cmd = cpu_to_be16(repeating_pixel);
+                       } else {
+                               repeating_pixel = *(uint32_t *)pixel;
+                               *(uint16_t *)cmd = cpu_to_be16(pixel32_to_be16(repeating_pixel));
+                       }
 
                        cmd += 2;
                        pixel += bpp;
 
                        if (unlikely((pixel < cmd_pixel_end) &&
-                                    (!memcmp(pixel, repeating_pixel, bpp)))) {
+                                    (pixel_repeats(pixel, repeating_pixel, bpp)))) {
                                /* go back and fill in raw pixel count */
-                               *raw_pixels_count_byte = (((repeating_pixel -
+                               *raw_pixels_count_byte = (((start -
                                                raw_pixel_start) / bpp) + 1) & 0xFF;
 
-                               while ((pixel < cmd_pixel_end)
-                                      && (!memcmp(pixel, repeating_pixel, bpp))) {
+                               while ((pixel < cmd_pixel_end) &&
+                                      (pixel_repeats(pixel, repeating_pixel, bpp))) {
                                        pixel += bpp;
                                }
 
                                /* immediately after raw data is repeat byte */
-                               *cmd++ = (((pixel - repeating_pixel) / bpp) - 1) & 0xFF;
+                               *cmd++ = (((pixel - start) / bpp) - 1) & 0xFF;
 
                                /* Then start another raw pixel span */
                                raw_pixel_start = pixel;
@@ -223,6 +231,8 @@ int udl_render_hline(struct drm_device *dev, int bpp, struct urb **urb_ptr,
        u8 *cmd = *urb_buf_ptr;
        u8 *cmd_end = (u8 *) urb->transfer_buffer + urb->transfer_buffer_length;
 
+       BUG_ON(!(bpp == 2 || bpp == 4));
+
        line_start = (u8 *) (front + byte_offset);
        next_pixel = line_start;
        line_end = next_pixel + byte_width;
index 161f8b2549aaf89e2050b4f5086b63888eebe28b..07dfd823cc304987bc41056d705bbd6253216013 100644 (file)
@@ -829,7 +829,7 @@ static void vmw_lastclose(struct drm_device *dev)
 
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
                set.crtc = crtc;
-               ret = crtc->funcs->set_config(&set);
+               ret = drm_mode_set_config_internal(&set);
                WARN_ON(ret != 0);
        }
 
index d9fbbe1910710f748fb45bac0f8649d50083b70f..c509d40c4897ad9e1bdf8fd37d6618c9ccb44be0 100644 (file)
@@ -131,7 +131,7 @@ int vmw_present_ioctl(struct drm_device *dev, void *data,
        struct vmw_master *vmaster = vmw_master(file_priv->master);
        struct drm_vmw_rect __user *clips_ptr;
        struct drm_vmw_rect *clips = NULL;
-       struct drm_mode_object *obj;
+       struct drm_framebuffer *fb;
        struct vmw_framebuffer *vfb;
        struct vmw_resource *res;
        uint32_t num_clips;
@@ -163,19 +163,15 @@ int vmw_present_ioctl(struct drm_device *dev, void *data,
                goto out_no_copy;
        }
 
-       ret = mutex_lock_interruptible(&dev->mode_config.mutex);
-       if (unlikely(ret != 0)) {
-               ret = -ERESTARTSYS;
-               goto out_no_mode_mutex;
-       }
+       drm_modeset_lock_all(dev);
 
-       obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB);
-       if (!obj) {
+       fb = drm_framebuffer_lookup(dev, arg->fb_id);
+       if (!fb) {
                DRM_ERROR("Invalid framebuffer id.\n");
                ret = -EINVAL;
                goto out_no_fb;
        }
-       vfb = vmw_framebuffer_to_vfb(obj_to_fb(obj));
+       vfb = vmw_framebuffer_to_vfb(fb);
 
        ret = ttm_read_lock(&vmaster->lock, true);
        if (unlikely(ret != 0))
@@ -199,9 +195,9 @@ int vmw_present_ioctl(struct drm_device *dev, void *data,
 out_no_surface:
        ttm_read_unlock(&vmaster->lock);
 out_no_ttm_lock:
+       drm_framebuffer_unreference(fb);
 out_no_fb:
-       mutex_unlock(&dev->mode_config.mutex);
-out_no_mode_mutex:
+       drm_modeset_unlock_all(dev);
 out_no_copy:
        kfree(clips);
 out_clips:
@@ -220,7 +216,7 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data,
        struct vmw_master *vmaster = vmw_master(file_priv->master);
        struct drm_vmw_rect __user *clips_ptr;
        struct drm_vmw_rect *clips = NULL;
-       struct drm_mode_object *obj;
+       struct drm_framebuffer *fb;
        struct vmw_framebuffer *vfb;
        uint32_t num_clips;
        int ret;
@@ -251,24 +247,20 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data,
                goto out_no_copy;
        }
 
-       ret = mutex_lock_interruptible(&dev->mode_config.mutex);
-       if (unlikely(ret != 0)) {
-               ret = -ERESTARTSYS;
-               goto out_no_mode_mutex;
-       }
+       drm_modeset_lock_all(dev);
 
-       obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB);
-       if (!obj) {
+       fb = drm_framebuffer_lookup(dev, arg->fb_id);
+       if (!fb) {
                DRM_ERROR("Invalid framebuffer id.\n");
                ret = -EINVAL;
                goto out_no_fb;
        }
 
-       vfb = vmw_framebuffer_to_vfb(obj_to_fb(obj));
+       vfb = vmw_framebuffer_to_vfb(fb);
        if (!vfb->dmabuf) {
                DRM_ERROR("Framebuffer not dmabuf backed.\n");
                ret = -EINVAL;
-               goto out_no_fb;
+               goto out_no_ttm_lock;
        }
 
        ret = ttm_read_lock(&vmaster->lock, true);
@@ -281,9 +273,9 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data,
 
        ttm_read_unlock(&vmaster->lock);
 out_no_ttm_lock:
+       drm_framebuffer_unreference(fb);
 out_no_fb:
-       mutex_unlock(&dev->mode_config.mutex);
-out_no_mode_mutex:
+       drm_modeset_unlock_all(dev);
 out_no_copy:
        kfree(clips);
 out_clips:
index 54743943d8b328320de41e231763b3878c4a4744..3e3c7ab33ca252008713e1050b297592d784e354 100644 (file)
@@ -180,16 +180,29 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
        struct vmw_dma_buffer *dmabuf = NULL;
        int ret;
 
+       /*
+        * FIXME: Unclear whether there's any global state touched by the
+        * cursor_set function, especially vmw_cursor_update_position looks
+        * suspicious. For now take the easy route and reacquire all locks. We
+        * can do this since the caller in the drm core doesn't check anything
+        * which is protected by any looks.
+        */
+       mutex_unlock(&crtc->mutex);
+       drm_modeset_lock_all(dev_priv->dev);
+
        /* A lot of the code assumes this */
-       if (handle && (width != 64 || height != 64))
-               return -EINVAL;
+       if (handle && (width != 64 || height != 64)) {
+               ret = -EINVAL;
+               goto out;
+       }
 
        if (handle) {
                ret = vmw_user_lookup_handle(dev_priv, tfile,
                                             handle, &surface, &dmabuf);
                if (ret) {
                        DRM_ERROR("failed to find surface or dmabuf: %i\n", ret);
-                       return -EINVAL;
+                       ret = -EINVAL;
+                       goto out;
                }
        }
 
@@ -197,7 +210,8 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
        if (surface && !surface->snooper.image) {
                DRM_ERROR("surface not suitable for cursor\n");
                vmw_surface_unreference(&surface);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
 
        /* takedown old cursor */
@@ -225,14 +239,20 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
                                               du->hotspot_x, du->hotspot_y);
        } else {
                vmw_cursor_update_position(dev_priv, false, 0, 0);
-               return 0;
+               ret = 0;
+               goto out;
        }
 
        vmw_cursor_update_position(dev_priv, true,
                                   du->cursor_x + du->hotspot_x,
                                   du->cursor_y + du->hotspot_y);
 
-       return 0;
+       ret = 0;
+out:
+       drm_modeset_unlock_all(dev_priv->dev);
+       mutex_lock(&crtc->mutex);
+
+       return ret;
 }
 
 int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
@@ -244,10 +264,23 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
        du->cursor_x = x + crtc->x;
        du->cursor_y = y + crtc->y;
 
+       /*
+        * FIXME: Unclear whether there's any global state touched by the
+        * cursor_set function, especially vmw_cursor_update_position looks
+        * suspicious. For now take the easy route and reacquire all locks. We
+        * can do this since the caller in the drm core doesn't check anything
+        * which is protected by any looks.
+        */
+       mutex_unlock(&crtc->mutex);
+       drm_modeset_lock_all(dev_priv->dev);
+
        vmw_cursor_update_position(dev_priv, shown,
                                   du->cursor_x + du->hotspot_x,
                                   du->cursor_y + du->hotspot_y);
 
+       drm_modeset_unlock_all(dev_priv->dev);
+       mutex_lock(&crtc->mutex);
+
        return 0;
 }
 
@@ -373,16 +406,6 @@ void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv)
  * Generic framebuffer code
  */
 
-int vmw_framebuffer_create_handle(struct drm_framebuffer *fb,
-                                 struct drm_file *file_priv,
-                                 unsigned int *handle)
-{
-       if (handle)
-               *handle = 0;
-
-       return 0;
-}
-
 /*
  * Surface framebuffer code
  */
@@ -610,7 +633,6 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
 static struct drm_framebuffer_funcs vmw_framebuffer_surface_funcs = {
        .destroy = vmw_framebuffer_surface_destroy,
        .dirty = vmw_framebuffer_surface_dirty,
-       .create_handle = vmw_framebuffer_create_handle,
 };
 
 static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,
@@ -681,14 +703,10 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,
                goto out_err1;
        }
 
-       ret = drm_framebuffer_init(dev, &vfbs->base.base,
-                                  &vmw_framebuffer_surface_funcs);
-       if (ret)
-               goto out_err2;
-
        if (!vmw_surface_reference(surface)) {
                DRM_ERROR("failed to reference surface %p\n", surface);
-               goto out_err3;
+               ret = -EINVAL;
+               goto out_err2;
        }
 
        /* XXX get the first 3 from the surface info */
@@ -707,10 +725,15 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,
 
        *out = &vfbs->base;
 
+       ret = drm_framebuffer_init(dev, &vfbs->base.base,
+                                  &vmw_framebuffer_surface_funcs);
+       if (ret)
+               goto out_err3;
+
        return 0;
 
 out_err3:
-       drm_framebuffer_cleanup(&vfbs->base.base);
+       vmw_surface_unreference(&surface);
 out_err2:
        kfree(vfbs);
 out_err1:
@@ -960,7 +983,6 @@ int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer,
 static struct drm_framebuffer_funcs vmw_framebuffer_dmabuf_funcs = {
        .destroy = vmw_framebuffer_dmabuf_destroy,
        .dirty = vmw_framebuffer_dmabuf_dirty,
-       .create_handle = vmw_framebuffer_create_handle,
 };
 
 /**
@@ -1053,14 +1075,10 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv,
                goto out_err1;
        }
 
-       ret = drm_framebuffer_init(dev, &vfbd->base.base,
-                                  &vmw_framebuffer_dmabuf_funcs);
-       if (ret)
-               goto out_err2;
-
        if (!vmw_dmabuf_reference(dmabuf)) {
                DRM_ERROR("failed to reference dmabuf %p\n", dmabuf);
-               goto out_err3;
+               ret = -EINVAL;
+               goto out_err2;
        }
 
        vfbd->base.base.bits_per_pixel = mode_cmd->bpp;
@@ -1077,10 +1095,15 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv,
        vfbd->base.user_handle = mode_cmd->handle;
        *out = &vfbd->base;
 
+       ret = drm_framebuffer_init(dev, &vfbd->base.base,
+                                  &vmw_framebuffer_dmabuf_funcs);
+       if (ret)
+               goto out_err3;
+
        return 0;
 
 out_err3:
-       drm_framebuffer_cleanup(&vfbd->base.base);
+       vmw_dmabuf_unreference(&dmabuf);
 out_err2:
        kfree(vfbd);
 out_err1:
index e01a17b407b2e642e2f7b5f3596f4b2e8af00755..16556170fb32e663cf7e80f004488206571fba0e 100644 (file)
@@ -959,13 +959,13 @@ void vmw_resource_unreserve(struct vmw_resource *res,
        if (new_backup && new_backup != res->backup) {
 
                if (res->backup) {
-                       BUG_ON(atomic_read(&res->backup->base.reserved) == 0);
+                       BUG_ON(!ttm_bo_is_reserved(&res->backup->base));
                        list_del_init(&res->mob_head);
                        vmw_dmabuf_unreference(&res->backup);
                }
 
                res->backup = vmw_dmabuf_reference(new_backup);
-               BUG_ON(atomic_read(&new_backup->base.reserved) == 0);
+               BUG_ON(!ttm_bo_is_reserved(&new_backup->base));
                list_add_tail(&res->mob_head, &new_backup->res_list);
        }
        if (new_backup)
diff --git a/drivers/gpu/stub/Kconfig b/drivers/gpu/stub/Kconfig
deleted file mode 100644 (file)
index 4199179..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-config STUB_POULSBO
-       tristate "Intel GMA500 Stub Driver"
-       depends on PCI
-       depends on NET # for THERMAL
-       # Poulsbo stub depends on ACPI_VIDEO when ACPI is enabled
-       # but for select to work, need to select ACPI_VIDEO's dependencies, ick
-       select BACKLIGHT_CLASS_DEVICE if ACPI
-       select VIDEO_OUTPUT_CONTROL if ACPI
-       select INPUT if ACPI
-       select ACPI_VIDEO if ACPI
-       select THERMAL if ACPI
-       help
-         Choose this option if you have a system that has Intel GMA500
-         (Poulsbo) integrated graphics. If M is selected, the module will
-         be called Poulsbo. This driver is a stub driver for Poulsbo that
-         will call poulsbo.ko to enable the acpi backlight control sysfs
-         entry file because there have no poulsbo native driver can support
-         intel opregion.
diff --git a/drivers/gpu/stub/Makefile b/drivers/gpu/stub/Makefile
deleted file mode 100644 (file)
index cd940cc..0000000
+++ /dev/null
@@ -1 +0,0 @@
-obj-$(CONFIG_STUB_POULSBO) += poulsbo.o
diff --git a/drivers/gpu/stub/poulsbo.c b/drivers/gpu/stub/poulsbo.c
deleted file mode 100644 (file)
index 7edfd27..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Intel Poulsbo Stub driver
- *
- * Copyright (C) 2010 Novell <jlee@novell.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/pci.h>
-#include <linux/acpi.h>
-#include <acpi/video.h>
-
-#define DRIVER_NAME "poulsbo"
-
-enum {
-       CHIP_PSB_8108 = 0,
-       CHIP_PSB_8109 = 1,
-};
-
-static DEFINE_PCI_DEVICE_TABLE(pciidlist) = {
-       {0x8086, 0x8108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PSB_8108}, \
-       {0x8086, 0x8109, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PSB_8109}, \
-       {0, 0, 0}
-};
-
-static int poulsbo_probe(struct pci_dev *pdev, const struct pci_device_id *id)
-{
-       return acpi_video_register();
-}
-
-static void poulsbo_remove(struct pci_dev *pdev)
-{
-       acpi_video_unregister();
-}
-
-static struct pci_driver poulsbo_driver = {
-       .name = DRIVER_NAME,
-       .id_table = pciidlist,
-       .probe = poulsbo_probe,
-       .remove = poulsbo_remove,
-};
-
-static int __init poulsbo_init(void)
-{
-       return pci_register_driver(&poulsbo_driver);
-}
-
-static void __exit poulsbo_exit(void)
-{
-       pci_unregister_driver(&poulsbo_driver);
-}
-
-module_init(poulsbo_init);
-module_exit(poulsbo_exit);
-
-MODULE_AUTHOR("Lee, Chun-Yi <jlee@novell.com>");
-MODULE_DESCRIPTION("Poulsbo Stub Driver");
-MODULE_LICENSE("GPL");
-
-MODULE_DEVICE_TABLE(pci, pciidlist);
index fa60add0ff63549d988215853f11af7cff9c6c43..cf787e1d93227f5d84e5af477b9f1a49f418c23c 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/fb.h>
 
 #include <linux/pci.h>
+#include <linux/console.h>
 #include <linux/vga_switcheroo.h>
 
 #include <linux/vgaarb.h>
@@ -337,8 +338,10 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client)
 
        if (new_client->fb_info) {
                struct fb_event event;
+               console_lock();
                event.info = new_client->fb_info;
                fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, &event);
+               console_unlock();
        }
 
        ret = vgasr_priv.handler->switchto(new_client->id);
index 4dfa605e2d14417203e734c4a68dc2447e86f5c2..34e25471aeaaaa547f1e4ea1a1879426d7350444 100644 (file)
 #define USB_VENDOR_ID_EZKEY            0x0518
 #define USB_DEVICE_ID_BTC_8193         0x0002
 
+#define USB_VENDOR_ID_FORMOSA          0x147a
+#define USB_DEVICE_ID_FORMOSA_IR_RECEIVER      0xe03e
+
 #define USB_VENDOR_ID_FREESCALE                0x15A2
 #define USB_DEVICE_ID_FREESCALE_MX28   0x004F
 
index 12e4fdc810bf22c0c3334cb1939b6de1e73ee2d3..e766b5614ef59fcae45aaeff49a98df9de3e9693 100644 (file)
@@ -540,13 +540,24 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
 {
        struct i2c_client *client = hid->driver_data;
        int report_id = buf[0];
+       int ret;
 
        if (report_type == HID_INPUT_REPORT)
                return -EINVAL;
 
-       return i2c_hid_set_report(client,
+       if (report_id) {
+               buf++;
+               count--;
+       }
+
+       ret = i2c_hid_set_report(client,
                                report_type == HID_FEATURE_REPORT ? 0x03 : 0x02,
                                report_id, buf, count);
+
+       if (report_id && ret >= 0)
+               ret++; /* add report_id to the number of transfered bytes */
+
+       return ret;
 }
 
 static int i2c_hid_parse(struct hid_device *hid)
index ac9e35228254f0e6a942845520bb542752ca0921..e0e6abf1cd3bd9b3cfa3d7cc73a2247c84c31be4 100644 (file)
@@ -70,6 +70,7 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
+       { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
index f6c0011a0337e53ff783837a0970a2002675ddb5..dd289fd179ca8e3953b3d8f2d0b65c94fb5b878e 100644 (file)
@@ -403,7 +403,7 @@ struct dm_info_header {
  */
 
 struct dm_info_msg {
-       struct dm_info_header header;
+       struct dm_header hdr;
        __u32 reserved;
        __u32 info_size;
        __u8  info[];
@@ -503,13 +503,17 @@ static void hot_add_req(struct hv_dynmem_device *dm, struct dm_hot_add *msg)
 
 static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg)
 {
-       switch (msg->header.type) {
+       struct dm_info_header *info_hdr;
+
+       info_hdr = (struct dm_info_header *)msg->info;
+
+       switch (info_hdr->type) {
        case INFO_TYPE_MAX_PAGE_CNT:
                pr_info("Received INFO_TYPE_MAX_PAGE_CNT\n");
-               pr_info("Data Size is %d\n", msg->header.data_size);
+               pr_info("Data Size is %d\n", info_hdr->data_size);
                break;
        default:
-               pr_info("Received Unknown type: %d\n", msg->header.type);
+               pr_info("Received Unknown type: %d\n", info_hdr->type);
        }
 }
 
@@ -879,7 +883,7 @@ static int balloon_probe(struct hv_device *dev,
                        balloon_onchannelcallback, dev);
 
        if (ret)
-               return ret;
+               goto probe_error0;
 
        dm_device.dev = dev;
        dm_device.state = DM_INITIALIZING;
@@ -891,7 +895,7 @@ static int balloon_probe(struct hv_device *dev,
                 kthread_run(dm_thread_func, &dm_device, "hv_balloon");
        if (IS_ERR(dm_device.thread)) {
                ret = PTR_ERR(dm_device.thread);
-               goto probe_error0;
+               goto probe_error1;
        }
 
        hv_set_drvdata(dev, &dm_device);
@@ -914,12 +918,12 @@ static int balloon_probe(struct hv_device *dev,
                                VM_PKT_DATA_INBAND,
                                VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
        if (ret)
-               goto probe_error1;
+               goto probe_error2;
 
        t = wait_for_completion_timeout(&dm_device.host_event, 5*HZ);
        if (t == 0) {
                ret = -ETIMEDOUT;
-               goto probe_error1;
+               goto probe_error2;
        }
 
        /*
@@ -928,7 +932,7 @@ static int balloon_probe(struct hv_device *dev,
         */
        if (dm_device.state == DM_INIT_ERROR) {
                ret = -ETIMEDOUT;
-               goto probe_error1;
+               goto probe_error2;
        }
        /*
         * Now submit our capabilities to the host.
@@ -961,12 +965,12 @@ static int balloon_probe(struct hv_device *dev,
                                VM_PKT_DATA_INBAND,
                                VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
        if (ret)
-               goto probe_error1;
+               goto probe_error2;
 
        t = wait_for_completion_timeout(&dm_device.host_event, 5*HZ);
        if (t == 0) {
                ret = -ETIMEDOUT;
-               goto probe_error1;
+               goto probe_error2;
        }
 
        /*
@@ -975,18 +979,20 @@ static int balloon_probe(struct hv_device *dev,
         */
        if (dm_device.state == DM_INIT_ERROR) {
                ret = -ETIMEDOUT;
-               goto probe_error1;
+               goto probe_error2;
        }
 
        dm_device.state = DM_INITIALIZED;
 
        return 0;
 
-probe_error1:
+probe_error2:
        kthread_stop(dm_device.thread);
 
-probe_error0:
+probe_error1:
        vmbus_close(dev->channel);
+probe_error0:
+       kfree(send_buffer);
        return ret;
 }
 
@@ -999,6 +1005,7 @@ static int balloon_remove(struct hv_device *dev)
 
        vmbus_close(dev->channel);
        kthread_stop(dm->thread);
+       kfree(send_buffer);
 
        return 0;
 }
index 86d7f6d858b1bbe1b61720539995c6cdd1747d1f..d867e6bb2be1f7e1b1ebb41788c27f568bd7c579 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/hwmon.h>
 #include <linux/hwmon-sysfs.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/vexpress.h>
index cbba7db9ad594df3cd10a37e6fa8b16fc456f817..f5258c205de555ec569f5a8c967531577336344c 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/io.h>
 #include <linux/pm_runtime.h>
 #include <linux/delay.h>
+#include <linux/module.h>
 #include "i2c-designware-core.h"
 
 /*
@@ -725,3 +726,6 @@ u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev)
        return dw_readl(dev, DW_IC_COMP_PARAM_1);
 }
 EXPORT_SYMBOL_GPL(i2c_dw_read_comp_param);
+
+MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter core");
+MODULE_LICENSE("GPL");
index 1b1a936eccc9ef11ba1e17cb61d9b7a54b226704..d6abaf2cf2e3535b4c7c4ad58dc9e5e772b56fee 100644 (file)
@@ -127,7 +127,7 @@ struct mxs_i2c_dev {
        struct device *dev;
        void __iomem *regs;
        struct completion cmd_complete;
-       u32 cmd_err;
+       int cmd_err;
        struct i2c_adapter adapter;
        const struct mxs_i2c_speed_config *speed;
 
@@ -316,7 +316,7 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg,
        if (msg->len == 0)
                return -EINVAL;
 
-       init_completion(&i2c->cmd_complete);
+       INIT_COMPLETION(i2c->cmd_complete);
        i2c->cmd_err = 0;
 
        ret = mxs_i2c_dma_setup_xfer(adap, msg, flags);
@@ -473,6 +473,8 @@ static int mxs_i2c_probe(struct platform_device *pdev)
        i2c->dev = dev;
        i2c->speed = &mxs_i2c_95kHz_config;
 
+       init_completion(&i2c->cmd_complete);
+
        if (dev->of_node) {
                err = mxs_i2c_get_ofdata(i2c);
                if (err)
index 20d41bfa7c1989ccc6001730ce11d1117aa3fa13..4cc2f0528c8869af7d96a99e913b84209530d7c4 100644 (file)
@@ -803,7 +803,7 @@ static int errata_omap3_i462(struct omap_i2c_dev *dev)
                        if (stat & OMAP_I2C_STAT_AL) {
                                dev_err(dev->dev, "Arbitration lost\n");
                                dev->cmd_err |= OMAP_I2C_STAT_AL;
-                               omap_i2c_ack_stat(dev, OMAP_I2C_STAT_NACK);
+                               omap_i2c_ack_stat(dev, OMAP_I2C_STAT_AL);
                        }
 
                        return -EIO;
@@ -963,7 +963,7 @@ omap_i2c_isr_thread(int this_irq, void *dev_id)
                                i2c_omap_errata_i207(dev, stat);
 
                        omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RDR);
-                       break;
+                       continue;
                }
 
                if (stat & OMAP_I2C_STAT_RRDY) {
@@ -989,7 +989,7 @@ omap_i2c_isr_thread(int this_irq, void *dev_id)
                                break;
 
                        omap_i2c_ack_stat(dev, OMAP_I2C_STAT_XDR);
-                       break;
+                       continue;
                }
 
                if (stat & OMAP_I2C_STAT_XRDY) {
index 3f1818b87974f14b77e1bce19a5116b1e9f7359e..e03381aee34f526cc8c8c66c67293fffdde97689 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/slab.h>
 #include <linux/platform_device.h>
 #include <linux/i2c.h>
+#include <linux/of_i2c.h>
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/io.h>
@@ -328,6 +329,7 @@ static int i2c_sirfsoc_probe(struct platform_device *pdev)
        adap->algo = &i2c_sirfsoc_algo;
        adap->algo_data = siic;
 
+       adap->dev.of_node = pdev->dev.of_node;
        adap->dev.parent = &pdev->dev;
        adap->nr = pdev->id;
 
@@ -371,6 +373,8 @@ static int i2c_sirfsoc_probe(struct platform_device *pdev)
 
        clk_disable(clk);
 
+       of_i2c_register_devices(adap);
+
        dev_info(&pdev->dev, " I2C adapter ready to operate\n");
 
        return 0;
index 1e44d04d1b22357294d0279b23d1c2e501697556..a43c0ce5e3d8728918afa810842292d00efdac8c 100644 (file)
@@ -167,7 +167,7 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev)
        }
 
        mux->busses = devm_kzalloc(&pdev->dev,
-                                  sizeof(mux->busses) * mux->pdata->bus_count,
+                                  sizeof(*mux->busses) * mux->pdata->bus_count,
                                   GFP_KERNEL);
        if (!mux->busses) {
                dev_err(&pdev->dev, "Cannot allocate busses\n");
index 4ba384f1ab544976621e6dc48c46dd0453687fef..2df9414a72f7169fe1fccaec285c633ca45f75d1 100644 (file)
@@ -448,8 +448,6 @@ static int intel_idle_probe(void)
        else
                on_each_cpu(__setup_broadcast_timer, (void *)true, 1);
 
-       register_cpu_notifier(&cpu_hotplug_notifier);
-
        pr_debug(PREFIX "v" INTEL_IDLE_VERSION
                " model 0x%X\n", boot_cpu_data.x86_model);
 
@@ -612,6 +610,7 @@ static int __init intel_idle_init(void)
                        return retval;
                }
        }
+       register_cpu_notifier(&cpu_hotplug_notifier);
 
        return 0;
 }
index fe4bcd7c5b12ea9ef5e4f23b56c25169038ad570..05e996fafc9d42a8dd76031dedbfd6b1d8520d0a 100644 (file)
@@ -8,6 +8,7 @@ config HID_SENSOR_ACCEL_3D
        select IIO_BUFFER
        select IIO_TRIGGERED_BUFFER
        select HID_SENSOR_IIO_COMMON
+       select HID_SENSOR_IIO_TRIGGER
        tristate "HID Accelerometers 3D"
        help
          Say yes here to build support for the HID SENSOR
index 4a5f639bc68492e6d912db45b5460d050b60cad3..bbad9b94cd75770b136499728497ff73ce4067d9 100644 (file)
@@ -411,7 +411,11 @@ static int ad7266_probe(struct spi_device *spi)
                if (ret)
                        goto error_put_reg;
 
-               st->vref_uv = regulator_get_voltage(st->reg);
+               ret = regulator_get_voltage(st->reg);
+               if (ret < 0)
+                       goto error_disable_reg;
+
+               st->vref_uv = ret;
        } else {
                /* Use internal reference */
                st->vref_uv = 2500000;
index 04b013561f0fc874fe7a2df1b0409b2aa3c9d4df..a526c0e3aaa84be092d25789d3ccaffd0d056204 100644 (file)
@@ -80,7 +80,7 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
                *timestamp = pf->timestamp;
        }
 
-       iio_push_to_buffers(indio_dev, (u8 *)st->buffer);
+       iio_push_to_buffers(idev, (u8 *)st->buffer);
 
        iio_trigger_notify_done(idev->trig);
 
index b5669be6f396f53122b8f704c63a8e7e32e962d8..03b25b3dc71eb95703d2131230e2f11cb2dae03c 100644 (file)
@@ -1605,19 +1605,20 @@ static int max1363_probe(struct i2c_client *client,
 
        return 0;
 error_free_irq:
-       free_irq(st->client->irq, indio_dev);
+       if (client->irq)
+               free_irq(st->client->irq, indio_dev);
 error_uninit_buffer:
        iio_buffer_unregister(indio_dev);
 error_cleanup_buffer:
        max1363_buffer_cleanup(indio_dev);
 error_free_available_scan_masks:
        kfree(indio_dev->available_scan_masks);
-error_unregister_map:
-       iio_map_array_unregister(indio_dev, client->dev.platform_data);
 error_disable_reg:
        regulator_disable(st->reg);
 error_put_reg:
        regulator_put(st->reg);
+error_unregister_map:
+       iio_map_array_unregister(indio_dev, client->dev.platform_data);
 error_free_device:
        iio_device_free(indio_dev);
 error_out:
@@ -1635,10 +1636,8 @@ static int max1363_remove(struct i2c_client *client)
        iio_buffer_unregister(indio_dev);
        max1363_buffer_cleanup(indio_dev);
        kfree(indio_dev->available_scan_masks);
-       if (!IS_ERR(st->reg)) {
-               regulator_disable(st->reg);
-               regulator_put(st->reg);
-       }
+       regulator_disable(st->reg);
+       regulator_put(st->reg);
        iio_map_array_unregister(indio_dev, client->dev.platform_data);
        iio_device_free(indio_dev);
 
index ae10778da7aa1e7b1c11b558a562e998e78cfdcb..1178121b55b032989257e3167c6724815458ec26 100644 (file)
@@ -6,7 +6,7 @@ menu "Hid Sensor IIO Common"
 config HID_SENSOR_IIO_COMMON
        tristate "Common modules for all HID Sensor IIO drivers"
        depends on HID_SENSOR_HUB
-       select IIO_TRIGGER if IIO_BUFFER
+       select HID_SENSOR_IIO_TRIGGER if IIO_BUFFER
        help
          Say yes here to build support for HID sensor to use
          HID sensor common processing for attributes and IIO triggers.
@@ -14,6 +14,17 @@ config HID_SENSOR_IIO_COMMON
          HID sensor drivers, this module contains processing for those
          attributes.
 
+config HID_SENSOR_IIO_TRIGGER
+       tristate "Common module (trigger) for all HID Sensor IIO drivers"
+       depends on HID_SENSOR_HUB && HID_SENSOR_IIO_COMMON
+       select IIO_TRIGGER
+       help
+         Say yes here to build trigger support for HID sensors.
+         Triggers will be send if all requested attributes were read.
+
+         If this driver is compiled as a module, it will be named
+         hid-sensor-trigger.
+
 config HID_SENSOR_ENUM_BASE_QUIRKS
        bool "ENUM base quirks for HID Sensor IIO drivers"
        depends on HID_SENSOR_IIO_COMMON
index 1f463e00c2426b706223827afc7363614d80691f..22e7c5a82325e13c3cfbded83dc41f0de45385ae 100644 (file)
@@ -3,4 +3,5 @@
 #
 
 obj-$(CONFIG_HID_SENSOR_IIO_COMMON) += hid-sensor-iio-common.o
-hid-sensor-iio-common-y := hid-sensor-attributes.o hid-sensor-trigger.o
+obj-$(CONFIG_HID_SENSOR_IIO_TRIGGER) += hid-sensor-trigger.o
+hid-sensor-iio-common-y := hid-sensor-attributes.o
index 6c7898c765d9599cb5a531a709c45d9a6a4944b0..483fc379a2da9ca64a29fb13313b16f3d76d8b43 100644 (file)
@@ -406,7 +406,11 @@ static int ad5380_probe(struct device *dev, struct regmap *regmap,
                        goto error_free_reg;
                }
 
-               st->vref = regulator_get_voltage(st->vref_reg);
+               ret = regulator_get_voltage(st->vref_reg);
+               if (ret < 0)
+                       goto error_disable_reg;
+
+               st->vref = ret;
        } else {
                st->vref = st->chip_info->int_vref;
                ctrl |= AD5380_CTRL_INT_VREF_EN;
index 29f653dab2f7bf7270f086737222cb48d5ed76e4..f5583aedfb597cf927e3e39969b80e5e0184085e 100644 (file)
@@ -226,7 +226,11 @@ static int ad5446_probe(struct device *dev, const char *name,
                if (ret)
                        goto error_put_reg;
 
-               voltage_uv = regulator_get_voltage(reg);
+               ret = regulator_get_voltage(reg);
+               if (ret < 0)
+                       goto error_disable_reg;
+
+               voltage_uv = ret;
        }
 
        indio_dev = iio_device_alloc(sizeof(*st));
index b2a31a0468eddc1d689852126877ce84842bd980..0661829f27737ed4f70e1a29137c6c8a18169457 100644 (file)
@@ -296,7 +296,11 @@ static int ad5504_probe(struct spi_device *spi)
                if (ret)
                        goto error_put_reg;
 
-               voltage_uv = regulator_get_voltage(reg);
+               ret = regulator_get_voltage(reg);
+               if (ret < 0)
+                       goto error_disable_reg;
+
+               voltage_uv = ret;
        }
 
        spi_set_drvdata(spi, indio_dev);
index e9947969f9fefce62047448e09e8ae4203bb5818..f6e116627b714e044569c1ef5e85fff59dfc5c20 100644 (file)
@@ -238,7 +238,11 @@ static int ad5624r_probe(struct spi_device *spi)
                if (ret)
                        goto error_put_reg;
 
-               voltage_uv = regulator_get_voltage(st->reg);
+               ret = regulator_get_voltage(st->reg);
+               if (ret < 0)
+                       goto error_disable_reg;
+
+               voltage_uv = ret;
        }
 
        spi_set_drvdata(spi, indio_dev);
index 36e51382ae528116201f67cb2cbd1d7b1372261a..ca9609d7a15c256a78b058a26c9191542e06a8f3 100644 (file)
@@ -332,7 +332,11 @@ static int ad5686_probe(struct spi_device *spi)
                if (ret)
                        goto error_put_reg;
 
-               voltage_uv = regulator_get_voltage(st->reg);
+               ret = regulator_get_voltage(st->reg);
+               if (ret < 0)
+                       goto error_disable_reg;
+
+               voltage_uv = ret;
        }
 
        st->chip_info =
index c84180f23139fd25191539ebf266fb122ca85950..6407b5407dddec57fe53ca20f9cba3e5bf90358e 100644 (file)
@@ -365,7 +365,11 @@ static int ad5791_probe(struct spi_device *spi)
                if (ret)
                        goto error_put_reg_pos;
 
-               pos_voltage_uv = regulator_get_voltage(st->reg_vdd);
+               ret = regulator_get_voltage(st->reg_vdd);
+               if (ret < 0)
+                       goto error_disable_reg_pos;
+
+               pos_voltage_uv = ret;
        }
 
        st->reg_vss = regulator_get(&spi->dev, "vss");
@@ -374,7 +378,11 @@ static int ad5791_probe(struct spi_device *spi)
                if (ret)
                        goto error_put_reg_neg;
 
-               neg_voltage_uv = regulator_get_voltage(st->reg_vss);
+               ret = regulator_get_voltage(st->reg_vss);
+               if (ret < 0)
+                       goto error_disable_reg_neg;
+
+               neg_voltage_uv = ret;
        }
 
        st->pwr_down = true;
@@ -428,6 +436,7 @@ error_put_reg_neg:
        if (!IS_ERR(st->reg_vss))
                regulator_put(st->reg_vss);
 
+error_disable_reg_pos:
        if (!IS_ERR(st->reg_vdd))
                regulator_disable(st->reg_vdd);
 error_put_reg_pos:
index e5033b4cfba0cf3c1c88fba9d766c5d433e69a60..a884252ac66b477db3a0fce966b40fb76ae456b2 100644 (file)
@@ -173,7 +173,7 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq)
                        } while ((st->r1_mod > ADF4350_MAX_MODULUS) && r_cnt);
                } while (r_cnt == 0);
 
-               tmp = freq * (u64)st->r1_mod + (st->fpfd > 1);
+               tmp = freq * (u64)st->r1_mod + (st->fpfd >> 1);
                do_div(tmp, st->fpfd); /* Div round closest (n + d/2)/d */
                st->r0_fract = do_div(tmp, st->r1_mod);
                st->r0_int = tmp;
index 48ed1483ff27260e8ac24634cc670acf32f33172..96b68f63a902580c6a45c7265641397ffdda9d52 100644 (file)
@@ -17,6 +17,7 @@ config HID_SENSOR_GYRO_3D
        select IIO_BUFFER
        select IIO_TRIGGERED_BUFFER
        select HID_SENSOR_IIO_COMMON
+       select HID_SENSOR_IIO_TRIGGER
        tristate "HID Gyroscope 3D"
        help
          Say yes here to build support for the HID SENSOR
index 1763c9bcb98ae19817086c4713a6067552d9e74c..dbf80abc834fd3f848a7945b9ddbf38da5a968d7 100644 (file)
@@ -47,6 +47,7 @@ config HID_SENSOR_ALS
        select IIO_BUFFER
        select IIO_TRIGGERED_BUFFER
        select HID_SENSOR_IIO_COMMON
+       select HID_SENSOR_IIO_TRIGGER
        tristate "HID ALS"
        help
          Say yes here to build support for the HID SENSOR
index c1f0cdd57037ee863e0ba12d639c4bc536a80a5d..ff11d68225cfb6d2e8f819345eb15907857601d4 100644 (file)
@@ -8,6 +8,7 @@ config HID_SENSOR_MAGNETOMETER_3D
        select IIO_BUFFER
        select IIO_TRIGGERED_BUFFER
        select HID_SENSOR_IIO_COMMON
+       select HID_SENSOR_IIO_TRIGGER
        tristate "HID Magenetometer 3D"
        help
          Say yes here to build support for the HID SENSOR
index 81837b0710a9ba0240b1eba3c1b3f8b51d997e2f..faf10ba1ed9ad1dcfcd00dabc26c725cf7e9ad00 100644 (file)
@@ -974,6 +974,38 @@ static void __init free_iommu_all(void)
        }
 }
 
+/*
+ * Family15h Model 10h-1fh erratum 746 (IOMMU Logging May Stall Translations)
+ * Workaround:
+ *     BIOS should disable L2B micellaneous clock gating by setting
+ *     L2_L2B_CK_GATE_CONTROL[CKGateL2BMiscDisable](D0F2xF4_x90[2]) = 1b
+ */
+static void __init amd_iommu_erratum_746_workaround(struct amd_iommu *iommu)
+{
+       u32 value;
+
+       if ((boot_cpu_data.x86 != 0x15) ||
+           (boot_cpu_data.x86_model < 0x10) ||
+           (boot_cpu_data.x86_model > 0x1f))
+               return;
+
+       pci_write_config_dword(iommu->dev, 0xf0, 0x90);
+       pci_read_config_dword(iommu->dev, 0xf4, &value);
+
+       if (value & BIT(2))
+               return;
+
+       /* Select NB indirect register 0x90 and enable writing */
+       pci_write_config_dword(iommu->dev, 0xf0, 0x90 | (1 << 8));
+
+       pci_write_config_dword(iommu->dev, 0xf4, value | 0x4);
+       pr_info("AMD-Vi: Applying erratum 746 workaround for IOMMU at %s\n",
+               dev_name(&iommu->dev->dev));
+
+       /* Clear the enable writing bit */
+       pci_write_config_dword(iommu->dev, 0xf0, 0x90);
+}
+
 /*
  * This function clues the initialization function for one IOMMU
  * together and also allocates the command buffer and programs the
@@ -1172,6 +1204,8 @@ static int iommu_init_pci(struct amd_iommu *iommu)
                        iommu->stored_l2[i] = iommu_read_l2(iommu, i);
        }
 
+       amd_iommu_erratum_746_workaround(iommu);
+
        return pci_enable_device(iommu->dev);
 }
 
index eca28014ef3e4a9d509d9d5472fc73da707806ef..f1e7b86a7c3764569bda7e88996ce4d89cda0e8f 100644 (file)
@@ -4253,13 +4253,19 @@ static void quirk_iommu_rwbf(struct pci_dev *dev)
 {
        /*
         * Mobile 4 Series Chipset neglects to set RWBF capability,
-        * but needs it:
+        * but needs it. Same seems to hold for the desktop versions.
         */
        printk(KERN_INFO "DMAR: Forcing write-buffer flush capability\n");
        rwbf_quirk = 1;
 }
 
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_rwbf);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e00, quirk_iommu_rwbf);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e10, quirk_iommu_rwbf);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e20, quirk_iommu_rwbf);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e30, quirk_iommu_rwbf);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e40, quirk_iommu_rwbf);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e90, quirk_iommu_rwbf);
 
 #define GGC 0x52
 #define GGC_MEMORY_SIZE_MASK   (0xf << 8)
index 68452b768da23dc31c5409b113446802476886c2..03a0a01a405451c8a85e0e9b83647c105a710f53 100644 (file)
@@ -248,6 +248,8 @@ static inline void dump_rawmsg(enum debuglevel level, const char *tag,
                CAPIMSG_APPID(data), CAPIMSG_MSGID(data), l,
                CAPIMSG_CONTROL(data));
        l -= 12;
+       if (l <= 0)
+               return;
        dbgline = kmalloc(3 * l, GFP_ATOMIC);
        if (!dbgline)
                return;
index 3d8984edeff79b4dda6910670d95d34802411f6e..9e58dbd8d8cba839da9613e5f4e4060795cd0dc1 100644 (file)
@@ -340,24 +340,22 @@ static int validate_region_size(struct raid_set *rs, unsigned long region_size)
 }
 
 /*
- * validate_rebuild_devices
+ * validate_raid_redundancy
  * @rs
  *
- * Determine if the devices specified for rebuild can result in a valid
- * usable array that is capable of rebuilding the given devices.
+ * Determine if there are enough devices in the array that haven't
+ * failed (or are being rebuilt) to form a usable array.
  *
  * Returns: 0 on success, -EINVAL on failure.
  */
-static int validate_rebuild_devices(struct raid_set *rs)
+static int validate_raid_redundancy(struct raid_set *rs)
 {
        unsigned i, rebuild_cnt = 0;
        unsigned rebuilds_per_group, copies, d;
 
-       if (!(rs->print_flags & DMPF_REBUILD))
-               return 0;
-
        for (i = 0; i < rs->md.raid_disks; i++)
-               if (!test_bit(In_sync, &rs->dev[i].rdev.flags))
+               if (!test_bit(In_sync, &rs->dev[i].rdev.flags) ||
+                   !rs->dev[i].rdev.sb_page)
                        rebuild_cnt++;
 
        switch (rs->raid_type->level) {
@@ -393,27 +391,24 @@ static int validate_rebuild_devices(struct raid_set *rs)
                 *          A    A    B    B    C
                 *          C    D    D    E    E
                 */
-               rebuilds_per_group = 0;
                for (i = 0; i < rs->md.raid_disks * copies; i++) {
+                       if (!(i % copies))
+                               rebuilds_per_group = 0;
                        d = i % rs->md.raid_disks;
-                       if (!test_bit(In_sync, &rs->dev[d].rdev.flags) &&
+                       if ((!rs->dev[d].rdev.sb_page ||
+                            !test_bit(In_sync, &rs->dev[d].rdev.flags)) &&
                            (++rebuilds_per_group >= copies))
                                goto too_many;
-                       if (!((i + 1) % copies))
-                               rebuilds_per_group = 0;
                }
                break;
        default:
-               DMERR("The rebuild parameter is not supported for %s",
-                     rs->raid_type->name);
-               rs->ti->error = "Rebuild not supported for this RAID type";
-               return -EINVAL;
+               if (rebuild_cnt)
+                       return -EINVAL;
        }
 
        return 0;
 
 too_many:
-       rs->ti->error = "Too many rebuild devices specified";
        return -EINVAL;
 }
 
@@ -664,9 +659,6 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
        }
        rs->md.dev_sectors = sectors_per_dev;
 
-       if (validate_rebuild_devices(rs))
-               return -EINVAL;
-
        /* Assume there are no metadata devices until the drives are parsed */
        rs->md.persistent = 0;
        rs->md.external = 1;
@@ -995,28 +987,10 @@ static int super_validate(struct mddev *mddev, struct md_rdev *rdev)
 static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs)
 {
        int ret;
-       unsigned redundancy = 0;
        struct raid_dev *dev;
        struct md_rdev *rdev, *tmp, *freshest;
        struct mddev *mddev = &rs->md;
 
-       switch (rs->raid_type->level) {
-       case 1:
-               redundancy = rs->md.raid_disks - 1;
-               break;
-       case 4:
-       case 5:
-       case 6:
-               redundancy = rs->raid_type->parity_devs;
-               break;
-       case 10:
-               redundancy = raid10_md_layout_to_copies(mddev->layout) - 1;
-               break;
-       default:
-               ti->error = "Unknown RAID type";
-               return -EINVAL;
-       }
-
        freshest = NULL;
        rdev_for_each_safe(rdev, tmp, mddev) {
                /*
@@ -1045,44 +1019,43 @@ static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs)
                        break;
                default:
                        dev = container_of(rdev, struct raid_dev, rdev);
-                       if (redundancy--) {
-                               if (dev->meta_dev)
-                                       dm_put_device(ti, dev->meta_dev);
-
-                               dev->meta_dev = NULL;
-                               rdev->meta_bdev = NULL;
+                       if (dev->meta_dev)
+                               dm_put_device(ti, dev->meta_dev);
 
-                               if (rdev->sb_page)
-                                       put_page(rdev->sb_page);
+                       dev->meta_dev = NULL;
+                       rdev->meta_bdev = NULL;
 
-                               rdev->sb_page = NULL;
+                       if (rdev->sb_page)
+                               put_page(rdev->sb_page);
 
-                               rdev->sb_loaded = 0;
+                       rdev->sb_page = NULL;
 
-                               /*
-                                * We might be able to salvage the data device
-                                * even though the meta device has failed.  For
-                                * now, we behave as though '- -' had been
-                                * set for this device in the table.
-                                */
-                               if (dev->data_dev)
-                                       dm_put_device(ti, dev->data_dev);
+                       rdev->sb_loaded = 0;
 
-                               dev->data_dev = NULL;
-                               rdev->bdev = NULL;
+                       /*
+                        * We might be able to salvage the data device
+                        * even though the meta device has failed.  For
+                        * now, we behave as though '- -' had been
+                        * set for this device in the table.
+                        */
+                       if (dev->data_dev)
+                               dm_put_device(ti, dev->data_dev);
 
-                               list_del(&rdev->same_set);
+                       dev->data_dev = NULL;
+                       rdev->bdev = NULL;
 
-                               continue;
-                       }
-                       ti->error = "Failed to load superblock";
-                       return ret;
+                       list_del(&rdev->same_set);
                }
        }
 
        if (!freshest)
                return 0;
 
+       if (validate_raid_redundancy(rs)) {
+               rs->ti->error = "Insufficient redundancy to activate array";
+               return -EINVAL;
+       }
+
        /*
         * Validation of the freshest device provides the source of
         * validation for the remaining devices.
@@ -1432,7 +1405,7 @@ static void raid_resume(struct dm_target *ti)
 
 static struct target_type raid_target = {
        .name = "raid",
-       .version = {1, 4, 0},
+       .version = {1, 4, 1},
        .module = THIS_MODULE,
        .ctr = raid_ctr,
        .dtr = raid_dtr,
index 675ae5274016da37484b2c05bfefccad8e158543..5409607d487533593d29c2207d857896121364a3 100644 (file)
@@ -2746,19 +2746,9 @@ static int thin_iterate_devices(struct dm_target *ti,
        return 0;
 }
 
-/*
- * A thin device always inherits its queue limits from its pool.
- */
-static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits)
-{
-       struct thin_c *tc = ti->private;
-
-       *limits = bdev_get_queue(tc->pool_dev->bdev)->limits;
-}
-
 static struct target_type thin_target = {
        .name = "thin",
-       .version = {1, 6, 0},
+       .version = {1, 7, 0},
        .module = THIS_MODULE,
        .ctr = thin_ctr,
        .dtr = thin_dtr,
@@ -2767,7 +2757,6 @@ static struct target_type thin_target = {
        .postsuspend = thin_postsuspend,
        .status = thin_status,
        .iterate_devices = thin_iterate_devices,
-       .io_hints = thin_io_hints,
 };
 
 /*----------------------------------------------------------------*/
index c72e4d5a96178c6a542442e2e66bd3da92473b50..314a0e2faf79d6c097e7c1201bf25af8600baaf6 100644 (file)
@@ -1188,6 +1188,7 @@ static int __clone_and_map_changing_extent_only(struct clone_info *ci,
 {
        struct dm_target *ti;
        sector_t len;
+       unsigned num_requests;
 
        do {
                ti = dm_table_find_target(ci->map, ci->sector);
@@ -1200,7 +1201,8 @@ static int __clone_and_map_changing_extent_only(struct clone_info *ci,
                 * reconfiguration might also have changed that since the
                 * check was performed.
                 */
-               if (!get_num_requests || !get_num_requests(ti))
+               num_requests = get_num_requests ? get_num_requests(ti) : 0;
+               if (!num_requests)
                        return -EOPNOTSUPP;
 
                if (is_split_required && !is_split_required(ti))
@@ -1208,7 +1210,7 @@ static int __clone_and_map_changing_extent_only(struct clone_info *ci,
                else
                        len = min(ci->sector_count, max_io_len(ci->sector, ti));
 
-               __issue_target_requests(ci, ti, ti->num_discard_requests, len);
+               __issue_target_requests(ci, ti, num_requests, len);
 
                ci->sector += len;
        } while (ci->sector_count -= len);
index 8a8d42fe26332e0765f6ebad93b435b954d42da0..d4e7567b367c04d25b9a21ba4c50d0db314596aa 100644 (file)
@@ -556,7 +556,7 @@ static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
        mutex_lock(&info->lock);
 
        format = __find_format(info, fh, fmt->which, info->res_type);
-       if (!format)
+       if (format)
                fmt->format = *format;
        else
                ret = -EINVAL;
index 1cf8293c0fb048054177b82e0f0ce44b704de81c..4a980e029ca70163b30288663a7f273943002e18 100644 (file)
@@ -23,8 +23,8 @@
 #include <linux/slab.h>
 #include <linux/videodev2.h>
 #include <linux/of.h>
+#include <linux/platform_data/imx-iram.h>
 
-#include <mach/iram.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
index e0d73a642186d34ae63d841cabe3f42c57604acd..8dac17511e618c84d27a9db8bbfbd7587f5c1d2a 100644 (file)
@@ -35,9 +35,6 @@
 #include <linux/vmalloc.h>
 #include <media/v4l2-dev.h>
 #include <media/v4l2-ioctl.h>
-#include <plat/iommu.h>
-#include <plat/iovmm.h>
-#include <plat/omap-pm.h>
 
 #include "ispvideo.h"
 #include "isp.h"
index 4ab99f3a7b0950310d89470d7be47854f4f6f219..b4a68ecf0ca78731ea98b0178c45dac926378e3d 100644 (file)
@@ -593,7 +593,7 @@ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd)
 {
        struct media_entity *source, *sink;
        unsigned int flags = MEDIA_LNK_FL_ENABLED;
-       int i, ret;
+       int i, ret = 0;
 
        for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
                struct fimc_lite *fimc = fmd->fimc_lite[i];
index 379f574337119e4aecc60896e3ae0fc6fc336e39..681bc6ba149db1bdf380b483c32b9babfb335a58 100644 (file)
@@ -412,62 +412,48 @@ leave_handle_frame:
 }
 
 /* Error handling for interrupt */
-static void s5p_mfc_handle_error(struct s5p_mfc_ctx *ctx,
-                                unsigned int reason, unsigned int err)
+static void s5p_mfc_handle_error(struct s5p_mfc_dev *dev,
+               struct s5p_mfc_ctx *ctx, unsigned int reason, unsigned int err)
 {
-       struct s5p_mfc_dev *dev;
        unsigned long flags;
 
-       /* If no context is available then all necessary
-        * processing has been done. */
-       if (ctx == NULL)
-               return;
-
-       dev = ctx->dev;
        mfc_err("Interrupt Error: %08x\n", err);
-       s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
-       wake_up_dev(dev, reason, err);
 
-       /* Error recovery is dependent on the state of context */
-       switch (ctx->state) {
-       case MFCINST_INIT:
-               /* This error had to happen while acquireing instance */
-       case MFCINST_GOT_INST:
-               /* This error had to happen while parsing the header */
-       case MFCINST_HEAD_PARSED:
-               /* This error had to happen while setting dst buffers */
-       case MFCINST_RETURN_INST:
-               /* This error had to happen while releasing instance */
-               clear_work_bit(ctx);
-               wake_up_ctx(ctx, reason, err);
-               if (test_and_clear_bit(0, &dev->hw_lock) == 0)
-                       BUG();
-               s5p_mfc_clock_off();
-               ctx->state = MFCINST_ERROR;
-               break;
-       case MFCINST_FINISHING:
-       case MFCINST_FINISHED:
-       case MFCINST_RUNNING:
-               /* It is higly probable that an error occured
-                * while decoding a frame */
-               clear_work_bit(ctx);
-               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, &ctx->dst_queue,
-                               &ctx->vq_dst);
-               /* Mark all src buffers as having an error */
-               s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->src_queue,
-                               &ctx->vq_src);
-               spin_unlock_irqrestore(&dev->irqlock, flags);
-               if (test_and_clear_bit(0, &dev->hw_lock) == 0)
-                       BUG();
-               s5p_mfc_clock_off();
-               break;
-       default:
-               mfc_err("Encountered an error interrupt which had not been handled\n");
-               break;
+       if (ctx != NULL) {
+               /* Error recovery is dependent on the state of context */
+               switch (ctx->state) {
+               case MFCINST_RES_CHANGE_INIT:
+               case MFCINST_RES_CHANGE_FLUSH:
+               case MFCINST_RES_CHANGE_END:
+               case MFCINST_FINISHING:
+               case MFCINST_FINISHED:
+               case MFCINST_RUNNING:
+                       /* It is higly probable that an error occured
+                        * while decoding a frame */
+                       clear_work_bit(ctx);
+                       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,
+                                               &ctx->dst_queue, &ctx->vq_dst);
+                       /* Mark all src buffers as having an error */
+                       s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue,
+                                               &ctx->src_queue, &ctx->vq_src);
+                       spin_unlock_irqrestore(&dev->irqlock, flags);
+                       wake_up_ctx(ctx, reason, err);
+                       break;
+               default:
+                       clear_work_bit(ctx);
+                       ctx->state = MFCINST_ERROR;
+                       wake_up_ctx(ctx, reason, err);
+                       break;
+               }
        }
+       if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+               BUG();
+       s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
+       s5p_mfc_clock_off();
+       wake_up_dev(dev, reason, err);
        return;
 }
 
@@ -632,7 +618,7 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv)
                                dev->warn_start)
                        s5p_mfc_handle_frame(ctx, reason, err);
                else
-                       s5p_mfc_handle_error(ctx, reason, err);
+                       s5p_mfc_handle_error(dev, ctx, reason, err);
                clear_bit(0, &dev->enter_suspend);
                break;
 
index e10e525f33e51f0a14b5c3f5b7ef0006ae3524cb..296941a9ae25decc88dd4d0da8b2240319eb2b34 100644 (file)
@@ -374,6 +374,7 @@ static int usb_keene_probe(struct usb_interface *intf,
        radio->vdev.ioctl_ops = &usb_keene_ioctl_ops;
        radio->vdev.lock = &radio->lock;
        radio->vdev.release = video_device_release_empty;
+       radio->vdev.vfl_dir = VFL_DIR_TX;
 
        radio->usbdev = interface_to_usbdev(intf);
        radio->intf = intf;
index a082e400ed0f6ed8022fd3562231e5cc969c10d2..1507c9d508d7c59e98ea0f58cbc017726b3329c5 100644 (file)
@@ -250,6 +250,7 @@ static struct video_device radio_si4713_vdev_template = {
        .name                   = "radio-si4713",
        .release                = video_device_release,
        .ioctl_ops              = &radio_si4713_ioctl_ops,
+       .vfl_dir                = VFL_DIR_TX,
 };
 
 /* Platform driver interface */
index c48be195bbad59a917141489887a8b4a1356a970..cabbe3adf435b6a9f3ef2176109489524565513f 100644 (file)
@@ -1971,6 +1971,7 @@ static struct video_device wl1273_viddev_template = {
        .ioctl_ops              = &wl1273_ioctl_ops,
        .name                   = WL1273_FM_DRIVER_NAME,
        .release                = wl1273_vdev_release,
+       .vfl_dir                = VFL_DIR_TX,
 };
 
 static int wl1273_fm_radio_remove(struct platform_device *pdev)
index 048de45360360251095639d1422f9931853d3fba..0a8ee8fab9242476c14c153b0cc00b51387a7b26 100644 (file)
@@ -518,6 +518,16 @@ static struct video_device fm_viddev_template = {
        .ioctl_ops = &fm_drv_ioctl_ops,
        .name = FM_DRV_NAME,
        .release = video_device_release,
+       /*
+        * To ensure both the tuner and modulator ioctls are accessible we
+        * set the vfl_dir to M2M to indicate this.
+        *
+        * It is not really a mem2mem device of course, but it can both receive
+        * and transmit using the same radio device. It's the only radio driver
+        * that does this and it should really be split in two radio devices,
+        * but that would affect applications using this driver.
+        */
+       .vfl_dir = VFL_DIR_M2M,
 };
 
 int fm_v4l2_init_video_device(struct fmdev *fmdev, int radio_nr)
index 40ad6687ee5dee1e53cad6489a386438f5d2a483..3773a8a745df9323719d4aaeb6dd949fa0e61cad 100644 (file)
@@ -381,6 +381,7 @@ static const struct sd_desc sd_desc = {
 /* -- module initialisation -- */
 static const struct usb_device_id device_table[] = {
        {USB_DEVICE(0x045e, 0x02ae)},
+       {USB_DEVICE(0x045e, 0x02bf)},
        {}
 };
 
index 70511d5f953857492aca04655cdf079836a998dc..1220340e76028654385ec448c7a98a310d3d89f6 100644 (file)
@@ -496,7 +496,7 @@ static void reg_w(struct gspca_dev *gspca_dev,
        }
 }
 
-static void i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer)
+static void i2c_w(struct gspca_dev *gspca_dev, const u8 *buf)
 {
        int retry = 60;
 
@@ -504,16 +504,19 @@ static void i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer)
                return;
 
        /* is i2c ready */
-       reg_w(gspca_dev, 0x08, buffer, 8);
+       reg_w(gspca_dev, 0x08, buf, 8);
        while (retry--) {
                if (gspca_dev->usb_err < 0)
                        return;
-               msleep(10);
+               msleep(1);
                reg_r(gspca_dev, 0x08);
                if (gspca_dev->usb_buf[0] & 0x04) {
                        if (gspca_dev->usb_buf[0] & 0x08) {
                                dev_err(gspca_dev->v4l2_dev.dev,
-                                       "i2c write error\n");
+                                       "i2c error writing %02x %02x %02x %02x"
+                                       " %02x %02x %02x %02x\n",
+                                       buf[0], buf[1], buf[2], buf[3],
+                                       buf[4], buf[5], buf[6], buf[7]);
                                gspca_dev->usb_err = -EIO;
                        }
                        return;
@@ -530,7 +533,7 @@ static void i2c_w_vector(struct gspca_dev *gspca_dev,
        for (;;) {
                if (gspca_dev->usb_err < 0)
                        return;
-               reg_w(gspca_dev, 0x08, *buffer, 8);
+               i2c_w(gspca_dev, *buffer);
                len -= 8;
                if (len <= 0)
                        break;
index 5a86047b846f484c2d1c22d88023878d2249b646..36307a9028a932b6414523e0a189c613479daa90 100644 (file)
@@ -1550,6 +1550,7 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
                        0,
                        gspca_dev->usb_buf, 8,
                        500);
+       msleep(2);
        if (ret < 0) {
                pr_err("i2c_w1 err %d\n", ret);
                gspca_dev->usb_err = ret;
index 2bb7613ddebbb0590d702416ac9f7c5872ffbbb5..d5baab17a5ef77b6bbeb85f8cf3e2c1b0127b5b7 100644 (file)
@@ -1431,8 +1431,10 @@ int uvc_ctrl_set(struct uvc_video_chain *chain,
        int ret;
 
        ctrl = uvc_find_control(chain, xctrl->id, &mapping);
-       if (ctrl == NULL || (ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) == 0)
+       if (ctrl == NULL)
                return -EINVAL;
+       if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR))
+               return -EACCES;
 
        /* Clamp out of range values. */
        switch (mapping->v4l2_type) {
index f2ee8c6b0d8dbb8ceb5d7ebad4bcfd85d878b6dd..68d59b52749288e00dcb816f42952516f160f6db 100644 (file)
@@ -657,8 +657,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
                        ret = uvc_ctrl_get(chain, ctrl);
                        if (ret < 0) {
                                uvc_ctrl_rollback(handle);
-                               ctrls->error_idx = ret == -ENOENT
-                                                ? ctrls->count : i;
+                               ctrls->error_idx = i;
                                return ret;
                        }
                }
@@ -686,8 +685,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
                        ret = uvc_ctrl_set(chain, ctrl);
                        if (ret < 0) {
                                uvc_ctrl_rollback(handle);
-                               ctrls->error_idx = (ret == -ENOENT &&
-                                                   cmd == VIDIOC_S_EXT_CTRLS)
+                               ctrls->error_idx = cmd == VIDIOC_S_EXT_CTRLS
                                                 ? ctrls->count : i;
                                return ret;
                        }
index 9f81be23a81f06a1d527ac792071e1b7a6154979..e02c4797b1c65fe46ef1352fa72f84e05317fe6c 100644 (file)
@@ -921,8 +921,10 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b
                 * In videobuf we use our internal V4l2_planes struct for
                 * single-planar buffers as well, for simplicity.
                 */
-               if (V4L2_TYPE_IS_OUTPUT(b->type))
+               if (V4L2_TYPE_IS_OUTPUT(b->type)) {
                        v4l2_planes[0].bytesused = b->bytesused;
+                       v4l2_planes[0].data_offset = 0;
+               }
 
                if (b->memory == V4L2_MEMORY_USERPTR) {
                        v4l2_planes[0].m.userptr = b->m.userptr;
index 1c0abd4dfc43eeef01bb096bb33e5a1bdc08ef5e..ff553babf455025c2a5ce303297a15f404d2d5e3 100644 (file)
@@ -237,6 +237,7 @@ config MFD_TPS65910
        depends on I2C=y && GPIOLIB
        select MFD_CORE
        select REGMAP_I2C
+       select REGMAP_IRQ
        select IRQ_DOMAIN
        help
          if you say yes here you get support for the TPS65910 series of
@@ -292,6 +293,7 @@ config TWL4030_CORE
        bool "Texas Instruments TWL4030/TWL5030/TWL6030/TPS659x0 Support"
        depends on I2C=y && GENERIC_HARDIRQS
        select IRQ_DOMAIN
+       select REGMAP_I2C
        help
          Say yes here if you have TWL4030 / TWL6030 family chip on your board.
          This core driver provides register access and IRQ handling
index e1650badd106da6e3992b219e77fdc35e20ad1c8..4778bb124efe09b27da77446896dfd6ae9e97f0e 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/mfd/core.h>
 #include <linux/mfd/abx500.h>
 #include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
 #include <linux/mfd/dbx500-prcmu.h>
 #include <linux/regulator/ab8500.h>
 #include <linux/of.h>
index bc8a3edb6bbf2b2fdc9c3f15a425eafb0bcb23af..222c03a5ddc0b3d3e9a37af453d4a1745f1b35e3 100644 (file)
@@ -239,7 +239,12 @@ static int arizona_runtime_resume(struct device *dev)
                return ret;
        }
 
-       regcache_sync(arizona->regmap);
+       ret = regcache_sync(arizona->regmap);
+       if (ret != 0) {
+               dev_err(arizona->dev, "Failed to restore register cache\n");
+               regulator_disable(arizona->dcvdd);
+               return ret;
+       }
 
        return 0;
 }
index 74713bf5371fa42361dbb2342407c0dfe9c039b6..2bec5f0db3ee5e80733a88043d96b3219405d2f0 100644 (file)
@@ -176,14 +176,7 @@ int arizona_irq_init(struct arizona *arizona)
                aod = &wm5102_aod;
                irq = &wm5102_irq;
 
-               switch (arizona->rev) {
-               case 0:
-               case 1:
-                       ctrlif_error = false;
-                       break;
-               default:
-                       break;
-               }
+               ctrlif_error = false;
                break;
 #endif
 #ifdef CONFIG_MFD_WM5110
@@ -191,14 +184,7 @@ int arizona_irq_init(struct arizona *arizona)
                aod = &wm5110_aod;
                irq = &wm5110_irq;
 
-               switch (arizona->rev) {
-               case 0:
-               case 1:
-                       ctrlif_error = false;
-                       break;
-               default:
-                       break;
-               }
+               ctrlif_error = false;
                break;
 #endif
        default:
index ac74a4d1daead1848ef00e9ac096013d69e67a21..885e567803583a42473ffb532beead311ad68cf2 100644 (file)
 #include <linux/of_device.h>
 #endif
 
+/* I2C safe register check */
+static inline bool i2c_safe_reg(unsigned char reg)
+{
+       switch (reg) {
+       case DA9052_STATUS_A_REG:
+       case DA9052_STATUS_B_REG:
+       case DA9052_STATUS_C_REG:
+       case DA9052_STATUS_D_REG:
+       case DA9052_ADC_RES_L_REG:
+       case DA9052_ADC_RES_H_REG:
+       case DA9052_VDD_RES_REG:
+       case DA9052_ICHG_AV_REG:
+       case DA9052_TBAT_RES_REG:
+       case DA9052_ADCIN4_RES_REG:
+       case DA9052_ADCIN5_RES_REG:
+       case DA9052_ADCIN6_RES_REG:
+       case DA9052_TJUNC_RES_REG:
+       case DA9052_TSI_X_MSB_REG:
+       case DA9052_TSI_Y_MSB_REG:
+       case DA9052_TSI_LSB_REG:
+       case DA9052_TSI_Z_MSB_REG:
+               return true;
+       default:
+               return false;
+       }
+}
+
+/*
+ * There is an issue with DA9052 and DA9053_AA/BA/BB PMIC where the PMIC
+ * gets lockup up or fails to respond following a system reset.
+ * This fix is to follow any read or write with a dummy read to a safe
+ * register.
+ */
+int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg)
+{
+       int val;
+
+       switch (da9052->chip_id) {
+       case DA9052:
+       case DA9053_AA:
+       case DA9053_BA:
+       case DA9053_BB:
+               /* A dummy read to a safe register address. */
+       if (!i2c_safe_reg(reg))
+                       return regmap_read(da9052->regmap,
+                                          DA9052_PARK_REGISTER,
+                                          &val);
+               break;
+       default:
+               /*
+                * For other chips parking of I2C register
+                * to a safe place is not required.
+                */
+               break;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(da9052_i2c_fix);
+
 static int da9052_i2c_enable_multiwrite(struct da9052 *da9052)
 {
        int reg_val, ret;
@@ -83,6 +143,7 @@ static int da9052_i2c_probe(struct i2c_client *client,
 
        da9052->dev = &client->dev;
        da9052->chip_irq = client->irq;
+       da9052->fix_io = da9052_i2c_fix;
 
        i2c_set_clientdata(client, da9052);
 
index dc8826d8d69da0d1ee8c29911dc6c95ade7f1da2..268f45d4239427bdb172f65e8c1768d4cd2bd435 100644 (file)
@@ -2524,7 +2524,7 @@ static bool read_mailbox_0(void)
 
                for (n = 0; n < NUM_PRCMU_WAKEUPS; n++) {
                        if (ev & prcmu_irq_bit[n])
-                               generic_handle_irq(IRQ_PRCMU_BASE + n);
+                               generic_handle_irq(irq_find_mapping(db8500_irq_domain, n));
                }
                r = true;
                break;
@@ -2737,13 +2737,14 @@ static int db8500_irq_map(struct irq_domain *d, unsigned int virq,
 }
 
 static struct irq_domain_ops db8500_irq_ops = {
-        .map    = db8500_irq_map,
-        .xlate  = irq_domain_xlate_twocell,
+       .map    = db8500_irq_map,
+       .xlate  = irq_domain_xlate_twocell,
 };
 
 static int db8500_irq_init(struct device_node *np)
 {
-       int irq_base = -1;
+       int irq_base = 0;
+       int i;
 
        /* In the device tree case, just take some IRQs */
        if (!np)
@@ -2758,6 +2759,10 @@ static int db8500_irq_init(struct device_node *np)
                return -ENOSYS;
        }
 
+       /* All wakeups will be used, so create mappings for all */
+       for (i = 0; i < NUM_PRCMU_WAKEUPS; i++)
+               irq_create_mapping(db8500_irq_domain, i);
+
        return 0;
 }
 
index f6878f8db57d105b663d399bc2830bbb0ce0b4bf..4d73963cd8f0188d2f9e09c9bbdcde0cf5c35007 100644 (file)
@@ -93,15 +93,6 @@ static int max77686_i2c_probe(struct i2c_client *i2c,
        if (max77686 == NULL)
                return -ENOMEM;
 
-       max77686->regmap = regmap_init_i2c(i2c, &max77686_regmap_config);
-       if (IS_ERR(max77686->regmap)) {
-               ret = PTR_ERR(max77686->regmap);
-               dev_err(max77686->dev, "Failed to allocate register map: %d\n",
-                               ret);
-               kfree(max77686);
-               return ret;
-       }
-
        i2c_set_clientdata(i2c, max77686);
        max77686->dev = &i2c->dev;
        max77686->i2c = i2c;
@@ -111,6 +102,15 @@ static int max77686_i2c_probe(struct i2c_client *i2c,
        max77686->irq_gpio = pdata->irq_gpio;
        max77686->irq = i2c->irq;
 
+       max77686->regmap = regmap_init_i2c(i2c, &max77686_regmap_config);
+       if (IS_ERR(max77686->regmap)) {
+               ret = PTR_ERR(max77686->regmap);
+               dev_err(max77686->dev, "Failed to allocate register map: %d\n",
+                               ret);
+               kfree(max77686);
+               return ret;
+       }
+
        if (regmap_read(max77686->regmap,
                         MAX77686_REG_DEVICE_ID, &data) < 0) {
                dev_err(max77686->dev,
index cc5155e20494726c2ae6954e64128f61973ebafd..9e60fed5ff82a81dbf774c7d8af7547f12861516 100644 (file)
@@ -114,35 +114,37 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
        u8 reg_data;
        int ret = 0;
 
+       if (!pdata) {
+               dev_err(&i2c->dev, "No platform data found.\n");
+               return -EINVAL;
+       }
+
        max77693 = devm_kzalloc(&i2c->dev,
                        sizeof(struct max77693_dev), GFP_KERNEL);
        if (max77693 == NULL)
                return -ENOMEM;
 
-       max77693->regmap = devm_regmap_init_i2c(i2c, &max77693_regmap_config);
-       if (IS_ERR(max77693->regmap)) {
-               ret = PTR_ERR(max77693->regmap);
-               dev_err(max77693->dev,"failed to allocate register map: %d\n",
-                               ret);
-               goto err_regmap;
-       }
-
        i2c_set_clientdata(i2c, max77693);
        max77693->dev = &i2c->dev;
        max77693->i2c = i2c;
        max77693->irq = i2c->irq;
        max77693->type = id->driver_data;
 
-       if (!pdata)
-               goto err_regmap;
+       max77693->regmap = devm_regmap_init_i2c(i2c, &max77693_regmap_config);
+       if (IS_ERR(max77693->regmap)) {
+               ret = PTR_ERR(max77693->regmap);
+               dev_err(max77693->dev, "failed to allocate register map: %d\n",
+                               ret);
+               return ret;
+       }
 
        max77693->wakeup = pdata->wakeup;
 
-       if (max77693_read_reg(max77693->regmap,
-                               MAX77693_PMIC_REG_PMIC_ID2, &reg_data) < 0) {
+       ret = max77693_read_reg(max77693->regmap, MAX77693_PMIC_REG_PMIC_ID2,
+                               &reg_data);
+       if (ret < 0) {
                dev_err(max77693->dev, "device not found on this channel\n");
-               ret = -ENODEV;
-               goto err_regmap;
+               return ret;
        } else
                dev_info(max77693->dev, "device ID: 0x%x\n", reg_data);
 
@@ -163,7 +165,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
                ret = PTR_ERR(max77693->regmap_muic);
                dev_err(max77693->dev,
                        "failed to allocate register map: %d\n", ret);
-               goto err_regmap;
+               goto err_regmap_muic;
        }
 
        ret = max77693_irq_init(max77693);
@@ -184,9 +186,9 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
 err_mfd:
        max77693_irq_exit(max77693);
 err_irq:
+err_regmap_muic:
        i2c_unregister_device(max77693->muic);
        i2c_unregister_device(max77693->haptic);
-err_regmap:
        return ret;
 }
 
index 64803f13bcecc4f6e8f221ecacf814e419d07104..d11567307fbed6c930f955abf198de1157dfb237 100644 (file)
@@ -208,6 +208,8 @@ static int pcf50633_probe(struct i2c_client *client,
        if (!pcf)
                return -ENOMEM;
 
+       i2c_set_clientdata(client, pcf);
+       pcf->dev = &client->dev;
        pcf->pdata = pdata;
 
        mutex_init(&pcf->lock);
@@ -219,9 +221,6 @@ static int pcf50633_probe(struct i2c_client *client,
                return ret;
        }
 
-       i2c_set_clientdata(client, pcf);
-       pcf->dev = &client->dev;
-
        version = pcf50633_reg_read(pcf, 0);
        variant = pcf50633_reg_read(pcf, 1);
        if (version < 0 || variant < 0) {
index 89f046ca9e410292b9b1a51423544ab1a40aebf2..3d3b4addf81a9b73b480ae3874e2675e1678b52b 100644 (file)
@@ -112,6 +112,21 @@ static int rtl8411_card_power_off(struct rtsx_pcr *pcr, int card)
                        BPP_LDO_POWB, BPP_LDO_SUSPEND);
 }
 
+static int rtl8411_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
+{
+       u8 mask, val;
+
+       mask = (BPP_REG_TUNED18 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_MASK;
+       if (voltage == OUTPUT_3V3)
+               val = (BPP_ASIC_3V3 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_3V3;
+       else if (voltage == OUTPUT_1V8)
+               val = (BPP_ASIC_1V8 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_1V8;
+       else
+               return -EINVAL;
+
+       return rtsx_pci_write_register(pcr, LDO_CTL, mask, val);
+}
+
 static unsigned int rtl8411_cd_deglitch(struct rtsx_pcr *pcr)
 {
        unsigned int card_exist;
@@ -163,6 +178,18 @@ static unsigned int rtl8411_cd_deglitch(struct rtsx_pcr *pcr)
        return card_exist;
 }
 
+static int rtl8411_conv_clk_and_div_n(int input, int dir)
+{
+       int output;
+
+       if (dir == CLK_TO_DIV_N)
+               output = input * 4 / 5 - 2;
+       else
+               output = (input + 2) * 5 / 4;
+
+       return output;
+}
+
 static const struct pcr_ops rtl8411_pcr_ops = {
        .extra_init_hw = rtl8411_extra_init_hw,
        .optimize_phy = NULL,
@@ -172,7 +199,9 @@ static const struct pcr_ops rtl8411_pcr_ops = {
        .disable_auto_blink = rtl8411_disable_auto_blink,
        .card_power_on = rtl8411_card_power_on,
        .card_power_off = rtl8411_card_power_off,
+       .switch_output_voltage = rtl8411_switch_output_voltage,
        .cd_deglitch = rtl8411_cd_deglitch,
+       .conv_clk_and_div_n = rtl8411_conv_clk_and_div_n,
 };
 
 /* SD Pull Control Enable:
index 283a4f148084ab29f34d2444d8f264e72345e846..98fe0f39463ed1596f1e0881f5d7352349da400d 100644 (file)
@@ -144,6 +144,25 @@ static int rts5209_card_power_off(struct rtsx_pcr *pcr, int card)
        return rtsx_pci_send_cmd(pcr, 100);
 }
 
+static int rts5209_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
+{
+       int err;
+
+       if (voltage == OUTPUT_3V3) {
+               err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24);
+               if (err < 0)
+                       return err;
+       } else if (voltage == OUTPUT_1V8) {
+               err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24);
+               if (err < 0)
+                       return err;
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static const struct pcr_ops rts5209_pcr_ops = {
        .extra_init_hw = rts5209_extra_init_hw,
        .optimize_phy = rts5209_optimize_phy,
@@ -153,7 +172,9 @@ static const struct pcr_ops rts5209_pcr_ops = {
        .disable_auto_blink = rts5209_disable_auto_blink,
        .card_power_on = rts5209_card_power_on,
        .card_power_off = rts5209_card_power_off,
+       .switch_output_voltage = rts5209_switch_output_voltage,
        .cd_deglitch = NULL,
+       .conv_clk_and_div_n = NULL,
 };
 
 /* SD Pull Control Enable:
index b9dbab266fdadc9f3f60e219f6dc5e25b9322036..29d889cbb9c5183262897bc5e29818c536bb227d 100644 (file)
@@ -114,6 +114,25 @@ static int rts5229_card_power_off(struct rtsx_pcr *pcr, int card)
        return rtsx_pci_send_cmd(pcr, 100);
 }
 
+static int rts5229_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
+{
+       int err;
+
+       if (voltage == OUTPUT_3V3) {
+               err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24);
+               if (err < 0)
+                       return err;
+       } else if (voltage == OUTPUT_1V8) {
+               err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24);
+               if (err < 0)
+                       return err;
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static const struct pcr_ops rts5229_pcr_ops = {
        .extra_init_hw = rts5229_extra_init_hw,
        .optimize_phy = rts5229_optimize_phy,
@@ -123,7 +142,9 @@ static const struct pcr_ops rts5229_pcr_ops = {
        .disable_auto_blink = rts5229_disable_auto_blink,
        .card_power_on = rts5229_card_power_on,
        .card_power_off = rts5229_card_power_off,
+       .switch_output_voltage = rts5229_switch_output_voltage,
        .cd_deglitch = NULL,
+       .conv_clk_and_div_n = NULL,
 };
 
 /* SD Pull Control Enable:
index 7a7b0bda4618926fffea21dc23a3f386e1303777..9fc57009e22813fe257481b39352533f2fbd5d45 100644 (file)
@@ -630,7 +630,10 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock,
        if (clk == pcr->cur_clock)
                return 0;
 
-       N = (u8)(clk - 2);
+       if (pcr->ops->conv_clk_and_div_n)
+               N = (u8)pcr->ops->conv_clk_and_div_n(clk, CLK_TO_DIV_N);
+       else
+               N = (u8)(clk - 2);
        if ((clk <= 2) || (N > max_N))
                return -EINVAL;
 
@@ -641,7 +644,14 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock,
        /* Make sure that the SSC clock div_n is equal or greater than min_N */
        div = CLK_DIV_1;
        while ((N < min_N) && (div < max_div)) {
-               N = (N + 2) * 2 - 2;
+               if (pcr->ops->conv_clk_and_div_n) {
+                       int dbl_clk = pcr->ops->conv_clk_and_div_n(N,
+                                       DIV_N_TO_CLK) * 2;
+                       N = (u8)pcr->ops->conv_clk_and_div_n(dbl_clk,
+                                       CLK_TO_DIV_N);
+               } else {
+                       N = (N + 2) * 2 - 2;
+               }
                div++;
        }
        dev_dbg(&(pcr->pci->dev), "N = %d, div = %d\n", N, div);
@@ -703,6 +713,15 @@ int rtsx_pci_card_power_off(struct rtsx_pcr *pcr, int card)
 }
 EXPORT_SYMBOL_GPL(rtsx_pci_card_power_off);
 
+int rtsx_pci_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
+{
+       if (pcr->ops->switch_output_voltage)
+               return pcr->ops->switch_output_voltage(pcr, voltage);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_switch_output_voltage);
+
 unsigned int rtsx_pci_card_exist(struct rtsx_pcr *pcr)
 {
        unsigned int val;
@@ -767,10 +786,10 @@ static void rtsx_pci_card_detect(struct work_struct *work)
 
        spin_unlock_irqrestore(&pcr->lock, flags);
 
-       if (card_detect & SD_EXIST)
+       if ((card_detect & SD_EXIST) && pcr->slots[RTSX_SD_CARD].card_event)
                pcr->slots[RTSX_SD_CARD].card_event(
                                pcr->slots[RTSX_SD_CARD].p_dev);
-       if (card_detect & MS_EXIST)
+       if ((card_detect & MS_EXIST) && pcr->slots[RTSX_MS_CARD].card_event)
                pcr->slots[RTSX_MS_CARD].card_event(
                                pcr->slots[RTSX_MS_CARD].p_dev);
 }
index a06d66b929b1ea96743826435d18899464fb24b5..ecc092c7f7453e4cfb7146fc222546869239f8b0 100644 (file)
@@ -219,25 +219,18 @@ static void tc3589x_irq_unmap(struct irq_domain *d, unsigned int virq)
 }
 
 static struct irq_domain_ops tc3589x_irq_ops = {
-        .map    = tc3589x_irq_map,
+       .map    = tc3589x_irq_map,
        .unmap  = tc3589x_irq_unmap,
-        .xlate  = irq_domain_xlate_twocell,
+       .xlate  = irq_domain_xlate_twocell,
 };
 
 static int tc3589x_irq_init(struct tc3589x *tc3589x, struct device_node *np)
 {
        int base = tc3589x->irq_base;
 
-       if (base) {
-               tc3589x->domain = irq_domain_add_legacy(
-                       NULL, TC3589x_NR_INTERNAL_IRQS, base,
-                       0, &tc3589x_irq_ops, tc3589x);
-       }
-       else {
-               tc3589x->domain = irq_domain_add_linear(
-                       np, TC3589x_NR_INTERNAL_IRQS,
-                       &tc3589x_irq_ops, tc3589x);
-       }
+       tc3589x->domain = irq_domain_add_simple(
+               np, TC3589x_NR_INTERNAL_IRQS, base,
+               &tc3589x_irq_ops, tc3589x);
 
        if (!tc3589x->domain) {
                dev_err(tc3589x->dev, "Failed to create irqdomain\n");
index 4dae241e501734f66c9080283a0c43188729b0b5..dd362c1078e1438b3751ec69dc1c8b1ad3efa0bb 100644 (file)
@@ -159,7 +159,7 @@ out:
 static int twl4030_write_script(u8 address, struct twl4030_ins *script,
                                       int len)
 {
-       int err;
+       int err = -EINVAL;
 
        for (; len; len--, address++, script++) {
                if (len == 1) {
index fae15d880758cc197ed0bfe8e095daca74f01bd5..3c1723aa62250c28c61aab4b3f83442e28770dc8 100644 (file)
@@ -67,6 +67,7 @@ struct vexpress_config_bridge *vexpress_config_bridge_register(
 
        return bridge;
 }
+EXPORT_SYMBOL(vexpress_config_bridge_register);
 
 void vexpress_config_bridge_unregister(struct vexpress_config_bridge *bridge)
 {
@@ -83,6 +84,7 @@ void vexpress_config_bridge_unregister(struct vexpress_config_bridge *bridge)
        while (!list_empty(&__bridge.transactions))
                cpu_relax();
 }
+EXPORT_SYMBOL(vexpress_config_bridge_unregister);
 
 
 struct vexpress_config_func {
@@ -142,6 +144,7 @@ struct vexpress_config_func *__vexpress_config_func_get(struct device *dev,
 
        return func;
 }
+EXPORT_SYMBOL(__vexpress_config_func_get);
 
 void vexpress_config_func_put(struct vexpress_config_func *func)
 {
@@ -149,7 +152,7 @@ void vexpress_config_func_put(struct vexpress_config_func *func)
        of_node_put(func->bridge->node);
        kfree(func);
 }
-
+EXPORT_SYMBOL(vexpress_config_func_put);
 
 struct vexpress_config_trans {
        struct vexpress_config_func *func;
@@ -229,6 +232,7 @@ void vexpress_config_complete(struct vexpress_config_bridge *bridge,
 
        complete(&trans->completion);
 }
+EXPORT_SYMBOL(vexpress_config_complete);
 
 int vexpress_config_wait(struct vexpress_config_trans *trans)
 {
@@ -236,7 +240,7 @@ int vexpress_config_wait(struct vexpress_config_trans *trans)
 
        return trans->status;
 }
-
+EXPORT_SYMBOL(vexpress_config_wait);
 
 int vexpress_config_read(struct vexpress_config_func *func, int offset,
                u32 *data)
index e5d8f63b252aa887e0493d3ae44725792217daea..77048b18439e3e6467b79f7ba4b2b3ac06e36a6e 100644 (file)
@@ -313,19 +313,11 @@ static void vexpress_sysreg_config_complete(unsigned long data)
 }
 
 
-void __init vexpress_sysreg_early_init(void __iomem *base)
+void __init vexpress_sysreg_setup(struct device_node *node)
 {
-       struct device_node *node = of_find_compatible_node(NULL, NULL,
-                       "arm,vexpress-sysreg");
-
-       if (node)
-               base = of_iomap(node, 0);
-
-       if (WARN_ON(!base))
+       if (WARN_ON(!vexpress_sysreg_base))
                return;
 
-       vexpress_sysreg_base = base;
-
        if (readl(vexpress_sysreg_base + SYS_MISC) & SYS_MISC_MASTERSITE)
                vexpress_master_site = VEXPRESS_SITE_DB2;
        else
@@ -336,9 +328,23 @@ void __init vexpress_sysreg_early_init(void __iomem *base)
        WARN_ON(!vexpress_sysreg_config_bridge);
 }
 
+void __init vexpress_sysreg_early_init(void __iomem *base)
+{
+       vexpress_sysreg_base = base;
+       vexpress_sysreg_setup(NULL);
+}
+
 void __init vexpress_sysreg_of_early_init(void)
 {
-       vexpress_sysreg_early_init(NULL);
+       struct device_node *node = of_find_compatible_node(NULL, NULL,
+                       "arm,vexpress-sysreg");
+
+       if (node) {
+               vexpress_sysreg_base = of_iomap(node, 0);
+               vexpress_sysreg_setup(node);
+       } else {
+               pr_info("vexpress-sysreg: No Device Tree node found.");
+       }
 }
 
 
@@ -426,9 +432,11 @@ static int vexpress_sysreg_probe(struct platform_device *pdev)
                return -EBUSY;
        }
 
-       if (!vexpress_sysreg_base)
+       if (!vexpress_sysreg_base) {
                vexpress_sysreg_base = devm_ioremap(&pdev->dev, res->start,
                                resource_size(res));
+               vexpress_sysreg_setup(pdev->dev.of_node);
+       }
 
        if (!vexpress_sysreg_base) {
                dev_err(&pdev->dev, "Failed to obtain base address!\n");
index 088872ab63389e49d04dd3f38fb02664fdc929ba..1133a64c2dc9388609ff125c480a82840d9285d1 100644 (file)
@@ -1882,7 +1882,7 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg)
        }
 }
 
-#define WM5102_MAX_REGISTER 0x1a8fff
+#define WM5102_MAX_REGISTER 0x1a9800
 
 const struct regmap_config wm5102_spi_regmap = {
        .reg_bits = 32,
index 158da5a81a661824a2dca0ee2a9fe93fb5cacfb6..3c09cbb70b1dc01e45f1d6f4fdb93d7e351ad880 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/module.h>
 
 #include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
 
 /* Serialize access to ssc_list and user count */
 static DEFINE_SPINLOCK(user_lock);
@@ -131,6 +132,13 @@ static int ssc_probe(struct platform_device *pdev)
        struct resource *regs;
        struct ssc_device *ssc;
        const struct atmel_ssc_platform_data *plat_dat;
+       struct pinctrl *pinctrl;
+
+       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+       if (IS_ERR(pinctrl)) {
+               dev_err(&pdev->dev, "Failed to request pinctrl\n");
+               return PTR_ERR(pinctrl);
+       }
 
        ssc = devm_kzalloc(&pdev->dev, sizeof(struct ssc_device), GFP_KERNEL);
        if (!ssc) {
index 18794aea606282a8925a4ef93d9596948760c3f0..e40ffd9502d17eddaf7f41618d3f33d41b8012af 100644 (file)
@@ -187,13 +187,13 @@ int mei_amthif_read(struct mei_device *dev, struct file *file,
                wait_ret = wait_event_interruptible(dev->iamthif_cl.wait,
                        (cb = mei_amthif_find_read_list_entry(dev, file)));
 
+               /* Locking again the Mutex */
+               mutex_lock(&dev->device_lock);
+
                if (wait_ret)
                        return -ERESTARTSYS;
 
                dev_dbg(&dev->pdev->dev, "woke up from sleep\n");
-
-               /* Locking again the Mutex */
-               mutex_lock(&dev->device_lock);
        }
 
 
index 9ff942a346edb59ff05d3a3fec52f052ab8849bb..83269f1d16e380e7b7c4e9ff234b456973c19f72 100644 (file)
@@ -468,6 +468,11 @@ long st_kim_start(void *kim_data)
                if (pdata->chip_enable)
                        pdata->chip_enable(kim_gdata);
 
+               /* Configure BT nShutdown to HIGH state */
+               gpio_set_value(kim_gdata->nshutdown, GPIO_LOW);
+               mdelay(5);      /* FIXME: a proper toggle */
+               gpio_set_value(kim_gdata->nshutdown, GPIO_HIGH);
+               mdelay(100);
                /* re-initialize the completion */
                INIT_COMPLETION(kim_gdata->ldisc_installed);
                /* send notification to UIM */
@@ -509,7 +514,8 @@ long st_kim_start(void *kim_data)
  *     (b) upon failure to either install ldisc or download firmware.
  *     The function is responsible to (a) notify UIM about un-installation,
  *     (b) flush UART if the ldisc was installed.
- *     (c) invoke platform's chip disabling routine.
+ *     (c) reset BT_EN - pull down nshutdown at the end.
+ *     (d) invoke platform's chip disabling routine.
  */
 long st_kim_stop(void *kim_data)
 {
@@ -541,6 +547,13 @@ long st_kim_stop(void *kim_data)
                err = -ETIMEDOUT;
        }
 
+       /* By default configure BT nShutdown to LOW state */
+       gpio_set_value(kim_gdata->nshutdown, GPIO_LOW);
+       mdelay(1);
+       gpio_set_value(kim_gdata->nshutdown, GPIO_HIGH);
+       mdelay(1);
+       gpio_set_value(kim_gdata->nshutdown, GPIO_LOW);
+
        /* platform specific disable */
        if (pdata->chip_disable)
                pdata->chip_disable(kim_gdata);
@@ -733,6 +746,20 @@ static int kim_probe(struct platform_device *pdev)
        /* refer to itself */
        kim_gdata->core_data->kim_data = kim_gdata;
 
+       /* Claim the chip enable nShutdown gpio from the system */
+       kim_gdata->nshutdown = pdata->nshutdown_gpio;
+       err = gpio_request(kim_gdata->nshutdown, "kim");
+       if (unlikely(err)) {
+               pr_err(" gpio %ld request failed ", kim_gdata->nshutdown);
+               return err;
+       }
+
+       /* Configure nShutdown GPIO as output=0 */
+       err = gpio_direction_output(kim_gdata->nshutdown, 0);
+       if (unlikely(err)) {
+               pr_err(" unable to configure gpio %ld", kim_gdata->nshutdown);
+               return err;
+       }
        /* get reference of pdev for request_firmware
         */
        kim_gdata->kim_pdev = pdev;
@@ -779,10 +806,18 @@ err_core_init:
 
 static int kim_remove(struct platform_device *pdev)
 {
+       /* free the GPIOs requested */
+       struct ti_st_plat_data  *pdata = pdev->dev.platform_data;
        struct kim_data_s       *kim_gdata;
 
        kim_gdata = dev_get_drvdata(&pdev->dev);
 
+       /* Free the Bluetooth/FM/GPIO
+        * nShutdown gpio from the system
+        */
+       gpio_free(pdata->nshutdown_gpio);
+       pr_info("nshutdown GPIO Freed");
+
        debugfs_remove_recursive(kim_debugfs_dir);
        sysfs_remove_group(&pdev->dev.kobj, &uim_attr_grp);
        pr_info("sysfs entries removed");
index de4c20b3936c0fb46d207f4a8896e97b4927be67..f8dd36102949a17bc9cdd7e1651053d5b2f01ed2 100644 (file)
@@ -50,8 +50,6 @@ struct mvsd_host {
        struct timer_list timer;
        struct mmc_host *mmc;
        struct device *dev;
-       struct resource *res;
-       int irq;
        struct clk *clk;
        int gpio_card_detect;
        int gpio_write_protect;
@@ -718,10 +716,6 @@ static int __init mvsd_probe(struct platform_device *pdev)
        if (!r || irq < 0 || !mvsd_data)
                return -ENXIO;
 
-       r = request_mem_region(r->start, SZ_1K, DRIVER_NAME);
-       if (!r)
-               return -EBUSY;
-
        mmc = mmc_alloc_host(sizeof(struct mvsd_host), &pdev->dev);
        if (!mmc) {
                ret = -ENOMEM;
@@ -731,8 +725,8 @@ static int __init mvsd_probe(struct platform_device *pdev)
        host = mmc_priv(mmc);
        host->mmc = mmc;
        host->dev = &pdev->dev;
-       host->res = r;
        host->base_clock = mvsd_data->clock / 2;
+       host->clk = ERR_PTR(-EINVAL);
 
        mmc->ops = &mvsd_ops;
 
@@ -752,7 +746,7 @@ static int __init mvsd_probe(struct platform_device *pdev)
 
        spin_lock_init(&host->lock);
 
-       host->base = ioremap(r->start, SZ_4K);
+       host->base = devm_request_and_ioremap(&pdev->dev, r);
        if (!host->base) {
                ret = -ENOMEM;
                goto out;
@@ -765,44 +759,45 @@ static int __init mvsd_probe(struct platform_device *pdev)
 
        mvsd_power_down(host);
 
-       ret = request_irq(irq, mvsd_irq, 0, DRIVER_NAME, host);
+       ret = devm_request_irq(&pdev->dev, irq, mvsd_irq, 0, DRIVER_NAME, host);
        if (ret) {
                pr_err("%s: cannot assign irq %d\n", DRIVER_NAME, irq);
                goto out;
-       } else
-               host->irq = irq;
+       }
 
        /* Not all platforms can gate the clock, so it is not
           an error if the clock does not exists. */
-       host->clk = clk_get(&pdev->dev, NULL);
-       if (!IS_ERR(host->clk)) {
+       host->clk = devm_clk_get(&pdev->dev, NULL);
+       if (!IS_ERR(host->clk))
                clk_prepare_enable(host->clk);
-       }
 
        if (mvsd_data->gpio_card_detect) {
-               ret = gpio_request(mvsd_data->gpio_card_detect,
-                                  DRIVER_NAME " cd");
+               ret = devm_gpio_request_one(&pdev->dev,
+                                           mvsd_data->gpio_card_detect,
+                                           GPIOF_IN, DRIVER_NAME " cd");
                if (ret == 0) {
-                       gpio_direction_input(mvsd_data->gpio_card_detect);
                        irq = gpio_to_irq(mvsd_data->gpio_card_detect);
-                       ret = request_irq(irq, mvsd_card_detect_irq,
-                                         IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING,
-                                         DRIVER_NAME " cd", host);
+                       ret = devm_request_irq(&pdev->dev, irq,
+                                              mvsd_card_detect_irq,
+                                              IRQ_TYPE_EDGE_RISING |
+                                              IRQ_TYPE_EDGE_FALLING,
+                                              DRIVER_NAME " cd", host);
                        if (ret == 0)
                                host->gpio_card_detect =
                                        mvsd_data->gpio_card_detect;
                        else
-                               gpio_free(mvsd_data->gpio_card_detect);
+                               devm_gpio_free(&pdev->dev,
+                                              mvsd_data->gpio_card_detect);
                }
        }
        if (!host->gpio_card_detect)
                mmc->caps |= MMC_CAP_NEEDS_POLL;
 
        if (mvsd_data->gpio_write_protect) {
-               ret = gpio_request(mvsd_data->gpio_write_protect,
-                                  DRIVER_NAME " wp");
+               ret = devm_gpio_request_one(&pdev->dev,
+                                           mvsd_data->gpio_write_protect,
+                                           GPIOF_IN, DRIVER_NAME " wp");
                if (ret == 0) {
-                       gpio_direction_input(mvsd_data->gpio_write_protect);
                        host->gpio_write_protect =
                                mvsd_data->gpio_write_protect;
                }
@@ -824,26 +819,11 @@ static int __init mvsd_probe(struct platform_device *pdev)
        return 0;
 
 out:
-       if (host) {
-               if (host->irq)
-                       free_irq(host->irq, host);
-               if (host->gpio_card_detect) {
-                       free_irq(gpio_to_irq(host->gpio_card_detect), host);
-                       gpio_free(host->gpio_card_detect);
-               }
-               if (host->gpio_write_protect)
-                       gpio_free(host->gpio_write_protect);
-               if (host->base)
-                       iounmap(host->base);
-       }
-       if (r)
-               release_resource(r);
-       if (mmc)
-               if (!IS_ERR_OR_NULL(host->clk)) {
+       if (mmc) {
+               if (!IS_ERR(host->clk))
                        clk_disable_unprepare(host->clk);
-                       clk_put(host->clk);
-               }
                mmc_free_host(mmc);
+       }
 
        return ret;
 }
@@ -852,28 +832,16 @@ static int __exit mvsd_remove(struct platform_device *pdev)
 {
        struct mmc_host *mmc = platform_get_drvdata(pdev);
 
-       if (mmc) {
-               struct mvsd_host *host = mmc_priv(mmc);
+       struct mvsd_host *host = mmc_priv(mmc);
 
-               if (host->gpio_card_detect) {
-                       free_irq(gpio_to_irq(host->gpio_card_detect), host);
-                       gpio_free(host->gpio_card_detect);
-               }
-               mmc_remove_host(mmc);
-               free_irq(host->irq, host);
-               if (host->gpio_write_protect)
-                       gpio_free(host->gpio_write_protect);
-               del_timer_sync(&host->timer);
-               mvsd_power_down(host);
-               iounmap(host->base);
-               release_resource(host->res);
+       mmc_remove_host(mmc);
+       del_timer_sync(&host->timer);
+       mvsd_power_down(host);
+
+       if (!IS_ERR(host->clk))
+               clk_disable_unprepare(host->clk);
+       mmc_free_host(mmc);
 
-               if (!IS_ERR(host->clk)) {
-                       clk_disable_unprepare(host->clk);
-                       clk_put(host->clk);
-               }
-               mmc_free_host(mmc);
-       }
        platform_set_drvdata(pdev, NULL);
        return 0;
 }
index 571915dfb218382f0d29ea72136b05669b9271ea..f74b5adca64232dd4a8ab7d4a397281b8f02c7a0 100644 (file)
@@ -1060,26 +1060,6 @@ static int sd_wait_voltage_stable_2(struct realtek_pci_sdmmc *host)
        return 0;
 }
 
-static int sd_change_bank_voltage(struct realtek_pci_sdmmc *host, u8 voltage)
-{
-       struct rtsx_pcr *pcr = host->pcr;
-       int err;
-
-       if (voltage == SD_IO_3V3) {
-               err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24);
-               if (err < 0)
-                       return err;
-       } else if (voltage == SD_IO_1V8) {
-               err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24);
-               if (err < 0)
-                       return err;
-       } else {
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
 static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
 {
        struct realtek_pci_sdmmc *host = mmc_priv(mmc);
@@ -1098,11 +1078,11 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
        rtsx_pci_start_run(pcr);
 
        if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)
-               voltage = SD_IO_3V3;
+               voltage = OUTPUT_3V3;
        else
-               voltage = SD_IO_1V8;
+               voltage = OUTPUT_1V8;
 
-       if (voltage == SD_IO_1V8) {
+       if (voltage == OUTPUT_1V8) {
                err = rtsx_pci_write_register(pcr,
                                SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_B);
                if (err < 0)
@@ -1113,11 +1093,11 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
                        goto out;
        }
 
-       err = sd_change_bank_voltage(host, voltage);
+       err = rtsx_pci_switch_output_voltage(pcr, voltage);
        if (err < 0)
                goto out;
 
-       if (voltage == SD_IO_1V8) {
+       if (voltage == OUTPUT_1V8) {
                err = sd_wait_voltage_stable_2(host);
                if (err < 0)
                        goto out;
index 27f80cd8aef3770ee1d5c67c030088407c0967ba..46dcb54c32ec7994610e3c4a20fbaf85aa879b09 100644 (file)
@@ -272,6 +272,7 @@ config MTD_DOCG3
        tristate "M-Systems Disk-On-Chip G3"
        select BCH
        select BCH_CONST_PARAMS
+       select BITREVERSE
        ---help---
          This provides an MTD device driver for the M-Systems DiskOnChip
          G3 devices.
index 67cc73c18ddd6e0ca10d0438403d61436971b734..7901d72c92425dc41485947468231cf7776db372 100644 (file)
@@ -170,7 +170,7 @@ static int of_flash_probe(struct platform_device *dev)
        resource_size_t res_size;
        struct mtd_part_parser_data ppdata;
        bool map_indirect;
-       const char *mtd_name;
+       const char *mtd_name = NULL;
 
        match = of_match_device(of_flash_match, &dev->dev);
        if (!match)
index 86c9a79b89b3e863f9377eebb8c9b6ae18500dd9..595de4012e71971c6453a5499334c28e6185390b 100644 (file)
@@ -17,8 +17,8 @@
 #include "bcm47xxnflash.h"
 
 /* Broadcom uses 1'000'000 but it seems to be too many. Tests on WNDR4500 has
- * shown 164 retries as maxiumum. */
-#define NFLASH_READY_RETRIES           1000
+ * shown ~1000 retries as maxiumum. */
+#define NFLASH_READY_RETRIES           10000
 
 #define NFLASH_SECTOR_SIZE             512
 
index 3502606f64806a06ad426a112174c0117482e8dc..feae55c7b88046b3169820449b002d0ae562a5c4 100644 (file)
@@ -523,7 +523,7 @@ static struct nand_ecclayout hwecc4_2048 __initconst = {
 static const struct of_device_id davinci_nand_of_match[] = {
        {.compatible = "ti,davinci-nand", },
        {},
-}
+};
 MODULE_DEVICE_TABLE(of, davinci_nand_of_match);
 
 static struct davinci_nand_pdata
index 8323ac991ad154d7304958059cab37af6f2d6541..3766682a02898135ce4274b5a0baeac0eee772c9 100644 (file)
@@ -2857,8 +2857,11 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
        int i;
        int val;
 
-       /* ONFI need to be probed in 8 bits mode */
-       WARN_ON(chip->options & NAND_BUSWIDTH_16);
+       /* ONFI need to be probed in 8 bits mode, and 16 bits should be selected with NAND_BUSWIDTH_AUTO */
+       if (chip->options & NAND_BUSWIDTH_16) {
+               pr_err("Trying ONFI probe in 16 bits mode, aborting !\n");
+               return 0;
+       }
        /* Try ONFI for unknown chip or LP */
        chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
        if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' ||
index 5233b8f58d773b6edb44f306f477674f05415084..58607f196c9ee5e5c3d214c70ca9abe0e2a5b6d0 100644 (file)
@@ -960,7 +960,7 @@ static int c_can_handle_bus_err(struct net_device *dev,
                break;
        case LEC_ACK_ERROR:
                netdev_dbg(dev, "ack error\n");
-               cf->data[2] |= (CAN_ERR_PROT_LOC_ACK |
+               cf->data[3] |= (CAN_ERR_PROT_LOC_ACK |
                                CAN_ERR_PROT_LOC_ACK_DEL);
                break;
        case LEC_BIT1_ERROR:
@@ -973,7 +973,7 @@ static int c_can_handle_bus_err(struct net_device *dev,
                break;
        case LEC_CRC_ERROR:
                netdev_dbg(dev, "CRC error\n");
-               cf->data[2] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
+               cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
                                CAN_ERR_PROT_LOC_CRC_DEL);
                break;
        default:
index 7d1748575b1fffc45f38351ea3f071269545548a..5c314a961970b0041c7776da283979f00c4114f8 100644 (file)
@@ -560,7 +560,7 @@ static void pch_can_error(struct net_device *ndev, u32 status)
                stats->rx_errors++;
                break;
        case PCH_CRC_ERR:
-               cf->data[2] |= CAN_ERR_PROT_LOC_CRC_SEQ |
+               cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ |
                               CAN_ERR_PROT_LOC_CRC_DEL;
                priv->can.can_stats.bus_error++;
                stats->rx_errors++;
index f898c6363729b2a41be58c824f3f3bb145a95897..300581b24ff32860447e3646079024646e77a592 100644 (file)
@@ -746,12 +746,12 @@ static int ti_hecc_error(struct net_device *ndev, int int_status,
                }
                if (err_status & HECC_CANES_CRCE) {
                        hecc_set_bit(priv, HECC_CANES, HECC_CANES_CRCE);
-                       cf->data[2] |= CAN_ERR_PROT_LOC_CRC_SEQ |
+                       cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ |
                                        CAN_ERR_PROT_LOC_CRC_DEL;
                }
                if (err_status & HECC_CANES_ACKE) {
                        hecc_set_bit(priv, HECC_CANES, HECC_CANES_ACKE);
-                       cf->data[2] |= CAN_ERR_PROT_LOC_ACK |
+                       cf->data[3] |= CAN_ERR_PROT_LOC_ACK |
                                        CAN_ERR_PROT_LOC_ACK_DEL;
                }
        }
index 66df936380859b55ab0b8b15595319bfeb98c98a..ffd8de28a76ad2c69a96daab3c59dbe29127faa1 100644 (file)
@@ -432,7 +432,7 @@ static int tc574_config(struct pcmcia_device *link)
        netdev_info(dev, "%s at io %#3lx, irq %d, hw_addr %pM\n",
                    cardname, dev->base_addr, dev->irq, dev->dev_addr);
        netdev_info(dev, " %dK FIFO split %s Rx:Tx, %sMII interface.\n",
-                   8 << config & Ram_size,
+                   8 << (config & Ram_size),
                    ram_split[(config & Ram_split) >> Ram_split_shift],
                    config & Autoselect ? "autoselect " : "");
 
index e49c0eff040b50a95bdacc050f8ff3a83f172a8e..a9481606bbcd713f6fa446325521b2309b64c856 100644 (file)
@@ -61,6 +61,7 @@ config BFIN_RX_DESC_NUM
 
 config BFIN_MAC_USE_HWSTAMP
        bool "Use IEEE 1588 hwstamp"
+       depends on BFIN_MAC && BF518
        select PTP_1588_CLOCK
        default y
        ---help---
index 01588b66a38c0fea2d489b869d730fb8966c3419..f771ddfba646b0a773e89f3365b8add151087836 100644 (file)
@@ -80,12 +80,37 @@ static inline void bnx2x_move_fp(struct bnx2x *bp, int from, int to)
                new_txdata_index = new_max_eth_txqs + FCOE_TXQ_IDX_OFFSET;
        }
 
-       memcpy(&bp->bnx2x_txq[old_txdata_index],
-              &bp->bnx2x_txq[new_txdata_index],
+       memcpy(&bp->bnx2x_txq[new_txdata_index],
+              &bp->bnx2x_txq[old_txdata_index],
               sizeof(struct bnx2x_fp_txdata));
        to_fp->txdata_ptr[0] = &bp->bnx2x_txq[new_txdata_index];
 }
 
+/**
+ * bnx2x_shrink_eth_fp - guarantees fastpath structures stay intact
+ *
+ * @bp:        driver handle
+ * @delta:     number of eth queues which were not allocated
+ */
+static void bnx2x_shrink_eth_fp(struct bnx2x *bp, int delta)
+{
+       int i, cos, old_eth_num = BNX2X_NUM_ETH_QUEUES(bp);
+
+       /* Queue pointer cannot be re-set on an fp-basis, as moving pointer
+        * backward along the array could cause memory to be overriden
+        */
+       for (cos = 1; cos < bp->max_cos; cos++) {
+               for (i = 0; i < old_eth_num - delta; i++) {
+                       struct bnx2x_fastpath *fp = &bp->fp[i];
+                       int new_idx = cos * (old_eth_num - delta) + i;
+
+                       memcpy(&bp->bnx2x_txq[new_idx], fp->txdata_ptr[cos],
+                              sizeof(struct bnx2x_fp_txdata));
+                       fp->txdata_ptr[cos] = &bp->bnx2x_txq[new_idx];
+               }
+       }
+}
+
 int load_count[2][3] = { {0} }; /* per-path: 0-common, 1-port0, 2-port1 */
 
 /* free skb in the packet ring at pos idx
@@ -3863,6 +3888,7 @@ int bnx2x_alloc_fp_mem(struct bnx2x *bp)
                int delta = BNX2X_NUM_ETH_QUEUES(bp) - i;
 
                WARN_ON(delta < 0);
+               bnx2x_shrink_eth_fp(bp, delta);
                if (CNIC_SUPPORT(bp))
                        /* move non eth FPs next to last eth FP
                         * must be done in that order
index 277f17e3c8f850bac954db7d99c55b7f06d194eb..a427b49a886ccea8a6d904daa473a59f5a676c5b 100644 (file)
@@ -2777,10 +2777,10 @@ static int bnx2x_set_rss_flags(struct bnx2x *bp, struct ethtool_rxnfc *info)
                } else if ((info->flow_type == UDP_V6_FLOW) &&
                           (bp->rss_conf_obj.udp_rss_v6 != udp_rss_requested)) {
                        bp->rss_conf_obj.udp_rss_v6 = udp_rss_requested;
-                       return bnx2x_config_rss_pf(bp, &bp->rss_conf_obj, 0);
                        DP(BNX2X_MSG_ETHTOOL,
                           "rss re-configured, UDP 4-tupple %s\n",
                           udp_rss_requested ? "enabled" : "disabled");
+                       return bnx2x_config_rss_pf(bp, &bp->rss_conf_obj, 0);
                } else {
                        return 0;
                }
index 940ef859dc6054338b884abce2c519e0ba4a89ea..5523da3afcdccd23f4b90c6191f722d02063c1d3 100644 (file)
@@ -127,6 +127,17 @@ MODULE_PARM_DESC(debug, " Default debug msglevel");
 
 struct workqueue_struct *bnx2x_wq;
 
+struct bnx2x_mac_vals {
+       u32 xmac_addr;
+       u32 xmac_val;
+       u32 emac_addr;
+       u32 emac_val;
+       u32 umac_addr;
+       u32 umac_val;
+       u32 bmac_addr;
+       u32 bmac_val[2];
+};
+
 enum bnx2x_board_type {
        BCM57710 = 0,
        BCM57711,
@@ -9420,12 +9431,19 @@ static inline void bnx2x_undi_int_disable(struct bnx2x *bp)
                bnx2x_undi_int_disable_e1h(bp);
 }
 
-static void bnx2x_prev_unload_close_mac(struct bnx2x *bp)
+static void bnx2x_prev_unload_close_mac(struct bnx2x *bp,
+                                       struct bnx2x_mac_vals *vals)
 {
        u32 val, base_addr, offset, mask, reset_reg;
        bool mac_stopped = false;
        u8 port = BP_PORT(bp);
 
+       /* reset addresses as they also mark which values were changed */
+       vals->bmac_addr = 0;
+       vals->umac_addr = 0;
+       vals->xmac_addr = 0;
+       vals->emac_addr = 0;
+
        reset_reg = REG_RD(bp, MISC_REG_RESET_REG_2);
 
        if (!CHIP_IS_E3(bp)) {
@@ -9447,14 +9465,18 @@ static void bnx2x_prev_unload_close_mac(struct bnx2x *bp)
                         */
                        wb_data[0] = REG_RD(bp, base_addr + offset);
                        wb_data[1] = REG_RD(bp, base_addr + offset + 0x4);
+                       vals->bmac_addr = base_addr + offset;
+                       vals->bmac_val[0] = wb_data[0];
+                       vals->bmac_val[1] = wb_data[1];
                        wb_data[0] &= ~BMAC_CONTROL_RX_ENABLE;
-                       REG_WR(bp, base_addr + offset, wb_data[0]);
-                       REG_WR(bp, base_addr + offset + 0x4, wb_data[1]);
+                       REG_WR(bp, vals->bmac_addr, wb_data[0]);
+                       REG_WR(bp, vals->bmac_addr + 0x4, wb_data[1]);
 
                }
                BNX2X_DEV_INFO("Disable emac Rx\n");
-               REG_WR(bp, NIG_REG_NIG_EMAC0_EN + BP_PORT(bp)*4, 0);
-
+               vals->emac_addr = NIG_REG_NIG_EMAC0_EN + BP_PORT(bp)*4;
+               vals->emac_val = REG_RD(bp, vals->emac_addr);
+               REG_WR(bp, vals->emac_addr, 0);
                mac_stopped = true;
        } else {
                if (reset_reg & MISC_REGISTERS_RESET_REG_2_XMAC) {
@@ -9465,14 +9487,18 @@ static void bnx2x_prev_unload_close_mac(struct bnx2x *bp)
                               val & ~(1 << 1));
                        REG_WR(bp, base_addr + XMAC_REG_PFC_CTRL_HI,
                               val | (1 << 1));
-                       REG_WR(bp, base_addr + XMAC_REG_CTRL, 0);
+                       vals->xmac_addr = base_addr + XMAC_REG_CTRL;
+                       vals->xmac_val = REG_RD(bp, vals->xmac_addr);
+                       REG_WR(bp, vals->xmac_addr, 0);
                        mac_stopped = true;
                }
                mask = MISC_REGISTERS_RESET_REG_2_UMAC0 << port;
                if (mask & reset_reg) {
                        BNX2X_DEV_INFO("Disable umac Rx\n");
                        base_addr = BP_PORT(bp) ? GRCBASE_UMAC1 : GRCBASE_UMAC0;
-                       REG_WR(bp, base_addr + UMAC_REG_COMMAND_CONFIG, 0);
+                       vals->umac_addr = base_addr + UMAC_REG_COMMAND_CONFIG;
+                       vals->umac_val = REG_RD(bp, vals->umac_addr);
+                       REG_WR(bp, vals->umac_addr, 0);
                        mac_stopped = true;
                }
        }
@@ -9664,12 +9690,16 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp)
 {
        u32 reset_reg, tmp_reg = 0, rc;
        bool prev_undi = false;
+       struct bnx2x_mac_vals mac_vals;
+
        /* It is possible a previous function received 'common' answer,
         * but hasn't loaded yet, therefore creating a scenario of
         * multiple functions receiving 'common' on the same path.
         */
        BNX2X_DEV_INFO("Common unload Flow\n");
 
+       memset(&mac_vals, 0, sizeof(mac_vals));
+
        if (bnx2x_prev_is_path_marked(bp))
                return bnx2x_prev_mcp_done(bp);
 
@@ -9680,7 +9710,10 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp)
                u32 timer_count = 1000;
 
                /* Close the MAC Rx to prevent BRB from filling up */
-               bnx2x_prev_unload_close_mac(bp);
+               bnx2x_prev_unload_close_mac(bp, &mac_vals);
+
+               /* close LLH filters towards the BRB */
+               bnx2x_set_rx_filter(&bp->link_params, 0);
 
                /* Check if the UNDI driver was previously loaded
                 * UNDI driver initializes CID offset for normal bell to 0x7
@@ -9727,6 +9760,17 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp)
        /* No packets are in the pipeline, path is ready for reset */
        bnx2x_reset_common(bp);
 
+       if (mac_vals.xmac_addr)
+               REG_WR(bp, mac_vals.xmac_addr, mac_vals.xmac_val);
+       if (mac_vals.umac_addr)
+               REG_WR(bp, mac_vals.umac_addr, mac_vals.umac_val);
+       if (mac_vals.emac_addr)
+               REG_WR(bp, mac_vals.emac_addr, mac_vals.emac_val);
+       if (mac_vals.bmac_addr) {
+               REG_WR(bp, mac_vals.bmac_addr, mac_vals.bmac_val[0]);
+               REG_WR(bp, mac_vals.bmac_addr + 4, mac_vals.bmac_val[1]);
+       }
+
        rc = bnx2x_prev_mark_path(bp, prev_undi);
        if (rc) {
                bnx2x_prev_mcp_done(bp);
index 78ea90c40e1902af09272a80b09010da52ae6c6b..bdb086934cd95de72cd1aa02cf1cb4b6baaaafc3 100644 (file)
@@ -1283,14 +1283,26 @@ static int tg3_phy_auxctl_write(struct tg3 *tp, int reg, u32 set)
        return tg3_writephy(tp, MII_TG3_AUX_CTRL, set | reg);
 }
 
-#define TG3_PHY_AUXCTL_SMDSP_ENABLE(tp) \
-       tg3_phy_auxctl_write((tp), MII_TG3_AUXCTL_SHDWSEL_AUXCTL, \
-                            MII_TG3_AUXCTL_ACTL_SMDSP_ENA | \
-                            MII_TG3_AUXCTL_ACTL_TX_6DB)
+static int tg3_phy_toggle_auxctl_smdsp(struct tg3 *tp, bool enable)
+{
+       u32 val;
+       int err;
 
-#define TG3_PHY_AUXCTL_SMDSP_DISABLE(tp) \
-       tg3_phy_auxctl_write((tp), MII_TG3_AUXCTL_SHDWSEL_AUXCTL, \
-                            MII_TG3_AUXCTL_ACTL_TX_6DB);
+       err = tg3_phy_auxctl_read(tp, MII_TG3_AUXCTL_SHDWSEL_AUXCTL, &val);
+
+       if (err)
+               return err;
+       if (enable)
+
+               val |= MII_TG3_AUXCTL_ACTL_SMDSP_ENA;
+       else
+               val &= ~MII_TG3_AUXCTL_ACTL_SMDSP_ENA;
+
+       err = tg3_phy_auxctl_write((tp), MII_TG3_AUXCTL_SHDWSEL_AUXCTL,
+                                  val | MII_TG3_AUXCTL_ACTL_TX_6DB);
+
+       return err;
+}
 
 static int tg3_bmcr_reset(struct tg3 *tp)
 {
@@ -2223,7 +2235,7 @@ static void tg3_phy_apply_otp(struct tg3 *tp)
 
        otp = tp->phy_otp;
 
-       if (TG3_PHY_AUXCTL_SMDSP_ENABLE(tp))
+       if (tg3_phy_toggle_auxctl_smdsp(tp, true))
                return;
 
        phy = ((otp & TG3_OTP_AGCTGT_MASK) >> TG3_OTP_AGCTGT_SHIFT);
@@ -2248,7 +2260,7 @@ static void tg3_phy_apply_otp(struct tg3 *tp)
              ((otp & TG3_OTP_RCOFF_MASK) >> TG3_OTP_RCOFF_SHIFT);
        tg3_phydsp_write(tp, MII_TG3_DSP_EXP97, phy);
 
-       TG3_PHY_AUXCTL_SMDSP_DISABLE(tp);
+       tg3_phy_toggle_auxctl_smdsp(tp, false);
 }
 
 static void tg3_phy_eee_adjust(struct tg3 *tp, u32 current_link_up)
@@ -2284,9 +2296,9 @@ static void tg3_phy_eee_adjust(struct tg3 *tp, u32 current_link_up)
 
        if (!tp->setlpicnt) {
                if (current_link_up == 1 &&
-                  !TG3_PHY_AUXCTL_SMDSP_ENABLE(tp)) {
+                  !tg3_phy_toggle_auxctl_smdsp(tp, true)) {
                        tg3_phydsp_write(tp, MII_TG3_DSP_TAP26, 0x0000);
-                       TG3_PHY_AUXCTL_SMDSP_DISABLE(tp);
+                       tg3_phy_toggle_auxctl_smdsp(tp, false);
                }
 
                val = tr32(TG3_CPMU_EEE_MODE);
@@ -2302,11 +2314,11 @@ static void tg3_phy_eee_enable(struct tg3 *tp)
            (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
             GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 ||
             tg3_flag(tp, 57765_CLASS)) &&
-           !TG3_PHY_AUXCTL_SMDSP_ENABLE(tp)) {
+           !tg3_phy_toggle_auxctl_smdsp(tp, true)) {
                val = MII_TG3_DSP_TAP26_ALNOKO |
                      MII_TG3_DSP_TAP26_RMRXSTO;
                tg3_phydsp_write(tp, MII_TG3_DSP_TAP26, val);
-               TG3_PHY_AUXCTL_SMDSP_DISABLE(tp);
+               tg3_phy_toggle_auxctl_smdsp(tp, false);
        }
 
        val = tr32(TG3_CPMU_EEE_MODE);
@@ -2450,7 +2462,7 @@ static int tg3_phy_reset_5703_4_5(struct tg3 *tp)
                tg3_writephy(tp, MII_CTRL1000,
                             CTL1000_AS_MASTER | CTL1000_ENABLE_MASTER);
 
-               err = TG3_PHY_AUXCTL_SMDSP_ENABLE(tp);
+               err = tg3_phy_toggle_auxctl_smdsp(tp, true);
                if (err)
                        return err;
 
@@ -2471,7 +2483,7 @@ static int tg3_phy_reset_5703_4_5(struct tg3 *tp)
        tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8200);
        tg3_writephy(tp, MII_TG3_DSP_CONTROL, 0x0000);
 
-       TG3_PHY_AUXCTL_SMDSP_DISABLE(tp);
+       tg3_phy_toggle_auxctl_smdsp(tp, false);
 
        tg3_writephy(tp, MII_CTRL1000, phy9_orig);
 
@@ -2572,10 +2584,10 @@ static int tg3_phy_reset(struct tg3 *tp)
 
 out:
        if ((tp->phy_flags & TG3_PHYFLG_ADC_BUG) &&
-           !TG3_PHY_AUXCTL_SMDSP_ENABLE(tp)) {
+           !tg3_phy_toggle_auxctl_smdsp(tp, true)) {
                tg3_phydsp_write(tp, 0x201f, 0x2aaa);
                tg3_phydsp_write(tp, 0x000a, 0x0323);
-               TG3_PHY_AUXCTL_SMDSP_DISABLE(tp);
+               tg3_phy_toggle_auxctl_smdsp(tp, false);
        }
 
        if (tp->phy_flags & TG3_PHYFLG_5704_A0_BUG) {
@@ -2584,14 +2596,14 @@ out:
        }
 
        if (tp->phy_flags & TG3_PHYFLG_BER_BUG) {
-               if (!TG3_PHY_AUXCTL_SMDSP_ENABLE(tp)) {
+               if (!tg3_phy_toggle_auxctl_smdsp(tp, true)) {
                        tg3_phydsp_write(tp, 0x000a, 0x310b);
                        tg3_phydsp_write(tp, 0x201f, 0x9506);
                        tg3_phydsp_write(tp, 0x401f, 0x14e2);
-                       TG3_PHY_AUXCTL_SMDSP_DISABLE(tp);
+                       tg3_phy_toggle_auxctl_smdsp(tp, false);
                }
        } else if (tp->phy_flags & TG3_PHYFLG_JITTER_BUG) {
-               if (!TG3_PHY_AUXCTL_SMDSP_ENABLE(tp)) {
+               if (!tg3_phy_toggle_auxctl_smdsp(tp, true)) {
                        tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x000a);
                        if (tp->phy_flags & TG3_PHYFLG_ADJUST_TRIM) {
                                tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x110b);
@@ -2600,7 +2612,7 @@ out:
                        } else
                                tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x010b);
 
-                       TG3_PHY_AUXCTL_SMDSP_DISABLE(tp);
+                       tg3_phy_toggle_auxctl_smdsp(tp, false);
                }
        }
 
@@ -4009,7 +4021,7 @@ static int tg3_phy_autoneg_cfg(struct tg3 *tp, u32 advertise, u32 flowctrl)
        tw32(TG3_CPMU_EEE_MODE,
             tr32(TG3_CPMU_EEE_MODE) & ~TG3_CPMU_EEEMD_LPI_ENABLE);
 
-       err = TG3_PHY_AUXCTL_SMDSP_ENABLE(tp);
+       err = tg3_phy_toggle_auxctl_smdsp(tp, true);
        if (!err) {
                u32 err2;
 
@@ -4042,7 +4054,7 @@ static int tg3_phy_autoneg_cfg(struct tg3 *tp, u32 advertise, u32 flowctrl)
                                                 MII_TG3_DSP_CH34TP2_HIBW01);
                }
 
-               err2 = TG3_PHY_AUXCTL_SMDSP_DISABLE(tp);
+               err2 = tg3_phy_toggle_auxctl_smdsp(tp, false);
                if (!err)
                        err = err2;
        }
@@ -6950,6 +6962,9 @@ static void tg3_poll_controller(struct net_device *dev)
        int i;
        struct tg3 *tp = netdev_priv(dev);
 
+       if (tg3_irq_sync(tp))
+               return;
+
        for (i = 0; i < tp->irq_cnt; i++)
                tg3_interrupt(tp->napi[i].irq_vec, &tp->napi[i]);
 }
@@ -16367,6 +16382,7 @@ static int tg3_init_one(struct pci_dev *pdev,
        tp->pm_cap = pm_cap;
        tp->rx_mode = TG3_DEF_RX_MODE;
        tp->tx_mode = TG3_DEF_TX_MODE;
+       tp->irq_sync = 1;
 
        if (tg3_debug > 0)
                tp->msg_enable = tg3_debug;
index b407043ce9b0df3c1286fb3c0051f012cfa5e5b0..f7f02900f6508f0c8a7f8b6b41558aa85550e291 100644 (file)
@@ -548,6 +548,10 @@ static int desc_get_rx_status(struct xgmac_priv *priv, struct xgmac_dma_desc *p)
                return -1;
        }
 
+       /* All frames should fit into a single buffer */
+       if (!(status & RXDESC_FIRST_SEG) || !(status & RXDESC_LAST_SEG))
+               return -1;
+
        /* Check if packet has checksum already */
        if ((status & RXDESC_FRAME_TYPE) && (status & RXDESC_EXT_STATUS) &&
                !(ext_status & RXDESC_IP_PAYLOAD_MASK))
index f0718e1a83696b0e2ec0b8fad9121288ecda81bc..c306df7d45684ce1fcd2f25012e8100da98abdd2 100644 (file)
@@ -1994,9 +1994,20 @@ static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
 {
        const struct port_info *pi = netdev_priv(dev);
        struct adapter *adap = pi->adapter;
-
-       return set_rxq_intr_params(adap, &adap->sge.ethrxq[pi->first_qset].rspq,
-                       c->rx_coalesce_usecs, c->rx_max_coalesced_frames);
+       struct sge_rspq *q;
+       int i;
+       int r = 0;
+
+       for (i = pi->first_qset; i < pi->first_qset + pi->nqsets; i++) {
+               q = &adap->sge.ethrxq[i].rspq;
+               r = set_rxq_intr_params(adap, q, c->rx_coalesce_usecs,
+                       c->rx_max_coalesced_frames);
+               if (r) {
+                       dev_err(&dev->dev, "failed to set coalesce %d\n", r);
+                       break;
+               }
+       }
+       return r;
 }
 
 static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
index 3bc1912afba936a629b16c7193b2ccacedbe9f76..4eba17b83ba8dae6ce466dd3eecbc957a1c25d0d 100644 (file)
@@ -190,6 +190,7 @@ struct be_eq_obj {
 
        u8 idx;                 /* array index */
        u16 tx_budget;
+       u16 spurious_intr;
        struct napi_struct napi;
        struct be_adapter *adapter;
 } ____cacheline_aligned_in_smp;
index 9dca22be81253bc1a66f5e9e136c4462acdeaed9..5c995700e53440331a9fe4cef3a8f68886fae466 100644 (file)
@@ -2026,19 +2026,30 @@ static irqreturn_t be_intx(int irq, void *dev)
        struct be_adapter *adapter = eqo->adapter;
        int num_evts = 0;
 
-       /* On Lancer, clear-intr bit of the EQ DB does not work.
-        * INTx is de-asserted only on notifying num evts.
+       /* IRQ is not expected when NAPI is scheduled as the EQ
+        * will not be armed.
+        * But, this can happen on Lancer INTx where it takes
+        * a while to de-assert INTx or in BE2 where occasionaly
+        * an interrupt may be raised even when EQ is unarmed.
+        * If NAPI is already scheduled, then counting & notifying
+        * events will orphan them.
         */
-       if (lancer_chip(adapter))
+       if (napi_schedule_prep(&eqo->napi)) {
                num_evts = events_get(eqo);
+               __napi_schedule(&eqo->napi);
+               if (num_evts)
+                       eqo->spurious_intr = 0;
+       }
+       be_eq_notify(adapter, eqo->q.id, false, true, num_evts);
 
-       /* The EQ-notify may not de-assert INTx rightaway, causing
-        * the ISR to be invoked again. So, return HANDLED even when
-        * num_evts is zero.
+       /* Return IRQ_HANDLED only for the the first spurious intr
+        * after a valid intr to stop the kernel from branding
+        * this irq as a bad one!
         */
-       be_eq_notify(adapter, eqo->q.id, false, true, num_evts);
-       napi_schedule(&eqo->napi);
-       return IRQ_HANDLED;
+       if (num_evts || eqo->spurious_intr++ == 0)
+               return IRQ_HANDLED;
+       else
+               return IRQ_NONE;
 }
 
 static irqreturn_t be_msix(int irq, void *dev)
index f3a632bf8d96f8bd76983e8990c80f1cefe984ac..687c83d1bdabb7589ac4492e9763b863ac5fcef0 100644 (file)
@@ -32,7 +32,7 @@
 
 obj-$(CONFIG_IXGBE) += ixgbe.o
 
-ixgbe-objs := ixgbe_main.o ixgbe_common.o ixgbe_ethtool.o ixgbe_debugfs.o\
+ixgbe-objs := ixgbe_main.o ixgbe_common.o ixgbe_ethtool.o \
               ixgbe_82599.o ixgbe_82598.o ixgbe_phy.o ixgbe_sriov.o \
               ixgbe_mbx.o ixgbe_x540.o ixgbe_lib.o ixgbe_ptp.o
 
@@ -40,4 +40,5 @@ ixgbe-$(CONFIG_IXGBE_DCB) +=  ixgbe_dcb.o ixgbe_dcb_82598.o \
                               ixgbe_dcb_82599.o ixgbe_dcb_nl.o
 
 ixgbe-$(CONFIG_IXGBE_HWMON) += ixgbe_sysfs.o
+ixgbe-$(CONFIG_DEBUG_FS) += ixgbe_debugfs.o
 ixgbe-$(CONFIG_FCOE:m=y) += ixgbe_fcoe.o
index 50aa546b8c7a589100be6bd88eac590adf314d98..3504686d3af515ed7f5e04924be29cd9098f2061 100644 (file)
@@ -24,9 +24,6 @@
   Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
 
 *******************************************************************************/
-
-#ifdef CONFIG_DEBUG_FS
-
 #include <linux/debugfs.h>
 #include <linux/module.h>
 
@@ -277,5 +274,3 @@ void ixgbe_dbg_exit(void)
 {
        debugfs_remove_recursive(ixgbe_dbg_root);
 }
-
-#endif /* CONFIG_DEBUG_FS */
index 1a751c9d09c47f319e45d76a9c2bbad8573016d3..bb9256a1b0a9b467412ce127911ed3cfb53a7053 100644 (file)
@@ -660,11 +660,11 @@ int ixgbe_ptp_hwtstamp_ioctl(struct ixgbe_adapter *adapter,
                break;
        case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
                tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_L4_V1;
-               tsync_rx_mtrl = IXGBE_RXMTRL_V1_SYNC_MSG;
+               tsync_rx_mtrl |= IXGBE_RXMTRL_V1_SYNC_MSG;
                break;
        case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
                tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_L4_V1;
-               tsync_rx_mtrl = IXGBE_RXMTRL_V1_DELAY_REQ_MSG;
+               tsync_rx_mtrl |= IXGBE_RXMTRL_V1_DELAY_REQ_MSG;
                break;
        case HWTSTAMP_FILTER_PTP_V2_EVENT:
        case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
index 2b799f4f1c37100f06eab93aef628d4124182230..6771b69f40d562bf58909a1f806bddd1ace9ea65 100644 (file)
@@ -630,10 +630,15 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev)
                ring->tx_csum++;
        }
 
-       /* Copy dst mac address to wqe */
-       ethh = (struct ethhdr *)skb->data;
-       tx_desc->ctrl.srcrb_flags16[0] = get_unaligned((__be16 *)ethh->h_dest);
-       tx_desc->ctrl.imm = get_unaligned((__be32 *)(ethh->h_dest + 2));
+       if (mlx4_is_mfunc(mdev->dev) || priv->validate_loopback) {
+               /* Copy dst mac address to wqe. This allows loopback in eSwitch,
+                * so that VFs and PF can communicate with each other
+                */
+               ethh = (struct ethhdr *)skb->data;
+               tx_desc->ctrl.srcrb_flags16[0] = get_unaligned((__be16 *)ethh->h_dest);
+               tx_desc->ctrl.imm = get_unaligned((__be32 *)(ethh->h_dest + 2));
+       }
+
        /* Handle LSO (TSO) packets */
        if (lso_header_size) {
                /* Mark opcode as LSO */
index e1bafffbc3b1d261f4cf0972dd3f95c942c5fc3d..a6542d75374cdce3f7a17c21f7022fadff109850 100644 (file)
@@ -1790,15 +1790,8 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev)
        int i;
 
        if (msi_x) {
-               /* In multifunction mode each function gets 2 msi-X vectors
-                * one for data path completions anf the other for asynch events
-                * or command completions */
-               if (mlx4_is_mfunc(dev)) {
-                       nreq = 2;
-               } else {
-                       nreq = min_t(int, dev->caps.num_eqs -
-                                    dev->caps.reserved_eqs, nreq);
-               }
+               nreq = min_t(int, dev->caps.num_eqs - dev->caps.reserved_eqs,
+                            nreq);
 
                entries = kcalloc(nreq, sizeof *entries, GFP_KERNEL);
                if (!entries)
index bc165f4d0f65cebb6cc82e3aa99590863963f767..695667d471a1bef8dc81dd8034a593a118993e07 100644 (file)
@@ -144,7 +144,7 @@ void netxen_release_tx_buffers(struct netxen_adapter *adapter)
                                         buffrag->length, PCI_DMA_TODEVICE);
                        buffrag->dma = 0ULL;
                }
-               for (j = 0; j < cmd_buf->frag_count; j++) {
+               for (j = 1; j < cmd_buf->frag_count; j++) {
                        buffrag++;
                        if (buffrag->dma) {
                                pci_unmap_page(adapter->pdev, buffrag->dma,
index 6098fd4adfeb89ef5ed9c15407799ddbdcf02e7a..69e321a650779c9d3bae975a273035b922fca08f 100644 (file)
@@ -1963,10 +1963,12 @@ unwind:
        while (--i >= 0) {
                nf = &pbuf->frag_array[i+1];
                pci_unmap_page(pdev, nf->dma, nf->length, PCI_DMA_TODEVICE);
+               nf->dma = 0ULL;
        }
 
        nf = &pbuf->frag_array[0];
        pci_unmap_single(pdev, nf->dma, skb_headlen(skb), PCI_DMA_TODEVICE);
+       nf->dma = 0ULL;
 
 out_err:
        return -ENOMEM;
index f80cd975daed2b84a31af47db58b828d28139638..3e73742024b0cb5af229df1336a9a3aeab653b1f 100644 (file)
@@ -4678,7 +4678,7 @@ static int qlge_probe(struct pci_dev *pdev,
        qdev = netdev_priv(ndev);
        SET_NETDEV_DEV(ndev, &pdev->dev);
        ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM |
-               NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN |
+               NETIF_F_TSO | NETIF_F_TSO_ECN |
                NETIF_F_HW_VLAN_TX | NETIF_F_RXCSUM;
        ndev->features = ndev->hw_features |
                NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER;
index ed96f309bca8e030a0c098df8b8e728314f04bb0..11702324a071b87deff9ef13f919be57e99b024b 100644 (file)
@@ -1826,8 +1826,6 @@ static void rtl8169_rx_vlan_tag(struct RxDesc *desc, struct sk_buff *skb)
 
        if (opts2 & RxVlanTag)
                __vlan_hwaccel_put_tag(skb, swab16(opts2 & 0xffff));
-
-       desc->opts2 = 0;
 }
 
 static int rtl8169_gset_tbi(struct net_device *dev, struct ethtool_cmd *cmd)
@@ -6064,8 +6062,6 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, u32 budget
                            !(status & (RxRWT | RxFOVF)) &&
                            (dev->features & NETIF_F_RXALL))
                                goto process_pkt;
-
-                       rtl8169_mark_to_asic(desc, rx_buf_sz);
                } else {
                        struct sk_buff *skb;
                        dma_addr_t addr;
@@ -6086,16 +6082,14 @@ process_pkt:
                        if (unlikely(rtl8169_fragmented_frame(status))) {
                                dev->stats.rx_dropped++;
                                dev->stats.rx_length_errors++;
-                               rtl8169_mark_to_asic(desc, rx_buf_sz);
-                               continue;
+                               goto release_descriptor;
                        }
 
                        skb = rtl8169_try_rx_copy(tp->Rx_databuff[entry],
                                                  tp, pkt_size, addr);
-                       rtl8169_mark_to_asic(desc, rx_buf_sz);
                        if (!skb) {
                                dev->stats.rx_dropped++;
-                               continue;
+                               goto release_descriptor;
                        }
 
                        rtl8169_rx_csum(skb, status);
@@ -6111,13 +6105,10 @@ process_pkt:
                        tp->rx_stats.bytes += pkt_size;
                        u64_stats_update_end(&tp->rx_stats.syncp);
                }
-
-               /* Work around for AMD plateform. */
-               if ((desc->opts2 & cpu_to_le32(0xfffe000)) &&
-                   (tp->mac_version == RTL_GIGA_MAC_VER_05)) {
-                       desc->opts2 = 0;
-                       cur_rx++;
-               }
+release_descriptor:
+               desc->opts2 = 0;
+               wmb();
+               rtl8169_mark_to_asic(desc, rx_buf_sz);
        }
 
        count = cur_rx - tp->cur_rx;
index 5778a4ae116466d637288fe668fb990ac623fb6e..122d60c0481b0d8eab2a95b56bc495665390a0ec 100644 (file)
@@ -27,7 +27,7 @@ config XILINX_EMACLITE
 
 config XILINX_AXI_EMAC
        tristate "Xilinx 10/100/1000 AXI Ethernet support"
-       depends on (PPC32 || MICROBLAZE)
+       depends on MICROBLAZE
        select PHYLIB
        ---help---
          This driver supports the 10/100/1000 Ethernet from Xilinx for the
index d9f69b82cc4ff638981798e23bbe3c0e9e6329f1..6f47100e58d71584ea13bb0c83c1607ca967a58a 100644 (file)
@@ -1590,7 +1590,7 @@ static int axienet_of_probe(struct platform_device *op)
        lp->rx_irq = irq_of_parse_and_map(np, 1);
        lp->tx_irq = irq_of_parse_and_map(np, 0);
        of_node_put(np);
-       if ((lp->rx_irq == NO_IRQ) || (lp->tx_irq == NO_IRQ)) {
+       if ((lp->rx_irq <= 0) || (lp->tx_irq <= 0)) {
                dev_err(&op->dev, "could not determine irqs\n");
                ret = -ENOMEM;
                goto err_iounmap_2;
index 5fd6f4674326f0d3236ae8a97127b842ffffa008..e6fe0d80d6122fcd8dfcf7bb65fa71dac68c1e73 100644 (file)
@@ -84,7 +84,7 @@ struct hv_netvsc_packet {
 };
 
 struct netvsc_device_info {
-       unsigned char mac_adr[6];
+       unsigned char mac_adr[ETH_ALEN];
        bool link_state;        /* 0 - link up, 1 - link down */
        int  ring_size;
 };
index f825a629a699cfe5fac73803353da4b47ca18974..8264f0ef7692f5c832a12336a1188f9c7620190f 100644 (file)
@@ -349,7 +349,7 @@ static int netvsc_set_mac_addr(struct net_device *ndev, void *p)
        struct net_device_context *ndevctx = netdev_priv(ndev);
        struct hv_device *hdev =  ndevctx->device_ctx;
        struct sockaddr *addr = p;
-       char save_adr[14];
+       char save_adr[ETH_ALEN];
        unsigned char save_aatype;
        int err;
 
index 81f8f9e31db510892acae3cdb39a799de7e21be9..fcbf680c3e62f73af4933896641045de34f1f68c 100644 (file)
@@ -77,6 +77,11 @@ static netdev_tx_t loopback_xmit(struct sk_buff *skb,
 
        skb_orphan(skb);
 
+       /* Before queueing this packet to netif_rx(),
+        * make sure dst is refcounted.
+        */
+       skb_dst_force(skb);
+
        skb->protocol = eth_type_trans(skb, dev);
 
        /* it's OK to use per_cpu_ptr() because BHs are off */
index 68a43fe602e7a89ccffa54d1a5b82e2da4e5330c..d3fb97d97cbcb61f68deda7c3009a83393515656 100644 (file)
@@ -822,7 +822,10 @@ static int macvlan_changelink(struct net_device *dev,
 
 static size_t macvlan_get_size(const struct net_device *dev)
 {
-       return nla_total_size(4);
+       return (0
+               + nla_total_size(4) /* IFLA_MACVLAN_MODE */
+               + nla_total_size(2) /* IFLA_MACVLAN_FLAGS */
+               );
 }
 
 static int macvlan_fill_info(struct sk_buff *skb,
index d5199cb4caecd06c1774295765476d3a508e4a10..b5ddd5077a80dd9c6324c92e68a1ecd8a03314e7 100644 (file)
@@ -36,8 +36,9 @@ MODULE_LICENSE("GPL");
 
 /* IP101A/G - IP1001 */
 #define IP10XX_SPEC_CTRL_STATUS                16      /* Spec. Control Register */
+#define IP1001_RXPHASE_SEL             (1<<0)  /* Add delay on RX_CLK */
+#define IP1001_TXPHASE_SEL             (1<<1)  /* Add delay on TX_CLK */
 #define IP1001_SPEC_CTRL_STATUS_2      20      /* IP1001 Spec. Control Reg 2 */
-#define IP1001_PHASE_SEL_MASK          3       /* IP1001 RX/TXPHASE_SEL */
 #define IP1001_APS_ON                  11      /* IP1001 APS Mode  bit */
 #define IP101A_G_APS_ON                        2       /* IP101A/G APS Mode bit */
 #define IP101A_G_IRQ_CONF_STATUS       0x11    /* Conf Info IRQ & Status Reg */
@@ -138,19 +139,24 @@ static int ip1001_config_init(struct phy_device *phydev)
        if (c < 0)
                return c;
 
-       /* INTR pin used: speed/link/duplex will cause an interrupt */
-       c = phy_write(phydev, IP101A_G_IRQ_CONF_STATUS, IP101A_G_IRQ_DEFAULT);
-       if (c < 0)
-               return c;
+       if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) ||
+           (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) ||
+           (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) ||
+           (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)) {
 
-       if (phydev->interface == PHY_INTERFACE_MODE_RGMII) {
-               /* Additional delay (2ns) used to adjust RX clock phase
-                * at RGMII interface */
                c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS);
                if (c < 0)
                        return c;
 
-               c |= IP1001_PHASE_SEL_MASK;
+               c &= ~(IP1001_RXPHASE_SEL | IP1001_TXPHASE_SEL);
+
+               if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
+                       c |= (IP1001_RXPHASE_SEL | IP1001_TXPHASE_SEL);
+               else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
+                       c |= IP1001_RXPHASE_SEL;
+               else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
+                       c |= IP1001_TXPHASE_SEL;
+
                c = phy_write(phydev, IP10XX_SPEC_CTRL_STATUS, c);
                if (c < 0)
                        return c;
@@ -167,6 +173,11 @@ static int ip101a_g_config_init(struct phy_device *phydev)
        if (c < 0)
                return c;
 
+       /* INTR pin used: speed/link/duplex will cause an interrupt */
+       c = phy_write(phydev, IP101A_G_IRQ_CONF_STATUS, IP101A_G_IRQ_DEFAULT);
+       if (c < 0)
+               return c;
+
        /* Enable Auto Power Saving mode */
        c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS);
        c |= IP101A_G_APS_ON;
index 5d2a3f2158876bb39ffc02f2647a57676b1bb718..22dec9c7ef05d7480e87b575605e2240f4476a20 100644 (file)
@@ -353,15 +353,6 @@ static int m88e1111_config_init(struct phy_device *phydev)
        int err;
        int temp;
 
-       /* Enable Fiber/Copper auto selection */
-       temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
-       temp &= ~MII_M1111_HWCFG_FIBER_COPPER_AUTO;
-       phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
-
-       temp = phy_read(phydev, MII_BMCR);
-       temp |= BMCR_RESET;
-       phy_write(phydev, MII_BMCR, temp);
-
        if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) ||
            (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) ||
            (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) ||
index fbd106edbe59bdcae871e29d6fd70c993634cdea..cc09b67c23bcd30402d73a93594f7aef9cb100da 100644 (file)
@@ -109,11 +109,11 @@ struct tap_filter {
        unsigned char   addr[FLT_EXACT_COUNT][ETH_ALEN];
 };
 
-/* 1024 is probably a high enough limit: modern hypervisors seem to support on
- * the order of 100-200 CPUs so this leaves us some breathing space if we want
- * to match a queue per guest CPU.
- */
-#define MAX_TAP_QUEUES 1024
+/* DEFAULT_MAX_NUM_RSS_QUEUES were choosed to let the rx/tx queues allocated for
+ * the netdevice to be fit in one page. So we can make sure the success of
+ * memory allocation. TODO: increase the limit. */
+#define MAX_TAP_QUEUES DEFAULT_MAX_NUM_RSS_QUEUES
+#define MAX_TAP_FLOWS  4096
 
 #define TUN_FLOW_EXPIRE (3 * HZ)
 
@@ -185,6 +185,8 @@ struct tun_struct {
        unsigned long ageing_time;
        unsigned int numdisabled;
        struct list_head disabled;
+       void *security;
+       u32 flow_count;
 };
 
 static inline u32 tun_hashfn(u32 rxhash)
@@ -218,6 +220,7 @@ static struct tun_flow_entry *tun_flow_create(struct tun_struct *tun,
                e->queue_index = queue_index;
                e->tun = tun;
                hlist_add_head_rcu(&e->hash_link, head);
+               ++tun->flow_count;
        }
        return e;
 }
@@ -228,6 +231,7 @@ static void tun_flow_delete(struct tun_struct *tun, struct tun_flow_entry *e)
                  e->rxhash, e->queue_index);
        hlist_del_rcu(&e->hash_link);
        kfree_rcu(e, rcu);
+       --tun->flow_count;
 }
 
 static void tun_flow_flush(struct tun_struct *tun)
@@ -317,7 +321,8 @@ static void tun_flow_update(struct tun_struct *tun, u32 rxhash,
                e->updated = jiffies;
        } else {
                spin_lock_bh(&tun->lock);
-               if (!tun_flow_find(head, rxhash))
+               if (!tun_flow_find(head, rxhash) &&
+                   tun->flow_count < MAX_TAP_FLOWS)
                        tun_flow_create(tun, head, rxhash, queue_index);
 
                if (!timer_pending(&tun->flow_gc_timer))
@@ -404,8 +409,8 @@ static void __tun_detach(struct tun_file *tfile, bool clean)
        struct tun_struct *tun;
        struct net_device *dev;
 
-       tun = rcu_dereference_protected(tfile->tun,
-                                       lockdep_rtnl_is_held());
+       tun = rtnl_dereference(tfile->tun);
+
        if (tun) {
                u16 index = tfile->queue_index;
                BUG_ON(index >= tun->numqueues);
@@ -414,8 +419,7 @@ static void __tun_detach(struct tun_file *tfile, bool clean)
                rcu_assign_pointer(tun->tfiles[index],
                                   tun->tfiles[tun->numqueues - 1]);
                rcu_assign_pointer(tfile->tun, NULL);
-               ntfile = rcu_dereference_protected(tun->tfiles[index],
-                                                  lockdep_rtnl_is_held());
+               ntfile = rtnl_dereference(tun->tfiles[index]);
                ntfile->queue_index = index;
 
                --tun->numqueues;
@@ -429,8 +433,10 @@ static void __tun_detach(struct tun_file *tfile, bool clean)
                /* Drop read queue */
                skb_queue_purge(&tfile->sk.sk_receive_queue);
                tun_set_real_num_queues(tun);
-       } else if (tfile->detached && clean)
+       } else if (tfile->detached && clean) {
                tun = tun_enable_queue(tfile);
+               sock_put(&tfile->sk);
+       }
 
        if (clean) {
                if (tun && tun->numqueues == 0 && tun->numdisabled == 0 &&
@@ -458,8 +464,7 @@ static void tun_detach_all(struct net_device *dev)
        int i, n = tun->numqueues;
 
        for (i = 0; i < n; i++) {
-               tfile = rcu_dereference_protected(tun->tfiles[i],
-                                                 lockdep_rtnl_is_held());
+               tfile = rtnl_dereference(tun->tfiles[i]);
                BUG_ON(!tfile);
                wake_up_all(&tfile->wq.wait);
                rcu_assign_pointer(tfile->tun, NULL);
@@ -469,8 +474,7 @@ static void tun_detach_all(struct net_device *dev)
 
        synchronize_net();
        for (i = 0; i < n; i++) {
-               tfile = rcu_dereference_protected(tun->tfiles[i],
-                                                 lockdep_rtnl_is_held());
+               tfile = rtnl_dereference(tun->tfiles[i]);
                /* Drop read queue */
                skb_queue_purge(&tfile->sk.sk_receive_queue);
                sock_put(&tfile->sk);
@@ -481,6 +485,9 @@ static void tun_detach_all(struct net_device *dev)
                sock_put(&tfile->sk);
        }
        BUG_ON(tun->numdisabled != 0);
+
+       if (tun->flags & TUN_PERSIST)
+               module_put(THIS_MODULE);
 }
 
 static int tun_attach(struct tun_struct *tun, struct file *file)
@@ -488,8 +495,12 @@ static int tun_attach(struct tun_struct *tun, struct file *file)
        struct tun_file *tfile = file->private_data;
        int err;
 
+       err = security_tun_dev_attach(tfile->socket.sk, tun->security);
+       if (err < 0)
+               goto out;
+
        err = -EINVAL;
-       if (rcu_dereference_protected(tfile->tun, lockdep_rtnl_is_held()))
+       if (rtnl_dereference(tfile->tun))
                goto out;
 
        err = -EBUSY;
@@ -1371,6 +1382,7 @@ static void tun_free_netdev(struct net_device *dev)
 
        BUG_ON(!(list_empty(&tun->disabled)));
        tun_flow_uninit(tun);
+       security_tun_dev_free_security(tun->security);
        free_netdev(dev);
 }
 
@@ -1544,6 +1556,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
        struct net_device *dev;
        int err;
 
+       if (tfile->detached)
+               return -EINVAL;
+
        dev = __dev_get_by_name(net, ifr->ifr_name);
        if (dev) {
                if (ifr->ifr_flags & IFF_TUN_EXCL)
@@ -1557,7 +1572,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
 
                if (tun_not_capable(tun))
                        return -EPERM;
-               err = security_tun_dev_attach(tfile->socket.sk);
+               err = security_tun_dev_open(tun->security);
                if (err < 0)
                        return err;
 
@@ -1572,6 +1587,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
        else {
                char *name;
                unsigned long flags = 0;
+               int queues = ifr->ifr_flags & IFF_MULTI_QUEUE ?
+                            MAX_TAP_QUEUES : 1;
 
                if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
                        return -EPERM;
@@ -1595,8 +1612,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
                        name = ifr->ifr_name;
 
                dev = alloc_netdev_mqs(sizeof(struct tun_struct), name,
-                                      tun_setup,
-                                      MAX_TAP_QUEUES, MAX_TAP_QUEUES);
+                                      tun_setup, queues, queues);
+
                if (!dev)
                        return -ENOMEM;
 
@@ -1614,7 +1631,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
 
                spin_lock_init(&tun->lock);
 
-               security_tun_dev_post_create(&tfile->sk);
+               err = security_tun_dev_alloc_security(&tun->security);
+               if (err < 0)
+                       goto err_free_dev;
 
                tun_net_init(dev);
 
@@ -1738,8 +1757,7 @@ static void tun_detach_filter(struct tun_struct *tun, int n)
        struct tun_file *tfile;
 
        for (i = 0; i < n; i++) {
-               tfile = rcu_dereference_protected(tun->tfiles[i],
-                                                 lockdep_rtnl_is_held());
+               tfile = rtnl_dereference(tun->tfiles[i]);
                sk_detach_filter(tfile->socket.sk);
        }
 
@@ -1752,8 +1770,7 @@ static int tun_attach_filter(struct tun_struct *tun)
        struct tun_file *tfile;
 
        for (i = 0; i < tun->numqueues; i++) {
-               tfile = rcu_dereference_protected(tun->tfiles[i],
-                                                 lockdep_rtnl_is_held());
+               tfile = rtnl_dereference(tun->tfiles[i]);
                ret = sk_attach_filter(&tun->fprog, tfile->socket.sk);
                if (ret) {
                        tun_detach_filter(tun, i);
@@ -1771,8 +1788,7 @@ static void tun_set_sndbuf(struct tun_struct *tun)
        int i;
 
        for (i = 0; i < tun->numqueues; i++) {
-               tfile = rcu_dereference_protected(tun->tfiles[i],
-                                               lockdep_rtnl_is_held());
+               tfile = rtnl_dereference(tun->tfiles[i]);
                tfile->socket.sk->sk_sndbuf = tun->sndbuf;
        }
 }
@@ -1787,15 +1803,16 @@ static int tun_set_queue(struct file *file, struct ifreq *ifr)
 
        if (ifr->ifr_flags & IFF_ATTACH_QUEUE) {
                tun = tfile->detached;
-               if (!tun)
+               if (!tun) {
                        ret = -EINVAL;
-               else if (tun_not_capable(tun))
-                       ret = -EPERM;
-               else
-                       ret = tun_attach(tun, file);
+                       goto unlock;
+               }
+               ret = security_tun_dev_attach_queue(tun->security);
+               if (ret < 0)
+                       goto unlock;
+               ret = tun_attach(tun, file);
        } else if (ifr->ifr_flags & IFF_DETACH_QUEUE) {
-               tun = rcu_dereference_protected(tfile->tun,
-                                               lockdep_rtnl_is_held());
+               tun = rtnl_dereference(tfile->tun);
                if (!tun || !(tun->flags & TUN_TAP_MQ))
                        ret = -EINVAL;
                else
@@ -1803,6 +1820,7 @@ static int tun_set_queue(struct file *file, struct ifreq *ifr)
        } else
                ret = -EINVAL;
 
+unlock:
        rtnl_unlock();
        return ret;
 }
@@ -1880,10 +1898,11 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
                /* Disable/Enable persist mode. Keep an extra reference to the
                 * module to prevent the module being unprobed.
                 */
-               if (arg) {
+               if (arg && !(tun->flags & TUN_PERSIST)) {
                        tun->flags |= TUN_PERSIST;
                        __module_get(THIS_MODULE);
-               } else {
+               }
+               if (!arg && (tun->flags & TUN_PERSIST)) {
                        tun->flags &= ~TUN_PERSIST;
                        module_put(THIS_MODULE);
                }
index 42f51c71ec1fc7444c668153bdaa200a2b0f4315..248d2dc765a5c06c64ab3c27c47f38d36bf14c20 100644 (file)
@@ -374,6 +374,21 @@ static const struct driver_info cdc_mbim_info = {
        .tx_fixup = cdc_mbim_tx_fixup,
 };
 
+/* MBIM and NCM devices should not need a ZLP after NTBs with
+ * dwNtbOutMaxSize length. This driver_info is for the exceptional
+ * devices requiring it anyway, allowing them to be supported without
+ * forcing the performance penalty on all the sane devices.
+ */
+static const struct driver_info cdc_mbim_info_zlp = {
+       .description = "CDC MBIM",
+       .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN | FLAG_SEND_ZLP,
+       .bind = cdc_mbim_bind,
+       .unbind = cdc_mbim_unbind,
+       .manage_power = cdc_mbim_manage_power,
+       .rx_fixup = cdc_mbim_rx_fixup,
+       .tx_fixup = cdc_mbim_tx_fixup,
+};
+
 static const struct usb_device_id mbim_devs[] = {
        /* This duplicate NCM entry is intentional. MBIM devices can
         * be disguised as NCM by default, and this is necessary to
@@ -385,6 +400,10 @@ static const struct usb_device_id mbim_devs[] = {
        { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
          .driver_info = (unsigned long)&cdc_mbim_info,
        },
+       /* Sierra Wireless MC7710 need ZLPs */
+       { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x68a2, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE),
+         .driver_info = (unsigned long)&cdc_mbim_info_zlp,
+       },
        { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE),
          .driver_info = (unsigned long)&cdc_mbim_info,
        },
index 71b6e92b8e9b687c70d0eda9416f1e235a788747..9197b2c72ca36dd9e62bbccafbea388d5fc99793 100644 (file)
@@ -435,6 +435,13 @@ advance:
                len -= temp;
        }
 
+       /* some buggy devices have an IAD but no CDC Union */
+       if (!ctx->union_desc && intf->intf_assoc && intf->intf_assoc->bInterfaceCount == 2) {
+               ctx->control = intf;
+               ctx->data = usb_ifnum_to_if(dev->udev, intf->cur_altsetting->desc.bInterfaceNumber + 1);
+               dev_dbg(&intf->dev, "CDC Union missing - got slave from IAD\n");
+       }
+
        /* check if we got everything */
        if ((ctx->control == NULL) || (ctx->data == NULL) ||
            ((!ctx->mbim_desc) && ((ctx->ether_desc == NULL) || (ctx->control != intf))))
@@ -497,7 +504,8 @@ advance:
 error2:
        usb_set_intfdata(ctx->control, NULL);
        usb_set_intfdata(ctx->data, NULL);
-       usb_driver_release_interface(driver, ctx->data);
+       if (ctx->data != ctx->control)
+               usb_driver_release_interface(driver, ctx->data);
 error:
        cdc_ncm_free((struct cdc_ncm_ctx *)dev->data[0]);
        dev->data[0] = 0;
@@ -1155,6 +1163,20 @@ static const struct driver_info wwan_info = {
        .tx_fixup = cdc_ncm_tx_fixup,
 };
 
+/* Same as wwan_info, but with FLAG_NOARP  */
+static const struct driver_info wwan_noarp_info = {
+       .description = "Mobile Broadband Network Device (NO ARP)",
+       .flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET
+                       | FLAG_WWAN | FLAG_NOARP,
+       .bind = cdc_ncm_bind,
+       .unbind = cdc_ncm_unbind,
+       .check_connect = cdc_ncm_check_connect,
+       .manage_power = usbnet_manage_power,
+       .status = cdc_ncm_status,
+       .rx_fixup = cdc_ncm_rx_fixup,
+       .tx_fixup = cdc_ncm_tx_fixup,
+};
+
 static const struct usb_device_id cdc_devs[] = {
        /* Ericsson MBM devices like F5521gw */
        { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
@@ -1194,6 +1216,13 @@ static const struct usb_device_id cdc_devs[] = {
          .driver_info = (unsigned long)&wwan_info,
        },
 
+       /* Infineon(now Intel) HSPA Modem platform */
+       { USB_DEVICE_AND_INTERFACE_INFO(0x1519, 0x0443,
+               USB_CLASS_COMM,
+               USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
+         .driver_info = (unsigned long)&wwan_noarp_info,
+       },
+
        /* Generic CDC-NCM devices */
        { USB_INTERFACE_INFO(USB_CLASS_COMM,
                USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
index 3f554c1149f36d2cf3221ee38e4f02507d566ba7..d7e99445518eb5981d064a7378d7fcf02e4bf5cd 100644 (file)
 #define DM_MCAST_ADDR  0x16    /* 8 bytes */
 #define DM_GPR_CTRL    0x1e
 #define DM_GPR_DATA    0x1f
+#define DM_CHIP_ID     0x2c
+#define DM_MODE_CTRL   0x91    /* only on dm9620 */
+
+/* chip id values */
+#define ID_DM9601      0
+#define ID_DM9620      1
 
 #define DM_MAX_MCAST   64
 #define DM_MCAST_SIZE  8
@@ -53,7 +59,6 @@
 #define DM_RX_OVERHEAD 7       /* 3 byte header + 4 byte crc tail */
 #define DM_TIMEOUT     1000
 
-
 static int dm_read(struct usbnet *dev, u8 reg, u16 length, void *data)
 {
        int err;
@@ -84,32 +89,23 @@ static int dm_write(struct usbnet *dev, u8 reg, u16 length, void *data)
 
 static int dm_write_reg(struct usbnet *dev, u8 reg, u8 value)
 {
-       return usbnet_write_cmd(dev, DM_WRITE_REGS,
+       return usbnet_write_cmd(dev, DM_WRITE_REG,
                                USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
                                value, reg, NULL, 0);
 }
 
-static void dm_write_async_helper(struct usbnet *dev, u8 reg, u8 value,
-                                 u16 length, void *data)
+static void dm_write_async(struct usbnet *dev, u8 reg, u16 length, void *data)
 {
        usbnet_write_cmd_async(dev, DM_WRITE_REGS,
                               USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-                              value, reg, data, length);
-}
-
-static void dm_write_async(struct usbnet *dev, u8 reg, u16 length, void *data)
-{
-       netdev_dbg(dev->net, "dm_write_async() reg=0x%02x length=%d\n", reg, length);
-
-       dm_write_async_helper(dev, reg, 0, length, data);
+                              0, reg, data, length);
 }
 
 static void dm_write_reg_async(struct usbnet *dev, u8 reg, u8 value)
 {
-       netdev_dbg(dev->net, "dm_write_reg_async() reg=0x%02x value=0x%02x\n",
-                  reg, value);
-
-       dm_write_async_helper(dev, reg, value, 0, NULL);
+       usbnet_write_cmd_async(dev, DM_WRITE_REG,
+                              USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                              value, reg, NULL, 0);
 }
 
 static int dm_read_shared_word(struct usbnet *dev, int phy, u8 reg, __le16 *value)
@@ -358,7 +354,7 @@ static const struct net_device_ops dm9601_netdev_ops = {
 static int dm9601_bind(struct usbnet *dev, struct usb_interface *intf)
 {
        int ret;
-       u8 mac[ETH_ALEN];
+       u8 mac[ETH_ALEN], id;
 
        ret = usbnet_get_endpoints(dev, intf);
        if (ret)
@@ -399,6 +395,24 @@ static int dm9601_bind(struct usbnet *dev, struct usb_interface *intf)
                __dm9601_set_mac_address(dev);
        }
 
+       if (dm_read_reg(dev, DM_CHIP_ID, &id) < 0) {
+               netdev_err(dev->net, "Error reading chip ID\n");
+               ret = -ENODEV;
+               goto out;
+       }
+
+       /* put dm9620 devices in dm9601 mode */
+       if (id == ID_DM9620) {
+               u8 mode;
+
+               if (dm_read_reg(dev, DM_MODE_CTRL, &mode) < 0) {
+                       netdev_err(dev->net, "Error reading MODE_CTRL\n");
+                       ret = -ENODEV;
+                       goto out;
+               }
+               dm_write_reg(dev, DM_MODE_CTRL, mode & 0x7f);
+       }
+
        /* power up phy */
        dm_write_reg(dev, DM_GPR_CTRL, 1);
        dm_write_reg(dev, DM_GPR_DATA, 0);
@@ -581,6 +595,10 @@ static const struct usb_device_id products[] = {
         USB_DEVICE(0x0a46, 0x9000),    /* DM9000E */
         .driver_info = (unsigned long)&dm9601_info,
         },
+       {
+        USB_DEVICE(0x0a46, 0x9620),    /* DM9620 USB to Fast Ethernet Adapter */
+        .driver_info = (unsigned long)&dm9601_info,
+        },
        {},                     // END
 };
 
index 6a1ca500e61267e9ba8858d3ed7405990b28e43d..575a5839ee3458434d355677d785589d3472395d 100644 (file)
@@ -433,6 +433,7 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x19d2, 0x0199, 1)},    /* ZTE MF820S */
        {QMI_FIXED_INTF(0x19d2, 0x0200, 1)},
        {QMI_FIXED_INTF(0x19d2, 0x0257, 3)},    /* ZTE MF821 */
+       {QMI_FIXED_INTF(0x19d2, 0x0265, 4)},    /* ONDA MT8205 4G LTE */
        {QMI_FIXED_INTF(0x19d2, 0x0284, 4)},    /* ZTE MF880 */
        {QMI_FIXED_INTF(0x19d2, 0x0326, 4)},    /* ZTE MF821D */
        {QMI_FIXED_INTF(0x19d2, 0x1008, 4)},    /* ZTE (Vodafone) K3570-Z */
@@ -459,6 +460,7 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x1199, 0x68a2, 19)},   /* Sierra Wireless MC7710 in QMI mode */
        {QMI_FIXED_INTF(0x1199, 0x901c, 8)},    /* Sierra Wireless EM7700 */
        {QMI_FIXED_INTF(0x1bbb, 0x011e, 4)},    /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */
+       {QMI_FIXED_INTF(0x2357, 0x0201, 4)},    /* TP-LINK HSUPA Modem MA180 */
 
        /* 4. Gobi 1000 devices */
        {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)},    /* Acer Gobi Modem Device */
index 3d4bf01641b49f0f7c5d820a547885806378c4e2..f34b2ebee815ff6b832ef280e26ce7039ef6bfd6 100644 (file)
@@ -1448,6 +1448,10 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
                if ((dev->driver_info->flags & FLAG_WWAN) != 0)
                        strcpy(net->name, "wwan%d");
 
+               /* devices that cannot do ARP */
+               if ((dev->driver_info->flags & FLAG_NOARP) != 0)
+                       net->flags |= IFF_NOARP;
+
                /* maybe the remote can't receive an Ethernet MTU */
                if (net->mtu > (dev->hard_mtu - net->hard_header_len))
                        net->mtu = dev->hard_mtu - net->hard_header_len;
index a6fcf15adc4ff3d36d928f44cc0ac1f49d249820..35c00c5ea02adcbdf6f2855b7ee506544944843a 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/scatterlist.h>
 #include <linux/if_vlan.h>
 #include <linux/slab.h>
+#include <linux/cpu.h>
 
 static int napi_weight = 128;
 module_param(napi_weight, int, 0444);
@@ -123,6 +124,12 @@ struct virtnet_info {
 
        /* Does the affinity hint is set for virtqueues? */
        bool affinity_hint_set;
+
+       /* Per-cpu variable to show the mapping from CPU to virtqueue */
+       int __percpu *vq_index;
+
+       /* CPU hot plug notifier */
+       struct notifier_block nb;
 };
 
 struct skb_vnet_hdr {
@@ -1013,32 +1020,75 @@ static int virtnet_vlan_rx_kill_vid(struct net_device *dev, u16 vid)
        return 0;
 }
 
-static void virtnet_set_affinity(struct virtnet_info *vi, bool set)
+static void virtnet_clean_affinity(struct virtnet_info *vi, long hcpu)
 {
        int i;
+       int cpu;
+
+       if (vi->affinity_hint_set) {
+               for (i = 0; i < vi->max_queue_pairs; i++) {
+                       virtqueue_set_affinity(vi->rq[i].vq, -1);
+                       virtqueue_set_affinity(vi->sq[i].vq, -1);
+               }
+
+               vi->affinity_hint_set = false;
+       }
+
+       i = 0;
+       for_each_online_cpu(cpu) {
+               if (cpu == hcpu) {
+                       *per_cpu_ptr(vi->vq_index, cpu) = -1;
+               } else {
+                       *per_cpu_ptr(vi->vq_index, cpu) =
+                               ++i % vi->curr_queue_pairs;
+               }
+       }
+}
+
+static void virtnet_set_affinity(struct virtnet_info *vi)
+{
+       int i;
+       int cpu;
 
        /* In multiqueue mode, when the number of cpu is equal to the number of
         * queue pairs, we let the queue pairs to be private to one cpu by
         * setting the affinity hint to eliminate the contention.
         */
-       if ((vi->curr_queue_pairs == 1 ||
-            vi->max_queue_pairs != num_online_cpus()) && set) {
-               if (vi->affinity_hint_set)
-                       set = false;
-               else
-                       return;
+       if (vi->curr_queue_pairs == 1 ||
+           vi->max_queue_pairs != num_online_cpus()) {
+               virtnet_clean_affinity(vi, -1);
+               return;
        }
 
-       for (i = 0; i < vi->max_queue_pairs; i++) {
-               int cpu = set ? i : -1;
+       i = 0;
+       for_each_online_cpu(cpu) {
                virtqueue_set_affinity(vi->rq[i].vq, cpu);
                virtqueue_set_affinity(vi->sq[i].vq, cpu);
+               *per_cpu_ptr(vi->vq_index, cpu) = i;
+               i++;
        }
 
-       if (set)
-               vi->affinity_hint_set = true;
-       else
-               vi->affinity_hint_set = false;
+       vi->affinity_hint_set = true;
+}
+
+static int virtnet_cpu_callback(struct notifier_block *nfb,
+                               unsigned long action, void *hcpu)
+{
+       struct virtnet_info *vi = container_of(nfb, struct virtnet_info, nb);
+
+       switch(action & ~CPU_TASKS_FROZEN) {
+       case CPU_ONLINE:
+       case CPU_DOWN_FAILED:
+       case CPU_DEAD:
+               virtnet_set_affinity(vi);
+               break;
+       case CPU_DOWN_PREPARE:
+               virtnet_clean_affinity(vi, (long)hcpu);
+               break;
+       default:
+               break;
+       }
+       return NOTIFY_OK;
 }
 
 static void virtnet_get_ringparam(struct net_device *dev,
@@ -1082,13 +1132,15 @@ static int virtnet_set_channels(struct net_device *dev,
        if (queue_pairs > vi->max_queue_pairs)
                return -EINVAL;
 
+       get_online_cpus();
        err = virtnet_set_queues(vi, queue_pairs);
        if (!err) {
                netif_set_real_num_tx_queues(dev, queue_pairs);
                netif_set_real_num_rx_queues(dev, queue_pairs);
 
-               virtnet_set_affinity(vi, true);
+               virtnet_set_affinity(vi);
        }
+       put_online_cpus();
 
        return err;
 }
@@ -1127,12 +1179,19 @@ static int virtnet_change_mtu(struct net_device *dev, int new_mtu)
 
 /* To avoid contending a lock hold by a vcpu who would exit to host, select the
  * txq based on the processor id.
- * TODO: handle cpu hotplug.
  */
 static u16 virtnet_select_queue(struct net_device *dev, struct sk_buff *skb)
 {
-       int txq = skb_rx_queue_recorded(skb) ? skb_get_rx_queue(skb) :
-                 smp_processor_id();
+       int txq;
+       struct virtnet_info *vi = netdev_priv(dev);
+
+       if (skb_rx_queue_recorded(skb)) {
+               txq = skb_get_rx_queue(skb);
+       } else {
+               txq = *__this_cpu_ptr(vi->vq_index);
+               if (txq == -1)
+                       txq = 0;
+       }
 
        while (unlikely(txq >= dev->real_num_tx_queues))
                txq -= dev->real_num_tx_queues;
@@ -1248,7 +1307,7 @@ static void virtnet_del_vqs(struct virtnet_info *vi)
 {
        struct virtio_device *vdev = vi->vdev;
 
-       virtnet_set_affinity(vi, false);
+       virtnet_clean_affinity(vi, -1);
 
        vdev->config->del_vqs(vdev);
 
@@ -1371,7 +1430,10 @@ static int init_vqs(struct virtnet_info *vi)
        if (ret)
                goto err_free;
 
-       virtnet_set_affinity(vi, true);
+       get_online_cpus();
+       virtnet_set_affinity(vi);
+       put_online_cpus();
+
        return 0;
 
 err_free:
@@ -1453,6 +1515,10 @@ static int virtnet_probe(struct virtio_device *vdev)
        if (vi->stats == NULL)
                goto free;
 
+       vi->vq_index = alloc_percpu(int);
+       if (vi->vq_index == NULL)
+               goto free_stats;
+
        mutex_init(&vi->config_lock);
        vi->config_enable = true;
        INIT_WORK(&vi->config_work, virtnet_config_changed_work);
@@ -1476,7 +1542,7 @@ static int virtnet_probe(struct virtio_device *vdev)
        /* Allocate/initialize the rx/tx queues, and invoke find_vqs */
        err = init_vqs(vi);
        if (err)
-               goto free_stats;
+               goto free_index;
 
        netif_set_real_num_tx_queues(dev, 1);
        netif_set_real_num_rx_queues(dev, 1);
@@ -1499,6 +1565,13 @@ static int virtnet_probe(struct virtio_device *vdev)
                }
        }
 
+       vi->nb.notifier_call = &virtnet_cpu_callback;
+       err = register_hotcpu_notifier(&vi->nb);
+       if (err) {
+               pr_debug("virtio_net: registering cpu notifier failed\n");
+               goto free_recv_bufs;
+       }
+
        /* Assume link up if device can't report link status,
           otherwise get link status from config. */
        if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) {
@@ -1520,6 +1593,8 @@ free_recv_bufs:
 free_vqs:
        cancel_delayed_work_sync(&vi->refill);
        virtnet_del_vqs(vi);
+free_index:
+       free_percpu(vi->vq_index);
 free_stats:
        free_percpu(vi->stats);
 free:
@@ -1543,6 +1618,8 @@ static void virtnet_remove(struct virtio_device *vdev)
 {
        struct virtnet_info *vi = vdev->priv;
 
+       unregister_hotcpu_notifier(&vi->nb);
+
        /* Prevent config work handler from accessing the device. */
        mutex_lock(&vi->config_lock);
        vi->config_enable = false;
@@ -1554,6 +1631,7 @@ static void virtnet_remove(struct virtio_device *vdev)
 
        flush_work(&vi->config_work);
 
+       free_percpu(vi->vq_index);
        free_percpu(vi->stats);
        free_netdev(vi->dev);
 }
index 1a67a4f829fe9f3fca15a6ad3533e335bec70a25..2c02b4e84094d32fc5605deaebb0ed148087fbf5 100644 (file)
@@ -30,5 +30,6 @@ source "drivers/net/wireless/ath/ath9k/Kconfig"
 source "drivers/net/wireless/ath/carl9170/Kconfig"
 source "drivers/net/wireless/ath/ath6kl/Kconfig"
 source "drivers/net/wireless/ath/ar5523/Kconfig"
+source "drivers/net/wireless/ath/wil6210/Kconfig"
 
 endif
index 1e18621326dc2fc5c70335e43e4e781436820a84..97b964ded2bef25e4218cc3843377ada32732077 100644 (file)
@@ -3,6 +3,7 @@ obj-$(CONFIG_ATH9K_HW)          += ath9k/
 obj-$(CONFIG_CARL9170)         += carl9170/
 obj-$(CONFIG_ATH6KL)           += ath6kl/
 obj-$(CONFIG_AR5523)           += ar5523/
+obj-$(CONFIG_WIL6210)          += wil6210/
 
 obj-$(CONFIG_ATH_COMMON)       += ath.o
 
index 8b0d8dcd76255239e7451b4a1258adca79ef8342..56317b0fb6b692f3f9ae20b8ba681ec18371788f 100644 (file)
@@ -976,6 +976,8 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
                                          AR_PHY_CL_TAB_1,
                                          AR_PHY_CL_TAB_2 };
 
+       ar9003_hw_set_chain_masks(ah, ah->caps.rx_chainmask, ah->caps.tx_chainmask);
+
        if (rtt) {
                if (!ar9003_hw_rtt_restore(ah, chan))
                        run_rtt_cal = true;
index ce19c09fa8e84aec2877e2f14861b0284f4d958e..3afc24bde6d65d88a0d469abb0ef73d93856edcb 100644 (file)
@@ -586,32 +586,19 @@ static void ar9003_hw_init_bb(struct ath_hw *ah,
        ath9k_hw_synth_delay(ah, chan, synthDelay);
 }
 
-static void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx)
+void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx)
 {
-       switch (rx) {
-       case 0x5:
+       if (ah->caps.tx_chainmask == 5 || ah->caps.rx_chainmask == 5)
                REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP,
                            AR_PHY_SWAP_ALT_CHAIN);
-       case 0x3:
-       case 0x1:
-       case 0x2:
-       case 0x7:
-               REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx);
-               REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx);
-               break;
-       default:
-               break;
-       }
+
+       REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx);
+       REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx);
 
        if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) && (tx == 0x7))
-               REG_WRITE(ah, AR_SELFGEN_MASK, 0x3);
-       else
-               REG_WRITE(ah, AR_SELFGEN_MASK, tx);
+               tx = 3;
 
-       if (tx == 0x5) {
-               REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP,
-                           AR_PHY_SWAP_ALT_CHAIN);
-       }
+       REG_WRITE(ah, AR_SELFGEN_MASK, tx);
 }
 
 /*
index 86e26a19efdac1923395543c1a50fb53a6cb8958..42794c546a4068ac91b47c1252153b9040362555 100644 (file)
@@ -317,7 +317,6 @@ struct ath_rx {
        u32 *rxlink;
        u32 num_pkts;
        unsigned int rxfilter;
-       spinlock_t rxbuflock;
        struct list_head rxbuf;
        struct ath_descdma rxdma;
        struct ath_buf *rx_bufptr;
@@ -328,7 +327,6 @@ struct ath_rx {
 
 int ath_startrecv(struct ath_softc *sc);
 bool ath_stoprecv(struct ath_softc *sc);
-void ath_flushrecv(struct ath_softc *sc);
 u32 ath_calcrxfilter(struct ath_softc *sc);
 int ath_rx_init(struct ath_softc *sc, int nbufs);
 void ath_rx_cleanup(struct ath_softc *sc);
@@ -646,7 +644,6 @@ void ath_ant_comb_update(struct ath_softc *sc);
 enum sc_op_flags {
        SC_OP_INVALID,
        SC_OP_BEACONS,
-       SC_OP_RXFLUSH,
        SC_OP_ANI_RUN,
        SC_OP_PRIM_STA_VIF,
        SC_OP_HW_RESET,
index 531fffd801a34eaa11b8d483aca51ac07e7a7fab..2ca355e94da65467e36595990423c80be1b897b6 100644 (file)
@@ -147,6 +147,7 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
                                 skb->len, DMA_TO_DEVICE);
                dev_kfree_skb_any(skb);
                bf->bf_buf_addr = 0;
+               bf->bf_mpdu = NULL;
        }
 
        skb = ieee80211_beacon_get(hw, vif);
@@ -359,7 +360,6 @@ void ath9k_beacon_tasklet(unsigned long data)
                return;
 
        bf = ath9k_beacon_generate(sc->hw, vif);
-       WARN_ON(!bf);
 
        if (sc->beacon.bmisscnt != 0) {
                ath_dbg(common, BSTUCK, "resume beacon xmit after %u misses\n",
index 13ff9edc24015e5e2126fc4f6c7fde8dcb003c90..e585fc827c50b2e9d5ff2fe08afb15cd49cedba6 100644 (file)
@@ -861,7 +861,6 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf,
        RXS_ERR("RX-LENGTH-ERR", rx_len_err);
        RXS_ERR("RX-OOM-ERR", rx_oom_err);
        RXS_ERR("RX-RATE-ERR", rx_rate_err);
-       RXS_ERR("RX-DROP-RXFLUSH", rx_drop_rxflush);
        RXS_ERR("RX-TOO-MANY-FRAGS", rx_too_many_frags_err);
 
        PHY_ERR("UNDERRUN ERR", ATH9K_PHYERR_UNDERRUN);
index 375c3b46411eee6cd140dbc22e6e435fe0e32ad9..6df2ab62dcb706df5bef4a8afdf04593651f111a 100644 (file)
@@ -216,7 +216,6 @@ struct ath_tx_stats {
  * @rx_oom_err:  No. of frames dropped due to OOM issues.
  * @rx_rate_err:  No. of frames dropped due to rate errors.
  * @rx_too_many_frags_err:  Frames dropped due to too-many-frags received.
- * @rx_drop_rxflush: No. of frames dropped due to RX-FLUSH.
  * @rx_beacons:  No. of beacons received.
  * @rx_frags:  No. of rx-fragements received.
  */
@@ -235,7 +234,6 @@ struct ath_rx_stats {
        u32 rx_oom_err;
        u32 rx_rate_err;
        u32 rx_too_many_frags_err;
-       u32 rx_drop_rxflush;
        u32 rx_beacons;
        u32 rx_frags;
 };
index 4a9570dfba72605d2f492ddf51cde8e8e7db835b..aac4a406a5134727e49fef2999e563e39169d83f 100644 (file)
@@ -344,6 +344,8 @@ void ath9k_htc_txcompletion_cb(struct htc_target *htc_handle,
                        endpoint->ep_callbacks.tx(endpoint->ep_callbacks.priv,
                                                  skb, htc_hdr->endpoint_id,
                                                  txok);
+               } else {
+                       kfree_skb(skb);
                }
        }
 
index 7f1a8e91c908c2dea314a7171e1d1d59139e6c6a..9d26fc56ca56a6bf7d4e8ac0ed91366df0de50cd 100644 (file)
@@ -1066,6 +1066,7 @@ void ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain);
 int ar9003_paprd_init_table(struct ath_hw *ah);
 bool ar9003_paprd_is_done(struct ath_hw *ah);
 bool ar9003_is_paprd_enabled(struct ath_hw *ah);
+void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx);
 
 /* Hardware family op attach helpers */
 void ar5008_hw_attach_phy_ops(struct ath_hw *ah);
index be30a9af152884d8e073b7a3be5b79e6b3d5a8ee..dd91f8fdc01c3ea44c922bbbbd14bee2df4fd6db 100644 (file)
@@ -182,7 +182,7 @@ static void ath_restart_work(struct ath_softc *sc)
        ath_start_ani(sc);
 }
 
-static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush)
+static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx)
 {
        struct ath_hw *ah = sc->sc_ah;
        bool ret = true;
@@ -202,14 +202,6 @@ static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush)
        if (!ath_drain_all_txq(sc, retry_tx))
                ret = false;
 
-       if (!flush) {
-               if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
-                       ath_rx_tasklet(sc, 1, true);
-               ath_rx_tasklet(sc, 1, false);
-       } else {
-               ath_flushrecv(sc);
-       }
-
        return ret;
 }
 
@@ -262,11 +254,11 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan,
        struct ath_common *common = ath9k_hw_common(ah);
        struct ath9k_hw_cal_data *caldata = NULL;
        bool fastcc = true;
-       bool flush = false;
        int r;
 
        __ath_cancel_work(sc);
 
+       tasklet_disable(&sc->intr_tq);
        spin_lock_bh(&sc->sc_pcu_lock);
 
        if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) {
@@ -276,11 +268,10 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan,
 
        if (!hchan) {
                fastcc = false;
-               flush = true;
                hchan = ah->curchan;
        }
 
-       if (!ath_prepare_reset(sc, retry_tx, flush))
+       if (!ath_prepare_reset(sc, retry_tx))
                fastcc = false;
 
        ath_dbg(common, CONFIG, "Reset to %u MHz, HT40: %d fastcc: %d\n",
@@ -302,6 +293,8 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan,
 
 out:
        spin_unlock_bh(&sc->sc_pcu_lock);
+       tasklet_enable(&sc->intr_tq);
+
        return r;
 }
 
@@ -804,7 +797,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
                ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
        }
 
-       ath_prepare_reset(sc, false, true);
+       ath_prepare_reset(sc, false);
 
        if (sc->rx.frag) {
                dev_kfree_skb_any(sc->rx.frag);
@@ -1833,6 +1826,9 @@ static u32 fill_chainmask(u32 cap, u32 new)
 
 static bool validate_antenna_mask(struct ath_hw *ah, u32 val)
 {
+       if (AR_SREV_9300_20_OR_LATER(ah))
+               return true;
+
        switch (val & 0x7) {
        case 0x1:
        case 0x3:
index d4df98a938bf4755d1af6238d41e5df0753970c6..90752f2469704bbbb2bf7cae86a1c2cb4029435a 100644 (file)
@@ -254,8 +254,6 @@ rx_init_fail:
 
 static void ath_edma_start_recv(struct ath_softc *sc)
 {
-       spin_lock_bh(&sc->rx.rxbuflock);
-
        ath9k_hw_rxena(sc->sc_ah);
 
        ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_HP,
@@ -267,8 +265,6 @@ static void ath_edma_start_recv(struct ath_softc *sc)
        ath_opmode_init(sc);
 
        ath9k_hw_startpcureceive(sc->sc_ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL));
-
-       spin_unlock_bh(&sc->rx.rxbuflock);
 }
 
 static void ath_edma_stop_recv(struct ath_softc *sc)
@@ -285,8 +281,6 @@ int ath_rx_init(struct ath_softc *sc, int nbufs)
        int error = 0;
 
        spin_lock_init(&sc->sc_pcu_lock);
-       spin_lock_init(&sc->rx.rxbuflock);
-       clear_bit(SC_OP_RXFLUSH, &sc->sc_flags);
 
        common->rx_bufsize = IEEE80211_MAX_MPDU_LEN / 2 +
                             sc->sc_ah->caps.rx_status_len;
@@ -447,7 +441,6 @@ int ath_startrecv(struct ath_softc *sc)
                return 0;
        }
 
-       spin_lock_bh(&sc->rx.rxbuflock);
        if (list_empty(&sc->rx.rxbuf))
                goto start_recv;
 
@@ -468,26 +461,31 @@ start_recv:
        ath_opmode_init(sc);
        ath9k_hw_startpcureceive(ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL));
 
-       spin_unlock_bh(&sc->rx.rxbuflock);
-
        return 0;
 }
 
+static void ath_flushrecv(struct ath_softc *sc)
+{
+       if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
+               ath_rx_tasklet(sc, 1, true);
+       ath_rx_tasklet(sc, 1, false);
+}
+
 bool ath_stoprecv(struct ath_softc *sc)
 {
        struct ath_hw *ah = sc->sc_ah;
        bool stopped, reset = false;
 
-       spin_lock_bh(&sc->rx.rxbuflock);
        ath9k_hw_abortpcurecv(ah);
        ath9k_hw_setrxfilter(ah, 0);
        stopped = ath9k_hw_stopdmarecv(ah, &reset);
 
+       ath_flushrecv(sc);
+
        if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
                ath_edma_stop_recv(sc);
        else
                sc->rx.rxlink = NULL;
-       spin_unlock_bh(&sc->rx.rxbuflock);
 
        if (!(ah->ah_flags & AH_UNPLUGGED) &&
            unlikely(!stopped)) {
@@ -499,15 +497,6 @@ bool ath_stoprecv(struct ath_softc *sc)
        return stopped && !reset;
 }
 
-void ath_flushrecv(struct ath_softc *sc)
-{
-       set_bit(SC_OP_RXFLUSH, &sc->sc_flags);
-       if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
-               ath_rx_tasklet(sc, 1, true);
-       ath_rx_tasklet(sc, 1, false);
-       clear_bit(SC_OP_RXFLUSH, &sc->sc_flags);
-}
-
 static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb)
 {
        /* Check whether the Beacon frame has DTIM indicating buffered bc/mc */
@@ -744,6 +733,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
                        return NULL;
        }
 
+       list_del(&bf->list);
        if (!bf->bf_mpdu)
                return bf;
 
@@ -1059,16 +1049,12 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
                dma_type = DMA_FROM_DEVICE;
 
        qtype = hp ? ATH9K_RX_QUEUE_HP : ATH9K_RX_QUEUE_LP;
-       spin_lock_bh(&sc->rx.rxbuflock);
 
        tsf = ath9k_hw_gettsf64(ah);
        tsf_lower = tsf & 0xffffffff;
 
        do {
                bool decrypt_error = false;
-               /* If handling rx interrupt and flush is in progress => exit */
-               if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags) && (flush == 0))
-                       break;
 
                memset(&rs, 0, sizeof(rs));
                if (edma)
@@ -1111,15 +1097,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
 
                ath_debug_stat_rx(sc, &rs);
 
-               /*
-                * If we're asked to flush receive queue, directly
-                * chain it back at the queue without processing it.
-                */
-               if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags)) {
-                       RX_STAT_INC(rx_drop_rxflush);
-                       goto requeue_drop_frag;
-               }
-
                memset(rxs, 0, sizeof(struct ieee80211_rx_status));
 
                rxs->mactime = (tsf & ~0xffffffffULL) | rs.rs_tstamp;
@@ -1254,19 +1231,18 @@ requeue_drop_frag:
                        sc->rx.frag = NULL;
                }
 requeue:
+               list_add_tail(&bf->list, &sc->rx.rxbuf);
+               if (flush)
+                       continue;
+
                if (edma) {
-                       list_add_tail(&bf->list, &sc->rx.rxbuf);
                        ath_rx_edma_buf_link(sc, qtype);
                } else {
-                       list_move_tail(&bf->list, &sc->rx.rxbuf);
                        ath_rx_buf_link(sc, bf);
-                       if (!flush)
-                               ath9k_hw_rxena(ah);
+                       ath9k_hw_rxena(ah);
                }
        } while (1);
 
-       spin_unlock_bh(&sc->rx.rxbuflock);
-
        if (!(ah->imask & ATH9K_INT_RXEOL)) {
                ah->imask |= (ATH9K_INT_RXEOL | ATH9K_INT_RXORN);
                ath9k_hw_set_interrupts(ah);
diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig
new file mode 100644 (file)
index 0000000..bac3d98
--- /dev/null
@@ -0,0 +1,29 @@
+config WIL6210
+       tristate "Wilocity 60g WiFi card wil6210 support"
+       depends on CFG80211
+       depends on PCI
+       default n
+       ---help---
+         This module adds support for wireless adapter based on
+         wil6210 chip by Wilocity. It supports operation on the
+         60 GHz band, covered by the IEEE802.11ad standard.
+
+         http://wireless.kernel.org/en/users/Drivers/wil6210
+
+         If you choose to build it as a module, it will be called
+         wil6210
+
+config WIL6210_ISR_COR
+       bool "Use Clear-On-Read mode for ISR registers for wil6210"
+       depends on WIL6210
+       default y
+       ---help---
+         ISR registers on wil6210 chip may operate in either
+         COR (Clear-On-Read) or W1C (Write-1-to-Clear) mode.
+         For production code, use COR (say y); is default since
+         it saves extra target transaction;
+         For ISR debug, use W1C (say n); is allows to monitor ISR
+         registers with debugfs. If COR were used, ISR would
+         self-clear when accessed for debug purposes, it makes
+         such monitoring impossible.
+         Say y unless you debug interrupts
diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile
new file mode 100644 (file)
index 0000000..9396dc9
--- /dev/null
@@ -0,0 +1,13 @@
+obj-$(CONFIG_WIL6210) += wil6210.o
+
+wil6210-objs := main.o
+wil6210-objs += netdev.o
+wil6210-objs += cfg80211.o
+wil6210-objs += pcie_bus.o
+wil6210-objs += debugfs.o
+wil6210-objs += wmi.o
+wil6210-objs += interrupt.o
+wil6210-objs += txrx.o
+
+subdir-ccflags-y += -Werror
+subdir-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
new file mode 100644 (file)
index 0000000..116f4e8
--- /dev/null
@@ -0,0 +1,573 @@
+/*
+ * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/sched.h>
+#include <linux/etherdevice.h>
+#include <linux/wireless.h>
+#include <linux/ieee80211.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <net/cfg80211.h>
+
+#include "wil6210.h"
+#include "wmi.h"
+
+#define CHAN60G(_channel, _flags) {                            \
+       .band                   = IEEE80211_BAND_60GHZ,         \
+       .center_freq            = 56160 + (2160 * (_channel)),  \
+       .hw_value               = (_channel),                   \
+       .flags                  = (_flags),                     \
+       .max_antenna_gain       = 0,                            \
+       .max_power              = 40,                           \
+}
+
+static struct ieee80211_channel wil_60ghz_channels[] = {
+       CHAN60G(1, 0),
+       CHAN60G(2, 0),
+       CHAN60G(3, 0),
+/* channel 4 not supported yet */
+};
+
+static struct ieee80211_supported_band wil_band_60ghz = {
+       .channels = wil_60ghz_channels,
+       .n_channels = ARRAY_SIZE(wil_60ghz_channels),
+       .ht_cap = {
+               .ht_supported = true,
+               .cap = 0, /* TODO */
+               .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, /* TODO */
+               .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, /* TODO */
+               .mcs = {
+                               /* MCS 1..12 - SC PHY */
+                       .rx_mask = {0xfe, 0x1f}, /* 1..12 */
+                       .tx_params = IEEE80211_HT_MCS_TX_DEFINED, /* TODO */
+               },
+       },
+};
+
+static const struct ieee80211_txrx_stypes
+wil_mgmt_stypes[NUM_NL80211_IFTYPES] = {
+       [NL80211_IFTYPE_STATION] = {
+               .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
+               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+       },
+       [NL80211_IFTYPE_AP] = {
+               .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
+               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+       },
+       [NL80211_IFTYPE_P2P_CLIENT] = {
+               .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
+               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+       },
+       [NL80211_IFTYPE_P2P_GO] = {
+               .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
+               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+       },
+};
+
+static const u32 wil_cipher_suites[] = {
+       WLAN_CIPHER_SUITE_GCMP,
+};
+
+int wil_iftype_nl2wmi(enum nl80211_iftype type)
+{
+       static const struct {
+               enum nl80211_iftype nl;
+               enum wmi_network_type wmi;
+       } __nl2wmi[] = {
+               {NL80211_IFTYPE_ADHOC,          WMI_NETTYPE_ADHOC},
+               {NL80211_IFTYPE_STATION,        WMI_NETTYPE_INFRA},
+               {NL80211_IFTYPE_AP,             WMI_NETTYPE_AP},
+               {NL80211_IFTYPE_P2P_CLIENT,     WMI_NETTYPE_P2P},
+               {NL80211_IFTYPE_P2P_GO,         WMI_NETTYPE_P2P},
+               {NL80211_IFTYPE_MONITOR,        WMI_NETTYPE_ADHOC}, /* FIXME */
+       };
+       uint i;
+
+       for (i = 0; i < ARRAY_SIZE(__nl2wmi); i++) {
+               if (__nl2wmi[i].nl == type)
+                       return __nl2wmi[i].wmi;
+       }
+
+       return -EOPNOTSUPP;
+}
+
+static int wil_cfg80211_get_station(struct wiphy *wiphy,
+                                   struct net_device *ndev,
+                                   u8 *mac, struct station_info *sinfo)
+{
+       struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+       int rc;
+       struct wmi_notify_req_cmd cmd = {
+               .cid = 0,
+               .interval_usec = 0,
+       };
+
+       if (memcmp(mac, wil->dst_addr[0], ETH_ALEN))
+               return -ENOENT;
+
+       /* WMI_NOTIFY_REQ_DONE_EVENTID handler fills wil->stats.bf_mcs */
+       rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd),
+                     WMI_NOTIFY_REQ_DONE_EVENTID, NULL, 0, 20);
+       if (rc)
+               return rc;
+
+       sinfo->generation = wil->sinfo_gen;
+
+       sinfo->filled |= STATION_INFO_TX_BITRATE;
+       sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G;
+       sinfo->txrate.mcs = wil->stats.bf_mcs;
+       sinfo->filled |= STATION_INFO_RX_BITRATE;
+       sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G;
+       sinfo->rxrate.mcs = wil->stats.last_mcs_rx;
+
+       if (test_bit(wil_status_fwconnected, &wil->status)) {
+               sinfo->filled |= STATION_INFO_SIGNAL;
+               sinfo->signal = 12; /* TODO: provide real value */
+       }
+
+       return 0;
+}
+
+static int wil_cfg80211_change_iface(struct wiphy *wiphy,
+                                    struct net_device *ndev,
+                                    enum nl80211_iftype type, u32 *flags,
+                                    struct vif_params *params)
+{
+       struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+       struct wireless_dev *wdev = wil->wdev;
+
+       switch (type) {
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_P2P_CLIENT:
+       case NL80211_IFTYPE_P2P_GO:
+               break;
+       case NL80211_IFTYPE_MONITOR:
+               if (flags)
+                       wil->monitor_flags = *flags;
+               else
+                       wil->monitor_flags = 0;
+
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       wdev->iftype = type;
+
+       return 0;
+}
+
+static int wil_cfg80211_scan(struct wiphy *wiphy,
+                            struct cfg80211_scan_request *request)
+{
+       struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+       struct wireless_dev *wdev = wil->wdev;
+       struct {
+               struct wmi_start_scan_cmd cmd;
+               u16 chnl[4];
+       } __packed cmd;
+       uint i, n;
+
+       if (wil->scan_request) {
+               wil_err(wil, "Already scanning\n");
+               return -EAGAIN;
+       }
+
+       /* check we are client side */
+       switch (wdev->iftype) {
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_P2P_CLIENT:
+               break;
+       default:
+               return -EOPNOTSUPP;
+
+       }
+
+       /* FW don't support scan after connection attempt */
+       if (test_bit(wil_status_dontscan, &wil->status)) {
+               wil_err(wil, "Scan after connect attempt not supported\n");
+               return -EBUSY;
+       }
+
+       wil->scan_request = request;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.cmd.num_channels = 0;
+       n = min(request->n_channels, 4U);
+       for (i = 0; i < n; i++) {
+               int ch = request->channels[i]->hw_value;
+               if (ch == 0) {
+                       wil_err(wil,
+                               "Scan requested for unknown frequency %dMhz\n",
+                               request->channels[i]->center_freq);
+                       continue;
+               }
+               /* 0-based channel indexes */
+               cmd.cmd.channel_list[cmd.cmd.num_channels++].channel = ch - 1;
+               wil_dbg(wil, "Scan for ch %d  : %d MHz\n", ch,
+                       request->channels[i]->center_freq);
+       }
+
+       return wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) +
+                       cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0]));
+}
+
+static int wil_cfg80211_connect(struct wiphy *wiphy,
+                               struct net_device *ndev,
+                               struct cfg80211_connect_params *sme)
+{
+       struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+       struct cfg80211_bss *bss;
+       struct wmi_connect_cmd conn;
+       const u8 *ssid_eid;
+       const u8 *rsn_eid;
+       int ch;
+       int rc = 0;
+
+       bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
+                              sme->ssid, sme->ssid_len,
+                              WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
+       if (!bss) {
+               wil_err(wil, "Unable to find BSS\n");
+               return -ENOENT;
+       }
+
+       ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
+       if (!ssid_eid) {
+               wil_err(wil, "No SSID\n");
+               rc = -ENOENT;
+               goto out;
+       }
+
+       rsn_eid = sme->ie ?
+                       cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len) :
+                       NULL;
+       if (rsn_eid) {
+               if (sme->ie_len > WMI_MAX_IE_LEN) {
+                       rc = -ERANGE;
+                       wil_err(wil, "IE too large (%td bytes)\n",
+                               sme->ie_len);
+                       goto out;
+               }
+               /*
+                * For secure assoc, send:
+                * (1) WMI_DELETE_CIPHER_KEY_CMD
+                * (2) WMI_SET_APPIE_CMD
+                */
+               rc = wmi_del_cipher_key(wil, 0, bss->bssid);
+               if (rc) {
+                       wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD failed\n");
+                       goto out;
+               }
+               /* WMI_SET_APPIE_CMD */
+               rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie);
+               if (rc) {
+                       wil_err(wil, "WMI_SET_APPIE_CMD failed\n");
+                       goto out;
+               }
+       }
+
+       /* WMI_CONNECT_CMD */
+       memset(&conn, 0, sizeof(conn));
+       switch (bss->capability & 0x03) {
+       case WLAN_CAPABILITY_DMG_TYPE_AP:
+               conn.network_type = WMI_NETTYPE_INFRA;
+               break;
+       case WLAN_CAPABILITY_DMG_TYPE_PBSS:
+               conn.network_type = WMI_NETTYPE_P2P;
+               break;
+       default:
+               wil_err(wil, "Unsupported BSS type, capability= 0x%04x\n",
+                       bss->capability);
+               goto out;
+       }
+       if (rsn_eid) {
+               conn.dot11_auth_mode = WMI_AUTH11_SHARED;
+               conn.auth_mode = WMI_AUTH_WPA2_PSK;
+               conn.pairwise_crypto_type = WMI_CRYPT_AES_GCMP;
+               conn.pairwise_crypto_len = 16;
+       } else {
+               conn.dot11_auth_mode = WMI_AUTH11_OPEN;
+               conn.auth_mode = WMI_AUTH_NONE;
+       }
+
+       conn.ssid_len = min_t(u8, ssid_eid[1], 32);
+       memcpy(conn.ssid, ssid_eid+2, conn.ssid_len);
+
+       ch = bss->channel->hw_value;
+       if (ch == 0) {
+               wil_err(wil, "BSS at unknown frequency %dMhz\n",
+                       bss->channel->center_freq);
+               rc = -EOPNOTSUPP;
+               goto out;
+       }
+       conn.channel = ch - 1;
+
+       memcpy(conn.bssid, bss->bssid, 6);
+       memcpy(conn.dst_mac, bss->bssid, 6);
+       /*
+        * FW don't support scan after connection attempt
+        */
+       set_bit(wil_status_dontscan, &wil->status);
+
+       rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn));
+       if (rc == 0) {
+               /* Connect can take lots of time */
+               mod_timer(&wil->connect_timer,
+                         jiffies + msecs_to_jiffies(2000));
+       }
+
+ out:
+       cfg80211_put_bss(bss);
+
+       return rc;
+}
+
+static int wil_cfg80211_disconnect(struct wiphy *wiphy,
+                                  struct net_device *ndev,
+                                  u16 reason_code)
+{
+       int rc;
+       struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+
+       rc = wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0);
+
+       return rc;
+}
+
+static int wil_cfg80211_set_channel(struct wiphy *wiphy,
+                                   struct cfg80211_chan_def *chandef)
+{
+       struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+       struct wireless_dev *wdev = wil->wdev;
+
+       wdev->preset_chandef = *chandef;
+
+       return 0;
+}
+
+static int wil_cfg80211_add_key(struct wiphy *wiphy,
+                               struct net_device *ndev,
+                               u8 key_index, bool pairwise,
+                               const u8 *mac_addr,
+                               struct key_params *params)
+{
+       struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+
+       /* group key is not used */
+       if (!pairwise)
+               return 0;
+
+       return wmi_add_cipher_key(wil, key_index, mac_addr,
+                                 params->key_len, params->key);
+}
+
+static int wil_cfg80211_del_key(struct wiphy *wiphy,
+                               struct net_device *ndev,
+                               u8 key_index, bool pairwise,
+                               const u8 *mac_addr)
+{
+       struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+
+       /* group key is not used */
+       if (!pairwise)
+               return 0;
+
+       return wmi_del_cipher_key(wil, key_index, mac_addr);
+}
+
+/* Need to be present or wiphy_new() will WARN */
+static int wil_cfg80211_set_default_key(struct wiphy *wiphy,
+                                       struct net_device *ndev,
+                                       u8 key_index, bool unicast,
+                                       bool multicast)
+{
+       return 0;
+}
+
+static int wil_cfg80211_start_ap(struct wiphy *wiphy,
+                                struct net_device *ndev,
+                                struct cfg80211_ap_settings *info)
+{
+       int rc = 0;
+       struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+       struct wireless_dev *wdev = ndev->ieee80211_ptr;
+       struct ieee80211_channel *channel = info->chandef.chan;
+       struct cfg80211_beacon_data *bcon = &info->beacon;
+       u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
+
+       if (!channel) {
+               wil_err(wil, "AP: No channel???\n");
+               return -EINVAL;
+       }
+
+       wil_dbg(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value,
+               channel->center_freq, info->privacy ? "secure" : "open");
+       print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET,
+                            info->ssid, info->ssid_len);
+
+       rc = wil_reset(wil);
+       if (rc)
+               return rc;
+
+       rc = wmi_set_ssid(wil, info->ssid_len, info->ssid);
+       if (rc)
+               return rc;
+
+       rc = wmi_set_channel(wil, channel->hw_value);
+       if (rc)
+               return rc;
+
+       /* MAC address - pre-requisite for other commands */
+       wmi_set_mac_address(wil, ndev->dev_addr);
+
+       /* IE's */
+       /* bcon 'head IE's are not relevant for 60g band */
+       wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->beacon_ies_len,
+                  bcon->beacon_ies);
+       wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, bcon->proberesp_ies_len,
+                  bcon->proberesp_ies);
+       wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, bcon->assocresp_ies_len,
+                  bcon->assocresp_ies);
+
+       wil->secure_pcp = info->privacy;
+
+       rc = wmi_set_bcon(wil, info->beacon_interval, wmi_nettype);
+       if (rc)
+               return rc;
+
+       /* Rx VRING. After MAC and beacon */
+       rc = wil_rx_init(wil);
+
+       netif_carrier_on(ndev);
+
+       return rc;
+}
+
+static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
+                               struct net_device *ndev)
+{
+       int rc = 0;
+       struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+       struct wireless_dev *wdev = ndev->ieee80211_ptr;
+       u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
+
+       /* To stop beaconing, set BI to 0 */
+       rc = wmi_set_bcon(wil, 0, wmi_nettype);
+
+       return rc;
+}
+
+static struct cfg80211_ops wil_cfg80211_ops = {
+       .scan = wil_cfg80211_scan,
+       .connect = wil_cfg80211_connect,
+       .disconnect = wil_cfg80211_disconnect,
+       .change_virtual_intf = wil_cfg80211_change_iface,
+       .get_station = wil_cfg80211_get_station,
+       .set_monitor_channel = wil_cfg80211_set_channel,
+       .add_key = wil_cfg80211_add_key,
+       .del_key = wil_cfg80211_del_key,
+       .set_default_key = wil_cfg80211_set_default_key,
+       /* AP mode */
+       .start_ap = wil_cfg80211_start_ap,
+       .stop_ap = wil_cfg80211_stop_ap,
+};
+
+static void wil_wiphy_init(struct wiphy *wiphy)
+{
+       /* TODO: set real value */
+       wiphy->max_scan_ssids = 10;
+       wiphy->max_num_pmkids = 0 /* TODO: */;
+       wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+                                BIT(NL80211_IFTYPE_AP) |
+                                BIT(NL80211_IFTYPE_MONITOR);
+       /* TODO: enable P2P when integrated with supplicant:
+        * BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO)
+        */
+       wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME |
+                       WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
+       dev_warn(wiphy_dev(wiphy), "%s : flags = 0x%08x\n",
+                __func__, wiphy->flags);
+       wiphy->probe_resp_offload =
+               NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
+               NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
+               NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
+
+       wiphy->bands[IEEE80211_BAND_60GHZ] = &wil_band_60ghz;
+
+       /* TODO: figure this out */
+       wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+
+       wiphy->cipher_suites = wil_cipher_suites;
+       wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites);
+       wiphy->mgmt_stypes = wil_mgmt_stypes;
+}
+
+struct wireless_dev *wil_cfg80211_init(struct device *dev)
+{
+       int rc = 0;
+       struct wireless_dev *wdev;
+
+       wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
+       if (!wdev)
+               return ERR_PTR(-ENOMEM);
+
+       wdev->wiphy = wiphy_new(&wil_cfg80211_ops,
+                               sizeof(struct wil6210_priv));
+       if (!wdev->wiphy) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       set_wiphy_dev(wdev->wiphy, dev);
+       wil_wiphy_init(wdev->wiphy);
+
+       rc = wiphy_register(wdev->wiphy);
+       if (rc < 0)
+               goto out_failed_reg;
+
+       return wdev;
+
+out_failed_reg:
+       wiphy_free(wdev->wiphy);
+out:
+       kfree(wdev);
+
+       return ERR_PTR(rc);
+}
+
+void wil_wdev_free(struct wil6210_priv *wil)
+{
+       struct wireless_dev *wdev = wil_to_wdev(wil);
+
+       if (!wdev)
+               return;
+
+       wiphy_unregister(wdev->wiphy);
+       wiphy_free(wdev->wiphy);
+       kfree(wdev);
+}
diff --git a/drivers/net/wireless/ath/wil6210/dbg_hexdump.h b/drivers/net/wireless/ath/wil6210/dbg_hexdump.h
new file mode 100644 (file)
index 0000000..6a315ba
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef WIL_DBG_HEXDUMP_H_
+#define WIL_DBG_HEXDUMP_H_
+
+#if defined(CONFIG_DYNAMIC_DEBUG)
+#define wil_dynamic_hex_dump(prefix_str, prefix_type, rowsize, \
+                            groupsize, buf, len, ascii)        \
+do {                                                           \
+       DEFINE_DYNAMIC_DEBUG_METADATA(descriptor,               \
+               __builtin_constant_p(prefix_str) ? prefix_str : "hexdump");\
+       if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT))  \
+               print_hex_dump(KERN_DEBUG, prefix_str,          \
+                              prefix_type, rowsize, groupsize, \
+                              buf, len, ascii);                \
+} while (0)
+
+#define wil_print_hex_dump_debug(prefix_str, prefix_type, rowsize,     \
+                                groupsize, buf, len, ascii)            \
+       wil_dynamic_hex_dump(prefix_str, prefix_type, rowsize,          \
+                            groupsize, buf, len, ascii)
+
+#define print_hex_dump_bytes(prefix_str, prefix_type, buf, len)        \
+       wil_dynamic_hex_dump(prefix_str, prefix_type, 16, 1, buf, len, true)
+#else /* defined(CONFIG_DYNAMIC_DEBUG) */
+#define wil_print_hex_dump_debug(prefix_str, prefix_type, rowsize,     \
+                                groupsize, buf, len, ascii)            \
+       print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, rowsize,    \
+                      groupsize, buf, len, ascii)
+#endif /* defined(CONFIG_DYNAMIC_DEBUG) */
+
+#endif /* WIL_DBG_HEXDUMP_H_ */
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
new file mode 100644 (file)
index 0000000..65fc968
--- /dev/null
@@ -0,0 +1,603 @@
+/*
+ * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/pci.h>
+#include <linux/rtnetlink.h>
+
+#include "wil6210.h"
+#include "txrx.h"
+
+/* Nasty hack. Better have per device instances */
+static u32 mem_addr;
+static u32 dbg_txdesc_index;
+
+static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil,
+                           const char *name, struct vring *vring)
+{
+       void __iomem *x = wmi_addr(wil, vring->hwtail);
+
+       seq_printf(s, "VRING %s = {\n", name);
+       seq_printf(s, "  pa     = 0x%016llx\n", (unsigned long long)vring->pa);
+       seq_printf(s, "  va     = 0x%p\n", vring->va);
+       seq_printf(s, "  size   = %d\n", vring->size);
+       seq_printf(s, "  swtail = %d\n", vring->swtail);
+       seq_printf(s, "  swhead = %d\n", vring->swhead);
+       seq_printf(s, "  hwtail = [0x%08x] -> ", vring->hwtail);
+       if (x)
+               seq_printf(s, "0x%08x\n", ioread32(x));
+       else
+               seq_printf(s, "???\n");
+
+       if (vring->va && (vring->size < 1025)) {
+               uint i;
+               for (i = 0; i < vring->size; i++) {
+                       volatile struct vring_tx_desc *d = &vring->va[i].tx;
+                       if ((i % 64) == 0 && (i != 0))
+                               seq_printf(s, "\n");
+                       seq_printf(s, "%s", (d->dma.status & BIT(0)) ?
+                                       "S" : (vring->ctx[i] ? "H" : "h"));
+               }
+               seq_printf(s, "\n");
+       }
+       seq_printf(s, "}\n");
+}
+
+static int wil_vring_debugfs_show(struct seq_file *s, void *data)
+{
+       uint i;
+       struct wil6210_priv *wil = s->private;
+
+       wil_print_vring(s, wil, "rx", &wil->vring_rx);
+
+       for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
+               struct vring *vring = &(wil->vring_tx[i]);
+               if (vring->va) {
+                       char name[10];
+                       snprintf(name, sizeof(name), "tx_%2d", i);
+                       wil_print_vring(s, wil, name, vring);
+               }
+       }
+
+       return 0;
+}
+
+static int wil_vring_seq_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, wil_vring_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations fops_vring = {
+       .open           = wil_vring_seq_open,
+       .release        = single_release,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+};
+
+static void wil_print_ring(struct seq_file *s, const char *prefix,
+                          void __iomem *off)
+{
+       struct wil6210_priv *wil = s->private;
+       struct wil6210_mbox_ring r;
+       int rsize;
+       uint i;
+
+       wil_memcpy_fromio_32(&r, off, sizeof(r));
+       wil_mbox_ring_le2cpus(&r);
+       /*
+        * we just read memory block from NIC. This memory may be
+        * garbage. Check validity before using it.
+        */
+       rsize = r.size / sizeof(struct wil6210_mbox_ring_desc);
+
+       seq_printf(s, "ring %s = {\n", prefix);
+       seq_printf(s, "  base = 0x%08x\n", r.base);
+       seq_printf(s, "  size = 0x%04x bytes -> %d entries\n", r.size, rsize);
+       seq_printf(s, "  tail = 0x%08x\n", r.tail);
+       seq_printf(s, "  head = 0x%08x\n", r.head);
+       seq_printf(s, "  entry size = %d\n", r.entry_size);
+
+       if (r.size % sizeof(struct wil6210_mbox_ring_desc)) {
+               seq_printf(s, "  ??? size is not multiple of %zd, garbage?\n",
+                          sizeof(struct wil6210_mbox_ring_desc));
+               goto out;
+       }
+
+       if (!wmi_addr(wil, r.base) ||
+           !wmi_addr(wil, r.tail) ||
+           !wmi_addr(wil, r.head)) {
+               seq_printf(s, "  ??? pointers are garbage?\n");
+               goto out;
+       }
+
+       for (i = 0; i < rsize; i++) {
+               struct wil6210_mbox_ring_desc d;
+               struct wil6210_mbox_hdr hdr;
+               size_t delta = i * sizeof(d);
+               void __iomem *x = wil->csr + HOSTADDR(r.base) + delta;
+
+               wil_memcpy_fromio_32(&d, x, sizeof(d));
+
+               seq_printf(s, "  [%2x] %s %s%s 0x%08x", i,
+                          d.sync ? "F" : "E",
+                          (r.tail - r.base == delta) ? "t" : " ",
+                          (r.head - r.base == delta) ? "h" : " ",
+                          le32_to_cpu(d.addr));
+               if (0 == wmi_read_hdr(wil, d.addr, &hdr)) {
+                       u16 len = le16_to_cpu(hdr.len);
+                       seq_printf(s, " -> %04x %04x %04x %02x\n",
+                                  le16_to_cpu(hdr.seq), len,
+                                  le16_to_cpu(hdr.type), hdr.flags);
+                       if (len <= MAX_MBOXITEM_SIZE) {
+                               int n = 0;
+                               unsigned char printbuf[16 * 3 + 2];
+                               unsigned char databuf[MAX_MBOXITEM_SIZE];
+                               void __iomem *src = wmi_buffer(wil, d.addr) +
+                                       sizeof(struct wil6210_mbox_hdr);
+                               /*
+                                * No need to check @src for validity -
+                                * we already validated @d.addr while
+                                * reading header
+                                */
+                               wil_memcpy_fromio_32(databuf, src, len);
+                               while (n < len) {
+                                       int l = min(len - n, 16);
+                                       hex_dump_to_buffer(databuf + n, l,
+                                                          16, 1, printbuf,
+                                                          sizeof(printbuf),
+                                                          false);
+                                       seq_printf(s, "      : %s\n", printbuf);
+                                       n += l;
+                               }
+                       }
+               } else {
+                       seq_printf(s, "\n");
+               }
+       }
+ out:
+       seq_printf(s, "}\n");
+}
+
+static int wil_mbox_debugfs_show(struct seq_file *s, void *data)
+{
+       struct wil6210_priv *wil = s->private;
+
+       wil_print_ring(s, "tx", wil->csr + HOST_MBOX +
+                      offsetof(struct wil6210_mbox_ctl, tx));
+       wil_print_ring(s, "rx", wil->csr + HOST_MBOX +
+                      offsetof(struct wil6210_mbox_ctl, rx));
+
+       return 0;
+}
+
+static int wil_mbox_seq_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, wil_mbox_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations fops_mbox = {
+       .open           = wil_mbox_seq_open,
+       .release        = single_release,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+};
+
+static int wil_debugfs_iomem_x32_set(void *data, u64 val)
+{
+       iowrite32(val, (void __iomem *)data);
+       wmb(); /* make sure write propagated to HW */
+
+       return 0;
+}
+
+static int wil_debugfs_iomem_x32_get(void *data, u64 *val)
+{
+       *val = ioread32((void __iomem *)data);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, wil_debugfs_iomem_x32_get,
+                       wil_debugfs_iomem_x32_set, "0x%08llx\n");
+
+static struct dentry *wil_debugfs_create_iomem_x32(const char *name,
+                                                  mode_t mode,
+                                                  struct dentry *parent,
+                                                  void __iomem *value)
+{
+       return debugfs_create_file(name, mode, parent, (void * __force)value,
+                                  &fops_iomem_x32);
+}
+
+static int wil6210_debugfs_create_ISR(struct wil6210_priv *wil,
+                                     const char *name,
+                                     struct dentry *parent, u32 off)
+{
+       struct dentry *d = debugfs_create_dir(name, parent);
+
+       if (IS_ERR_OR_NULL(d))
+               return -ENODEV;
+
+       wil_debugfs_create_iomem_x32("ICC", S_IRUGO | S_IWUSR, d,
+                                    wil->csr + off);
+       wil_debugfs_create_iomem_x32("ICR", S_IRUGO | S_IWUSR, d,
+                                    wil->csr + off + 4);
+       wil_debugfs_create_iomem_x32("ICM", S_IRUGO | S_IWUSR, d,
+                                    wil->csr + off + 8);
+       wil_debugfs_create_iomem_x32("ICS", S_IWUSR, d,
+                                    wil->csr + off + 12);
+       wil_debugfs_create_iomem_x32("IMV", S_IRUGO | S_IWUSR, d,
+                                    wil->csr + off + 16);
+       wil_debugfs_create_iomem_x32("IMS", S_IWUSR, d,
+                                    wil->csr + off + 20);
+       wil_debugfs_create_iomem_x32("IMC", S_IWUSR, d,
+                                    wil->csr + off + 24);
+
+       return 0;
+}
+
+static int wil6210_debugfs_create_pseudo_ISR(struct wil6210_priv *wil,
+                                            struct dentry *parent)
+{
+       struct dentry *d = debugfs_create_dir("PSEUDO_ISR", parent);
+
+       if (IS_ERR_OR_NULL(d))
+               return -ENODEV;
+
+       wil_debugfs_create_iomem_x32("CAUSE", S_IRUGO, d, wil->csr +
+                                    HOSTADDR(RGF_DMA_PSEUDO_CAUSE));
+       wil_debugfs_create_iomem_x32("MASK_SW", S_IRUGO, d, wil->csr +
+                                    HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW));
+       wil_debugfs_create_iomem_x32("MASK_FW", S_IRUGO, d, wil->csr +
+                                    HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_FW));
+
+       return 0;
+}
+
+static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil,
+                                         struct dentry *parent)
+{
+       struct dentry *d = debugfs_create_dir("ITR_CNT", parent);
+
+       if (IS_ERR_OR_NULL(d))
+               return -ENODEV;
+
+       wil_debugfs_create_iomem_x32("TRSH", S_IRUGO, d, wil->csr +
+                                    HOSTADDR(RGF_DMA_ITR_CNT_TRSH));
+       wil_debugfs_create_iomem_x32("DATA", S_IRUGO, d, wil->csr +
+                                    HOSTADDR(RGF_DMA_ITR_CNT_DATA));
+       wil_debugfs_create_iomem_x32("CTL", S_IRUGO, d, wil->csr +
+                                    HOSTADDR(RGF_DMA_ITR_CNT_CRL));
+
+       return 0;
+}
+
+static int wil_memread_debugfs_show(struct seq_file *s, void *data)
+{
+       struct wil6210_priv *wil = s->private;
+       void __iomem *a = wmi_buffer(wil, cpu_to_le32(mem_addr));
+
+       if (a)
+               seq_printf(s, "[0x%08x] = 0x%08x\n", mem_addr, ioread32(a));
+       else
+               seq_printf(s, "[0x%08x] = INVALID\n", mem_addr);
+
+       return 0;
+}
+
+static int wil_memread_seq_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, wil_memread_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations fops_memread = {
+       .open           = wil_memread_seq_open,
+       .release        = single_release,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+};
+
+static int wil_default_open(struct inode *inode, struct file *file)
+{
+       if (inode->i_private)
+               file->private_data = inode->i_private;
+
+       return 0;
+}
+
+static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
+                               size_t count, loff_t *ppos)
+{
+       enum { max_count = 4096 };
+       struct debugfs_blob_wrapper *blob = file->private_data;
+       loff_t pos = *ppos;
+       size_t available = blob->size;
+       void *buf;
+       size_t ret;
+
+       if (pos < 0)
+               return -EINVAL;
+
+       if (pos >= available || !count)
+               return 0;
+
+       if (count > available - pos)
+               count = available - pos;
+       if (count > max_count)
+               count = max_count;
+
+       buf = kmalloc(count, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       wil_memcpy_fromio_32(buf, (const volatile void __iomem *)blob->data +
+                            pos, count);
+
+       ret = copy_to_user(user_buf, buf, count);
+       kfree(buf);
+       if (ret == count)
+               return -EFAULT;
+
+       count -= ret;
+       *ppos = pos + count;
+
+       return count;
+}
+
+static const struct file_operations fops_ioblob = {
+       .read =         wil_read_file_ioblob,
+       .open =         wil_default_open,
+       .llseek =       default_llseek,
+};
+
+static
+struct dentry *wil_debugfs_create_ioblob(const char *name,
+                                        mode_t mode,
+                                        struct dentry *parent,
+                                        struct debugfs_blob_wrapper *blob)
+{
+       return debugfs_create_file(name, mode, parent, blob, &fops_ioblob);
+}
+/*---reset---*/
+static ssize_t wil_write_file_reset(struct file *file, const char __user *buf,
+                                   size_t len, loff_t *ppos)
+{
+       struct wil6210_priv *wil = file->private_data;
+       struct net_device *ndev = wil_to_ndev(wil);
+
+       /**
+        * BUG:
+        * this code does NOT sync device state with the rest of system
+        * use with care, debug only!!!
+        */
+       rtnl_lock();
+       dev_close(ndev);
+       ndev->flags &= ~IFF_UP;
+       rtnl_unlock();
+       wil_reset(wil);
+
+       return len;
+}
+
+static const struct file_operations fops_reset = {
+       .write = wil_write_file_reset,
+       .open  = wil_default_open,
+};
+/*---------Tx descriptor------------*/
+
+static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
+{
+       struct wil6210_priv *wil = s->private;
+       struct vring *vring = &(wil->vring_tx[0]);
+
+       if (!vring->va) {
+               seq_printf(s, "No Tx VRING\n");
+               return 0;
+       }
+
+       if (dbg_txdesc_index < vring->size) {
+               volatile struct vring_tx_desc *d =
+                               &(vring->va[dbg_txdesc_index].tx);
+               volatile u32 *u = (volatile u32 *)d;
+               struct sk_buff *skb = vring->ctx[dbg_txdesc_index];
+
+               seq_printf(s, "Tx[%3d] = {\n", dbg_txdesc_index);
+               seq_printf(s, "  MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n",
+                          u[0], u[1], u[2], u[3]);
+               seq_printf(s, "  DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n",
+                          u[4], u[5], u[6], u[7]);
+               seq_printf(s, "  SKB = %p\n", skb);
+
+               if (skb) {
+                       unsigned char printbuf[16 * 3 + 2];
+                       int i = 0;
+                       int len = skb_headlen(skb);
+                       void *p = skb->data;
+
+                       seq_printf(s, "    len = %d\n", len);
+
+                       while (i < len) {
+                               int l = min(len - i, 16);
+                               hex_dump_to_buffer(p + i, l, 16, 1, printbuf,
+                                                  sizeof(printbuf), false);
+                               seq_printf(s, "      : %s\n", printbuf);
+                               i += l;
+                       }
+               }
+               seq_printf(s, "}\n");
+       } else {
+               seq_printf(s, "TxDesc index (%d) >= size (%d)\n",
+                          dbg_txdesc_index, vring->size);
+       }
+
+       return 0;
+}
+
+static int wil_txdesc_seq_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, wil_txdesc_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations fops_txdesc = {
+       .open           = wil_txdesc_seq_open,
+       .release        = single_release,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+};
+
+/*---------beamforming------------*/
+static int wil_bf_debugfs_show(struct seq_file *s, void *data)
+{
+       struct wil6210_priv *wil = s->private;
+       seq_printf(s,
+                  "TSF : 0x%016llx\n"
+                  "TxMCS : %d\n"
+                  "Sectors(rx:tx) my %2d:%2d peer %2d:%2d\n",
+                  wil->stats.tsf, wil->stats.bf_mcs,
+                  wil->stats.my_rx_sector, wil->stats.my_tx_sector,
+                  wil->stats.peer_rx_sector, wil->stats.peer_tx_sector);
+       return 0;
+}
+
+static int wil_bf_seq_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, wil_bf_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations fops_bf = {
+       .open           = wil_bf_seq_open,
+       .release        = single_release,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+};
+/*---------SSID------------*/
+static ssize_t wil_read_file_ssid(struct file *file, char __user *user_buf,
+                                 size_t count, loff_t *ppos)
+{
+       struct wil6210_priv *wil = file->private_data;
+       struct wireless_dev *wdev = wil_to_wdev(wil);
+
+       return simple_read_from_buffer(user_buf, count, ppos,
+                                      wdev->ssid, wdev->ssid_len);
+}
+
+static ssize_t wil_write_file_ssid(struct file *file, const char __user *buf,
+                                  size_t count, loff_t *ppos)
+{
+       struct wil6210_priv *wil = file->private_data;
+       struct wireless_dev *wdev = wil_to_wdev(wil);
+       struct net_device *ndev = wil_to_ndev(wil);
+
+       if (*ppos != 0) {
+               wil_err(wil, "Unable to set SSID substring from [%d]\n",
+                       (int)*ppos);
+               return -EINVAL;
+       }
+
+       if (count > sizeof(wdev->ssid)) {
+               wil_err(wil, "SSID too long, len = %d\n", (int)count);
+               return -EINVAL;
+       }
+       if (netif_running(ndev)) {
+               wil_err(wil, "Unable to change SSID on running interface\n");
+               return -EINVAL;
+       }
+
+       wdev->ssid_len = count;
+       return simple_write_to_buffer(wdev->ssid, wdev->ssid_len, ppos,
+                                     buf, count);
+}
+
+static const struct file_operations fops_ssid = {
+       .read = wil_read_file_ssid,
+       .write = wil_write_file_ssid,
+       .open  = wil_default_open,
+};
+
+/*----------------*/
+int wil6210_debugfs_init(struct wil6210_priv *wil)
+{
+       struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME,
+                       wil_to_wiphy(wil)->debugfsdir);
+
+       if (IS_ERR_OR_NULL(dbg))
+               return -ENODEV;
+
+       debugfs_create_file("mbox", S_IRUGO, dbg, wil, &fops_mbox);
+       debugfs_create_file("vrings", S_IRUGO, dbg, wil, &fops_vring);
+       debugfs_create_file("txdesc", S_IRUGO, dbg, wil, &fops_txdesc);
+       debugfs_create_u32("txdesc_index", S_IRUGO | S_IWUSR, dbg,
+                          &dbg_txdesc_index);
+       debugfs_create_file("bf", S_IRUGO, dbg, wil, &fops_bf);
+       debugfs_create_file("ssid", S_IRUGO | S_IWUSR, dbg, wil, &fops_ssid);
+       debugfs_create_u32("secure_pcp", S_IRUGO | S_IWUSR, dbg,
+                          &wil->secure_pcp);
+
+       wil6210_debugfs_create_ISR(wil, "USER_ICR", dbg,
+                                  HOSTADDR(RGF_USER_USER_ICR));
+       wil6210_debugfs_create_ISR(wil, "DMA_EP_TX_ICR", dbg,
+                                  HOSTADDR(RGF_DMA_EP_TX_ICR));
+       wil6210_debugfs_create_ISR(wil, "DMA_EP_RX_ICR", dbg,
+                                  HOSTADDR(RGF_DMA_EP_RX_ICR));
+       wil6210_debugfs_create_ISR(wil, "DMA_EP_MISC_ICR", dbg,
+                                  HOSTADDR(RGF_DMA_EP_MISC_ICR));
+       wil6210_debugfs_create_pseudo_ISR(wil, dbg);
+       wil6210_debugfs_create_ITR_CNT(wil, dbg);
+
+       debugfs_create_u32("mem_addr", S_IRUGO | S_IWUSR, dbg, &mem_addr);
+       debugfs_create_file("mem_val", S_IRUGO, dbg, wil, &fops_memread);
+
+       debugfs_create_file("reset", S_IWUSR, dbg, wil, &fops_reset);
+
+       wil->rgf_blob.data = (void * __force)wil->csr + 0;
+       wil->rgf_blob.size = 0xa000;
+       wil_debugfs_create_ioblob("blob_rgf", S_IRUGO, dbg, &wil->rgf_blob);
+
+       wil->fw_code_blob.data = (void * __force)wil->csr + 0x40000;
+       wil->fw_code_blob.size = 0x40000;
+       wil_debugfs_create_ioblob("blob_fw_code", S_IRUGO, dbg,
+                                 &wil->fw_code_blob);
+
+       wil->fw_data_blob.data = (void * __force)wil->csr + 0x80000;
+       wil->fw_data_blob.size = 0x8000;
+       wil_debugfs_create_ioblob("blob_fw_data", S_IRUGO, dbg,
+                                 &wil->fw_data_blob);
+
+       wil->fw_peri_blob.data = (void * __force)wil->csr + 0x88000;
+       wil->fw_peri_blob.size = 0x18000;
+       wil_debugfs_create_ioblob("blob_fw_peri", S_IRUGO, dbg,
+                                 &wil->fw_peri_blob);
+
+       wil->uc_code_blob.data = (void * __force)wil->csr + 0xa0000;
+       wil->uc_code_blob.size = 0x10000;
+       wil_debugfs_create_ioblob("blob_uc_code", S_IRUGO, dbg,
+                                 &wil->uc_code_blob);
+
+       wil->uc_data_blob.data = (void * __force)wil->csr + 0xb0000;
+       wil->uc_data_blob.size = 0x4000;
+       wil_debugfs_create_ioblob("blob_uc_data", S_IRUGO, dbg,
+                                 &wil->uc_data_blob);
+
+       return 0;
+}
+
+void wil6210_debugfs_remove(struct wil6210_priv *wil)
+{
+       debugfs_remove_recursive(wil->debug);
+       wil->debug = NULL;
+}
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
new file mode 100644 (file)
index 0000000..38049da
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/interrupt.h>
+
+#include "wil6210.h"
+
+/**
+ * Theory of operation:
+ *
+ * There is ISR pseudo-cause register,
+ * dma_rgf->DMA_RGF.PSEUDO_CAUSE.PSEUDO_CAUSE
+ * Its bits represents OR'ed bits from 3 real ISR registers:
+ * TX, RX, and MISC.
+ *
+ * Registers may be configured to either "write 1 to clear" or
+ * "clear on read" mode
+ *
+ * When handling interrupt, one have to mask/unmask interrupts for the
+ * real ISR registers, or hardware may malfunction.
+ *
+ */
+
+#define WIL6210_IRQ_DISABLE    (0xFFFFFFFFUL)
+#define WIL6210_IMC_RX         BIT_DMA_EP_RX_ICR_RX_DONE
+#define WIL6210_IMC_TX         (BIT_DMA_EP_TX_ICR_TX_DONE | \
+                               BIT_DMA_EP_TX_ICR_TX_DONE_N(0))
+#define WIL6210_IMC_MISC       (ISR_MISC_FW_READY | ISR_MISC_MBOX_EVT)
+
+#define WIL6210_IRQ_PSEUDO_MASK (u32)(~(BIT_DMA_PSEUDO_CAUSE_RX | \
+                                       BIT_DMA_PSEUDO_CAUSE_TX | \
+                                       BIT_DMA_PSEUDO_CAUSE_MISC))
+
+#if defined(CONFIG_WIL6210_ISR_COR)
+/* configure to Clear-On-Read mode */
+#define WIL_ICR_ICC_VALUE      (0xFFFFFFFFUL)
+
+static inline void wil_icr_clear(u32 x, void __iomem *addr)
+{
+
+}
+#else /* defined(CONFIG_WIL6210_ISR_COR) */
+/* configure to Write-1-to-Clear mode */
+#define WIL_ICR_ICC_VALUE      (0UL)
+
+static inline void wil_icr_clear(u32 x, void __iomem *addr)
+{
+       iowrite32(x, addr);
+}
+#endif /* defined(CONFIG_WIL6210_ISR_COR) */
+
+static inline u32 wil_ioread32_and_clear(void __iomem *addr)
+{
+       u32 x = ioread32(addr);
+
+       wil_icr_clear(x, addr);
+
+       return x;
+}
+
+static void wil6210_mask_irq_tx(struct wil6210_priv *wil)
+{
+       iowrite32(WIL6210_IRQ_DISABLE, wil->csr +
+                 HOSTADDR(RGF_DMA_EP_TX_ICR) +
+                 offsetof(struct RGF_ICR, IMS));
+}
+
+static void wil6210_mask_irq_rx(struct wil6210_priv *wil)
+{
+       iowrite32(WIL6210_IRQ_DISABLE, wil->csr +
+                 HOSTADDR(RGF_DMA_EP_RX_ICR) +
+                 offsetof(struct RGF_ICR, IMS));
+}
+
+static void wil6210_mask_irq_misc(struct wil6210_priv *wil)
+{
+       iowrite32(WIL6210_IRQ_DISABLE, wil->csr +
+                 HOSTADDR(RGF_DMA_EP_MISC_ICR) +
+                 offsetof(struct RGF_ICR, IMS));
+}
+
+static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil)
+{
+       wil_dbg_IRQ(wil, "%s()\n", __func__);
+
+       iowrite32(WIL6210_IRQ_DISABLE, wil->csr +
+                 HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW));
+
+       clear_bit(wil_status_irqen, &wil->status);
+}
+
+static void wil6210_unmask_irq_tx(struct wil6210_priv *wil)
+{
+       iowrite32(WIL6210_IMC_TX, wil->csr +
+                 HOSTADDR(RGF_DMA_EP_TX_ICR) +
+                 offsetof(struct RGF_ICR, IMC));
+}
+
+static void wil6210_unmask_irq_rx(struct wil6210_priv *wil)
+{
+       iowrite32(WIL6210_IMC_RX, wil->csr +
+                 HOSTADDR(RGF_DMA_EP_RX_ICR) +
+                 offsetof(struct RGF_ICR, IMC));
+}
+
+static void wil6210_unmask_irq_misc(struct wil6210_priv *wil)
+{
+       iowrite32(WIL6210_IMC_MISC, wil->csr +
+                 HOSTADDR(RGF_DMA_EP_MISC_ICR) +
+                 offsetof(struct RGF_ICR, IMC));
+}
+
+static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil)
+{
+       wil_dbg_IRQ(wil, "%s()\n", __func__);
+
+       set_bit(wil_status_irqen, &wil->status);
+
+       iowrite32(WIL6210_IRQ_PSEUDO_MASK, wil->csr +
+                 HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW));
+}
+
+void wil6210_disable_irq(struct wil6210_priv *wil)
+{
+       wil_dbg_IRQ(wil, "%s()\n", __func__);
+
+       wil6210_mask_irq_tx(wil);
+       wil6210_mask_irq_rx(wil);
+       wil6210_mask_irq_misc(wil);
+       wil6210_mask_irq_pseudo(wil);
+}
+
+void wil6210_enable_irq(struct wil6210_priv *wil)
+{
+       wil_dbg_IRQ(wil, "%s()\n", __func__);
+
+       iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) +
+                 offsetof(struct RGF_ICR, ICC));
+       iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) +
+                 offsetof(struct RGF_ICR, ICC));
+       iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) +
+                 offsetof(struct RGF_ICR, ICC));
+
+       wil6210_unmask_irq_pseudo(wil);
+       wil6210_unmask_irq_tx(wil);
+       wil6210_unmask_irq_rx(wil);
+       wil6210_unmask_irq_misc(wil);
+}
+
+static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
+{
+       struct wil6210_priv *wil = cookie;
+       u32 isr = wil_ioread32_and_clear(wil->csr +
+                                        HOSTADDR(RGF_DMA_EP_RX_ICR) +
+                                        offsetof(struct RGF_ICR, ICR));
+
+       wil_dbg_IRQ(wil, "ISR RX 0x%08x\n", isr);
+
+       if (!isr) {
+               wil_err(wil, "spurious IRQ: RX\n");
+               return IRQ_NONE;
+       }
+
+       wil6210_mask_irq_rx(wil);
+
+       if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) {
+               wil_dbg_IRQ(wil, "RX done\n");
+               isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE;
+               wil_rx_handle(wil);
+       }
+
+       if (isr)
+               wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr);
+
+       wil6210_unmask_irq_rx(wil);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
+{
+       struct wil6210_priv *wil = cookie;
+       u32 isr = wil_ioread32_and_clear(wil->csr +
+                                        HOSTADDR(RGF_DMA_EP_TX_ICR) +
+                                        offsetof(struct RGF_ICR, ICR));
+
+       wil_dbg_IRQ(wil, "ISR TX 0x%08x\n", isr);
+
+       if (!isr) {
+               wil_err(wil, "spurious IRQ: TX\n");
+               return IRQ_NONE;
+       }
+
+       wil6210_mask_irq_tx(wil);
+
+       if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) {
+               uint i;
+               wil_dbg_IRQ(wil, "TX done\n");
+               isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE;
+               for (i = 0; i < 24; i++) {
+                       u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i);
+                       if (isr & mask) {
+                               isr &= ~mask;
+                               wil_dbg_IRQ(wil, "TX done(%i)\n", i);
+                               wil_tx_complete(wil, i);
+                       }
+               }
+       }
+
+       if (isr)
+               wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr);
+
+       wil6210_unmask_irq_tx(wil);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
+{
+       struct wil6210_priv *wil = cookie;
+       u32 isr = wil_ioread32_and_clear(wil->csr +
+                                        HOSTADDR(RGF_DMA_EP_MISC_ICR) +
+                                        offsetof(struct RGF_ICR, ICR));
+
+       wil_dbg_IRQ(wil, "ISR MISC 0x%08x\n", isr);
+
+       if (!isr) {
+               wil_err(wil, "spurious IRQ: MISC\n");
+               return IRQ_NONE;
+       }
+
+       wil6210_mask_irq_misc(wil);
+
+       if (isr & ISR_MISC_FW_READY) {
+               wil_dbg_IRQ(wil, "IRQ: FW ready\n");
+               /**
+                * Actual FW ready indicated by the
+                * WMI_FW_READY_EVENTID
+                */
+               isr &= ~ISR_MISC_FW_READY;
+       }
+
+       wil->isr_misc = isr;
+
+       if (isr) {
+               return IRQ_WAKE_THREAD;
+       } else {
+               wil6210_unmask_irq_misc(wil);
+               return IRQ_HANDLED;
+       }
+}
+
+static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)
+{
+       struct wil6210_priv *wil = cookie;
+       u32 isr = wil->isr_misc;
+
+       wil_dbg_IRQ(wil, "Thread ISR MISC 0x%08x\n", isr);
+
+       if (isr & ISR_MISC_MBOX_EVT) {
+               wil_dbg_IRQ(wil, "MBOX event\n");
+               wmi_recv_cmd(wil);
+               isr &= ~ISR_MISC_MBOX_EVT;
+       }
+
+       if (isr)
+               wil_err(wil, "un-handled MISC ISR bits 0x%08x\n", isr);
+
+       wil->isr_misc = 0;
+
+       wil6210_unmask_irq_misc(wil);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * thread IRQ handler
+ */
+static irqreturn_t wil6210_thread_irq(int irq, void *cookie)
+{
+       struct wil6210_priv *wil = cookie;
+
+       wil_dbg_IRQ(wil, "Thread IRQ\n");
+       /* Discover real IRQ cause */
+       if (wil->isr_misc)
+               wil6210_irq_misc_thread(irq, cookie);
+
+       wil6210_unmask_irq_pseudo(wil);
+
+       return IRQ_HANDLED;
+}
+
+/* DEBUG
+ * There is subtle bug in hardware that causes IRQ to raise when it should be
+ * masked. It is quite rare and hard to debug.
+ *
+ * Catch irq issue if it happens and print all I can.
+ */
+static int wil6210_debug_irq_mask(struct wil6210_priv *wil, u32 pseudo_cause)
+{
+       if (!test_bit(wil_status_irqen, &wil->status)) {
+               u32 icm_rx = wil_ioread32_and_clear(wil->csr +
+                               HOSTADDR(RGF_DMA_EP_RX_ICR) +
+                               offsetof(struct RGF_ICR, ICM));
+               u32 icr_rx = wil_ioread32_and_clear(wil->csr +
+                               HOSTADDR(RGF_DMA_EP_RX_ICR) +
+                               offsetof(struct RGF_ICR, ICR));
+               u32 imv_rx = ioread32(wil->csr +
+                               HOSTADDR(RGF_DMA_EP_RX_ICR) +
+                               offsetof(struct RGF_ICR, IMV));
+               u32 icm_tx = wil_ioread32_and_clear(wil->csr +
+                               HOSTADDR(RGF_DMA_EP_TX_ICR) +
+                               offsetof(struct RGF_ICR, ICM));
+               u32 icr_tx = wil_ioread32_and_clear(wil->csr +
+                               HOSTADDR(RGF_DMA_EP_TX_ICR) +
+                               offsetof(struct RGF_ICR, ICR));
+               u32 imv_tx = ioread32(wil->csr +
+                               HOSTADDR(RGF_DMA_EP_TX_ICR) +
+                               offsetof(struct RGF_ICR, IMV));
+               u32 icm_misc = wil_ioread32_and_clear(wil->csr +
+                               HOSTADDR(RGF_DMA_EP_MISC_ICR) +
+                               offsetof(struct RGF_ICR, ICM));
+               u32 icr_misc = wil_ioread32_and_clear(wil->csr +
+                               HOSTADDR(RGF_DMA_EP_MISC_ICR) +
+                               offsetof(struct RGF_ICR, ICR));
+               u32 imv_misc = ioread32(wil->csr +
+                               HOSTADDR(RGF_DMA_EP_MISC_ICR) +
+                               offsetof(struct RGF_ICR, IMV));
+               wil_err(wil, "IRQ when it should be masked: pseudo 0x%08x\n"
+                               "Rx   icm:icr:imv 0x%08x 0x%08x 0x%08x\n"
+                               "Tx   icm:icr:imv 0x%08x 0x%08x 0x%08x\n"
+                               "Misc icm:icr:imv 0x%08x 0x%08x 0x%08x\n",
+                               pseudo_cause,
+                               icm_rx, icr_rx, imv_rx,
+                               icm_tx, icr_tx, imv_tx,
+                               icm_misc, icr_misc, imv_misc);
+
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static irqreturn_t wil6210_hardirq(int irq, void *cookie)
+{
+       irqreturn_t rc = IRQ_HANDLED;
+       struct wil6210_priv *wil = cookie;
+       u32 pseudo_cause = ioread32(wil->csr + HOSTADDR(RGF_DMA_PSEUDO_CAUSE));
+
+       /**
+        * pseudo_cause is Clear-On-Read, no need to ACK
+        */
+       if ((pseudo_cause == 0) || ((pseudo_cause & 0xff) == 0xff))
+               return IRQ_NONE;
+
+       /* FIXME: IRQ mask debug */
+       if (wil6210_debug_irq_mask(wil, pseudo_cause))
+               return IRQ_NONE;
+
+       wil6210_mask_irq_pseudo(wil);
+
+       /* Discover real IRQ cause
+        * There are 2 possible phases for every IRQ:
+        * - hard IRQ handler called right here
+        * - threaded handler called later
+        *
+        * Hard IRQ handler reads and clears ISR.
+        *
+        * If threaded handler requested, hard IRQ handler
+        * returns IRQ_WAKE_THREAD and saves ISR register value
+        * for the threaded handler use.
+        *
+        * voting for wake thread - need at least 1 vote
+        */
+       if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_RX) &&
+           (wil6210_irq_rx(irq, cookie) == IRQ_WAKE_THREAD))
+               rc = IRQ_WAKE_THREAD;
+
+       if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_TX) &&
+           (wil6210_irq_tx(irq, cookie) == IRQ_WAKE_THREAD))
+               rc = IRQ_WAKE_THREAD;
+
+       if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_MISC) &&
+           (wil6210_irq_misc(irq, cookie) == IRQ_WAKE_THREAD))
+               rc = IRQ_WAKE_THREAD;
+
+       /* if thread is requested, it will unmask IRQ */
+       if (rc != IRQ_WAKE_THREAD)
+               wil6210_unmask_irq_pseudo(wil);
+
+       wil_dbg_IRQ(wil, "Hard IRQ 0x%08x\n", pseudo_cause);
+
+       return rc;
+}
+
+static int wil6210_request_3msi(struct wil6210_priv *wil, int irq)
+{
+       int rc;
+       /*
+        * IRQ's are in the following order:
+        * - Tx
+        * - Rx
+        * - Misc
+        */
+
+       rc = request_irq(irq, wil6210_irq_tx, IRQF_SHARED,
+                        WIL_NAME"_tx", wil);
+       if (rc)
+               return rc;
+
+       rc = request_irq(irq + 1, wil6210_irq_rx, IRQF_SHARED,
+                        WIL_NAME"_rx", wil);
+       if (rc)
+               goto free0;
+
+       rc = request_threaded_irq(irq + 2, wil6210_irq_misc,
+                                 wil6210_irq_misc_thread,
+                                 IRQF_SHARED, WIL_NAME"_misc", wil);
+       if (rc)
+               goto free1;
+
+       return 0;
+       /* error branch */
+free1:
+       free_irq(irq + 1, wil);
+free0:
+       free_irq(irq, wil);
+
+       return rc;
+}
+
+int wil6210_init_irq(struct wil6210_priv *wil, int irq)
+{
+       int rc;
+       if (wil->n_msi == 3)
+               rc = wil6210_request_3msi(wil, irq);
+       else
+               rc = request_threaded_irq(irq, wil6210_hardirq,
+                                         wil6210_thread_irq,
+                                         wil->n_msi ? 0 : IRQF_SHARED,
+                                         WIL_NAME, wil);
+       if (rc)
+               return rc;
+
+       wil6210_enable_irq(wil);
+
+       return 0;
+}
+
+void wil6210_fini_irq(struct wil6210_priv *wil, int irq)
+{
+       wil6210_disable_irq(wil);
+       free_irq(irq, wil);
+       if (wil->n_msi == 3) {
+               free_irq(irq + 1, wil);
+               free_irq(irq + 2, wil);
+       }
+}
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
new file mode 100644 (file)
index 0000000..95fcd36
--- /dev/null
@@ -0,0 +1,407 @@
+/*
+ * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/sched.h>
+#include <linux/ieee80211.h>
+#include <linux/wireless.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/if_arp.h>
+
+#include "wil6210.h"
+
+/*
+ * Due to a hardware issue,
+ * one has to read/write to/from NIC in 32-bit chunks;
+ * regular memcpy_fromio and siblings will
+ * not work on 64-bit platform - it uses 64-bit transactions
+ *
+ * Force 32-bit transactions to enable NIC on 64-bit platforms
+ *
+ * To avoid byte swap on big endian host, __raw_{read|write}l
+ * should be used - {read|write}l would swap bytes to provide
+ * little endian on PCI value in host endianness.
+ */
+void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src,
+                         size_t count)
+{
+       u32 *d = dst;
+       const volatile u32 __iomem *s = src;
+
+       /* size_t is unsigned, if (count%4 != 0) it will wrap */
+       for (count += 4; count > 4; count -= 4)
+               *d++ = __raw_readl(s++);
+}
+
+void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,
+                       size_t count)
+{
+       volatile u32 __iomem *d = dst;
+       const u32 *s = src;
+
+       for (count += 4; count > 4; count -= 4)
+               __raw_writel(*s++, d++);
+}
+
+static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
+{
+       uint i;
+       struct net_device *ndev = wil_to_ndev(wil);
+       struct wireless_dev *wdev = wil->wdev;
+
+       wil_dbg(wil, "%s()\n", __func__);
+
+       wil_link_off(wil);
+       clear_bit(wil_status_fwconnected, &wil->status);
+
+       switch (wdev->sme_state) {
+       case CFG80211_SME_CONNECTED:
+               cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE,
+                                     NULL, 0, GFP_KERNEL);
+               break;
+       case CFG80211_SME_CONNECTING:
+               cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
+                                       WLAN_STATUS_UNSPECIFIED_FAILURE,
+                                       GFP_KERNEL);
+               break;
+       default:
+               ;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++)
+               wil_vring_fini_tx(wil, i);
+}
+
+static void wil_disconnect_worker(struct work_struct *work)
+{
+       struct wil6210_priv *wil = container_of(work,
+                       struct wil6210_priv, disconnect_worker);
+
+       _wil6210_disconnect(wil, NULL);
+}
+
+static void wil_connect_timer_fn(ulong x)
+{
+       struct wil6210_priv *wil = (void *)x;
+
+       wil_dbg(wil, "Connect timeout\n");
+
+       /* reschedule to thread context - disconnect won't
+        * run from atomic context
+        */
+       schedule_work(&wil->disconnect_worker);
+}
+
+int wil_priv_init(struct wil6210_priv *wil)
+{
+       wil_dbg(wil, "%s()\n", __func__);
+
+       mutex_init(&wil->mutex);
+       mutex_init(&wil->wmi_mutex);
+
+       init_completion(&wil->wmi_ready);
+
+       wil->pending_connect_cid = -1;
+       setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil);
+
+       INIT_WORK(&wil->wmi_connect_worker, wmi_connect_worker);
+       INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker);
+       INIT_WORK(&wil->wmi_event_worker, wmi_event_worker);
+
+       INIT_LIST_HEAD(&wil->pending_wmi_ev);
+       spin_lock_init(&wil->wmi_ev_lock);
+
+       wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi");
+       if (!wil->wmi_wq)
+               return -EAGAIN;
+
+       wil->wmi_wq_conn = create_singlethread_workqueue(WIL_NAME"_connect");
+       if (!wil->wmi_wq_conn) {
+               destroy_workqueue(wil->wmi_wq);
+               return -EAGAIN;
+       }
+
+       /* make shadow copy of registers that should not change on run time */
+       wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX,
+                            sizeof(struct wil6210_mbox_ctl));
+       wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx);
+       wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx);
+
+       return 0;
+}
+
+void wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
+{
+       del_timer_sync(&wil->connect_timer);
+       _wil6210_disconnect(wil, bssid);
+}
+
+void wil_priv_deinit(struct wil6210_priv *wil)
+{
+       cancel_work_sync(&wil->disconnect_worker);
+       wil6210_disconnect(wil, NULL);
+       wmi_event_flush(wil);
+       destroy_workqueue(wil->wmi_wq_conn);
+       destroy_workqueue(wil->wmi_wq);
+}
+
+static void wil_target_reset(struct wil6210_priv *wil)
+{
+       wil_dbg(wil, "Resetting...\n");
+
+       /* register write */
+#define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a))
+       /* register set = read, OR, write */
+#define S(a, v) iowrite32(ioread32(wil->csr + HOSTADDR(a)) | v, \
+               wil->csr + HOSTADDR(a))
+
+       /* hpal_perst_from_pad_src_n_mask */
+       S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6));
+       /* car_perst_rst_src_n_mask */
+       S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7));
+
+       W(RGF_USER_MAC_CPU_0,  BIT(1)); /* mac_cpu_man_rst */
+       W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */
+
+       msleep(100);
+
+       W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000);
+       W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F);
+       W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170);
+       W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00);
+
+       msleep(100);
+
+       W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0);
+       W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0);
+       W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0);
+       W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
+
+       W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001);
+       W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080);
+       W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
+
+       msleep(2000);
+
+       W(RGF_USER_USER_CPU_0, BIT(0)); /* user_cpu_man_de_rst */
+
+       msleep(2000);
+
+       wil_dbg(wil, "Reset completed\n");
+
+#undef W
+#undef S
+}
+
+void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r)
+{
+       le32_to_cpus(&r->base);
+       le16_to_cpus(&r->entry_size);
+       le16_to_cpus(&r->size);
+       le32_to_cpus(&r->tail);
+       le32_to_cpus(&r->head);
+}
+
+static int wil_wait_for_fw_ready(struct wil6210_priv *wil)
+{
+       ulong to = msecs_to_jiffies(1000);
+       ulong left = wait_for_completion_timeout(&wil->wmi_ready, to);
+       if (0 == left) {
+               wil_err(wil, "Firmware not ready\n");
+               return -ETIME;
+       } else {
+               wil_dbg(wil, "FW ready after %d ms\n",
+                       jiffies_to_msecs(to-left));
+       }
+       return 0;
+}
+
+/*
+ * We reset all the structures, and we reset the UMAC.
+ * After calling this routine, you're expected to reload
+ * the firmware.
+ */
+int wil_reset(struct wil6210_priv *wil)
+{
+       int rc;
+
+       cancel_work_sync(&wil->disconnect_worker);
+       wil6210_disconnect(wil, NULL);
+
+       wmi_event_flush(wil);
+
+       flush_workqueue(wil->wmi_wq);
+       flush_workqueue(wil->wmi_wq_conn);
+
+       wil6210_disable_irq(wil);
+       wil->status = 0;
+
+       /* TODO: put MAC in reset */
+       wil_target_reset(wil);
+
+       /* init after reset */
+       wil->pending_connect_cid = -1;
+       INIT_COMPLETION(wil->wmi_ready);
+
+       /* make shadow copy of registers that should not change on run time */
+       wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX,
+                            sizeof(struct wil6210_mbox_ctl));
+       wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx);
+       wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx);
+
+       /* TODO: release MAC reset */
+       wil6210_enable_irq(wil);
+
+       /* we just started MAC, wait for FW ready */
+       rc = wil_wait_for_fw_ready(wil);
+
+       return rc;
+}
+
+
+void wil_link_on(struct wil6210_priv *wil)
+{
+       struct net_device *ndev = wil_to_ndev(wil);
+
+       wil_dbg(wil, "%s()\n", __func__);
+
+       netif_carrier_on(ndev);
+       netif_tx_wake_all_queues(ndev);
+}
+
+void wil_link_off(struct wil6210_priv *wil)
+{
+       struct net_device *ndev = wil_to_ndev(wil);
+
+       wil_dbg(wil, "%s()\n", __func__);
+
+       netif_tx_stop_all_queues(ndev);
+       netif_carrier_off(ndev);
+}
+
+static int __wil_up(struct wil6210_priv *wil)
+{
+       struct net_device *ndev = wil_to_ndev(wil);
+       struct wireless_dev *wdev = wil->wdev;
+       struct ieee80211_channel *channel = wdev->preset_chandef.chan;
+       int rc;
+       int bi;
+       u16 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
+
+       rc = wil_reset(wil);
+       if (rc)
+               return rc;
+
+       /* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */
+       wmi_nettype = wil_iftype_nl2wmi(NL80211_IFTYPE_ADHOC);
+       switch (wdev->iftype) {
+       case NL80211_IFTYPE_STATION:
+               wil_dbg(wil, "type: STATION\n");
+               bi = 0;
+               ndev->type = ARPHRD_ETHER;
+               break;
+       case NL80211_IFTYPE_AP:
+               wil_dbg(wil, "type: AP\n");
+               bi = 100;
+               ndev->type = ARPHRD_ETHER;
+               break;
+       case NL80211_IFTYPE_P2P_CLIENT:
+               wil_dbg(wil, "type: P2P_CLIENT\n");
+               bi = 0;
+               ndev->type = ARPHRD_ETHER;
+               break;
+       case NL80211_IFTYPE_P2P_GO:
+               wil_dbg(wil, "type: P2P_GO\n");
+               bi = 100;
+               ndev->type = ARPHRD_ETHER;
+               break;
+       case NL80211_IFTYPE_MONITOR:
+               wil_dbg(wil, "type: Monitor\n");
+               bi = 0;
+               ndev->type = ARPHRD_IEEE80211_RADIOTAP;
+               /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       /* Apply profile in the following order: */
+       /* SSID and channel for the AP */
+       switch (wdev->iftype) {
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_P2P_GO:
+               if (wdev->ssid_len == 0) {
+                       wil_err(wil, "SSID not set\n");
+                       return -EINVAL;
+               }
+               wmi_set_ssid(wil, wdev->ssid_len, wdev->ssid);
+               if (channel)
+                       wmi_set_channel(wil, channel->hw_value);
+               break;
+       default:
+               ;
+       }
+
+       /* MAC address - pre-requisite for other commands */
+       wmi_set_mac_address(wil, ndev->dev_addr);
+
+       /* Set up beaconing if required. */
+       rc = wmi_set_bcon(wil, bi, wmi_nettype);
+       if (rc)
+               return rc;
+
+       /* Rx VRING. After MAC and beacon */
+       wil_rx_init(wil);
+
+       return 0;
+}
+
+int wil_up(struct wil6210_priv *wil)
+{
+       int rc;
+
+       mutex_lock(&wil->mutex);
+       rc = __wil_up(wil);
+       mutex_unlock(&wil->mutex);
+
+       return rc;
+}
+
+static int __wil_down(struct wil6210_priv *wil)
+{
+       if (wil->scan_request) {
+               cfg80211_scan_done(wil->scan_request, true);
+               wil->scan_request = NULL;
+       }
+
+       wil6210_disconnect(wil, NULL);
+       wil_rx_fini(wil);
+
+       return 0;
+}
+
+int wil_down(struct wil6210_priv *wil)
+{
+       int rc;
+
+       mutex_lock(&wil->mutex);
+       rc = __wil_down(wil);
+       mutex_unlock(&wil->mutex);
+
+       return rc;
+}
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
new file mode 100644 (file)
index 0000000..3068b5c
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+
+#include "wil6210.h"
+
+static int wil_open(struct net_device *ndev)
+{
+       struct wil6210_priv *wil = ndev_to_wil(ndev);
+
+       return wil_up(wil);
+}
+
+static int wil_stop(struct net_device *ndev)
+{
+       struct wil6210_priv *wil = ndev_to_wil(ndev);
+
+       return wil_down(wil);
+}
+
+/*
+ * AC to queue mapping
+ *
+ * AC_VO -> queue 3
+ * AC_VI -> queue 2
+ * AC_BE -> queue 1
+ * AC_BK -> queue 0
+ */
+static u16 wil_select_queue(struct net_device *ndev, struct sk_buff *skb)
+{
+       static const u16 wil_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
+       struct wil6210_priv *wil = ndev_to_wil(ndev);
+       u16 rc;
+
+       skb->priority = cfg80211_classify8021d(skb);
+
+       rc = wil_1d_to_queue[skb->priority];
+
+       wil_dbg_TXRX(wil, "%s() %d -> %d\n", __func__, (int)skb->priority,
+                    (int)rc);
+
+       return rc;
+}
+
+static const struct net_device_ops wil_netdev_ops = {
+       .ndo_open               = wil_open,
+       .ndo_stop               = wil_stop,
+       .ndo_start_xmit         = wil_start_xmit,
+       .ndo_select_queue       = wil_select_queue,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
+void *wil_if_alloc(struct device *dev, void __iomem *csr)
+{
+       struct net_device *ndev;
+       struct wireless_dev *wdev;
+       struct wil6210_priv *wil;
+       struct ieee80211_channel *ch;
+       int rc = 0;
+
+       wdev = wil_cfg80211_init(dev);
+       if (IS_ERR(wdev)) {
+               dev_err(dev, "wil_cfg80211_init failed\n");
+               return wdev;
+       }
+
+       wil = wdev_to_wil(wdev);
+       wil->csr = csr;
+       wil->wdev = wdev;
+
+       rc = wil_priv_init(wil);
+       if (rc) {
+               dev_err(dev, "wil_priv_init failed\n");
+               goto out_wdev;
+       }
+
+       wdev->iftype = NL80211_IFTYPE_STATION; /* TODO */
+       /* default monitor channel */
+       ch = wdev->wiphy->bands[IEEE80211_BAND_60GHZ]->channels;
+       cfg80211_chandef_create(&wdev->preset_chandef, ch, NL80211_CHAN_NO_HT);
+
+       ndev = alloc_netdev_mqs(0, "wlan%d", ether_setup, WIL6210_TX_QUEUES, 1);
+       if (!ndev) {
+               dev_err(dev, "alloc_netdev_mqs failed\n");
+               rc = -ENOMEM;
+               goto out_priv;
+       }
+
+       ndev->netdev_ops = &wil_netdev_ops;
+       ndev->ieee80211_ptr = wdev;
+       SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
+       wdev->netdev = ndev;
+
+       wil_link_off(wil);
+
+       return wil;
+
+ out_priv:
+       wil_priv_deinit(wil);
+
+ out_wdev:
+       wil_wdev_free(wil);
+
+       return ERR_PTR(rc);
+}
+
+void wil_if_free(struct wil6210_priv *wil)
+{
+       struct net_device *ndev = wil_to_ndev(wil);
+       if (!ndev)
+               return;
+
+       free_netdev(ndev);
+       wil_priv_deinit(wil);
+       wil_wdev_free(wil);
+}
+
+int wil_if_add(struct wil6210_priv *wil)
+{
+       struct net_device *ndev = wil_to_ndev(wil);
+       int rc;
+
+       rc = register_netdev(ndev);
+       if (rc < 0) {
+               dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc);
+               return rc;
+       }
+
+       wil_link_off(wil);
+
+       return 0;
+}
+
+void wil_if_remove(struct wil6210_priv *wil)
+{
+       struct net_device *ndev = wil_to_ndev(wil);
+
+       unregister_netdev(ndev);
+}
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
new file mode 100644 (file)
index 0000000..0fc83ed
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/debugfs.h>
+#include <linux/pci.h>
+#include <linux/moduleparam.h>
+
+#include "wil6210.h"
+
+static int use_msi = 1;
+module_param(use_msi, int, S_IRUGO);
+MODULE_PARM_DESC(use_msi,
+                " Use MSI interrupt: "
+                "0 - don't, 1 - (default) - single, or 3");
+
+/* Bus ops */
+static int wil_if_pcie_enable(struct wil6210_priv *wil)
+{
+       struct pci_dev *pdev = wil->pdev;
+       int rc;
+
+       pci_set_master(pdev);
+
+       /*
+        * how many MSI interrupts to request?
+        */
+       switch (use_msi) {
+       case 3:
+       case 1:
+       case 0:
+               break;
+       default:
+               wil_err(wil, "Invalid use_msi=%d, default to 1\n",
+                       use_msi);
+               use_msi = 1;
+       }
+       wil->n_msi = use_msi;
+       if (wil->n_msi) {
+               wil_dbg(wil, "Setup %d MSI interrupts\n", use_msi);
+               rc = pci_enable_msi_block(pdev, wil->n_msi);
+               if (rc && (wil->n_msi == 3)) {
+                       wil_err(wil, "3 MSI mode failed, try 1 MSI\n");
+                       wil->n_msi = 1;
+                       rc = pci_enable_msi_block(pdev, wil->n_msi);
+               }
+               if (rc) {
+                       wil_err(wil, "pci_enable_msi failed, use INTx\n");
+                       wil->n_msi = 0;
+               }
+       } else {
+               wil_dbg(wil, "MSI interrupts disabled, use INTx\n");
+       }
+
+       rc = wil6210_init_irq(wil, pdev->irq);
+       if (rc)
+               goto stop_master;
+
+       /* need reset here to obtain MAC */
+       rc = wil_reset(wil);
+       if (rc)
+               goto release_irq;
+
+       return 0;
+
+ release_irq:
+       wil6210_fini_irq(wil, pdev->irq);
+       /* safe to call if no MSI */
+       pci_disable_msi(pdev);
+ stop_master:
+       pci_clear_master(pdev);
+       return rc;
+}
+
+static int wil_if_pcie_disable(struct wil6210_priv *wil)
+{
+       struct pci_dev *pdev = wil->pdev;
+
+       pci_clear_master(pdev);
+       /* disable and release IRQ */
+       wil6210_fini_irq(wil, pdev->irq);
+       /* safe to call if no MSI */
+       pci_disable_msi(pdev);
+       /* TODO: disable HW */
+
+       return 0;
+}
+
+static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+       struct wil6210_priv *wil;
+       struct device *dev = &pdev->dev;
+       void __iomem *csr;
+       int rc;
+
+       /* check HW */
+       dev_info(&pdev->dev, WIL_NAME " device found [%04x:%04x] (rev %x)\n",
+                (int)pdev->vendor, (int)pdev->device, (int)pdev->revision);
+
+       if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) {
+               dev_err(&pdev->dev, "Not " WIL_NAME "? "
+                       "BAR0 size is %lu while expecting %lu\n",
+                       (ulong)pci_resource_len(pdev, 0), WIL6210_MEM_SIZE);
+               return -ENODEV;
+       }
+
+       rc = pci_enable_device(pdev);
+       if (rc) {
+               dev_err(&pdev->dev, "pci_enable_device failed\n");
+               return -ENODEV;
+       }
+       /* rollback to err_disable_pdev */
+
+       rc = pci_request_region(pdev, 0, WIL_NAME);
+       if (rc) {
+               dev_err(&pdev->dev, "pci_request_region failed\n");
+               goto err_disable_pdev;
+       }
+       /* rollback to err_release_reg */
+
+       csr = pci_ioremap_bar(pdev, 0);
+       if (!csr) {
+               dev_err(&pdev->dev, "pci_ioremap_bar failed\n");
+               rc = -ENODEV;
+               goto err_release_reg;
+       }
+       /* rollback to err_iounmap */
+       dev_info(&pdev->dev, "CSR at %pR -> %p\n", &pdev->resource[0], csr);
+
+       wil = wil_if_alloc(dev, csr);
+       if (IS_ERR(wil)) {
+               rc = (int)PTR_ERR(wil);
+               dev_err(dev, "wil_if_alloc failed: %d\n", rc);
+               goto err_iounmap;
+       }
+       /* rollback to if_free */
+
+       pci_set_drvdata(pdev, wil);
+       wil->pdev = pdev;
+
+       /* FW should raise IRQ when ready */
+       rc = wil_if_pcie_enable(wil);
+       if (rc) {
+               wil_err(wil, "Enable device failed\n");
+               goto if_free;
+       }
+       /* rollback to bus_disable */
+
+       rc = wil_if_add(wil);
+       if (rc) {
+               wil_err(wil, "wil_if_add failed: %d\n", rc);
+               goto bus_disable;
+       }
+
+       wil6210_debugfs_init(wil);
+
+       /* check FW is alive */
+       wmi_echo(wil);
+
+       return 0;
+
+ bus_disable:
+       wil_if_pcie_disable(wil);
+ if_free:
+       wil_if_free(wil);
+ err_iounmap:
+       pci_iounmap(pdev, csr);
+ err_release_reg:
+       pci_release_region(pdev, 0);
+ err_disable_pdev:
+       pci_disable_device(pdev);
+
+       return rc;
+}
+
+static void wil_pcie_remove(struct pci_dev *pdev)
+{
+       struct wil6210_priv *wil = pci_get_drvdata(pdev);
+
+       wil6210_debugfs_remove(wil);
+       wil_if_pcie_disable(wil);
+       wil_if_remove(wil);
+       wil_if_free(wil);
+       pci_iounmap(pdev, wil->csr);
+       pci_release_region(pdev, 0);
+       pci_disable_device(pdev);
+       pci_set_drvdata(pdev, NULL);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(wil6210_pcie_ids) = {
+       { PCI_DEVICE(0x1ae9, 0x0301) },
+       { /* end: all zeroes */ },
+};
+MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids);
+
+static struct pci_driver wil6210_driver = {
+       .probe          = wil_pcie_probe,
+       .remove         = wil_pcie_remove,
+       .id_table       = wil6210_pcie_ids,
+       .name           = WIL_NAME,
+};
+
+module_pci_driver(wil6210_driver);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Qualcomm Atheros <wil6210@qca.qualcomm.com>");
+MODULE_DESCRIPTION("Driver for 60g WiFi WIL6210 card");
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
new file mode 100644 (file)
index 0000000..f29c294
--- /dev/null
@@ -0,0 +1,871 @@
+/*
+ * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/hardirq.h>
+#include <net/ieee80211_radiotap.h>
+#include <linux/if_arp.h>
+#include <linux/moduleparam.h>
+
+#include "wil6210.h"
+#include "wmi.h"
+#include "txrx.h"
+
+static bool rtap_include_phy_info;
+module_param(rtap_include_phy_info, bool, S_IRUGO);
+MODULE_PARM_DESC(rtap_include_phy_info,
+                " Include PHY info in the radiotap header, default - no");
+
+static inline int wil_vring_is_empty(struct vring *vring)
+{
+       return vring->swhead == vring->swtail;
+}
+
+static inline u32 wil_vring_next_tail(struct vring *vring)
+{
+       return (vring->swtail + 1) % vring->size;
+}
+
+static inline void wil_vring_advance_head(struct vring *vring, int n)
+{
+       vring->swhead = (vring->swhead + n) % vring->size;
+}
+
+static inline int wil_vring_is_full(struct vring *vring)
+{
+       return wil_vring_next_tail(vring) == vring->swhead;
+}
+/*
+ * Available space in Tx Vring
+ */
+static inline int wil_vring_avail_tx(struct vring *vring)
+{
+       u32 swhead = vring->swhead;
+       u32 swtail = vring->swtail;
+       int used = (vring->size + swhead - swtail) % vring->size;
+
+       return vring->size - used - 1;
+}
+
+static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
+{
+       struct device *dev = wil_to_dev(wil);
+       size_t sz = vring->size * sizeof(vring->va[0]);
+       uint i;
+
+       BUILD_BUG_ON(sizeof(vring->va[0]) != 32);
+
+       vring->swhead = 0;
+       vring->swtail = 0;
+       vring->ctx = kzalloc(vring->size * sizeof(vring->ctx[0]), GFP_KERNEL);
+       if (!vring->ctx) {
+               wil_err(wil, "vring_alloc [%d] failed to alloc ctx mem\n",
+                       vring->size);
+               vring->va = NULL;
+               return -ENOMEM;
+       }
+       /*
+        * vring->va should be aligned on its size rounded up to power of 2
+        * This is granted by the dma_alloc_coherent
+        */
+       vring->va = dma_alloc_coherent(dev, sz, &vring->pa, GFP_KERNEL);
+       if (!vring->va) {
+               wil_err(wil, "vring_alloc [%d] failed to alloc DMA mem\n",
+                       vring->size);
+               kfree(vring->ctx);
+               vring->ctx = NULL;
+               return -ENOMEM;
+       }
+       /* initially, all descriptors are SW owned
+        * For Tx and Rx, ownership bit is at the same location, thus
+        * we can use any
+        */
+       for (i = 0; i < vring->size; i++) {
+               volatile struct vring_tx_desc *d = &(vring->va[i].tx);
+               d->dma.status = TX_DMA_STATUS_DU;
+       }
+
+       wil_dbg(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size,
+               vring->va, (unsigned long long)vring->pa, vring->ctx);
+
+       return 0;
+}
+
+static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring,
+                          int tx)
+{
+       struct device *dev = wil_to_dev(wil);
+       size_t sz = vring->size * sizeof(vring->va[0]);
+
+       while (!wil_vring_is_empty(vring)) {
+               if (tx) {
+                       volatile struct vring_tx_desc *d =
+                                       &vring->va[vring->swtail].tx;
+                       dma_addr_t pa = d->dma.addr_low |
+                                       ((u64)d->dma.addr_high << 32);
+                       struct sk_buff *skb = vring->ctx[vring->swtail];
+                       if (skb) {
+                               dma_unmap_single(dev, pa, d->dma.length,
+                                                DMA_TO_DEVICE);
+                               dev_kfree_skb_any(skb);
+                               vring->ctx[vring->swtail] = NULL;
+                       } else {
+                               dma_unmap_page(dev, pa, d->dma.length,
+                                              DMA_TO_DEVICE);
+                       }
+                       vring->swtail = wil_vring_next_tail(vring);
+               } else { /* rx */
+                       volatile struct vring_rx_desc *d =
+                                       &vring->va[vring->swtail].rx;
+                       dma_addr_t pa = d->dma.addr_low |
+                                       ((u64)d->dma.addr_high << 32);
+                       struct sk_buff *skb = vring->ctx[vring->swhead];
+                       dma_unmap_single(dev, pa, d->dma.length,
+                                        DMA_FROM_DEVICE);
+                       kfree_skb(skb);
+                       wil_vring_advance_head(vring, 1);
+               }
+       }
+       dma_free_coherent(dev, sz, (void *)vring->va, vring->pa);
+       kfree(vring->ctx);
+       vring->pa = 0;
+       vring->va = NULL;
+       vring->ctx = NULL;
+}
+
+/**
+ * Allocate one skb for Rx VRING
+ *
+ * Safe to call from IRQ
+ */
+static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring,
+                              u32 i, int headroom)
+{
+       struct device *dev = wil_to_dev(wil);
+       unsigned int sz = RX_BUF_LEN;
+       volatile struct vring_rx_desc *d = &(vring->va[i].rx);
+       dma_addr_t pa;
+
+       /* TODO align */
+       struct sk_buff *skb = dev_alloc_skb(sz + headroom);
+       if (unlikely(!skb))
+               return -ENOMEM;
+
+       skb_reserve(skb, headroom);
+       skb_put(skb, sz);
+
+       pa = dma_map_single(dev, skb->data, skb->len, DMA_FROM_DEVICE);
+       if (unlikely(dma_mapping_error(dev, pa))) {
+               kfree_skb(skb);
+               return -ENOMEM;
+       }
+
+       d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT;
+       d->dma.addr_low = lower_32_bits(pa);
+       d->dma.addr_high = (u16)upper_32_bits(pa);
+       /* ip_length don't care */
+       /* b11 don't care */
+       /* error don't care */
+       d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */
+       d->dma.length = sz;
+       vring->ctx[i] = skb;
+
+       return 0;
+}
+
+/**
+ * Adds radiotap header
+ *
+ * Any error indicated as "Bad FCS"
+ *
+ * Vendor data for 04:ce:14-1 (Wilocity-1) consists of:
+ *  - Rx descriptor: 32 bytes
+ *  - Phy info
+ */
+static void wil_rx_add_radiotap_header(struct wil6210_priv *wil,
+                                      struct sk_buff *skb,
+                                      volatile struct vring_rx_desc *d)
+{
+       struct wireless_dev *wdev = wil->wdev;
+       struct wil6210_rtap {
+               struct ieee80211_radiotap_header rthdr;
+               /* fields should be in the order of bits in rthdr.it_present */
+               /* flags */
+               u8 flags;
+               /* channel */
+               __le16 chnl_freq __aligned(2);
+               __le16 chnl_flags;
+               /* MCS */
+               u8 mcs_present;
+               u8 mcs_flags;
+               u8 mcs_index;
+       } __packed;
+       struct wil6210_rtap_vendor {
+               struct wil6210_rtap rtap;
+               /* vendor */
+               u8 vendor_oui[3] __aligned(2);
+               u8 vendor_ns;
+               __le16 vendor_skip;
+               u8 vendor_data[0];
+       } __packed;
+       struct wil6210_rtap_vendor *rtap_vendor;
+       int rtap_len = sizeof(struct wil6210_rtap);
+       int phy_length = 0; /* phy info header size, bytes */
+       static char phy_data[128];
+       struct ieee80211_channel *ch = wdev->preset_chandef.chan;
+
+       if (rtap_include_phy_info) {
+               rtap_len = sizeof(*rtap_vendor) + sizeof(*d);
+               /* calculate additional length */
+               if (d->dma.status & RX_DMA_STATUS_PHY_INFO) {
+                       /**
+                        * PHY info starts from 8-byte boundary
+                        * there are 8-byte lines, last line may be partially
+                        * written (HW bug), thus FW configures for last line
+                        * to be excessive. Driver skips this last line.
+                        */
+                       int len = min_t(int, 8 + sizeof(phy_data),
+                                       wil_rxdesc_phy_length(d));
+                       if (len > 8) {
+                               void *p = skb_tail_pointer(skb);
+                               void *pa = PTR_ALIGN(p, 8);
+                               if (skb_tailroom(skb) >= len + (pa - p)) {
+                                       phy_length = len - 8;
+                                       memcpy(phy_data, pa, phy_length);
+                               }
+                       }
+               }
+               rtap_len += phy_length;
+       }
+
+       if (skb_headroom(skb) < rtap_len &&
+           pskb_expand_head(skb, rtap_len, 0, GFP_ATOMIC)) {
+               wil_err(wil, "Unable to expand headrom to %d\n", rtap_len);
+               return;
+       }
+
+       rtap_vendor = (void *)skb_push(skb, rtap_len);
+       memset(rtap_vendor, 0, rtap_len);
+
+       rtap_vendor->rtap.rthdr.it_version = PKTHDR_RADIOTAP_VERSION;
+       rtap_vendor->rtap.rthdr.it_len = cpu_to_le16(rtap_len);
+       rtap_vendor->rtap.rthdr.it_present = cpu_to_le32(
+                       (1 << IEEE80211_RADIOTAP_FLAGS) |
+                       (1 << IEEE80211_RADIOTAP_CHANNEL) |
+                       (1 << IEEE80211_RADIOTAP_MCS));
+       if (d->dma.status & RX_DMA_STATUS_ERROR)
+               rtap_vendor->rtap.flags |= IEEE80211_RADIOTAP_F_BADFCS;
+
+       rtap_vendor->rtap.chnl_freq = cpu_to_le16(ch ? ch->center_freq : 58320);
+       rtap_vendor->rtap.chnl_flags = cpu_to_le16(0);
+
+       rtap_vendor->rtap.mcs_present = IEEE80211_RADIOTAP_MCS_HAVE_MCS;
+       rtap_vendor->rtap.mcs_flags = 0;
+       rtap_vendor->rtap.mcs_index = wil_rxdesc_mcs(d);
+
+       if (rtap_include_phy_info) {
+               rtap_vendor->rtap.rthdr.it_present |= cpu_to_le32(1 <<
+                               IEEE80211_RADIOTAP_VENDOR_NAMESPACE);
+               /* OUI for Wilocity 04:ce:14 */
+               rtap_vendor->vendor_oui[0] = 0x04;
+               rtap_vendor->vendor_oui[1] = 0xce;
+               rtap_vendor->vendor_oui[2] = 0x14;
+               rtap_vendor->vendor_ns = 1;
+               /* Rx descriptor + PHY data  */
+               rtap_vendor->vendor_skip = cpu_to_le16(sizeof(*d) +
+                                                      phy_length);
+               memcpy(rtap_vendor->vendor_data, (void *)d, sizeof(*d));
+               memcpy(rtap_vendor->vendor_data + sizeof(*d), phy_data,
+                      phy_length);
+       }
+}
+
+/*
+ * Fast swap in place between 2 registers
+ */
+static void wil_swap_u16(u16 *a, u16 *b)
+{
+       *a ^= *b;
+       *b ^= *a;
+       *a ^= *b;
+}
+
+static void wil_swap_ethaddr(void *data)
+{
+       struct ethhdr *eth = data;
+       u16 *s = (u16 *)eth->h_source;
+       u16 *d = (u16 *)eth->h_dest;
+
+       wil_swap_u16(s++, d++);
+       wil_swap_u16(s++, d++);
+       wil_swap_u16(s, d);
+}
+
+/**
+ * reap 1 frame from @swhead
+ *
+ * Safe to call from IRQ
+ */
+static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
+                                        struct vring *vring)
+{
+       struct device *dev = wil_to_dev(wil);
+       struct net_device *ndev = wil_to_ndev(wil);
+       volatile struct vring_rx_desc *d;
+       struct sk_buff *skb;
+       dma_addr_t pa;
+       unsigned int sz = RX_BUF_LEN;
+       u8 ftype;
+       u8 ds_bits;
+
+       if (wil_vring_is_empty(vring))
+               return NULL;
+
+       d = &(vring->va[vring->swhead].rx);
+       if (!(d->dma.status & RX_DMA_STATUS_DU)) {
+               /* it is not error, we just reached end of Rx done area */
+               return NULL;
+       }
+
+       pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
+       skb = vring->ctx[vring->swhead];
+       dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE);
+       skb_trim(skb, d->dma.length);
+
+       wil->stats.last_mcs_rx = wil_rxdesc_mcs(d);
+
+       /* use radiotap header only if required */
+       if (ndev->type == ARPHRD_IEEE80211_RADIOTAP)
+               wil_rx_add_radiotap_header(wil, skb, d);
+
+       wil_dbg_TXRX(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length);
+       wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_NONE, 32, 4,
+                         (const void *)d, sizeof(*d), false);
+
+       wil_vring_advance_head(vring, 1);
+
+       /* no extra checks if in sniffer mode */
+       if (ndev->type != ARPHRD_ETHER)
+               return skb;
+       /*
+        * Non-data frames may be delivered through Rx DMA channel (ex: BAR)
+        * Driver should recognize it by frame type, that is found
+        * in Rx descriptor. If type is not data, it is 802.11 frame as is
+        */
+       ftype = wil_rxdesc_ftype(d) << 2;
+       if (ftype != IEEE80211_FTYPE_DATA) {
+               wil_dbg_TXRX(wil, "Non-data frame ftype 0x%08x\n", ftype);
+               /* TODO: process it */
+               kfree_skb(skb);
+               return NULL;
+       }
+
+       if (skb->len < ETH_HLEN) {
+               wil_err(wil, "Short frame, len = %d\n", skb->len);
+               /* TODO: process it (i.e. BAR) */
+               kfree_skb(skb);
+               return NULL;
+       }
+
+       ds_bits = wil_rxdesc_ds_bits(d);
+       if (ds_bits == 1) {
+               /*
+                * HW bug - in ToDS mode, i.e. Rx on AP side,
+                * addresses get swapped
+                */
+               wil_swap_ethaddr(skb->data);
+       }
+
+       return skb;
+}
+
+/**
+ * allocate and fill up to @count buffers in rx ring
+ * buffers posted at @swtail
+ */
+static int wil_rx_refill(struct wil6210_priv *wil, int count)
+{
+       struct net_device *ndev = wil_to_ndev(wil);
+       struct vring *v = &wil->vring_rx;
+       u32 next_tail;
+       int rc = 0;
+       int headroom = ndev->type == ARPHRD_IEEE80211_RADIOTAP ?
+                       WIL6210_RTAP_SIZE : 0;
+
+       for (; next_tail = wil_vring_next_tail(v),
+                       (next_tail != v->swhead) && (count-- > 0);
+                       v->swtail = next_tail) {
+               rc = wil_vring_alloc_skb(wil, v, v->swtail, headroom);
+               if (rc) {
+                       wil_err(wil, "Error %d in wil_rx_refill[%d]\n",
+                               rc, v->swtail);
+                       break;
+               }
+       }
+       iowrite32(v->swtail, wil->csr + HOSTADDR(v->hwtail));
+
+       return rc;
+}
+
+/*
+ * Pass Rx packet to the netif. Update statistics.
+ */
+static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
+{
+       int rc;
+       unsigned int len = skb->len;
+
+       if (in_interrupt())
+               rc = netif_rx(skb);
+       else
+               rc = netif_rx_ni(skb);
+
+       if (likely(rc == NET_RX_SUCCESS)) {
+               ndev->stats.rx_packets++;
+               ndev->stats.rx_bytes += len;
+
+       } else {
+               ndev->stats.rx_dropped++;
+       }
+}
+
+/**
+ * Proceed all completed skb's from Rx VRING
+ *
+ * Safe to call from IRQ
+ */
+void wil_rx_handle(struct wil6210_priv *wil)
+{
+       struct net_device *ndev = wil_to_ndev(wil);
+       struct vring *v = &wil->vring_rx;
+       struct sk_buff *skb;
+
+       if (!v->va) {
+               wil_err(wil, "Rx IRQ while Rx not yet initialized\n");
+               return;
+       }
+       wil_dbg_TXRX(wil, "%s()\n", __func__);
+       while (NULL != (skb = wil_vring_reap_rx(wil, v))) {
+               wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
+                                 skb->data, skb_headlen(skb), false);
+
+               skb_orphan(skb);
+
+               if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) {
+                       skb->dev = ndev;
+                       skb_reset_mac_header(skb);
+                       skb->ip_summed = CHECKSUM_UNNECESSARY;
+                       skb->pkt_type = PACKET_OTHERHOST;
+                       skb->protocol = htons(ETH_P_802_2);
+
+               } else {
+                       skb->protocol = eth_type_trans(skb, ndev);
+               }
+
+               wil_netif_rx_any(skb, ndev);
+       }
+       wil_rx_refill(wil, v->size);
+}
+
+int wil_rx_init(struct wil6210_priv *wil)
+{
+       struct net_device *ndev = wil_to_ndev(wil);
+       struct wireless_dev *wdev = wil->wdev;
+       struct vring *vring = &wil->vring_rx;
+       int rc;
+       struct wmi_cfg_rx_chain_cmd cmd = {
+               .action = WMI_RX_CHAIN_ADD,
+               .rx_sw_ring = {
+                       .max_mpdu_size = cpu_to_le16(RX_BUF_LEN),
+               },
+               .mid = 0, /* TODO - what is it? */
+               .decap_trans_type = WMI_DECAP_TYPE_802_3,
+       };
+       struct {
+               struct wil6210_mbox_hdr_wmi wmi;
+               struct wmi_cfg_rx_chain_done_event evt;
+       } __packed evt;
+
+       vring->size = WIL6210_RX_RING_SIZE;
+       rc = wil_vring_alloc(wil, vring);
+       if (rc)
+               return rc;
+
+       cmd.rx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa);
+       cmd.rx_sw_ring.ring_size = cpu_to_le16(vring->size);
+       if (wdev->iftype == NL80211_IFTYPE_MONITOR) {
+               struct ieee80211_channel *ch = wdev->preset_chandef.chan;
+
+               cmd.sniffer_cfg.mode = cpu_to_le32(WMI_SNIFFER_ON);
+               if (ch)
+                       cmd.sniffer_cfg.channel = ch->hw_value - 1;
+               cmd.sniffer_cfg.phy_info_mode =
+                       cpu_to_le32(ndev->type == ARPHRD_IEEE80211_RADIOTAP);
+               cmd.sniffer_cfg.phy_support =
+                       cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL)
+                                   ? WMI_SNIFFER_CP : WMI_SNIFFER_DP);
+       }
+       /* typical time for secure PCP is 840ms */
+       rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd),
+                     WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000);
+       if (rc)
+               goto err_free;
+
+       vring->hwtail = le32_to_cpu(evt.evt.rx_ring_tail_ptr);
+
+       wil_dbg(wil, "Rx init: status %d tail 0x%08x\n",
+               le32_to_cpu(evt.evt.status), vring->hwtail);
+
+       rc = wil_rx_refill(wil, vring->size);
+       if (rc)
+               goto err_free;
+
+       return 0;
+ err_free:
+       wil_vring_free(wil, vring, 0);
+
+       return rc;
+}
+
+void wil_rx_fini(struct wil6210_priv *wil)
+{
+       struct vring *vring = &wil->vring_rx;
+
+       if (vring->va) {
+               int rc;
+               struct wmi_cfg_rx_chain_cmd cmd = {
+                       .action = cpu_to_le32(WMI_RX_CHAIN_DEL),
+                       .rx_sw_ring = {
+                               .max_mpdu_size = cpu_to_le16(RX_BUF_LEN),
+                       },
+               };
+               struct {
+                       struct wil6210_mbox_hdr_wmi wmi;
+                       struct wmi_cfg_rx_chain_done_event cfg;
+               } __packed wmi_rx_cfg_reply;
+
+               rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd),
+                             WMI_CFG_RX_CHAIN_DONE_EVENTID,
+                             &wmi_rx_cfg_reply, sizeof(wmi_rx_cfg_reply),
+                             100);
+               wil_vring_free(wil, vring, 0);
+       }
+}
+
+int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
+                     int cid, int tid)
+{
+       int rc;
+       struct wmi_vring_cfg_cmd cmd = {
+               .action = cpu_to_le32(WMI_VRING_CMD_ADD),
+               .vring_cfg = {
+                       .tx_sw_ring = {
+                               .max_mpdu_size = cpu_to_le16(TX_BUF_LEN),
+                       },
+                       .ringid = id,
+                       .cidxtid = (cid & 0xf) | ((tid & 0xf) << 4),
+                       .encap_trans_type = WMI_VRING_ENC_TYPE_802_3,
+                       .mac_ctrl = 0,
+                       .to_resolution = 0,
+                       .agg_max_wsize = 16,
+                       .schd_params = {
+                               .priority = cpu_to_le16(0),
+                               .timeslot_us = cpu_to_le16(0xfff),
+                       },
+               },
+       };
+       struct {
+               struct wil6210_mbox_hdr_wmi wmi;
+               struct wmi_vring_cfg_done_event cmd;
+       } __packed reply;
+       struct vring *vring = &wil->vring_tx[id];
+
+       if (vring->va) {
+               wil_err(wil, "Tx ring [%d] already allocated\n", id);
+               rc = -EINVAL;
+               goto out;
+       }
+
+       vring->size = size;
+       rc = wil_vring_alloc(wil, vring);
+       if (rc)
+               goto out;
+
+       cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa);
+       cmd.vring_cfg.tx_sw_ring.ring_size = cpu_to_le16(vring->size);
+
+       rc = wmi_call(wil, WMI_VRING_CFG_CMDID, &cmd, sizeof(cmd),
+                     WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100);
+       if (rc)
+               goto out_free;
+
+       if (reply.cmd.status != WMI_VRING_CFG_SUCCESS) {
+               wil_err(wil, "Tx config failed, status 0x%02x\n",
+                       reply.cmd.status);
+               goto out_free;
+       }
+       vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr);
+
+       return 0;
+ out_free:
+       wil_vring_free(wil, vring, 1);
+ out:
+
+       return rc;
+}
+
+void wil_vring_fini_tx(struct wil6210_priv *wil, int id)
+{
+       struct vring *vring = &wil->vring_tx[id];
+
+       if (!vring->va)
+               return;
+
+       wil_vring_free(wil, vring, 1);
+}
+
+static struct vring *wil_find_tx_vring(struct wil6210_priv *wil,
+                                      struct sk_buff *skb)
+{
+       struct vring *v = &wil->vring_tx[0];
+
+       if (v->va)
+               return v;
+
+       return NULL;
+}
+
+static int wil_tx_desc_map(volatile struct vring_tx_desc *d,
+                          dma_addr_t pa, u32 len)
+{
+       d->dma.addr_low = lower_32_bits(pa);
+       d->dma.addr_high = (u16)upper_32_bits(pa);
+       d->dma.ip_length = 0;
+       /* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/
+       d->dma.b11 = 0/*14 | BIT(7)*/;
+       d->dma.error = 0;
+       d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */
+       d->dma.length = len;
+       d->dma.d0 = 0;
+       d->mac.d[0] = 0;
+       d->mac.d[1] = 0;
+       d->mac.d[2] = 0;
+       d->mac.ucode_cmd = 0;
+       /* use dst index 0 */
+       d->mac.d[1] |= BIT(MAC_CFG_DESC_TX_1_DST_INDEX_EN_POS) |
+                      (0 << MAC_CFG_DESC_TX_1_DST_INDEX_POS);
+       /* translation type:  0 - bypass; 1 - 802.3; 2 - native wifi */
+       d->mac.d[2] = BIT(MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS) |
+                     (1 << MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS);
+
+       return 0;
+}
+
+static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
+                       struct sk_buff *skb)
+{
+       struct device *dev = wil_to_dev(wil);
+       volatile struct vring_tx_desc *d;
+       u32 swhead = vring->swhead;
+       int avail = wil_vring_avail_tx(vring);
+       int nr_frags = skb_shinfo(skb)->nr_frags;
+       uint f;
+       int vring_index = vring - wil->vring_tx;
+       uint i = swhead;
+       dma_addr_t pa;
+
+       wil_dbg_TXRX(wil, "%s()\n", __func__);
+
+       if (avail < vring->size/8)
+               netif_tx_stop_all_queues(wil_to_ndev(wil));
+       if (avail < 1 + nr_frags) {
+               wil_err(wil, "Tx ring full. No space for %d fragments\n",
+                       1 + nr_frags);
+               return -ENOMEM;
+       }
+       d = &(vring->va[i].tx);
+
+       /* FIXME FW can accept only unicast frames for the peer */
+       memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN);
+
+       pa = dma_map_single(dev, skb->data,
+                       skb_headlen(skb), DMA_TO_DEVICE);
+
+       wil_dbg_TXRX(wil, "Tx skb %d bytes %p -> %#08llx\n", skb_headlen(skb),
+                    skb->data, (unsigned long long)pa);
+       wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_OFFSET, 16, 1,
+                         skb->data, skb_headlen(skb), false);
+
+       if (unlikely(dma_mapping_error(dev, pa)))
+               return -EINVAL;
+       /* 1-st segment */
+       wil_tx_desc_map(d, pa, skb_headlen(skb));
+       d->mac.d[2] |= ((nr_frags + 1) <<
+                      MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS);
+       /* middle segments */
+       for (f = 0; f < nr_frags; f++) {
+               const struct skb_frag_struct *frag =
+                               &skb_shinfo(skb)->frags[f];
+               int len = skb_frag_size(frag);
+               i = (swhead + f + 1) % vring->size;
+               d = &(vring->va[i].tx);
+               pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag),
+                               DMA_TO_DEVICE);
+               if (unlikely(dma_mapping_error(dev, pa)))
+                       goto dma_error;
+               wil_tx_desc_map(d, pa, len);
+               vring->ctx[i] = NULL;
+       }
+       /* for the last seg only */
+       d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS);
+       d->dma.d0 |= BIT(9); /* BUG: undocumented bit */
+       d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS);
+       d->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_0_QID_POS);
+
+       wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_NONE, 32, 4,
+                         (const void *)d, sizeof(*d), false);
+
+       /* advance swhead */
+       wil_vring_advance_head(vring, nr_frags + 1);
+       wil_dbg_TXRX(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
+       iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail));
+       /* hold reference to skb
+        * to prevent skb release before accounting
+        * in case of immediate "tx done"
+        */
+       vring->ctx[i] = skb_get(skb);
+
+       return 0;
+ dma_error:
+       /* unmap what we have mapped */
+       /* Note: increment @f to operate with positive index */
+       for (f++; f > 0; f--) {
+               i = (swhead + f) % vring->size;
+               d = &(vring->va[i].tx);
+               d->dma.status = TX_DMA_STATUS_DU;
+               pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
+               if (vring->ctx[i])
+                       dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE);
+               else
+                       dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE);
+       }
+
+       return -EINVAL;
+}
+
+
+netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+       struct wil6210_priv *wil = ndev_to_wil(ndev);
+       struct vring *vring;
+       int rc;
+
+       wil_dbg_TXRX(wil, "%s()\n", __func__);
+       if (!test_bit(wil_status_fwready, &wil->status)) {
+               wil_err(wil, "FW not ready\n");
+               goto drop;
+       }
+       if (!test_bit(wil_status_fwconnected, &wil->status)) {
+               wil_err(wil, "FW not connected\n");
+               goto drop;
+       }
+       if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) {
+               wil_err(wil, "Xmit in monitor mode not supported\n");
+               goto drop;
+       }
+       if (skb->protocol == cpu_to_be16(ETH_P_PAE)) {
+               rc = wmi_tx_eapol(wil, skb);
+       } else {
+               /* find vring */
+               vring = wil_find_tx_vring(wil, skb);
+               if (!vring) {
+                       wil_err(wil, "No Tx VRING available\n");
+                       goto drop;
+               }
+               /* set up vring entry */
+               rc = wil_tx_vring(wil, vring, skb);
+       }
+       switch (rc) {
+       case 0:
+               ndev->stats.tx_packets++;
+               ndev->stats.tx_bytes += skb->len;
+               dev_kfree_skb_any(skb);
+               return NETDEV_TX_OK;
+       case -ENOMEM:
+               return NETDEV_TX_BUSY;
+       default:
+               ; /* goto drop; */
+               break;
+       }
+ drop:
+       netif_tx_stop_all_queues(ndev);
+       ndev->stats.tx_dropped++;
+       dev_kfree_skb_any(skb);
+
+       return NET_XMIT_DROP;
+}
+
+/**
+ * Clean up transmitted skb's from the Tx VRING
+ *
+ * Safe to call from IRQ
+ */
+void wil_tx_complete(struct wil6210_priv *wil, int ringid)
+{
+       struct device *dev = wil_to_dev(wil);
+       struct vring *vring = &wil->vring_tx[ringid];
+
+       if (!vring->va) {
+               wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid);
+               return;
+       }
+
+       wil_dbg_TXRX(wil, "%s(%d)\n", __func__, ringid);
+
+       while (!wil_vring_is_empty(vring)) {
+               volatile struct vring_tx_desc *d = &vring->va[vring->swtail].tx;
+               dma_addr_t pa;
+               struct sk_buff *skb;
+               if (!(d->dma.status & TX_DMA_STATUS_DU))
+                       break;
+
+               wil_dbg_TXRX(wil,
+                            "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n",
+                            vring->swtail, d->dma.length, d->dma.status,
+                            d->dma.error);
+               wil_hex_dump_TXRX("TxC ", DUMP_PREFIX_NONE, 32, 4,
+                                 (const void *)d, sizeof(*d), false);
+
+               pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
+               skb = vring->ctx[vring->swtail];
+               if (skb) {
+                       dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE);
+                       dev_kfree_skb_any(skb);
+                       vring->ctx[vring->swtail] = NULL;
+               } else {
+                       dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE);
+               }
+               d->dma.addr_low = 0;
+               d->dma.addr_high = 0;
+               d->dma.length = 0;
+               d->dma.status = TX_DMA_STATUS_DU;
+               vring->swtail = wil_vring_next_tail(vring);
+       }
+       if (wil_vring_avail_tx(vring) > vring->size/4)
+               netif_tx_wake_all_queues(wil_to_ndev(wil));
+}
diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h
new file mode 100644 (file)
index 0000000..45a61f5
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef WIL6210_TXRX_H
+#define WIL6210_TXRX_H
+
+#define BUF_SW_OWNED    (1)
+#define BUF_HW_OWNED    (0)
+
+/* size of max. Rx packet */
+#define RX_BUF_LEN      (2048)
+#define TX_BUF_LEN      (2048)
+/* how many bytes to reserve for rtap header? */
+#define WIL6210_RTAP_SIZE (128)
+
+/* Tx/Rx path */
+/*
+ * Tx descriptor - MAC part
+ * [dword 0]
+ * bit  0.. 9 : lifetime_expiry_value:10
+ * bit     10 : interrup_en:1
+ * bit     11 : status_en:1
+ * bit 12..13 : txss_override:2
+ * bit     14 : timestamp_insertion:1
+ * bit     15 : duration_preserve:1
+ * bit 16..21 : reserved0:6
+ * bit 22..26 : mcs_index:5
+ * bit     27 : mcs_en:1
+ * bit 28..29 : reserved1:2
+ * bit     30 : reserved2:1
+ * bit     31 : sn_preserved:1
+ * [dword 1]
+ * bit  0.. 3 : pkt_mode:4
+ * bit      4 : pkt_mode_en:1
+ * bit  5.. 7 : reserved0:3
+ * bit  8..13 : reserved1:6
+ * bit     14 : reserved2:1
+ * bit     15 : ack_policy_en:1
+ * bit 16..19 : dst_index:4
+ * bit     20 : dst_index_en:1
+ * bit 21..22 : ack_policy:2
+ * bit     23 : lifetime_en:1
+ * bit 24..30 : max_retry:7
+ * bit     31 : max_retry_en:1
+ * [dword 2]
+ * bit  0.. 7 : num_of_descriptors:8
+ * bit  8..17 : reserved:10
+ * bit 18..19 : l2_translation_type:2
+ * bit     20 : snap_hdr_insertion_en:1
+ * bit     21 : vlan_removal_en:1
+ * bit 22..31 : reserved0:10
+ * [dword 3]
+ * bit  0.. 31: ucode_cmd:32
+ */
+struct vring_tx_mac {
+       u32 d[3];
+       u32 ucode_cmd;
+} __packed;
+
+/* TX MAC Dword 0 */
+#define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_POS 0
+#define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_LEN 10
+#define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_MSK 0x3FF
+
+#define MAC_CFG_DESC_TX_0_INTERRUP_EN_POS 10
+#define MAC_CFG_DESC_TX_0_INTERRUP_EN_LEN 1
+#define MAC_CFG_DESC_TX_0_INTERRUP_EN_MSK 0x400
+
+#define MAC_CFG_DESC_TX_0_STATUS_EN_POS 11
+#define MAC_CFG_DESC_TX_0_STATUS_EN_LEN 1
+#define MAC_CFG_DESC_TX_0_STATUS_EN_MSK 0x800
+
+#define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_POS 12
+#define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_LEN 2
+#define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_MSK 0x3000
+
+#define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_POS 14
+#define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_LEN 1
+#define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_MSK 0x4000
+
+#define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_POS 15
+#define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_LEN 1
+#define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_MSK 0x8000
+
+#define MAC_CFG_DESC_TX_0_MCS_INDEX_POS 22
+#define MAC_CFG_DESC_TX_0_MCS_INDEX_LEN 5
+#define MAC_CFG_DESC_TX_0_MCS_INDEX_MSK 0x7C00000
+
+#define MAC_CFG_DESC_TX_0_MCS_EN_POS 27
+#define MAC_CFG_DESC_TX_0_MCS_EN_LEN 1
+#define MAC_CFG_DESC_TX_0_MCS_EN_MSK 0x8000000
+
+#define MAC_CFG_DESC_TX_0_SN_PRESERVED_POS 31
+#define MAC_CFG_DESC_TX_0_SN_PRESERVED_LEN 1
+#define MAC_CFG_DESC_TX_0_SN_PRESERVED_MSK 0x80000000
+
+/* TX MAC Dword 1 */
+#define MAC_CFG_DESC_TX_1_PKT_MODE_POS 0
+#define MAC_CFG_DESC_TX_1_PKT_MODE_LEN 4
+#define MAC_CFG_DESC_TX_1_PKT_MODE_MSK 0xF
+
+#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_POS 4
+#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_LEN 1
+#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_MSK 0x10
+
+#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_POS 15
+#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_LEN 1
+#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_MSK 0x8000
+
+#define MAC_CFG_DESC_TX_1_DST_INDEX_POS 16
+#define MAC_CFG_DESC_TX_1_DST_INDEX_LEN 4
+#define MAC_CFG_DESC_TX_1_DST_INDEX_MSK 0xF0000
+
+#define MAC_CFG_DESC_TX_1_DST_INDEX_EN_POS 20
+#define MAC_CFG_DESC_TX_1_DST_INDEX_EN_LEN 1
+#define MAC_CFG_DESC_TX_1_DST_INDEX_EN_MSK 0x100000
+
+#define MAC_CFG_DESC_TX_1_ACK_POLICY_POS 21
+#define MAC_CFG_DESC_TX_1_ACK_POLICY_LEN 2
+#define MAC_CFG_DESC_TX_1_ACK_POLICY_MSK 0x600000
+
+#define MAC_CFG_DESC_TX_1_LIFETIME_EN_POS 23
+#define MAC_CFG_DESC_TX_1_LIFETIME_EN_LEN 1
+#define MAC_CFG_DESC_TX_1_LIFETIME_EN_MSK 0x800000
+
+#define MAC_CFG_DESC_TX_1_MAX_RETRY_POS 24
+#define MAC_CFG_DESC_TX_1_MAX_RETRY_LEN 7
+#define MAC_CFG_DESC_TX_1_MAX_RETRY_MSK 0x7F000000
+
+#define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_POS 31
+#define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_LEN 1
+#define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_MSK 0x80000000
+
+/* TX MAC Dword 2 */
+#define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS 0
+#define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_LEN 8
+#define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_MSK 0xFF
+
+#define MAC_CFG_DESC_TX_2_RESERVED_POS 8
+#define MAC_CFG_DESC_TX_2_RESERVED_LEN 10
+#define MAC_CFG_DESC_TX_2_RESERVED_MSK 0x3FF00
+
+#define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS 18
+#define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_LEN 2
+#define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_MSK 0xC0000
+
+#define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS 20
+#define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_LEN 1
+#define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_MSK 0x100000
+
+#define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_POS 21
+#define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_LEN 1
+#define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_MSK 0x200000
+
+/* TX MAC Dword 3 */
+#define MAC_CFG_DESC_TX_3_UCODE_CMD_POS 0
+#define MAC_CFG_DESC_TX_3_UCODE_CMD_LEN 32
+#define MAC_CFG_DESC_TX_3_UCODE_CMD_MSK 0xFFFFFFFF
+
+/* TX DMA Dword 0 */
+#define DMA_CFG_DESC_TX_0_L4_LENGTH_POS 0
+#define DMA_CFG_DESC_TX_0_L4_LENGTH_LEN 8
+#define DMA_CFG_DESC_TX_0_L4_LENGTH_MSK 0xFF
+
+#define DMA_CFG_DESC_TX_0_CMD_EOP_POS 8
+#define DMA_CFG_DESC_TX_0_CMD_EOP_LEN 1
+#define DMA_CFG_DESC_TX_0_CMD_EOP_MSK 0x100
+
+#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS 10
+#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_LEN 1
+#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_MSK 0x400
+
+#define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_POS 11
+#define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_LEN 2
+#define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_MSK 0x1800
+
+#define DMA_CFG_DESC_TX_0_TCP_SEG_EN_POS 13
+#define DMA_CFG_DESC_TX_0_TCP_SEG_EN_LEN 1
+#define DMA_CFG_DESC_TX_0_TCP_SEG_EN_MSK 0x2000
+
+#define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_POS 14
+#define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_LEN 1
+#define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_MSK 0x4000
+
+#define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_POS 15
+#define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_LEN 1
+#define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_MSK 0x8000
+
+#define DMA_CFG_DESC_TX_0_QID_POS 16
+#define DMA_CFG_DESC_TX_0_QID_LEN 5
+#define DMA_CFG_DESC_TX_0_QID_MSK 0x1F0000
+
+#define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_POS 21
+#define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_LEN 1
+#define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_MSK 0x200000
+
+#define DMA_CFG_DESC_TX_0_L4_TYPE_POS 30
+#define DMA_CFG_DESC_TX_0_L4_TYPE_LEN 2
+#define DMA_CFG_DESC_TX_0_L4_TYPE_MSK 0xC0000000
+
+
+#define TX_DMA_STATUS_DU         BIT(0)
+
+struct vring_tx_dma {
+       u32 d0;
+       u32 addr_low;
+       u16 addr_high;
+       u8  ip_length;
+       u8  b11;       /* 0..6: mac_length; 7:ip_version */
+       u8  error;     /* 0..2: err; 3..7: reserved; */
+       u8  status;    /* 0: used; 1..7; reserved */
+       u16 length;
+} __packed;
+
+/*
+ * Rx descriptor - MAC part
+ * [dword 0]
+ * bit  0.. 3 : tid:4 The QoS (b3-0) TID Field
+ * bit  4.. 6 : connection_id:3 :The Source index that  was found during
+ *  Parsing the TA.  This field is used to  define the source of the packet
+ * bit      7 : reserved:1
+ * bit  8.. 9 : mac_id:2 : The MAC virtual  Ring number (always zero)
+ * bit 10..11 : frame_type:2 : The FC Control  (b3-2) -  MPDU Type
+ *              (management, data, control  and extension)
+ * bit 12..15 : frame_subtype:4 : The FC Control  (b7-4) -  Frame Subtype
+ * bit 16..27 : seq_number:12 The received Sequence number field
+ * bit 28..31 : extended:4 extended subtype
+ * [dword 1]
+ * bit  0.. 3 : reserved
+ * bit  4.. 5 : key_id:2
+ * bit      6 : decrypt_bypass:1
+ * bit      7 : security:1
+ * bit  8.. 9 : ds_bits:2
+ * bit     10 : a_msdu_present:1  from qos header
+ * bit     11 : a_msdu_type:1  from qos header
+ * bit     12 : a_mpdu:1  part of AMPDU aggregation
+ * bit     13 : broadcast:1
+ * bit     14 : mutlicast:1
+ * bit     15 : reserved:1
+ * bit 16..20 : rx_mac_qid:5   The Queue Identifier that the packet
+ *                             is received from
+ * bit 21..24 : mcs:4
+ * bit 25..28 : mic_icr:4
+ * bit 29..31 : reserved:3
+ * [dword 2]
+ * bit  0.. 2 : time_slot:3 The timeslot that the MPDU is received
+ * bit      3 : fc_protocol_ver:1 The FC Control  (b0) - Protocol  Version
+ * bit      4 : fc_order:1 The FC Control (b15) -Order
+ * bit  5.. 7 : qos_ack_policy:3  The QoS (b6-5) ack policy Field
+ * bit      8 : esop:1 The QoS (b4) ESOP field
+ * bit      9 : qos_rdg_more_ppdu:1 The QoS (b9) RDG  field
+ * bit 10..14 : qos_reserved:5 The QoS (b14-10) Reserved  field
+ * bit     15 : qos_ac_constraint:1
+ * bit 16..31 : pn_15_0:16 low 2 bytes of PN
+ * [dword 3]
+ * bit  0..31 : pn_47_16:32 high 4 bytes of PN
+ */
+struct vring_rx_mac {
+       u32 d0;
+       u32 d1;
+       u16 w4;
+       u16 pn_15_0;
+       u32 pn_47_16;
+} __packed;
+
+/*
+ * Rx descriptor - DMA part
+ * [dword 0]
+ * bit  0.. 7 : l4_length:8 layer 4 length
+ * bit  8.. 9 : reserved:2
+ * bit     10 : cmd_dma_it:1
+ * bit 11..15 : reserved:5
+ * bit 16..29 : phy_info_length:14
+ * bit 30..31 : l4_type:2 valid if the L4I bit is set in the status field
+ * [dword 1]
+ * bit  0..31 : addr_low:32 The payload buffer low address
+ * [dword 2]
+ * bit  0..15 : addr_high:16 The payload buffer high address
+ * bit 16..23 : ip_length:8
+ * bit 24..30 : mac_length:7
+ * bit     31 : ip_version:1
+ * [dword 3]
+ *  [byte 12] error
+ *  [byte 13] status
+ * bit      0 : du:1
+ * bit      1 : eop:1
+ * bit      2 : error:1
+ * bit      3 : mi:1
+ * bit      4 : l3_identified:1
+ * bit      5 : l4_identified:1
+ * bit      6 : phy_info_included:1
+ * bit      7 : reserved:1
+ *  [word 7] length
+ *
+ */
+
+#define RX_DMA_D0_CMD_DMA_IT     BIT(10)
+
+#define RX_DMA_STATUS_DU         BIT(0)
+#define RX_DMA_STATUS_ERROR      BIT(2)
+#define RX_DMA_STATUS_PHY_INFO   BIT(6)
+
+struct vring_rx_dma {
+       u32 d0;
+       u32 addr_low;
+       u16 addr_high;
+       u8  ip_length;
+       u8  b11;
+       u8  error;
+       u8  status;
+       u16 length;
+} __packed;
+
+struct vring_tx_desc {
+       struct vring_tx_mac mac;
+       struct vring_tx_dma dma;
+} __packed;
+
+struct vring_rx_desc {
+       struct vring_rx_mac mac;
+       struct vring_rx_dma dma;
+} __packed;
+
+union vring_desc {
+       struct vring_tx_desc tx;
+       struct vring_rx_desc rx;
+} __packed;
+
+static inline int wil_rxdesc_phy_length(volatile struct vring_rx_desc *d)
+{
+       return WIL_GET_BITS(d->dma.d0, 16, 29);
+}
+
+static inline int wil_rxdesc_mcs(volatile struct vring_rx_desc *d)
+{
+       return WIL_GET_BITS(d->mac.d1, 21, 24);
+}
+
+static inline int wil_rxdesc_ds_bits(volatile struct vring_rx_desc *d)
+{
+       return WIL_GET_BITS(d->mac.d1, 8, 9);
+}
+
+static inline int wil_rxdesc_ftype(volatile struct vring_rx_desc *d)
+{
+       return WIL_GET_BITS(d->mac.d0, 10, 11);
+}
+
+#endif /* WIL6210_TXRX_H */
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
new file mode 100644 (file)
index 0000000..9bcfffa
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __WIL6210_H__
+#define __WIL6210_H__
+
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#include <net/cfg80211.h>
+
+#include "dbg_hexdump.h"
+
+#define WIL_NAME "wil6210"
+
+/**
+ * extract bits [@b0:@b1] (inclusive) from the value @x
+ * it should be @b0 <= @b1, or result is incorrect
+ */
+static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)
+{
+       return (x >> b0) & ((1 << (b1 - b0 + 1)) - 1);
+}
+
+#define WIL6210_MEM_SIZE (2*1024*1024UL)
+
+#define WIL6210_TX_QUEUES (4)
+
+#define WIL6210_RX_RING_SIZE (128)
+#define WIL6210_TX_RING_SIZE (128)
+#define WIL6210_MAX_TX_RINGS (24)
+
+/* Hardware definitions begin */
+
+/*
+ * Mapping
+ * RGF File      | Host addr    |  FW addr
+ *               |              |
+ * user_rgf      | 0x000000     | 0x880000
+ *  dma_rgf      | 0x001000     | 0x881000
+ * pcie_rgf      | 0x002000     | 0x882000
+ *               |              |
+ */
+
+/* Where various structures placed in host address space */
+#define WIL6210_FW_HOST_OFF      (0x880000UL)
+
+#define HOSTADDR(fwaddr)        (fwaddr - WIL6210_FW_HOST_OFF)
+
+/*
+ * Interrupt control registers block
+ *
+ * each interrupt controlled by the same bit in all registers
+ */
+struct RGF_ICR {
+       u32 ICC; /* Cause Control, RW: 0 - W1C, 1 - COR */
+       u32 ICR; /* Cause, W1C/COR depending on ICC */
+       u32 ICM; /* Cause masked (ICR & ~IMV), W1C/COR depending on ICC */
+       u32 ICS; /* Cause Set, WO */
+       u32 IMV; /* Mask, RW+S/C */
+       u32 IMS; /* Mask Set, write 1 to set */
+       u32 IMC; /* Mask Clear, write 1 to clear */
+} __packed;
+
+/* registers - FW addresses */
+#define RGF_USER_USER_SCRATCH_PAD      (0x8802bc)
+#define RGF_USER_USER_ICR              (0x880b4c) /* struct RGF_ICR */
+       #define BIT_USER_USER_ICR_SW_INT_2      BIT(18)
+#define RGF_USER_CLKS_CTL_SW_RST_MASK_0        (0x880b14)
+#define RGF_USER_MAC_CPU_0             (0x8801fc)
+#define RGF_USER_USER_CPU_0            (0x8801e0)
+#define RGF_USER_CLKS_CTL_SW_RST_VEC_0 (0x880b04)
+#define RGF_USER_CLKS_CTL_SW_RST_VEC_1 (0x880b08)
+#define RGF_USER_CLKS_CTL_SW_RST_VEC_2 (0x880b0c)
+#define RGF_USER_CLKS_CTL_SW_RST_VEC_3 (0x880b10)
+
+#define RGF_DMA_PSEUDO_CAUSE           (0x881c68)
+#define RGF_DMA_PSEUDO_CAUSE_MASK_SW   (0x881c6c)
+#define RGF_DMA_PSEUDO_CAUSE_MASK_FW   (0x881c70)
+       #define BIT_DMA_PSEUDO_CAUSE_RX         BIT(0)
+       #define BIT_DMA_PSEUDO_CAUSE_TX         BIT(1)
+       #define BIT_DMA_PSEUDO_CAUSE_MISC       BIT(2)
+
+#define RGF_DMA_EP_TX_ICR              (0x881bb4) /* struct RGF_ICR */
+       #define BIT_DMA_EP_TX_ICR_TX_DONE       BIT(0)
+       #define BIT_DMA_EP_TX_ICR_TX_DONE_N(n)  BIT(n+1) /* n = [0..23] */
+#define RGF_DMA_EP_RX_ICR              (0x881bd0) /* struct RGF_ICR */
+       #define BIT_DMA_EP_RX_ICR_RX_DONE       BIT(0)
+#define RGF_DMA_EP_MISC_ICR            (0x881bec) /* struct RGF_ICR */
+       #define BIT_DMA_EP_MISC_ICR_RX_HTRSH    BIT(0)
+       #define BIT_DMA_EP_MISC_ICR_TX_NO_ACT   BIT(1)
+       #define BIT_DMA_EP_MISC_ICR_FW_INT0     BIT(28)
+       #define BIT_DMA_EP_MISC_ICR_FW_INT1     BIT(29)
+
+/* Interrupt moderation control */
+#define RGF_DMA_ITR_CNT_TRSH           (0x881c5c)
+#define RGF_DMA_ITR_CNT_DATA           (0x881c60)
+#define RGF_DMA_ITR_CNT_CRL            (0x881C64)
+       #define BIT_DMA_ITR_CNT_CRL_EN          BIT(0)
+       #define BIT_DMA_ITR_CNT_CRL_EXT_TICK    BIT(1)
+       #define BIT_DMA_ITR_CNT_CRL_FOREVER     BIT(2)
+       #define BIT_DMA_ITR_CNT_CRL_CLR         BIT(3)
+       #define BIT_DMA_ITR_CNT_CRL_REACH_TRSH  BIT(4)
+
+/* popular locations */
+#define HOST_MBOX   HOSTADDR(RGF_USER_USER_SCRATCH_PAD)
+#define HOST_SW_INT (HOSTADDR(RGF_USER_USER_ICR) + \
+       offsetof(struct RGF_ICR, ICS))
+#define SW_INT_MBOX BIT_USER_USER_ICR_SW_INT_2
+
+/* ISR register bits */
+#define ISR_MISC_FW_READY BIT_DMA_EP_MISC_ICR_FW_INT0
+#define ISR_MISC_MBOX_EVT BIT_DMA_EP_MISC_ICR_FW_INT1
+
+/* Hardware definitions end */
+
+struct wil6210_mbox_ring {
+       u32 base;
+       u16 entry_size; /* max. size of mbox entry, incl. all headers */
+       u16 size;
+       u32 tail;
+       u32 head;
+} __packed;
+
+struct wil6210_mbox_ring_desc {
+       __le32 sync;
+       __le32 addr;
+} __packed;
+
+/* at HOST_OFF_WIL6210_MBOX_CTL */
+struct wil6210_mbox_ctl {
+       struct wil6210_mbox_ring tx;
+       struct wil6210_mbox_ring rx;
+} __packed;
+
+struct wil6210_mbox_hdr {
+       __le16 seq;
+       __le16 len; /* payload, bytes after this header */
+       __le16 type;
+       u8 flags;
+       u8 reserved;
+} __packed;
+
+#define WIL_MBOX_HDR_TYPE_WMI (0)
+
+/* max. value for wil6210_mbox_hdr.len */
+#define MAX_MBOXITEM_SIZE   (240)
+
+struct wil6210_mbox_hdr_wmi {
+       u8 reserved0[2];
+       __le16 id;
+       __le16 info1; /* bits [0..3] - device_id, rest - unused */
+       u8 reserved1[2];
+} __packed;
+
+struct pending_wmi_event {
+       struct list_head list;
+       struct {
+               struct wil6210_mbox_hdr hdr;
+               struct wil6210_mbox_hdr_wmi wmi;
+               u8 data[0];
+       } __packed event;
+};
+
+union vring_desc;
+
+struct vring {
+       dma_addr_t pa;
+       volatile union vring_desc *va; /* vring_desc[size], WriteBack by DMA */
+       u16 size; /* number of vring_desc elements */
+       u32 swtail;
+       u32 swhead;
+       u32 hwtail; /* write here to inform hw */
+       void **ctx; /* void *ctx[size] - software context */
+};
+
+enum { /* for wil6210_priv.status */
+       wil_status_fwready = 0,
+       wil_status_fwconnected,
+       wil_status_dontscan,
+       wil_status_irqen, /* FIXME: interrupts enabled - for debug */
+};
+
+struct pci_dev;
+
+struct wil6210_stats {
+       u64 tsf;
+       u32 snr;
+       u16 last_mcs_rx;
+       u16 bf_mcs; /* last BF, used for Tx */
+       u16 my_rx_sector;
+       u16 my_tx_sector;
+       u16 peer_rx_sector;
+       u16 peer_tx_sector;
+};
+
+struct wil6210_priv {
+       struct pci_dev *pdev;
+       int n_msi;
+       struct wireless_dev *wdev;
+       void __iomem *csr;
+       ulong status;
+       /* profile */
+       u32 monitor_flags;
+       u32 secure_pcp; /* create secure PCP? */
+       int sinfo_gen;
+       /* cached ISR registers */
+       u32 isr_misc;
+       /* mailbox related */
+       struct mutex wmi_mutex;
+       struct wil6210_mbox_ctl mbox_ctl;
+       struct completion wmi_ready;
+       u16 wmi_seq;
+       u16 reply_id; /**< wait for this WMI event */
+       void *reply_buf;
+       u16 reply_size;
+       struct workqueue_struct *wmi_wq; /* for deferred calls */
+       struct work_struct wmi_event_worker;
+       struct workqueue_struct *wmi_wq_conn; /* for connect worker */
+       struct work_struct wmi_connect_worker;
+       struct work_struct disconnect_worker;
+       struct timer_list connect_timer;
+       int pending_connect_cid;
+       struct list_head pending_wmi_ev;
+       /*
+        * protect pending_wmi_ev
+        * - fill in IRQ from wil6210_irq_misc,
+        * - consumed in thread by wmi_event_worker
+        */
+       spinlock_t wmi_ev_lock;
+       /* DMA related */
+       struct vring vring_rx;
+       struct vring vring_tx[WIL6210_MAX_TX_RINGS];
+       u8 dst_addr[WIL6210_MAX_TX_RINGS][ETH_ALEN];
+       /* scan */
+       struct cfg80211_scan_request *scan_request;
+
+       struct mutex mutex; /* for wil6210_priv access in wil_{up|down} */
+       /* statistics */
+       struct wil6210_stats stats;
+       /* debugfs */
+       struct dentry *debug;
+       struct debugfs_blob_wrapper fw_code_blob;
+       struct debugfs_blob_wrapper fw_data_blob;
+       struct debugfs_blob_wrapper fw_peri_blob;
+       struct debugfs_blob_wrapper uc_code_blob;
+       struct debugfs_blob_wrapper uc_data_blob;
+       struct debugfs_blob_wrapper rgf_blob;
+};
+
+#define wil_to_wiphy(i) (i->wdev->wiphy)
+#define wil_to_dev(i) (wiphy_dev(wil_to_wiphy(i)))
+#define wiphy_to_wil(w) (struct wil6210_priv *)(wiphy_priv(w))
+#define wil_to_wdev(i) (i->wdev)
+#define wdev_to_wil(w) (struct wil6210_priv *)(wdev_priv(w))
+#define wil_to_ndev(i) (wil_to_wdev(i)->netdev)
+#define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr))
+
+#define wil_dbg(wil, fmt, arg...) netdev_dbg(wil_to_ndev(wil), fmt, ##arg)
+#define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg)
+#define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg)
+
+#define wil_dbg_IRQ(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg)
+#define wil_dbg_TXRX(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg)
+#define wil_dbg_WMI(wil, fmt, arg...) wil_dbg(wil, "DBG[ WMI]" fmt, ##arg)
+
+#define wil_hex_dump_TXRX(prefix_str, prefix_type, rowsize,    \
+                         groupsize, buf, len, ascii)           \
+                         wil_print_hex_dump_debug("DBG[TXRX]" prefix_str,\
+                                        prefix_type, rowsize,  \
+                                        groupsize, buf, len, ascii)
+
+#define wil_hex_dump_WMI(prefix_str, prefix_type, rowsize,     \
+                        groupsize, buf, len, ascii)            \
+                        wil_print_hex_dump_debug("DBG[ WMI]" prefix_str,\
+                                       prefix_type, rowsize,   \
+                                       groupsize, buf, len, ascii)
+
+void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src,
+                         size_t count);
+void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,
+                       size_t count);
+
+void *wil_if_alloc(struct device *dev, void __iomem *csr);
+void wil_if_free(struct wil6210_priv *wil);
+int wil_if_add(struct wil6210_priv *wil);
+void wil_if_remove(struct wil6210_priv *wil);
+int wil_priv_init(struct wil6210_priv *wil);
+void wil_priv_deinit(struct wil6210_priv *wil);
+int wil_reset(struct wil6210_priv *wil);
+void wil_link_on(struct wil6210_priv *wil);
+void wil_link_off(struct wil6210_priv *wil);
+int wil_up(struct wil6210_priv *wil);
+int wil_down(struct wil6210_priv *wil);
+void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r);
+
+void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr);
+void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr);
+int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr,
+                struct wil6210_mbox_hdr *hdr);
+int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len);
+void wmi_recv_cmd(struct wil6210_priv *wil);
+int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
+            u16 reply_id, void *reply, u8 reply_size, int to_msec);
+void wmi_connect_worker(struct work_struct *work);
+void wmi_event_worker(struct work_struct *work);
+void wmi_event_flush(struct wil6210_priv *wil);
+int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid);
+int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid);
+int wmi_set_channel(struct wil6210_priv *wil, int channel);
+int wmi_get_channel(struct wil6210_priv *wil, int *channel);
+int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb);
+int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index,
+                      const void *mac_addr);
+int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index,
+                      const void *mac_addr, int key_len, const void *key);
+int wmi_echo(struct wil6210_priv *wil);
+int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie);
+
+int wil6210_init_irq(struct wil6210_priv *wil, int irq);
+void wil6210_fini_irq(struct wil6210_priv *wil, int irq);
+void wil6210_disable_irq(struct wil6210_priv *wil);
+void wil6210_enable_irq(struct wil6210_priv *wil);
+
+int wil6210_debugfs_init(struct wil6210_priv *wil);
+void wil6210_debugfs_remove(struct wil6210_priv *wil);
+
+struct wireless_dev *wil_cfg80211_init(struct device *dev);
+void wil_wdev_free(struct wil6210_priv *wil);
+
+int wmi_set_mac_address(struct wil6210_priv *wil, void *addr);
+int wmi_set_bcon(struct wil6210_priv *wil, int bi, u8 wmi_nettype);
+void wil6210_disconnect(struct wil6210_priv *wil, void *bssid);
+
+int wil_rx_init(struct wil6210_priv *wil);
+void wil_rx_fini(struct wil6210_priv *wil);
+
+/* TX API */
+int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
+                     int cid, int tid);
+void wil_vring_fini_tx(struct wil6210_priv *wil, int id);
+
+netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev);
+void wil_tx_complete(struct wil6210_priv *wil, int ringid);
+
+/* RX API */
+void wil_rx_handle(struct wil6210_priv *wil);
+
+int wil_iftype_nl2wmi(enum nl80211_iftype type);
+
+#endif /* __WIL6210_H__ */
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
new file mode 100644 (file)
index 0000000..12915f6
--- /dev/null
@@ -0,0 +1,975 @@
+/*
+ * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/etherdevice.h>
+
+#include "wil6210.h"
+#include "wmi.h"
+
+/**
+ * WMI event receiving - theory of operations
+ *
+ * When firmware about to report WMI event, it fills memory area
+ * in the mailbox and raises misc. IRQ. Thread interrupt handler invoked for
+ * the misc IRQ, function @wmi_recv_cmd called by thread IRQ handler.
+ *
+ * @wmi_recv_cmd reads event, allocates memory chunk  and attaches it to the
+ * event list @wil->pending_wmi_ev. Then, work queue @wil->wmi_wq wakes up
+ * and handles events within the @wmi_event_worker. Every event get detached
+ * from list, processed and deleted.
+ *
+ * Purpose for this mechanism is to release IRQ thread; otherwise,
+ * if WMI event handling involves another WMI command flow, this 2-nd flow
+ * won't be completed because of blocked IRQ thread.
+ */
+
+/**
+ * Addressing - theory of operations
+ *
+ * There are several buses present on the WIL6210 card.
+ * Same memory areas are visible at different address on
+ * the different busses. There are 3 main bus masters:
+ *  - MAC CPU (ucode)
+ *  - User CPU (firmware)
+ *  - AHB (host)
+ *
+ * On the PCI bus, there is one BAR (BAR0) of 2Mb size, exposing
+ * AHB addresses starting from 0x880000
+ *
+ * Internally, firmware uses addresses that allows faster access but
+ * are invisible from the host. To read from these addresses, alternative
+ * AHB address must be used.
+ *
+ * Memory mapping
+ * Linker address         PCI/Host address
+ *                        0x880000 .. 0xa80000  2Mb BAR0
+ * 0x800000 .. 0x807000   0x900000 .. 0x907000  28k DCCM
+ * 0x840000 .. 0x857000   0x908000 .. 0x91f000  92k PERIPH
+ */
+
+/**
+ * @fw_mapping provides memory remapping table
+ */
+static const struct {
+       u32 from; /* linker address - from, inclusive */
+       u32 to;   /* linker address - to, exclusive */
+       u32 host; /* PCI/Host address - BAR0 + 0x880000 */
+} fw_mapping[] = {
+       {0x000000, 0x040000, 0x8c0000}, /* FW code RAM 256k */
+       {0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */
+       {0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */
+       {0x880000, 0x88a000, 0x880000}, /* various RGF */
+       {0x8c0000, 0x932000, 0x8c0000}, /* trivial mapping for upper area */
+       /*
+        * 920000..930000 ucode code RAM
+        * 930000..932000 ucode data RAM
+        */
+};
+
+/**
+ * return AHB address for given firmware/ucode internal (linker) address
+ * @x - internal address
+ * If address have no valid AHB mapping, return 0
+ */
+static u32 wmi_addr_remap(u32 x)
+{
+       uint i;
+
+       for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) {
+               if ((x >= fw_mapping[i].from) && (x < fw_mapping[i].to))
+                       return x + fw_mapping[i].host - fw_mapping[i].from;
+       }
+
+       return 0;
+}
+
+/**
+ * Check address validity for WMI buffer; remap if needed
+ * @ptr - internal (linker) fw/ucode address
+ *
+ * Valid buffer should be DWORD aligned
+ *
+ * return address for accessing buffer from the host;
+ * if buffer is not valid, return NULL.
+ */
+void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_)
+{
+       u32 off;
+       u32 ptr = le32_to_cpu(ptr_);
+
+       if (ptr % 4)
+               return NULL;
+
+       ptr = wmi_addr_remap(ptr);
+       if (ptr < WIL6210_FW_HOST_OFF)
+               return NULL;
+
+       off = HOSTADDR(ptr);
+       if (off > WIL6210_MEM_SIZE - 4)
+               return NULL;
+
+       return wil->csr + off;
+}
+
+/**
+ * Check address validity
+ */
+void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr)
+{
+       u32 off;
+
+       if (ptr % 4)
+               return NULL;
+
+       if (ptr < WIL6210_FW_HOST_OFF)
+               return NULL;
+
+       off = HOSTADDR(ptr);
+       if (off > WIL6210_MEM_SIZE - 4)
+               return NULL;
+
+       return wil->csr + off;
+}
+
+int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr,
+                struct wil6210_mbox_hdr *hdr)
+{
+       void __iomem *src = wmi_buffer(wil, ptr);
+       if (!src)
+               return -EINVAL;
+
+       wil_memcpy_fromio_32(hdr, src, sizeof(*hdr));
+
+       return 0;
+}
+
+static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
+{
+       struct {
+               struct wil6210_mbox_hdr hdr;
+               struct wil6210_mbox_hdr_wmi wmi;
+       } __packed cmd = {
+               .hdr = {
+                       .type = WIL_MBOX_HDR_TYPE_WMI,
+                       .flags = 0,
+                       .len = cpu_to_le16(sizeof(cmd.wmi) + len),
+               },
+               .wmi = {
+                       .id = cpu_to_le16(cmdid),
+                       .info1 = 0,
+               },
+       };
+       struct wil6210_mbox_ring *r = &wil->mbox_ctl.tx;
+       struct wil6210_mbox_ring_desc d_head;
+       u32 next_head;
+       void __iomem *dst;
+       void __iomem *head = wmi_addr(wil, r->head);
+       uint retry;
+
+       if (sizeof(cmd) + len > r->entry_size) {
+               wil_err(wil, "WMI size too large: %d bytes, max is %d\n",
+                       (int)(sizeof(cmd) + len), r->entry_size);
+               return -ERANGE;
+
+       }
+
+       might_sleep();
+
+       if (!test_bit(wil_status_fwready, &wil->status)) {
+               wil_err(wil, "FW not ready\n");
+               return -EAGAIN;
+       }
+
+       if (!head) {
+               wil_err(wil, "WMI head is garbage: 0x%08x\n", r->head);
+               return -EINVAL;
+       }
+       /* read Tx head till it is not busy */
+       for (retry = 5; retry > 0; retry--) {
+               wil_memcpy_fromio_32(&d_head, head, sizeof(d_head));
+               if (d_head.sync == 0)
+                       break;
+               msleep(20);
+       }
+       if (d_head.sync != 0) {
+               wil_err(wil, "WMI head busy\n");
+               return -EBUSY;
+       }
+       /* next head */
+       next_head = r->base + ((r->head - r->base + sizeof(d_head)) % r->size);
+       wil_dbg_WMI(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head);
+       /* wait till FW finish with previous command */
+       for (retry = 5; retry > 0; retry--) {
+               r->tail = ioread32(wil->csr + HOST_MBOX +
+                                  offsetof(struct wil6210_mbox_ctl, tx.tail));
+               if (next_head != r->tail)
+                       break;
+               msleep(20);
+       }
+       if (next_head == r->tail) {
+               wil_err(wil, "WMI ring full\n");
+               return -EBUSY;
+       }
+       dst = wmi_buffer(wil, d_head.addr);
+       if (!dst) {
+               wil_err(wil, "invalid WMI buffer: 0x%08x\n",
+                       le32_to_cpu(d_head.addr));
+               return -EINVAL;
+       }
+       cmd.hdr.seq = cpu_to_le16(++wil->wmi_seq);
+       /* set command */
+       wil_dbg_WMI(wil, "WMI command 0x%04x [%d]\n", cmdid, len);
+       wil_hex_dump_WMI("Cmd ", DUMP_PREFIX_OFFSET, 16, 1, &cmd,
+                        sizeof(cmd), true);
+       wil_hex_dump_WMI("cmd ", DUMP_PREFIX_OFFSET, 16, 1, buf,
+                        len, true);
+       wil_memcpy_toio_32(dst, &cmd, sizeof(cmd));
+       wil_memcpy_toio_32(dst + sizeof(cmd), buf, len);
+       /* mark entry as full */
+       iowrite32(1, wil->csr + HOSTADDR(r->head) +
+                 offsetof(struct wil6210_mbox_ring_desc, sync));
+       /* advance next ptr */
+       iowrite32(r->head = next_head, wil->csr + HOST_MBOX +
+                 offsetof(struct wil6210_mbox_ctl, tx.head));
+
+       /* interrupt to FW */
+       iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT);
+
+       return 0;
+}
+
+int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
+{
+       int rc;
+
+       mutex_lock(&wil->wmi_mutex);
+       rc = __wmi_send(wil, cmdid, buf, len);
+       mutex_unlock(&wil->wmi_mutex);
+
+       return rc;
+}
+
+/*=== Event handlers ===*/
+static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len)
+{
+       struct net_device *ndev = wil_to_ndev(wil);
+       struct wireless_dev *wdev = wil->wdev;
+       struct wmi_ready_event *evt = d;
+       u32 ver = le32_to_cpu(evt->sw_version);
+
+       wil_dbg_WMI(wil, "FW ver. %d; MAC %pM\n", ver, evt->mac);
+
+       if (!is_valid_ether_addr(ndev->dev_addr)) {
+               memcpy(ndev->dev_addr, evt->mac, ETH_ALEN);
+               memcpy(ndev->perm_addr, evt->mac, ETH_ALEN);
+       }
+       snprintf(wdev->wiphy->fw_version, sizeof(wdev->wiphy->fw_version),
+                "%d", ver);
+}
+
+static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d,
+                            int len)
+{
+       wil_dbg_WMI(wil, "WMI: FW ready\n");
+
+       set_bit(wil_status_fwready, &wil->status);
+       /* reuse wmi_ready for the firmware ready indication */
+       complete(&wil->wmi_ready);
+}
+
+static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
+{
+       struct wmi_rx_mgmt_packet_event *data = d;
+       struct wiphy *wiphy = wil_to_wiphy(wil);
+       struct ieee80211_mgmt *rx_mgmt_frame =
+                       (struct ieee80211_mgmt *)data->payload;
+       int ch_no = data->info.channel+1;
+       u32 freq = ieee80211_channel_to_frequency(ch_no,
+                       IEEE80211_BAND_60GHZ);
+       struct ieee80211_channel *channel = ieee80211_get_channel(wiphy, freq);
+       /* TODO convert LE to CPU */
+       s32 signal = 0; /* TODO */
+       __le16 fc = rx_mgmt_frame->frame_control;
+       u32 d_len = le32_to_cpu(data->info.len);
+       u16 d_status = le16_to_cpu(data->info.status);
+
+       wil_dbg_WMI(wil, "MGMT: channel %d MCS %d SNR %d\n",
+                   data->info.channel, data->info.mcs, data->info.snr);
+       wil_dbg_WMI(wil, "status 0x%04x len %d stype %04x\n", d_status, d_len,
+                   le16_to_cpu(data->info.stype));
+       wil_dbg_WMI(wil, "qid %d mid %d cid %d\n",
+                   data->info.qid, data->info.mid, data->info.cid);
+
+       if (!channel) {
+               wil_err(wil, "Frame on unsupported channel\n");
+               return;
+       }
+
+       if (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc)) {
+               struct cfg80211_bss *bss;
+               u64 tsf = le64_to_cpu(rx_mgmt_frame->u.beacon.timestamp);
+               u16 cap = le16_to_cpu(rx_mgmt_frame->u.beacon.capab_info);
+               u16 bi = le16_to_cpu(rx_mgmt_frame->u.beacon.beacon_int);
+               const u8 *ie_buf = rx_mgmt_frame->u.beacon.variable;
+               size_t ie_len = d_len - offsetof(struct ieee80211_mgmt,
+                                                u.beacon.variable);
+               wil_dbg_WMI(wil, "Capability info : 0x%04x\n", cap);
+
+               bss = cfg80211_inform_bss(wiphy, channel, rx_mgmt_frame->bssid,
+                                         tsf, cap, bi, ie_buf, ie_len,
+                                         signal, GFP_KERNEL);
+               if (bss) {
+                       wil_dbg_WMI(wil, "Added BSS %pM\n",
+                                   rx_mgmt_frame->bssid);
+                       cfg80211_put_bss(bss);
+               } else {
+                       wil_err(wil, "cfg80211_inform_bss() failed\n");
+               }
+       }
+}
+
+static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id,
+                                 void *d, int len)
+{
+       if (wil->scan_request) {
+               struct wmi_scan_complete_event *data = d;
+               bool aborted = (data->status != 0);
+
+               wil_dbg_WMI(wil, "SCAN_COMPLETE(0x%08x)\n", data->status);
+               cfg80211_scan_done(wil->scan_request, aborted);
+               wil->scan_request = NULL;
+       } else {
+               wil_err(wil, "SCAN_COMPLETE while not scanning\n");
+       }
+}
+
+static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
+{
+       struct net_device *ndev = wil_to_ndev(wil);
+       struct wireless_dev *wdev = wil->wdev;
+       struct wmi_connect_event *evt = d;
+       int ch; /* channel number */
+       struct station_info sinfo;
+       u8 *assoc_req_ie, *assoc_resp_ie;
+       size_t assoc_req_ielen, assoc_resp_ielen;
+       /* capinfo(u16) + listen_interval(u16) + IEs */
+       const size_t assoc_req_ie_offset = sizeof(u16) * 2;
+       /* capinfo(u16) + status_code(u16) + associd(u16) + IEs */
+       const size_t assoc_resp_ie_offset = sizeof(u16) * 3;
+
+       if (len < sizeof(*evt)) {
+               wil_err(wil, "Connect event too short : %d bytes\n", len);
+               return;
+       }
+       if (len != sizeof(*evt) + evt->beacon_ie_len + evt->assoc_req_len +
+                  evt->assoc_resp_len) {
+               wil_err(wil,
+                       "Connect event corrupted : %d != %d + %d + %d + %d\n",
+                       len, (int)sizeof(*evt), evt->beacon_ie_len,
+                       evt->assoc_req_len, evt->assoc_resp_len);
+               return;
+       }
+       ch = evt->channel + 1;
+       wil_dbg_WMI(wil, "Connect %pM channel [%d] cid %d\n",
+                   evt->bssid, ch, evt->cid);
+       wil_hex_dump_WMI("connect AI : ", DUMP_PREFIX_OFFSET, 16, 1,
+                        evt->assoc_info, len - sizeof(*evt), true);
+
+       /* figure out IE's */
+       assoc_req_ie = &evt->assoc_info[evt->beacon_ie_len +
+                                       assoc_req_ie_offset];
+       assoc_req_ielen = evt->assoc_req_len - assoc_req_ie_offset;
+       if (evt->assoc_req_len <= assoc_req_ie_offset) {
+               assoc_req_ie = NULL;
+               assoc_req_ielen = 0;
+       }
+
+       assoc_resp_ie = &evt->assoc_info[evt->beacon_ie_len +
+                                        evt->assoc_req_len +
+                                        assoc_resp_ie_offset];
+       assoc_resp_ielen = evt->assoc_resp_len - assoc_resp_ie_offset;
+       if (evt->assoc_resp_len <= assoc_resp_ie_offset) {
+               assoc_resp_ie = NULL;
+               assoc_resp_ielen = 0;
+       }
+
+       if ((wdev->iftype == NL80211_IFTYPE_STATION) ||
+           (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
+               if (wdev->sme_state != CFG80211_SME_CONNECTING) {
+                       wil_err(wil, "Not in connecting state\n");
+                       return;
+               }
+               del_timer_sync(&wil->connect_timer);
+               cfg80211_connect_result(ndev, evt->bssid,
+                                       assoc_req_ie, assoc_req_ielen,
+                                       assoc_resp_ie, assoc_resp_ielen,
+                                       WLAN_STATUS_SUCCESS, GFP_KERNEL);
+
+       } else if ((wdev->iftype == NL80211_IFTYPE_AP) ||
+                  (wdev->iftype == NL80211_IFTYPE_P2P_GO)) {
+               memset(&sinfo, 0, sizeof(sinfo));
+
+               sinfo.generation = wil->sinfo_gen++;
+
+               if (assoc_req_ie) {
+                       sinfo.assoc_req_ies = assoc_req_ie;
+                       sinfo.assoc_req_ies_len = assoc_req_ielen;
+                       sinfo.filled |= STATION_INFO_ASSOC_REQ_IES;
+               }
+
+               cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL);
+       }
+       set_bit(wil_status_fwconnected, &wil->status);
+
+       /* FIXME FW can transmit only ucast frames to peer */
+       /* FIXME real ring_id instead of hard coded 0 */
+       memcpy(wil->dst_addr[0], evt->bssid, ETH_ALEN);
+
+       wil->pending_connect_cid = evt->cid;
+       queue_work(wil->wmi_wq_conn, &wil->wmi_connect_worker);
+}
+
+static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
+                              void *d, int len)
+{
+       struct wmi_disconnect_event *evt = d;
+
+       wil_dbg_WMI(wil, "Disconnect %pM reason %d proto %d wmi\n",
+                   evt->bssid,
+                   evt->protocol_reason_status, evt->disconnect_reason);
+
+       wil->sinfo_gen++;
+
+       wil6210_disconnect(wil, evt->bssid);
+       clear_bit(wil_status_dontscan, &wil->status);
+}
+
+static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len)
+{
+       struct wmi_notify_req_done_event *evt = d;
+
+       if (len < sizeof(*evt)) {
+               wil_err(wil, "Short NOTIFY event\n");
+               return;
+       }
+
+       wil->stats.tsf = le64_to_cpu(evt->tsf);
+       wil->stats.snr = le32_to_cpu(evt->snr_val);
+       wil->stats.bf_mcs = le16_to_cpu(evt->bf_mcs);
+       wil->stats.my_rx_sector = le16_to_cpu(evt->my_rx_sector);
+       wil->stats.my_tx_sector = le16_to_cpu(evt->my_tx_sector);
+       wil->stats.peer_rx_sector = le16_to_cpu(evt->other_rx_sector);
+       wil->stats.peer_tx_sector = le16_to_cpu(evt->other_tx_sector);
+       wil_dbg_WMI(wil, "Link status, MCS %d TSF 0x%016llx\n"
+                   "BF status 0x%08x SNR 0x%08x\n"
+                   "Tx Tpt %d goodput %d Rx goodput %d\n"
+                   "Sectors(rx:tx) my %d:%d peer %d:%d\n",
+                   wil->stats.bf_mcs, wil->stats.tsf, evt->status,
+                   wil->stats.snr, le32_to_cpu(evt->tx_tpt),
+                   le32_to_cpu(evt->tx_goodput), le32_to_cpu(evt->rx_goodput),
+                   wil->stats.my_rx_sector, wil->stats.my_tx_sector,
+                   wil->stats.peer_rx_sector, wil->stats.peer_tx_sector);
+}
+
+/*
+ * Firmware reports EAPOL frame using WME event.
+ * Reconstruct Ethernet frame and deliver it via normal Rx
+ */
+static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id,
+                            void *d, int len)
+{
+       struct net_device *ndev = wil_to_ndev(wil);
+       struct wmi_eapol_rx_event *evt = d;
+       u16 eapol_len = le16_to_cpu(evt->eapol_len);
+       int sz = eapol_len + ETH_HLEN;
+       struct sk_buff *skb;
+       struct ethhdr *eth;
+
+       wil_dbg_WMI(wil, "EAPOL len %d from %pM\n", eapol_len,
+                   evt->src_mac);
+
+       if (eapol_len > 196) { /* TODO: revisit size limit */
+               wil_err(wil, "EAPOL too large\n");
+               return;
+       }
+
+       skb = alloc_skb(sz, GFP_KERNEL);
+       if (!skb) {
+               wil_err(wil, "Failed to allocate skb\n");
+               return;
+       }
+       eth = (struct ethhdr *)skb_put(skb, ETH_HLEN);
+       memcpy(eth->h_dest, ndev->dev_addr, ETH_ALEN);
+       memcpy(eth->h_source, evt->src_mac, ETH_ALEN);
+       eth->h_proto = cpu_to_be16(ETH_P_PAE);
+       memcpy(skb_put(skb, eapol_len), evt->eapol, eapol_len);
+       skb->protocol = eth_type_trans(skb, ndev);
+       if (likely(netif_rx_ni(skb) == NET_RX_SUCCESS)) {
+               ndev->stats.rx_packets++;
+               ndev->stats.rx_bytes += skb->len;
+       } else {
+               ndev->stats.rx_dropped++;
+       }
+}
+
+static const struct {
+       int eventid;
+       void (*handler)(struct wil6210_priv *wil, int eventid,
+                       void *data, int data_len);
+} wmi_evt_handlers[] = {
+       {WMI_READY_EVENTID,             wmi_evt_ready},
+       {WMI_FW_READY_EVENTID,          wmi_evt_fw_ready},
+       {WMI_RX_MGMT_PACKET_EVENTID,    wmi_evt_rx_mgmt},
+       {WMI_SCAN_COMPLETE_EVENTID,     wmi_evt_scan_complete},
+       {WMI_CONNECT_EVENTID,           wmi_evt_connect},
+       {WMI_DISCONNECT_EVENTID,        wmi_evt_disconnect},
+       {WMI_NOTIFY_REQ_DONE_EVENTID,   wmi_evt_notify},
+       {WMI_EAPOL_RX_EVENTID,          wmi_evt_eapol_rx},
+};
+
+/*
+ * Run in IRQ context
+ * Extract WMI command from mailbox. Queue it to the @wil->pending_wmi_ev
+ * that will be eventually handled by the @wmi_event_worker in the thread
+ * context of thread "wil6210_wmi"
+ */
+void wmi_recv_cmd(struct wil6210_priv *wil)
+{
+       struct wil6210_mbox_ring_desc d_tail;
+       struct wil6210_mbox_hdr hdr;
+       struct wil6210_mbox_ring *r = &wil->mbox_ctl.rx;
+       struct pending_wmi_event *evt;
+       u8 *cmd;
+       void __iomem *src;
+       ulong flags;
+
+       for (;;) {
+               u16 len;
+
+               r->head = ioread32(wil->csr + HOST_MBOX +
+                                  offsetof(struct wil6210_mbox_ctl, rx.head));
+               if (r->tail == r->head)
+                       return;
+
+               /* read cmd from tail */
+               wil_memcpy_fromio_32(&d_tail, wil->csr + HOSTADDR(r->tail),
+                                    sizeof(struct wil6210_mbox_ring_desc));
+               if (d_tail.sync == 0) {
+                       wil_err(wil, "Mbox evt not owned by FW?\n");
+                       return;
+               }
+
+               if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) {
+                       wil_err(wil, "Mbox evt at 0x%08x?\n",
+                               le32_to_cpu(d_tail.addr));
+                       return;
+               }
+
+               len = le16_to_cpu(hdr.len);
+               src = wmi_buffer(wil, d_tail.addr) +
+                     sizeof(struct wil6210_mbox_hdr);
+               evt = kmalloc(ALIGN(offsetof(struct pending_wmi_event,
+                                            event.wmi) + len, 4),
+                             GFP_KERNEL);
+               if (!evt) {
+                       wil_err(wil, "kmalloc for WMI event (%d) failed\n",
+                               len);
+                       return;
+               }
+               evt->event.hdr = hdr;
+               cmd = (void *)&evt->event.wmi;
+               wil_memcpy_fromio_32(cmd, src, len);
+               /* mark entry as empty */
+               iowrite32(0, wil->csr + HOSTADDR(r->tail) +
+                         offsetof(struct wil6210_mbox_ring_desc, sync));
+               /* indicate */
+               wil_dbg_WMI(wil, "Mbox evt %04x %04x %04x %02x\n",
+                           le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type),
+                           hdr.flags);
+               if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) &&
+                   (len >= sizeof(struct wil6210_mbox_hdr_wmi))) {
+                       wil_dbg_WMI(wil, "WMI event 0x%04x\n",
+                                   evt->event.wmi.id);
+               }
+               wil_hex_dump_WMI("evt ", DUMP_PREFIX_OFFSET, 16, 1,
+                                &evt->event.hdr, sizeof(hdr) + len, true);
+
+               /* advance tail */
+               r->tail = r->base + ((r->tail - r->base +
+                         sizeof(struct wil6210_mbox_ring_desc)) % r->size);
+               iowrite32(r->tail, wil->csr + HOST_MBOX +
+                         offsetof(struct wil6210_mbox_ctl, rx.tail));
+
+               /* add to the pending list */
+               spin_lock_irqsave(&wil->wmi_ev_lock, flags);
+               list_add_tail(&evt->list, &wil->pending_wmi_ev);
+               spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
+               {
+                       int q = queue_work(wil->wmi_wq,
+                                          &wil->wmi_event_worker);
+                       wil_dbg_WMI(wil, "queue_work -> %d\n", q);
+               }
+       }
+}
+
+int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
+            u16 reply_id, void *reply, u8 reply_size, int to_msec)
+{
+       int rc;
+       int remain;
+
+       mutex_lock(&wil->wmi_mutex);
+
+       rc = __wmi_send(wil, cmdid, buf, len);
+       if (rc)
+               goto out;
+
+       wil->reply_id = reply_id;
+       wil->reply_buf = reply;
+       wil->reply_size = reply_size;
+       remain = wait_for_completion_timeout(&wil->wmi_ready,
+                       msecs_to_jiffies(to_msec));
+       if (0 == remain) {
+               wil_err(wil, "wmi_call(0x%04x->0x%04x) timeout %d msec\n",
+                       cmdid, reply_id, to_msec);
+               rc = -ETIME;
+       } else {
+               wil_dbg_WMI(wil,
+                           "wmi_call(0x%04x->0x%04x) completed in %d msec\n",
+                           cmdid, reply_id,
+                           to_msec - jiffies_to_msecs(remain));
+       }
+       wil->reply_id = 0;
+       wil->reply_buf = NULL;
+       wil->reply_size = 0;
+ out:
+       mutex_unlock(&wil->wmi_mutex);
+
+       return rc;
+}
+
+int wmi_echo(struct wil6210_priv *wil)
+{
+       struct wmi_echo_cmd cmd = {
+               .value = cpu_to_le32(0x12345678),
+       };
+
+       return wmi_call(wil, WMI_ECHO_CMDID, &cmd, sizeof(cmd),
+                        WMI_ECHO_RSP_EVENTID, NULL, 0, 20);
+}
+
+int wmi_set_mac_address(struct wil6210_priv *wil, void *addr)
+{
+       struct wmi_set_mac_address_cmd cmd;
+
+       memcpy(cmd.mac, addr, ETH_ALEN);
+
+       wil_dbg_WMI(wil, "Set MAC %pM\n", addr);
+
+       return wmi_send(wil, WMI_SET_MAC_ADDRESS_CMDID, &cmd, sizeof(cmd));
+}
+
+int wmi_set_bcon(struct wil6210_priv *wil, int bi, u8 wmi_nettype)
+{
+       struct wmi_bcon_ctrl_cmd cmd = {
+               .bcon_interval = cpu_to_le16(bi),
+               .network_type = wmi_nettype,
+               .disable_sec_offload = 1,
+       };
+
+       if (!wil->secure_pcp)
+               cmd.disable_sec = 1;
+
+       return wmi_send(wil, WMI_BCON_CTRL_CMDID, &cmd, sizeof(cmd));
+}
+
+int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid)
+{
+       struct wmi_set_ssid_cmd cmd = {
+               .ssid_len = cpu_to_le32(ssid_len),
+       };
+
+       if (ssid_len > sizeof(cmd.ssid))
+               return -EINVAL;
+
+       memcpy(cmd.ssid, ssid, ssid_len);
+
+       return wmi_send(wil, WMI_SET_SSID_CMDID, &cmd, sizeof(cmd));
+}
+
+int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid)
+{
+       int rc;
+       struct {
+               struct wil6210_mbox_hdr_wmi wmi;
+               struct wmi_set_ssid_cmd cmd;
+       } __packed reply;
+       int len; /* reply.cmd.ssid_len in CPU order */
+
+       rc = wmi_call(wil, WMI_GET_SSID_CMDID, NULL, 0, WMI_GET_SSID_EVENTID,
+                     &reply, sizeof(reply), 20);
+       if (rc)
+               return rc;
+
+       len = le32_to_cpu(reply.cmd.ssid_len);
+       if (len > sizeof(reply.cmd.ssid))
+               return -EINVAL;
+
+       *ssid_len = len;
+       memcpy(ssid, reply.cmd.ssid, len);
+
+       return 0;
+}
+
+int wmi_set_channel(struct wil6210_priv *wil, int channel)
+{
+       struct wmi_set_pcp_channel_cmd cmd = {
+               .channel = channel - 1,
+       };
+
+       return wmi_send(wil, WMI_SET_PCP_CHANNEL_CMDID, &cmd, sizeof(cmd));
+}
+
+int wmi_get_channel(struct wil6210_priv *wil, int *channel)
+{
+       int rc;
+       struct {
+               struct wil6210_mbox_hdr_wmi wmi;
+               struct wmi_set_pcp_channel_cmd cmd;
+       } __packed reply;
+
+       rc = wmi_call(wil, WMI_GET_PCP_CHANNEL_CMDID, NULL, 0,
+                     WMI_GET_PCP_CHANNEL_EVENTID, &reply, sizeof(reply), 20);
+       if (rc)
+               return rc;
+
+       if (reply.cmd.channel > 3)
+               return -EINVAL;
+
+       *channel = reply.cmd.channel + 1;
+
+       return 0;
+}
+
+int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb)
+{
+       struct wmi_eapol_tx_cmd *cmd;
+       struct ethhdr *eth;
+       u16 eapol_len = skb->len - ETH_HLEN;
+       void *eapol = skb->data + ETH_HLEN;
+       uint i;
+       int rc;
+
+       skb_set_mac_header(skb, 0);
+       eth = eth_hdr(skb);
+       wil_dbg_WMI(wil, "EAPOL %d bytes to %pM\n", eapol_len, eth->h_dest);
+       for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
+               if (memcmp(wil->dst_addr[i], eth->h_dest, ETH_ALEN) == 0)
+                       goto found_dest;
+       }
+
+       return -EINVAL;
+
+ found_dest:
+       /* find out eapol data & len */
+       cmd = kzalloc(sizeof(*cmd) + eapol_len, GFP_KERNEL);
+       if (!cmd)
+               return -EINVAL;
+
+       memcpy(cmd->dst_mac, eth->h_dest, ETH_ALEN);
+       cmd->eapol_len = cpu_to_le16(eapol_len);
+       memcpy(cmd->eapol, eapol, eapol_len);
+       rc = wmi_send(wil, WMI_EAPOL_TX_CMDID, cmd, sizeof(*cmd) + eapol_len);
+       kfree(cmd);
+
+       return rc;
+}
+
+int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index,
+                      const void *mac_addr)
+{
+       struct wmi_delete_cipher_key_cmd cmd = {
+               .key_index = key_index,
+       };
+
+       if (mac_addr)
+               memcpy(cmd.mac, mac_addr, WMI_MAC_LEN);
+
+       return wmi_send(wil, WMI_DELETE_CIPHER_KEY_CMDID, &cmd, sizeof(cmd));
+}
+
+int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index,
+                      const void *mac_addr, int key_len, const void *key)
+{
+       struct wmi_add_cipher_key_cmd cmd = {
+               .key_index = key_index,
+               .key_usage = WMI_KEY_USE_PAIRWISE,
+               .key_len = key_len,
+       };
+
+       if (!key || (key_len > sizeof(cmd.key)))
+               return -EINVAL;
+
+       memcpy(cmd.key, key, key_len);
+       if (mac_addr)
+               memcpy(cmd.mac, mac_addr, WMI_MAC_LEN);
+
+       return wmi_send(wil, WMI_ADD_CIPHER_KEY_CMDID, &cmd, sizeof(cmd));
+}
+
+int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie)
+{
+       int rc;
+       u16 len = sizeof(struct wmi_set_appie_cmd) + ie_len;
+       struct wmi_set_appie_cmd *cmd = kzalloc(len, GFP_KERNEL);
+       if (!cmd) {
+               wil_err(wil, "kmalloc(%d) failed\n", len);
+               return -ENOMEM;
+       }
+
+       cmd->mgmt_frm_type = type;
+       /* BUG: FW API define ieLen as u8. Will fix FW */
+       cmd->ie_len = cpu_to_le16(ie_len);
+       memcpy(cmd->ie_info, ie, ie_len);
+       rc = wmi_send(wil, WMI_SET_APPIE_CMDID, &cmd, len);
+       kfree(cmd);
+
+       return rc;
+}
+
+void wmi_event_flush(struct wil6210_priv *wil)
+{
+       struct pending_wmi_event *evt, *t;
+
+       wil_dbg_WMI(wil, "%s()\n", __func__);
+
+       list_for_each_entry_safe(evt, t, &wil->pending_wmi_ev, list) {
+               list_del(&evt->list);
+               kfree(evt);
+       }
+}
+
+static bool wmi_evt_call_handler(struct wil6210_priv *wil, int id,
+                                void *d, int len)
+{
+       uint i;
+
+       for (i = 0; i < ARRAY_SIZE(wmi_evt_handlers); i++) {
+               if (wmi_evt_handlers[i].eventid == id) {
+                       wmi_evt_handlers[i].handler(wil, id, d, len);
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static void wmi_event_handle(struct wil6210_priv *wil,
+                            struct wil6210_mbox_hdr *hdr)
+{
+       u16 len = le16_to_cpu(hdr->len);
+
+       if ((hdr->type == WIL_MBOX_HDR_TYPE_WMI) &&
+           (len >= sizeof(struct wil6210_mbox_hdr_wmi))) {
+               struct wil6210_mbox_hdr_wmi *wmi = (void *)(&hdr[1]);
+               void *evt_data = (void *)(&wmi[1]);
+               u16 id = le16_to_cpu(wmi->id);
+               /* check if someone waits for this event */
+               if (wil->reply_id && wil->reply_id == id) {
+                       if (wil->reply_buf) {
+                               memcpy(wil->reply_buf, wmi,
+                                      min(len, wil->reply_size));
+                       } else {
+                               wmi_evt_call_handler(wil, id, evt_data,
+                                                    len - sizeof(*wmi));
+                       }
+                       wil_dbg_WMI(wil, "Complete WMI 0x%04x\n", id);
+                       complete(&wil->wmi_ready);
+                       return;
+               }
+               /* unsolicited event */
+               /* search for handler */
+               if (!wmi_evt_call_handler(wil, id, evt_data,
+                                         len - sizeof(*wmi))) {
+                       wil_err(wil, "Unhandled event 0x%04x\n", id);
+               }
+       } else {
+               wil_err(wil, "Unknown event type\n");
+               print_hex_dump(KERN_ERR, "evt?? ", DUMP_PREFIX_OFFSET, 16, 1,
+                              hdr, sizeof(*hdr) + len, true);
+       }
+}
+
+/*
+ * Retrieve next WMI event from the pending list
+ */
+static struct list_head *next_wmi_ev(struct wil6210_priv *wil)
+{
+       ulong flags;
+       struct list_head *ret = NULL;
+
+       spin_lock_irqsave(&wil->wmi_ev_lock, flags);
+
+       if (!list_empty(&wil->pending_wmi_ev)) {
+               ret = wil->pending_wmi_ev.next;
+               list_del(ret);
+       }
+
+       spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
+
+       return ret;
+}
+
+/*
+ * Handler for the WMI events
+ */
+void wmi_event_worker(struct work_struct *work)
+{
+       struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
+                                                wmi_event_worker);
+       struct pending_wmi_event *evt;
+       struct list_head *lh;
+
+       while ((lh = next_wmi_ev(wil)) != NULL) {
+               evt = list_entry(lh, struct pending_wmi_event, list);
+               wmi_event_handle(wil, &evt->event.hdr);
+               kfree(evt);
+       }
+}
+
+void wmi_connect_worker(struct work_struct *work)
+{
+       int rc;
+       struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
+                                               wmi_connect_worker);
+
+       if (wil->pending_connect_cid < 0) {
+               wil_err(wil, "No connection pending\n");
+               return;
+       }
+
+       wil_dbg_WMI(wil, "Configure for connection CID %d\n",
+                   wil->pending_connect_cid);
+
+       rc = wil_vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE,
+                              wil->pending_connect_cid, 0);
+       wil->pending_connect_cid = -1;
+       if (rc == 0)
+               wil_link_on(wil);
+}
diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h
new file mode 100644 (file)
index 0000000..3bbf875
--- /dev/null
@@ -0,0 +1,1116 @@
+/*
+ * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ * Copyright (c) 2006-2012 Wilocity .
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * This file contains the definitions of the WMI protocol specified in the
+ * Wireless Module Interface (WMI) for the Wilocity
+ * MARLON 60 Gigabit wireless solution.
+ * It includes definitions of all the commands and events.
+ * Commands are messages from the host to the WM.
+ * Events are messages from the WM to the host.
+ */
+
+#ifndef __WILOCITY_WMI_H__
+#define __WILOCITY_WMI_H__
+
+/* General */
+
+#define WMI_MAC_LEN            (6)
+#define WMI_PROX_RANGE_NUM     (3)
+
+/* List of Commands */
+enum wmi_command_id {
+       WMI_CONNECT_CMDID               = 0x0001,
+       WMI_DISCONNECT_CMDID            = 0x0003,
+       WMI_START_SCAN_CMDID            = 0x0007,
+       WMI_SET_BSS_FILTER_CMDID        = 0x0009,
+       WMI_SET_PROBED_SSID_CMDID       = 0x000a,
+       WMI_SET_LISTEN_INT_CMDID        = 0x000b,
+       WMI_BCON_CTRL_CMDID             = 0x000f,
+       WMI_ADD_CIPHER_KEY_CMDID        = 0x0016,
+       WMI_DELETE_CIPHER_KEY_CMDID     = 0x0017,
+       WMI_SET_APPIE_CMDID             = 0x003f,
+       WMI_GET_APPIE_CMDID             = 0x0040,
+       WMI_SET_WSC_STATUS_CMDID        = 0x0041,
+       WMI_PXMT_RANGE_CFG_CMDID        = 0x0042,
+       WMI_PXMT_SNR2_RANGE_CFG_CMDID   = 0x0043,
+       WMI_FAST_MEM_ACC_MODE_CMDID     = 0x0300,
+       WMI_MEM_READ_CMDID              = 0x0800,
+       WMI_MEM_WR_CMDID                = 0x0801,
+       WMI_ECHO_CMDID                  = 0x0803,
+       WMI_DEEP_ECHO_CMDID             = 0x0804,
+       WMI_CONFIG_MAC_CMDID            = 0x0805,
+       WMI_CONFIG_PHY_DEBUG_CMDID      = 0x0806,
+       WMI_ADD_STATION_CMDID           = 0x0807,
+       WMI_ADD_DEBUG_TX_PCKT_CMDID     = 0x0808,
+       WMI_PHY_GET_STATISTICS_CMDID    = 0x0809,
+       WMI_FS_TUNE_CMDID               = 0x080a,
+       WMI_CORR_MEASURE_CMDID          = 0x080b,
+       WMI_TEMP_SENSE_CMDID            = 0x080e,
+       WMI_DC_CALIB_CMDID              = 0x080f,
+       WMI_SEND_TONE_CMDID             = 0x0810,
+       WMI_IQ_TX_CALIB_CMDID           = 0x0811,
+       WMI_IQ_RX_CALIB_CMDID           = 0x0812,
+       WMI_SET_UCODE_IDLE_CMDID        = 0x0813,
+       WMI_SET_WORK_MODE_CMDID         = 0x0815,
+       WMI_LO_LEAKAGE_CALIB_CMDID      = 0x0816,
+       WMI_MARLON_R_ACTIVATE_CMDID     = 0x0817,
+       WMI_MARLON_R_READ_CMDID         = 0x0818,
+       WMI_MARLON_R_WRITE_CMDID        = 0x0819,
+       WMI_MARLON_R_TXRX_SEL_CMDID     = 0x081a,
+       MAC_IO_STATIC_PARAMS_CMDID      = 0x081b,
+       MAC_IO_DYNAMIC_PARAMS_CMDID     = 0x081c,
+       WMI_SILENT_RSSI_CALIB_CMDID     = 0x081d,
+       WMI_CFG_RX_CHAIN_CMDID          = 0x0820,
+       WMI_VRING_CFG_CMDID             = 0x0821,
+       WMI_RX_ON_CMDID                 = 0x0822,
+       WMI_VRING_BA_EN_CMDID           = 0x0823,
+       WMI_VRING_BA_DIS_CMDID          = 0x0824,
+       WMI_RCP_ADDBA_RESP_CMDID        = 0x0825,
+       WMI_RCP_DELBA_CMDID             = 0x0826,
+       WMI_SET_SSID_CMDID              = 0x0827,
+       WMI_GET_SSID_CMDID              = 0x0828,
+       WMI_SET_PCP_CHANNEL_CMDID       = 0x0829,
+       WMI_GET_PCP_CHANNEL_CMDID       = 0x082a,
+       WMI_SW_TX_REQ_CMDID             = 0x082b,
+       WMI_RX_OFF_CMDID                = 0x082c,
+       WMI_READ_MAC_RXQ_CMDID          = 0x0830,
+       WMI_READ_MAC_TXQ_CMDID          = 0x0831,
+       WMI_WRITE_MAC_RXQ_CMDID         = 0x0832,
+       WMI_WRITE_MAC_TXQ_CMDID         = 0x0833,
+       WMI_WRITE_MAC_XQ_FIELD_CMDID    = 0x0834,
+       WMI_MLME_PUSH_CMDID             = 0x0835,
+       WMI_BEAMFORMING_MGMT_CMDID      = 0x0836,
+       WMI_BF_TXSS_MGMT_CMDID          = 0x0837,
+       WMI_BF_SM_MGMT_CMDID            = 0x0838,
+       WMI_BF_RXSS_MGMT_CMDID          = 0x0839,
+       WMI_SET_SECTORS_CMDID           = 0x0849,
+       WMI_MAINTAIN_PAUSE_CMDID        = 0x0850,
+       WMI_MAINTAIN_RESUME_CMDID       = 0x0851,
+       WMI_RS_MGMT_CMDID               = 0x0852,
+       WMI_RF_MGMT_CMDID               = 0x0853,
+       /* Performance monitoring commands */
+       WMI_BF_CTRL_CMDID               = 0x0862,
+       WMI_NOTIFY_REQ_CMDID            = 0x0863,
+       WMI_GET_STATUS_CMDID            = 0x0864,
+       WMI_UNIT_TEST_CMDID             = 0x0900,
+       WMI_HICCUP_CMDID                = 0x0901,
+       WMI_FLASH_READ_CMDID            = 0x0902,
+       WMI_FLASH_WRITE_CMDID           = 0x0903,
+       WMI_SECURITY_UNIT_TEST_CMDID    = 0x0904,
+
+       WMI_SET_MAC_ADDRESS_CMDID       = 0xf003,
+       WMI_ABORT_SCAN_CMDID            = 0xf007,
+       WMI_SET_PMK_CMDID               = 0xf028,
+
+       WMI_SET_PROMISCUOUS_MODE_CMDID  = 0xf041,
+       WMI_GET_PMK_CMDID               = 0xf048,
+       WMI_SET_PASSPHRASE_CMDID        = 0xf049,
+       WMI_SEND_ASSOC_RES_CMDID        = 0xf04a,
+       WMI_SET_ASSOC_REQ_RELAY_CMDID   = 0xf04b,
+       WMI_EAPOL_TX_CMDID              = 0xf04c,
+       WMI_MAC_ADDR_REQ_CMDID          = 0xf04d,
+       WMI_FW_VER_CMDID                = 0xf04e,
+};
+
+/*
+ * Commands data structures
+ */
+
+/*
+ * Frame Types
+ */
+enum wmi_mgmt_frame_type {
+       WMI_FRAME_BEACON        = 0,
+       WMI_FRAME_PROBE_REQ     = 1,
+       WMI_FRAME_PROBE_RESP    = 2,
+       WMI_FRAME_ASSOC_REQ     = 3,
+       WMI_FRAME_ASSOC_RESP    = 4,
+       WMI_NUM_MGMT_FRAME,
+};
+
+/*
+ * WMI_CONNECT_CMDID
+ */
+enum wmi_network_type {
+       WMI_NETTYPE_INFRA               = 0x01,
+       WMI_NETTYPE_ADHOC               = 0x02,
+       WMI_NETTYPE_ADHOC_CREATOR       = 0x04,
+       WMI_NETTYPE_AP                  = 0x10,
+       WMI_NETTYPE_P2P                 = 0x20,
+       WMI_NETTYPE_WBE                 = 0x40, /* PCIE over 60g */
+};
+
+enum wmi_dot11_auth_mode {
+       WMI_AUTH11_OPEN                 = 0x01,
+       WMI_AUTH11_SHARED               = 0x02,
+       WMI_AUTH11_LEAP                 = 0x04,
+       WMI_AUTH11_WSC                  = 0x08,
+};
+
+enum wmi_auth_mode {
+       WMI_AUTH_NONE                   = 0x01,
+       WMI_AUTH_WPA                    = 0x02,
+       WMI_AUTH_WPA2                   = 0x04,
+       WMI_AUTH_WPA_PSK                = 0x08,
+       WMI_AUTH_WPA2_PSK               = 0x10,
+       WMI_AUTH_WPA_CCKM               = 0x20,
+       WMI_AUTH_WPA2_CCKM              = 0x40,
+};
+
+enum wmi_crypto_type {
+       WMI_CRYPT_NONE                  = 0x01,
+       WMI_CRYPT_WEP                   = 0x02,
+       WMI_CRYPT_TKIP                  = 0x04,
+       WMI_CRYPT_AES                   = 0x08,
+       WMI_CRYPT_AES_GCMP              = 0x20,
+};
+
+
+enum wmi_connect_ctrl_flag_bits {
+       WMI_CONNECT_ASSOC_POLICY_USER           = 0x0001,
+       WMI_CONNECT_SEND_REASSOC                = 0x0002,
+       WMI_CONNECT_IGNORE_WPAx_GROUP_CIPHER    = 0x0004,
+       WMI_CONNECT_PROFILE_MATCH_DONE          = 0x0008,
+       WMI_CONNECT_IGNORE_AAC_BEACON           = 0x0010,
+       WMI_CONNECT_CSA_FOLLOW_BSS              = 0x0020,
+       WMI_CONNECT_DO_WPA_OFFLOAD              = 0x0040,
+       WMI_CONNECT_DO_NOT_DEAUTH               = 0x0080,
+};
+
+#define WMI_MAX_SSID_LEN    (32)
+
+struct wmi_connect_cmd {
+       u8 network_type;
+       u8 dot11_auth_mode;
+       u8 auth_mode;
+       u8 pairwise_crypto_type;
+       u8 pairwise_crypto_len;
+       u8 group_crypto_type;
+       u8 group_crypto_len;
+       u8 ssid_len;
+       u8 ssid[WMI_MAX_SSID_LEN];
+       u8 channel;
+       u8 reserved0;
+       u8 bssid[WMI_MAC_LEN];
+       __le32 ctrl_flags;
+       u8 dst_mac[WMI_MAC_LEN];
+       u8 reserved1[2];
+} __packed;
+
+
+/*
+ * WMI_RECONNECT_CMDID
+ */
+struct wmi_reconnect_cmd {
+       u8 channel;                     /* hint */
+       u8 reserved;
+       u8 bssid[WMI_MAC_LEN];          /* mandatory if set */
+} __packed;
+
+
+/*
+ * WMI_SET_PMK_CMDID
+ */
+
+#define WMI_MIN_KEY_INDEX      (0)
+#define WMI_MAX_KEY_INDEX      (3)
+#define WMI_MAX_KEY_LEN                (32)
+#define WMI_PASSPHRASE_LEN     (64)
+#define WMI_PMK_LEN            (32)
+
+struct  wmi_set_pmk_cmd {
+       u8 pmk[WMI_PMK_LEN];
+} __packed;
+
+
+/*
+ * WMI_SET_PASSPHRASE_CMDID
+ */
+struct wmi_set_passphrase_cmd {
+       u8 ssid[WMI_MAX_SSID_LEN];
+       u8 passphrase[WMI_PASSPHRASE_LEN];
+       u8 ssid_len;
+       u8 passphrase_len;
+} __packed;
+
+/*
+ * WMI_ADD_CIPHER_KEY_CMDID
+ */
+enum wmi_key_usage {
+       WMI_KEY_USE_PAIRWISE    = 0,
+       WMI_KEY_USE_GROUP       = 1,
+       WMI_KEY_USE_TX          = 2,  /* default Tx Key - Static WEP only */
+};
+
+struct wmi_add_cipher_key_cmd {
+       u8 key_index;
+       u8 key_type;
+       u8 key_usage;           /* enum wmi_key_usage */
+       u8 key_len;
+       u8 key_rsc[8];          /* key replay sequence counter */
+       u8 key[WMI_MAX_KEY_LEN];
+       u8 key_op_ctrl;         /* Additional Key Control information */
+       u8 mac[WMI_MAC_LEN];
+} __packed;
+
+/*
+ * WMI_DELETE_CIPHER_KEY_CMDID
+ */
+struct wmi_delete_cipher_key_cmd {
+       u8 key_index;
+       u8 mac[WMI_MAC_LEN];
+} __packed;
+
+
+/*
+ * WMI_START_SCAN_CMDID
+ *
+ * Start L1 scan operation
+ *
+ * Returned events:
+ * - WMI_RX_MGMT_PACKET_EVENTID - for every probe resp.
+ * - WMI_SCAN_COMPLETE_EVENTID
+ */
+enum wmi_scan_type {
+       WMI_LONG_SCAN           = 0,
+       WMI_SHORT_SCAN          = 1,
+};
+
+struct wmi_start_scan_cmd {
+       u8 reserved[8];
+       __le32 home_dwell_time; /* Max duration in the home channel(ms) */
+       __le32 force_scan_interval;     /* Time interval between scans (ms)*/
+       u8 scan_type;           /* wmi_scan_type */
+       u8 num_channels;                /* how many channels follow */
+       struct {
+               u8 channel;
+               u8 reserved;
+       } channel_list[0];      /* channels ID's */
+                               /* 0 - 58320 MHz */
+                               /* 1 - 60480 MHz */
+                               /* 2 - 62640 MHz */
+} __packed;
+
+/*
+ * WMI_SET_PROBED_SSID_CMDID
+ */
+#define MAX_PROBED_SSID_INDEX   (15)
+
+enum wmi_ssid_flag {
+       WMI_SSID_FLAG_DISABLE   = 0,    /* disables entry */
+       WMI_SSID_FLAG_SPECIFIC  = 1,    /* probes specified ssid */
+       WMI_SSID_FLAG_ANY       = 2,    /* probes for any ssid */
+};
+
+struct wmi_probed_ssid_cmd {
+       u8 entry_index;                 /* 0 to MAX_PROBED_SSID_INDEX */
+       u8 flag;                        /* enum wmi_ssid_flag */
+       u8 ssid_len;
+       u8 ssid[WMI_MAX_SSID_LEN];
+} __packed;
+
+/*
+ * WMI_SET_APPIE_CMDID
+ * Add Application specified IE to a management frame
+ */
+struct wmi_set_appie_cmd {
+       u8 mgmt_frm_type;       /* enum wmi_mgmt_frame_type */
+       u8 reserved;
+       __le16 ie_len;  /* Length of the IE to be added to MGMT frame */
+       u8 ie_info[0];
+} __packed;
+
+#define WMI_MAX_IE_LEN (1024)
+
+struct wmi_pxmt_range_cfg_cmd {
+       u8 dst_mac[WMI_MAC_LEN];
+       __le16 range;
+} __packed;
+
+struct wmi_pxmt_snr2_range_cfg_cmd {
+       s8 snr2range_arr[WMI_PROX_RANGE_NUM-1];
+} __packed;
+
+/*
+ * WMI_RF_MGMT_CMDID
+ */
+enum wmi_rf_mgmt_type {
+       WMI_RF_MGMT_W_DISABLE   = 0,
+       WMI_RF_MGMT_W_ENABLE    = 1,
+       WMI_RF_MGMT_GET_STATUS  = 2,
+};
+
+struct wmi_rf_mgmt_cmd {
+       __le32 rf_mgmt_type;
+} __packed;
+
+/*
+ * WMI_SET_SSID_CMDID
+ */
+struct wmi_set_ssid_cmd {
+       __le32 ssid_len;
+       u8 ssid[WMI_MAX_SSID_LEN];
+} __packed;
+
+/*
+ * WMI_SET_PCP_CHANNEL_CMDID
+ */
+struct wmi_set_pcp_channel_cmd {
+       u8 channel;
+       u8 reserved[3];
+} __packed;
+
+/*
+ * WMI_BCON_CTRL_CMDID
+ */
+struct wmi_bcon_ctrl_cmd {
+       __le16 bcon_interval;
+       __le16 frag_num;
+       __le64 ss_mask;
+       u8 network_type;
+       u8 reserved;
+       u8 disable_sec_offload;
+       u8 disable_sec;
+} __packed;
+
+/*
+ * WMI_SW_TX_REQ_CMDID
+ */
+struct wmi_sw_tx_req_cmd {
+       u8 dst_mac[WMI_MAC_LEN];
+       __le16 len;
+       u8 payload[0];
+} __packed;
+
+/*
+ * WMI_VRING_CFG_CMDID
+ */
+
+struct wmi_sw_ring_cfg {
+       __le64 ring_mem_base;
+       __le16 ring_size;
+       __le16 max_mpdu_size;
+} __packed;
+
+struct wmi_vring_cfg_schd {
+       __le16 priority;
+       __le16 timeslot_us;
+} __packed;
+
+enum wmi_vring_cfg_encap_trans_type {
+       WMI_VRING_ENC_TYPE_802_3                = 0,
+       WMI_VRING_ENC_TYPE_NATIVE_WIFI          = 1,
+};
+
+enum wmi_vring_cfg_ds_cfg {
+       WMI_VRING_DS_PBSS                       = 0,
+       WMI_VRING_DS_STATION                    = 1,
+       WMI_VRING_DS_AP                         = 2,
+       WMI_VRING_DS_ADDR4                      = 3,
+};
+
+enum wmi_vring_cfg_nwifi_ds_trans_type {
+       WMI_NWIFI_TX_TRANS_MODE_NO              = 0,
+       WMI_NWIFI_TX_TRANS_MODE_AP2PBSS         = 1,
+       WMI_NWIFI_TX_TRANS_MODE_STA2PBSS        = 2,
+};
+
+enum wmi_vring_cfg_schd_params_priority {
+       WMI_SCH_PRIO_REGULAR                    = 0,
+       WMI_SCH_PRIO_HIGH                       = 1,
+};
+
+struct wmi_vring_cfg {
+       struct wmi_sw_ring_cfg tx_sw_ring;
+       u8 ringid;                              /* 0-23 vrings */
+
+       #define CIDXTID_CID_POS (0)
+       #define CIDXTID_CID_LEN (4)
+       #define CIDXTID_CID_MSK (0xF)
+       #define CIDXTID_TID_POS (4)
+       #define CIDXTID_TID_LEN (4)
+       #define CIDXTID_TID_MSK (0xF0)
+       u8 cidxtid;
+
+       u8 encap_trans_type;
+       u8 ds_cfg;                              /* 802.3 DS cfg */
+       u8 nwifi_ds_trans_type;
+
+       #define VRING_CFG_MAC_CTRL_LIFETIME_EN_POS (0)
+       #define VRING_CFG_MAC_CTRL_LIFETIME_EN_LEN (1)
+       #define VRING_CFG_MAC_CTRL_LIFETIME_EN_MSK (0x1)
+       #define VRING_CFG_MAC_CTRL_AGGR_EN_POS (1)
+       #define VRING_CFG_MAC_CTRL_AGGR_EN_LEN (1)
+       #define VRING_CFG_MAC_CTRL_AGGR_EN_MSK (0x2)
+       u8 mac_ctrl;
+
+       #define VRING_CFG_TO_RESOLUTION_VALUE_POS (0)
+       #define VRING_CFG_TO_RESOLUTION_VALUE_LEN (6)
+       #define VRING_CFG_TO_RESOLUTION_VALUE_MSK (0x3F)
+       u8 to_resolution;
+       u8 agg_max_wsize;
+       struct wmi_vring_cfg_schd schd_params;
+} __packed;
+
+enum wmi_vring_cfg_cmd_action {
+       WMI_VRING_CMD_ADD                       = 0,
+       WMI_VRING_CMD_MODIFY                    = 1,
+       WMI_VRING_CMD_DELETE                    = 2,
+};
+
+struct wmi_vring_cfg_cmd {
+       __le32 action;
+       struct wmi_vring_cfg vring_cfg;
+} __packed;
+
+/*
+ * WMI_VRING_BA_EN_CMDID
+ */
+struct wmi_vring_ba_en_cmd {
+       u8 ringid;
+       u8 agg_max_wsize;
+       __le16 ba_timeout;
+} __packed;
+
+/*
+ * WMI_VRING_BA_DIS_CMDID
+ */
+struct wmi_vring_ba_dis_cmd {
+       u8 ringid;
+       u8 reserved;
+       __le16 reason;
+} __packed;
+
+/*
+ * WMI_NOTIFY_REQ_CMDID
+ */
+struct wmi_notify_req_cmd {
+       u8 cid;
+       u8 reserved[3];
+       __le32 interval_usec;
+} __packed;
+
+/*
+ * WMI_CFG_RX_CHAIN_CMDID
+ */
+enum wmi_sniffer_cfg_mode {
+       WMI_SNIFFER_OFF                         = 0,
+       WMI_SNIFFER_ON                          = 1,
+};
+
+enum wmi_sniffer_cfg_phy_info_mode {
+       WMI_SNIFFER_PHY_INFO_DISABLED           = 0,
+       WMI_SNIFFER_PHY_INFO_ENABLED            = 1,
+};
+
+enum wmi_sniffer_cfg_phy_support {
+       WMI_SNIFFER_CP                          = 0,
+       WMI_SNIFFER_DP                          = 1,
+       WMI_SNIFFER_BOTH_PHYS                   = 2,
+};
+
+struct wmi_sniffer_cfg {
+       __le32 mode;            /* enum wmi_sniffer_cfg_mode */
+       __le32 phy_info_mode;   /* enum wmi_sniffer_cfg_phy_info_mode */
+       __le32 phy_support;     /* enum wmi_sniffer_cfg_phy_support */
+       u8 channel;
+       u8 reserved[3];
+} __packed;
+
+enum wmi_cfg_rx_chain_cmd_action {
+       WMI_RX_CHAIN_ADD                        = 0,
+       WMI_RX_CHAIN_DEL                        = 1,
+};
+
+enum wmi_cfg_rx_chain_cmd_decap_trans_type {
+       WMI_DECAP_TYPE_802_3                    = 0,
+       WMI_DECAP_TYPE_NATIVE_WIFI              = 1,
+};
+
+enum wmi_cfg_rx_chain_cmd_nwifi_ds_trans_type {
+       WMI_NWIFI_RX_TRANS_MODE_NO              = 0,
+       WMI_NWIFI_RX_TRANS_MODE_PBSS2AP         = 1,
+       WMI_NWIFI_RX_TRANS_MODE_PBSS2STA        = 2,
+};
+
+struct wmi_cfg_rx_chain_cmd {
+       __le32 action;
+       struct wmi_sw_ring_cfg rx_sw_ring;
+       u8 mid;
+       u8 decap_trans_type;
+
+       #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_POS (0)
+       #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_LEN (1)
+       #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_MSK (0x1)
+       u8 l2_802_3_offload_ctrl;
+
+       #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_POS (0)
+       #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_LEN (1)
+       #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_MSK (0x1)
+       #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_POS (1)
+       #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_LEN (1)
+       #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_MSK (0x2)
+       u8 l2_nwifi_offload_ctrl;
+
+       u8 vlan_id;
+       u8 nwifi_ds_trans_type;
+
+       #define L3_L4_CTRL_IPV4_CHECKSUM_EN_POS (0)
+       #define L3_L4_CTRL_IPV4_CHECKSUM_EN_LEN (1)
+       #define L3_L4_CTRL_IPV4_CHECKSUM_EN_MSK (0x1)
+       #define L3_L4_CTRL_TCPIP_CHECKSUM_EN_POS (1)
+       #define L3_L4_CTRL_TCPIP_CHECKSUM_EN_LEN (1)
+       #define L3_L4_CTRL_TCPIP_CHECKSUM_EN_MSK (0x2)
+       u8 l3_l4_ctrl;
+
+       #define RING_CTRL_OVERRIDE_PREFETCH_THRSH_POS (0)
+       #define RING_CTRL_OVERRIDE_PREFETCH_THRSH_LEN (1)
+       #define RING_CTRL_OVERRIDE_PREFETCH_THRSH_MSK (0x1)
+       #define RING_CTRL_OVERRIDE_WB_THRSH_POS (1)
+       #define RING_CTRL_OVERRIDE_WB_THRSH_LEN (1)
+       #define RING_CTRL_OVERRIDE_WB_THRSH_MSK (0x2)
+       #define RING_CTRL_OVERRIDE_ITR_THRSH_POS (2)
+       #define RING_CTRL_OVERRIDE_ITR_THRSH_LEN (1)
+       #define RING_CTRL_OVERRIDE_ITR_THRSH_MSK (0x4)
+       #define RING_CTRL_OVERRIDE_HOST_THRSH_POS (3)
+       #define RING_CTRL_OVERRIDE_HOST_THRSH_LEN (1)
+       #define RING_CTRL_OVERRIDE_HOST_THRSH_MSK (0x8)
+       u8 ring_ctrl;
+
+       __le16 prefetch_thrsh;
+       __le16 wb_thrsh;
+       __le32 itr_value;
+       __le16 host_thrsh;
+       u8 reserved[2];
+       struct wmi_sniffer_cfg sniffer_cfg;
+} __packed;
+
+/*
+ * WMI_RCP_ADDBA_RESP_CMDID
+ */
+struct wmi_rcp_addba_resp_cmd {
+
+       #define CIDXTID_CID_POS (0)
+       #define CIDXTID_CID_LEN (4)
+       #define CIDXTID_CID_MSK (0xF)
+       #define CIDXTID_TID_POS (4)
+       #define CIDXTID_TID_LEN (4)
+       #define CIDXTID_TID_MSK (0xF0)
+       u8 cidxtid;
+
+       u8 dialog_token;
+       __le16 status_code;
+       __le16 ba_param_set;    /* ieee80211_ba_parameterset field to send */
+       __le16 ba_timeout;
+} __packed;
+
+/*
+ * WMI_RCP_DELBA_CMDID
+ */
+struct wmi_rcp_delba_cmd {
+
+       #define CIDXTID_CID_POS (0)
+       #define CIDXTID_CID_LEN (4)
+       #define CIDXTID_CID_MSK (0xF)
+       #define CIDXTID_TID_POS (4)
+       #define CIDXTID_TID_LEN (4)
+       #define CIDXTID_TID_MSK (0xF0)
+       u8 cidxtid;
+
+       u8 reserved;
+       __le16 reason;
+} __packed;
+
+/*
+ * WMI_RCP_ADDBA_REQ_CMDID
+ */
+struct wmi_rcp_addba_req_cmd {
+
+       #define CIDXTID_CID_POS (0)
+       #define CIDXTID_CID_LEN (4)
+       #define CIDXTID_CID_MSK (0xF)
+       #define CIDXTID_TID_POS (4)
+       #define CIDXTID_TID_LEN (4)
+       #define CIDXTID_TID_MSK (0xF0)
+       u8 cidxtid;
+
+       u8 dialog_token;
+       /* ieee80211_ba_parameterset field as it received */
+       __le16 ba_param_set;
+       __le16 ba_timeout;
+       /* ieee80211_ba_seqstrl field as it received */
+       __le16 ba_seq_ctrl;
+} __packed;
+
+/*
+ * WMI_SET_MAC_ADDRESS_CMDID
+ */
+struct wmi_set_mac_address_cmd {
+       u8 mac[WMI_MAC_LEN];
+       u8 reserved[2];
+} __packed;
+
+
+/*
+* WMI_EAPOL_TX_CMDID
+*/
+struct wmi_eapol_tx_cmd {
+       u8 dst_mac[WMI_MAC_LEN];
+       __le16 eapol_len;
+       u8 eapol[0];
+} __packed;
+
+/*
+ * WMI_ECHO_CMDID
+ *
+ * Check FW is alive
+ *
+ * WMI_DEEP_ECHO_CMDID
+ *
+ * Check FW and ucode are alive
+ *
+ * Returned event: WMI_ECHO_RSP_EVENTID
+ * same event for both commands
+ */
+struct wmi_echo_cmd {
+       __le32 value;
+} __packed;
+
+/*
+ * WMI Events
+ */
+
+/*
+ * List of Events (target to host)
+ */
+enum wmi_event_id {
+       WMI_IMM_RSP_EVENTID                     = 0x0000,
+       WMI_READY_EVENTID                       = 0x1001,
+       WMI_CONNECT_EVENTID                     = 0x1002,
+       WMI_DISCONNECT_EVENTID                  = 0x1003,
+       WMI_SCAN_COMPLETE_EVENTID               = 0x100a,
+       WMI_REPORT_STATISTICS_EVENTID           = 0x100b,
+       WMI_RD_MEM_RSP_EVENTID                  = 0x1800,
+       WMI_FW_READY_EVENTID                    = 0x1801,
+       WMI_EXIT_FAST_MEM_ACC_MODE_EVENTID      = 0x0200,
+       WMI_ECHO_RSP_EVENTID                    = 0x1803,
+       WMI_CONFIG_MAC_DONE_EVENTID             = 0x1805,
+       WMI_CONFIG_PHY_DEBUG_DONE_EVENTID       = 0x1806,
+       WMI_ADD_STATION_DONE_EVENTID            = 0x1807,
+       WMI_ADD_DEBUG_TX_PCKT_DONE_EVENTID      = 0x1808,
+       WMI_PHY_GET_STATISTICS_EVENTID          = 0x1809,
+       WMI_FS_TUNE_DONE_EVENTID                = 0x180a,
+       WMI_CORR_MEASURE_DONE_EVENTID           = 0x180b,
+       WMI_TEMP_SENSE_DONE_EVENTID             = 0x180e,
+       WMI_DC_CALIB_DONE_EVENTID               = 0x180f,
+       WMI_IQ_TX_CALIB_DONE_EVENTID            = 0x1811,
+       WMI_IQ_RX_CALIB_DONE_EVENTID            = 0x1812,
+       WMI_SET_WORK_MODE_DONE_EVENTID          = 0x1815,
+       WMI_LO_LEAKAGE_CALIB_DONE_EVENTID       = 0x1816,
+       WMI_MARLON_R_ACTIVATE_DONE_EVENTID      = 0x1817,
+       WMI_MARLON_R_READ_DONE_EVENTID          = 0x1818,
+       WMI_MARLON_R_WRITE_DONE_EVENTID         = 0x1819,
+       WMI_MARLON_R_TXRX_SEL_DONE_EVENTID      = 0x181a,
+       WMI_SILENT_RSSI_CALIB_DONE_EVENTID      = 0x181d,
+
+       WMI_CFG_RX_CHAIN_DONE_EVENTID           = 0x1820,
+       WMI_VRING_CFG_DONE_EVENTID              = 0x1821,
+       WMI_RX_ON_DONE_EVENTID                  = 0x1822,
+       WMI_BA_STATUS_EVENTID                   = 0x1823,
+       WMI_RCP_ADDBA_REQ_EVENTID               = 0x1824,
+       WMI_ADDBA_RESP_SENT_EVENTID             = 0x1825,
+       WMI_DELBA_EVENTID                       = 0x1826,
+       WMI_GET_SSID_EVENTID                    = 0x1828,
+       WMI_GET_PCP_CHANNEL_EVENTID             = 0x182a,
+       WMI_SW_TX_COMPLETE_EVENTID              = 0x182b,
+       WMI_RX_OFF_DONE_EVENTID                 = 0x182c,
+
+       WMI_READ_MAC_RXQ_EVENTID                = 0x1830,
+       WMI_READ_MAC_TXQ_EVENTID                = 0x1831,
+       WMI_WRITE_MAC_RXQ_EVENTID               = 0x1832,
+       WMI_WRITE_MAC_TXQ_EVENTID               = 0x1833,
+       WMI_WRITE_MAC_XQ_FIELD_EVENTID          = 0x1834,
+
+       WMI_BEAFORMING_MGMT_DONE_EVENTID        = 0x1836,
+       WMI_BF_TXSS_MGMT_DONE_EVENTID           = 0x1837,
+       WMI_BF_RXSS_MGMT_DONE_EVENTID           = 0x1839,
+       WMI_RS_MGMT_DONE_EVENTID                = 0x1852,
+       WMI_RF_MGMT_STATUS_EVENTID              = 0x1853,
+       WMI_BF_SM_MGMT_DONE_EVENTID             = 0x1838,
+       WMI_RX_MGMT_PACKET_EVENTID              = 0x1840,
+
+       /* Performance monitoring events */
+       WMI_DATA_PORT_OPEN_EVENTID              = 0x1860,
+       WMI_WBE_LINKDOWN_EVENTID                = 0x1861,
+
+       WMI_BF_CTRL_DONE_EVENTID                = 0x1862,
+       WMI_NOTIFY_REQ_DONE_EVENTID             = 0x1863,
+       WMI_GET_STATUS_DONE_EVENTID             = 0x1864,
+
+       WMI_UNIT_TEST_EVENTID                   = 0x1900,
+       WMI_FLASH_READ_DONE_EVENTID             = 0x1902,
+       WMI_FLASH_WRITE_DONE_EVENTID            = 0x1903,
+
+       WMI_SET_CHANNEL_EVENTID                 = 0x9000,
+       WMI_ASSOC_REQ_EVENTID                   = 0x9001,
+       WMI_EAPOL_RX_EVENTID                    = 0x9002,
+       WMI_MAC_ADDR_RESP_EVENTID               = 0x9003,
+       WMI_FW_VER_EVENTID                      = 0x9004,
+};
+
+/*
+ * Events data structures
+ */
+
+/*
+ * WMI_RF_MGMT_STATUS_EVENTID
+ */
+enum wmi_rf_status {
+       WMI_RF_ENABLED                  = 0,
+       WMI_RF_DISABLED_HW              = 1,
+       WMI_RF_DISABLED_SW              = 2,
+       WMI_RF_DISABLED_HW_SW           = 3,
+};
+
+struct wmi_rf_mgmt_status_event {
+       __le32 rf_status;
+} __packed;
+
+/*
+ * WMI_GET_STATUS_DONE_EVENTID
+ */
+struct wmi_get_status_done_event {
+       __le32 is_associated;
+       u8 cid;
+       u8 reserved0[3];
+       u8 bssid[WMI_MAC_LEN];
+       u8 channel;
+       u8 reserved1;
+       u8 network_type;
+       u8 reserved2[3];
+       __le32 ssid_len;
+       u8 ssid[WMI_MAX_SSID_LEN];
+       __le32 rf_status;
+       __le32 is_secured;
+} __packed;
+
+/*
+ * WMI_FW_VER_EVENTID
+ */
+struct wmi_fw_ver_event {
+       u8 major;
+       u8 minor;
+       __le16 subminor;
+       __le16 build;
+} __packed;
+
+/*
+* WMI_MAC_ADDR_RESP_EVENTID
+*/
+struct wmi_mac_addr_resp_event {
+       u8 mac[WMI_MAC_LEN];
+       u8 auth_mode;
+       u8 crypt_mode;
+       __le32 offload_mode;
+} __packed;
+
+/*
+* WMI_EAPOL_RX_EVENTID
+*/
+struct wmi_eapol_rx_event {
+       u8 src_mac[WMI_MAC_LEN];
+       __le16 eapol_len;
+       u8 eapol[0];
+} __packed;
+
+/*
+* WMI_READY_EVENTID
+*/
+enum wmi_phy_capability {
+       WMI_11A_CAPABILITY              = 1,
+       WMI_11G_CAPABILITY              = 2,
+       WMI_11AG_CAPABILITY             = 3,
+       WMI_11NA_CAPABILITY             = 4,
+       WMI_11NG_CAPABILITY             = 5,
+       WMI_11NAG_CAPABILITY            = 6,
+       WMI_11AD_CAPABILITY             = 7,
+       WMI_11N_CAPABILITY_OFFSET = WMI_11NA_CAPABILITY - WMI_11A_CAPABILITY,
+};
+
+struct wmi_ready_event {
+       __le32 sw_version;
+       __le32 abi_version;
+       u8 mac[WMI_MAC_LEN];
+       u8 phy_capability;              /* enum wmi_phy_capability */
+       u8 reserved;
+} __packed;
+
+/*
+ * WMI_NOTIFY_REQ_DONE_EVENTID
+ */
+struct wmi_notify_req_done_event {
+       __le32 status;
+       __le64 tsf;
+       __le32 snr_val;
+       __le32 tx_tpt;
+       __le32 tx_goodput;
+       __le32 rx_goodput;
+       __le16 bf_mcs;
+       __le16 my_rx_sector;
+       __le16 my_tx_sector;
+       __le16 other_rx_sector;
+       __le16 other_tx_sector;
+       __le16 range;
+} __packed;
+
+/*
+ * WMI_CONNECT_EVENTID
+ */
+struct wmi_connect_event {
+       u8 channel;
+       u8 reserved0;
+       u8 bssid[WMI_MAC_LEN];
+       __le16 listen_interval;
+       __le16 beacon_interval;
+       u8 network_type;
+       u8 reserved1[3];
+       u8 beacon_ie_len;
+       u8 assoc_req_len;
+       u8 assoc_resp_len;
+       u8 cid;
+       u8 reserved2[3];
+       u8 assoc_info[0];
+} __packed;
+
+/*
+ * WMI_DISCONNECT_EVENTID
+ */
+enum wmi_disconnect_reason {
+       WMI_DIS_REASON_NO_NETWORK_AVAIL         = 1,
+       WMI_DIS_REASON_LOST_LINK                = 2, /* bmiss */
+       WMI_DIS_REASON_DISCONNECT_CMD           = 3,
+       WMI_DIS_REASON_BSS_DISCONNECTED         = 4,
+       WMI_DIS_REASON_AUTH_FAILED              = 5,
+       WMI_DIS_REASON_ASSOC_FAILED             = 6,
+       WMI_DIS_REASON_NO_RESOURCES_AVAIL       = 7,
+       WMI_DIS_REASON_CSERV_DISCONNECT         = 8,
+       WMI_DIS_REASON_INVALID_PROFILE          = 10,
+       WMI_DIS_REASON_DOT11H_CHANNEL_SWITCH    = 11,
+       WMI_DIS_REASON_PROFILE_MISMATCH         = 12,
+       WMI_DIS_REASON_CONNECTION_EVICTED       = 13,
+       WMI_DIS_REASON_IBSS_MERGE               = 14,
+};
+
+struct wmi_disconnect_event {
+       __le16 protocol_reason_status;  /* reason code, see 802.11 spec. */
+       u8 bssid[WMI_MAC_LEN];          /* set if known */
+       u8 disconnect_reason;           /* see wmi_disconnect_reason_e */
+       u8 assoc_resp_len;
+       u8 assoc_info[0];
+} __packed;
+
+/*
+ * WMI_SCAN_COMPLETE_EVENTID
+ */
+struct wmi_scan_complete_event {
+       __le32 status;
+} __packed;
+
+/*
+ * WMI_BA_STATUS_EVENTID
+ */
+enum wmi_vring_ba_status {
+       WMI_BA_AGREED                   = 0,
+       WMI_BA_NON_AGREED               = 1,
+};
+
+struct wmi_vring_ba_status_event {
+       __le16 status;
+       u8 reserved[2];
+       u8 ringid;
+       u8 agg_wsize;
+       __le16 ba_timeout;
+} __packed;
+
+/*
+ * WMI_DELBA_EVENTID
+ */
+struct wmi_delba_event {
+
+       #define CIDXTID_CID_POS (0)
+       #define CIDXTID_CID_LEN (4)
+       #define CIDXTID_CID_MSK (0xF)
+       #define CIDXTID_TID_POS (4)
+       #define CIDXTID_TID_LEN (4)
+       #define CIDXTID_TID_MSK (0xF0)
+       u8 cidxtid;
+
+       u8 from_initiator;
+       __le16 reason;
+} __packed;
+
+/*
+ * WMI_VRING_CFG_DONE_EVENTID
+ */
+enum wmi_vring_cfg_done_event_status {
+       WMI_VRING_CFG_SUCCESS           = 0,
+       WMI_VRING_CFG_FAILURE           = 1,
+};
+
+struct wmi_vring_cfg_done_event {
+       u8 ringid;
+       u8 status;
+       u8 reserved[2];
+       __le32 tx_vring_tail_ptr;
+} __packed;
+
+/*
+ * WMI_ADDBA_RESP_SENT_EVENTID
+ */
+enum wmi_rcp_addba_resp_sent_event_status {
+       WMI_ADDBA_SUCCESS               = 0,
+       WMI_ADDBA_FAIL                  = 1,
+};
+
+struct wmi_rcp_addba_resp_sent_event {
+
+       #define CIDXTID_CID_POS (0)
+       #define CIDXTID_CID_LEN (4)
+       #define CIDXTID_CID_MSK (0xF)
+       #define CIDXTID_TID_POS (4)
+       #define CIDXTID_TID_LEN (4)
+       #define CIDXTID_TID_MSK (0xF0)
+       u8 cidxtid;
+
+       u8 reserved;
+       __le16 status;
+} __packed;
+
+/*
+ * WMI_RCP_ADDBA_REQ_EVENTID
+ */
+struct wmi_rcp_addba_req_event {
+
+       #define CIDXTID_CID_POS (0)
+       #define CIDXTID_CID_LEN (4)
+       #define CIDXTID_CID_MSK (0xF)
+       #define CIDXTID_TID_POS (4)
+       #define CIDXTID_TID_LEN (4)
+       #define CIDXTID_TID_MSK (0xF0)
+       u8 cidxtid;
+
+       u8 dialog_token;
+       __le16 ba_param_set;    /* ieee80211_ba_parameterset as it received */
+       __le16 ba_timeout;
+       __le16 ba_seq_ctrl;     /* ieee80211_ba_seqstrl field as it received */
+} __packed;
+
+/*
+ * WMI_CFG_RX_CHAIN_DONE_EVENTID
+ */
+enum wmi_cfg_rx_chain_done_event_status {
+       WMI_CFG_RX_CHAIN_SUCCESS        = 1,
+};
+
+struct wmi_cfg_rx_chain_done_event {
+       __le32 rx_ring_tail_ptr;        /* Rx V-Ring Tail pointer */
+       __le32 status;
+} __packed;
+
+/*
+ * WMI_WBE_LINKDOWN_EVENTID
+ */
+enum wmi_wbe_link_down_event_reason {
+       WMI_WBE_REASON_USER_REQUEST     = 0,
+       WMI_WBE_REASON_RX_DISASSOC      = 1,
+       WMI_WBE_REASON_BAD_PHY_LINK     = 2,
+};
+
+struct wmi_wbe_link_down_event {
+       u8 cid;
+       u8 reserved[3];
+       __le32 reason;
+} __packed;
+
+/*
+ * WMI_DATA_PORT_OPEN_EVENTID
+ */
+struct wmi_data_port_open_event {
+       u8 cid;
+       u8 reserved[3];
+} __packed;
+
+/*
+ * WMI_GET_PCP_CHANNEL_EVENTID
+ */
+struct wmi_get_pcp_channel_event {
+       u8 channel;
+       u8 reserved[3];
+} __packed;
+
+/*
+ * WMI_SW_TX_COMPLETE_EVENTID
+ */
+enum wmi_sw_tx_status {
+       WMI_TX_SW_STATUS_SUCCESS                = 0,
+       WMI_TX_SW_STATUS_FAILED_NO_RESOURCES    = 1,
+       WMI_TX_SW_STATUS_FAILED_TX              = 2,
+};
+
+struct wmi_sw_tx_complete_event {
+       u8 status;      /* enum wmi_sw_tx_status */
+       u8 reserved[3];
+} __packed;
+
+/*
+ * WMI_GET_SSID_EVENTID
+ */
+struct wmi_get_ssid_event {
+       __le32 ssid_len;
+       u8 ssid[WMI_MAX_SSID_LEN];
+} __packed;
+
+/*
+ * WMI_RX_MGMT_PACKET_EVENTID
+ */
+struct wmi_rx_mgmt_info {
+       u8 mcs;
+       s8 snr;
+       __le16 range;
+       __le16 stype;
+       __le16 status;
+       __le32 len;
+       u8 qid;
+       u8 mid;
+       u8 cid;
+       u8 channel;     /* From Radio MNGR */
+} __packed;
+
+struct wmi_rx_mgmt_packet_event {
+       struct wmi_rx_mgmt_info info;
+       u8 payload[0];
+} __packed;
+
+/*
+ * WMI_ECHO_RSP_EVENTID
+ */
+struct wmi_echo_event {
+       __le32 echoed_value;
+} __packed;
+
+#endif /* __WILOCITY_WMI_H__ */
index b298e5d68be2f0a58cf02d45d2ccd9a1e1e464bd..10e288d470e75be03ef936048fe70574ac3ea2c2 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/hw_random.h>
 #include <linux/bcma/bcma.h>
 #include <linux/ssb/ssb.h>
+#include <linux/completion.h>
 #include <net/mac80211.h>
 
 #include "debugfs.h"
@@ -722,6 +723,10 @@ enum b43_firmware_file_type {
 struct b43_request_fw_context {
        /* The device we are requesting the fw for. */
        struct b43_wldev *dev;
+       /* a completion event structure needed if this call is asynchronous */
+       struct completion fw_load_complete;
+       /* a pointer to the firmware object */
+       const struct firmware *blob;
        /* The type of firmware to request. */
        enum b43_firmware_file_type req_type;
        /* Error messages for each firmware type. */
index 16ab280359bd9ffbb9ee68770606d949b3060e6a..806e34c19281792b4444ad1e6b8ea03724c9c773 100644 (file)
@@ -2088,11 +2088,18 @@ static void b43_print_fw_helptext(struct b43_wl *wl, bool error)
                b43warn(wl, text);
 }
 
+static void b43_fw_cb(const struct firmware *firmware, void *context)
+{
+       struct b43_request_fw_context *ctx = context;
+
+       ctx->blob = firmware;
+       complete(&ctx->fw_load_complete);
+}
+
 int b43_do_request_fw(struct b43_request_fw_context *ctx,
                      const char *name,
-                     struct b43_firmware_file *fw)
+                     struct b43_firmware_file *fw, bool async)
 {
-       const struct firmware *blob;
        struct b43_fw_header *hdr;
        u32 size;
        int err;
@@ -2131,11 +2138,31 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx,
                B43_WARN_ON(1);
                return -ENOSYS;
        }
-       err = request_firmware(&blob, ctx->fwname, ctx->dev->dev->dev);
+       if (async) {
+               /* do this part asynchronously */
+               init_completion(&ctx->fw_load_complete);
+               err = request_firmware_nowait(THIS_MODULE, 1, ctx->fwname,
+                                             ctx->dev->dev->dev, GFP_KERNEL,
+                                             ctx, b43_fw_cb);
+               if (err < 0) {
+                       pr_err("Unable to load firmware\n");
+                       return err;
+               }
+               /* stall here until fw ready */
+               wait_for_completion(&ctx->fw_load_complete);
+               if (ctx->blob)
+                       goto fw_ready;
+       /* On some ARM systems, the async request will fail, but the next sync
+        * request works. For this reason, we dall through here
+        */
+       }
+       err = request_firmware(&ctx->blob, ctx->fwname,
+                              ctx->dev->dev->dev);
        if (err == -ENOENT) {
                snprintf(ctx->errors[ctx->req_type],
                         sizeof(ctx->errors[ctx->req_type]),
-                        "Firmware file \"%s\" not found\n", ctx->fwname);
+                        "Firmware file \"%s\" not found\n",
+                        ctx->fwname);
                return err;
        } else if (err) {
                snprintf(ctx->errors[ctx->req_type],
@@ -2144,14 +2171,15 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx,
                         ctx->fwname, err);
                return err;
        }
-       if (blob->size < sizeof(struct b43_fw_header))
+fw_ready:
+       if (ctx->blob->size < sizeof(struct b43_fw_header))
                goto err_format;
-       hdr = (struct b43_fw_header *)(blob->data);
+       hdr = (struct b43_fw_header *)(ctx->blob->data);
        switch (hdr->type) {
        case B43_FW_TYPE_UCODE:
        case B43_FW_TYPE_PCM:
                size = be32_to_cpu(hdr->size);
-               if (size != blob->size - sizeof(struct b43_fw_header))
+               if (size != ctx->blob->size - sizeof(struct b43_fw_header))
                        goto err_format;
                /* fallthrough */
        case B43_FW_TYPE_IV:
@@ -2162,7 +2190,7 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx,
                goto err_format;
        }
 
-       fw->data = blob;
+       fw->data = ctx->blob;
        fw->filename = name;
        fw->type = ctx->req_type;
 
@@ -2172,7 +2200,7 @@ err_format:
        snprintf(ctx->errors[ctx->req_type],
                 sizeof(ctx->errors[ctx->req_type]),
                 "Firmware file \"%s\" format error.\n", ctx->fwname);
-       release_firmware(blob);
+       release_firmware(ctx->blob);
 
        return -EPROTO;
 }
@@ -2223,7 +2251,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx)
                        goto err_no_ucode;
                }
        }
-       err = b43_do_request_fw(ctx, filename, &fw->ucode);
+       err = b43_do_request_fw(ctx, filename, &fw->ucode, true);
        if (err)
                goto err_load;
 
@@ -2235,7 +2263,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx)
        else
                goto err_no_pcm;
        fw->pcm_request_failed = false;
-       err = b43_do_request_fw(ctx, filename, &fw->pcm);
+       err = b43_do_request_fw(ctx, filename, &fw->pcm, false);
        if (err == -ENOENT) {
                /* We did not find a PCM file? Not fatal, but
                 * core rev <= 10 must do without hwcrypto then. */
@@ -2296,7 +2324,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx)
        default:
                goto err_no_initvals;
        }
-       err = b43_do_request_fw(ctx, filename, &fw->initvals);
+       err = b43_do_request_fw(ctx, filename, &fw->initvals, false);
        if (err)
                goto err_load;
 
@@ -2355,7 +2383,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx)
        default:
                goto err_no_initvals;
        }
-       err = b43_do_request_fw(ctx, filename, &fw->initvals_band);
+       err = b43_do_request_fw(ctx, filename, &fw->initvals_band, false);
        if (err)
                goto err_load;
 
index 8c684cd3352942ccaadcf843290d014cbb8599bd..abac25ee958dad54f140c550ef62ba05bf2bc3ae 100644 (file)
@@ -137,9 +137,8 @@ void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on);
 
 
 struct b43_request_fw_context;
-int b43_do_request_fw(struct b43_request_fw_context *ctx,
-                     const char *name,
-                     struct b43_firmware_file *fw);
+int b43_do_request_fw(struct b43_request_fw_context *ctx, const char *name,
+                     struct b43_firmware_file *fw, bool async);
 void b43_do_release_fw(struct b43_firmware_file *fw);
 
 #endif /* B43_MAIN_H_ */
index 1fbd8ecbe2ea293a28d6a65648fe011a2fff3fa6..0f71d1d4339dc38ccfdd89428fdc4a62bb5b7bd9 100644 (file)
@@ -1407,9 +1407,10 @@ void brcms_add_timer(struct brcms_timer *t, uint ms, int periodic)
 #endif
        t->ms = ms;
        t->periodic = (bool) periodic;
-       t->set = true;
-
-       atomic_inc(&t->wl->callbacks);
+       if (!t->set) {
+               t->set = true;
+               atomic_inc(&t->wl->callbacks);
+       }
 
        ieee80211_queue_delayed_work(hw, &t->dly_wrk, msecs_to_jiffies(ms));
 }
index d604b4036a764358e52bd2c2ca9d02bd24f14ce9..3726cd6fcd754812d65cb38d89782e96aace0de8 100644 (file)
@@ -3273,7 +3273,7 @@ il3945_store_measurement(struct device *d, struct device_attribute *attr,
 
        if (count) {
                char *p = buffer;
-               strncpy(buffer, buf, min(sizeof(buffer), count));
+               strlcpy(buffer, buf, sizeof(buffer));
                channel = simple_strtoul(p, NULL, 0);
                if (channel)
                        params.channel = channel;
index 7e16d10a7f140e4bc0a1414af1ec317f7284b9c6..90b8970eadf0fa7c296be194c2d18ef9266b1043 100644 (file)
@@ -3958,17 +3958,21 @@ il_connection_init_rx_config(struct il_priv *il)
 
        memset(&il->staging, 0, sizeof(il->staging));
 
-       if (!il->vif) {
+       switch (il->iw_mode) {
+       case NL80211_IFTYPE_UNSPECIFIED:
                il->staging.dev_type = RXON_DEV_TYPE_ESS;
-       } else if (il->vif->type == NL80211_IFTYPE_STATION) {
+               break;
+       case NL80211_IFTYPE_STATION:
                il->staging.dev_type = RXON_DEV_TYPE_ESS;
                il->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK;
-       } else if (il->vif->type == NL80211_IFTYPE_ADHOC) {
+               break;
+       case NL80211_IFTYPE_ADHOC:
                il->staging.dev_type = RXON_DEV_TYPE_IBSS;
                il->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK;
                il->staging.filter_flags =
                    RXON_FILTER_BCON_AWARE_MSK | RXON_FILTER_ACCEPT_GRP_MSK;
-       } else {
+               break;
+       default:
                IL_ERR("Unsupported interface type %d\n", il->vif->type);
                return;
        }
@@ -4550,8 +4554,7 @@ out:
 EXPORT_SYMBOL(il_mac_add_interface);
 
 static void
-il_teardown_interface(struct il_priv *il, struct ieee80211_vif *vif,
-                     bool mode_change)
+il_teardown_interface(struct il_priv *il, struct ieee80211_vif *vif)
 {
        lockdep_assert_held(&il->mutex);
 
@@ -4560,9 +4563,7 @@ il_teardown_interface(struct il_priv *il, struct ieee80211_vif *vif,
                il_force_scan_end(il);
        }
 
-       if (!mode_change)
-               il_set_mode(il);
-
+       il_set_mode(il);
 }
 
 void
@@ -4575,8 +4576,8 @@ il_mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 
        WARN_ON(il->vif != vif);
        il->vif = NULL;
-
-       il_teardown_interface(il, vif, false);
+       il->iw_mode = NL80211_IFTYPE_UNSPECIFIED;
+       il_teardown_interface(il, vif);
        memset(il->bssid, 0, ETH_ALEN);
 
        D_MAC80211("leave\n");
@@ -4685,18 +4686,10 @@ il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
        }
 
        /* success */
-       il_teardown_interface(il, vif, true);
        vif->type = newtype;
        vif->p2p = false;
-       err = il_set_mode(il);
-       WARN_ON(err);
-       /*
-        * We've switched internally, but submitting to the
-        * device may have failed for some reason. Mask this
-        * error, because otherwise mac80211 will not switch
-        * (and set the interface type back) and we'll be
-        * out of sync with it.
-        */
+       il->iw_mode = newtype;
+       il_teardown_interface(il, vif);
        err = 0;
 
 out:
index da21328ca8ed84e9e06e3aebde44fade0973e870..31534f7c05488ba38d4c65c7ce0209a43712fd51 100644 (file)
@@ -1079,6 +1079,8 @@ static void iwlagn_set_tx_status(struct iwl_priv *priv,
 {
        u16 status = le16_to_cpu(tx_resp->status.status);
 
+       info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+
        info->status.rates[0].count = tx_resp->failure_frame + 1;
        info->flags |= iwl_tx_status_to_mac80211(status);
        iwlagn_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags),
@@ -1151,13 +1153,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
                        next_reclaimed = ssn;
                }
 
-               if (tid != IWL_TID_NON_QOS) {
-                       priv->tid_data[sta_id][tid].next_reclaimed =
-                               next_reclaimed;
-                       IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n",
-                                                 next_reclaimed);
-               }
-
                iwl_trans_reclaim(priv->trans, txq_id, ssn, &skbs);
 
                iwlagn_check_ratid_empty(priv, sta_id, tid);
@@ -1208,11 +1203,28 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
                        if (!is_agg)
                                iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1);
 
+                       /*
+                        * W/A for FW bug - the seq_ctl isn't updated when the
+                        * queues are flushed. Fetch it from the packet itself
+                        */
+                       if (!is_agg && status == TX_STATUS_FAIL_FIFO_FLUSHED) {
+                               next_reclaimed = le16_to_cpu(hdr->seq_ctrl);
+                               next_reclaimed =
+                                       SEQ_TO_SN(next_reclaimed + 0x10);
+                       }
+
                        is_offchannel_skb =
                                (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN);
                        freed++;
                }
 
+               if (tid != IWL_TID_NON_QOS) {
+                       priv->tid_data[sta_id][tid].next_reclaimed =
+                               next_reclaimed;
+                       IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n",
+                                          next_reclaimed);
+               }
+
                WARN_ON(!is_agg && freed != 1);
 
                /*
index dad4c4aad91f2a461cdebfc9ac879b51a36db827..8389cd38338ba70766561d13c35591e1073faca6 100644 (file)
@@ -1166,6 +1166,7 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data)
        else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) &&
                 !trans_pcie->inta)
                iwl_enable_interrupts(trans);
+       return IRQ_HANDLED;
 
 none:
        /* re-enable interrupts here since we don't have anything to service. */
index a875499f89452d7ab4a4307b72c0475d5829f470..cdb11b3964e27dd68de20ac68ab8442772a070a1 100644 (file)
@@ -1459,7 +1459,7 @@ mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid,
        struct cfg80211_ssid req_ssid;
        int ret, auth_type = 0;
        struct cfg80211_bss *bss = NULL;
-       u8 is_scanning_required = 0, config_bands = 0;
+       u8 is_scanning_required = 0;
 
        memset(&req_ssid, 0, sizeof(struct cfg80211_ssid));
 
@@ -1478,19 +1478,6 @@ mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid,
        /* disconnect before try to associate */
        mwifiex_deauthenticate(priv, NULL);
 
-       if (channel) {
-               if (mode == NL80211_IFTYPE_STATION) {
-                       if (channel->band == IEEE80211_BAND_2GHZ)
-                               config_bands = BAND_B | BAND_G | BAND_GN;
-                       else
-                               config_bands = BAND_A | BAND_AN;
-
-                       if (!((config_bands | priv->adapter->fw_bands) &
-                             ~priv->adapter->fw_bands))
-                               priv->adapter->config_bands = config_bands;
-               }
-       }
-
        /* As this is new association, clear locally stored
         * keys and security related flags */
        priv->sec_info.wpa_enabled = false;
@@ -1707,9 +1694,9 @@ static int mwifiex_set_ibss_params(struct mwifiex_private *priv,
 
                if (cfg80211_get_chandef_type(&params->chandef) !=
                                                NL80211_CHAN_NO_HT)
-                       config_bands |= BAND_GN;
+                       config_bands |= BAND_G | BAND_GN;
        } else {
-               if (cfg80211_get_chandef_type(&params->chandef) !=
+               if (cfg80211_get_chandef_type(&params->chandef) ==
                                                NL80211_CHAN_NO_HT)
                        config_bands = BAND_A;
                else
index 13fbc4eb15952fe375be1e10c55518bb2b6b3836..b879e1338a54f5347a7a5f0b955b98a3f3566b66 100644 (file)
@@ -161,7 +161,7 @@ static int mwifiex_pcie_suspend(struct pci_dev *pdev, pm_message_t state)
 
        if (pdev) {
                card = (struct pcie_service_card *) pci_get_drvdata(pdev);
-               if (!card || card->adapter) {
+               if (!card || !card->adapter) {
                        pr_err("Card or adapter structure is not valid\n");
                        return 0;
                }
index cb682561c43898fd6f27c03ab3aba38adbf269d8..f542bb8ccbc8d4ce6859e8fff1e45784183f8fbb 100644 (file)
@@ -56,7 +56,6 @@ int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist,
  */
 int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter)
 {
-       bool cancel_flag = false;
        int status;
        struct cmd_ctrl_node *cmd_queued;
 
@@ -70,14 +69,11 @@ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter)
        atomic_inc(&adapter->cmd_pending);
 
        /* Wait for completion */
-       wait_event_interruptible(adapter->cmd_wait_q.wait,
-                                *(cmd_queued->condition));
-       if (!*(cmd_queued->condition))
-               cancel_flag = true;
-
-       if (cancel_flag) {
-               mwifiex_cancel_pending_ioctl(adapter);
-               dev_dbg(adapter->dev, "cmd cancel\n");
+       status = wait_event_interruptible(adapter->cmd_wait_q.wait,
+                                         *(cmd_queued->condition));
+       if (status) {
+               dev_err(adapter->dev, "cmd_wait_q terminated: %d\n", status);
+               return status;
        }
 
        status = adapter->cmd_wait_q.status;
@@ -287,6 +283,20 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
                if (ret)
                        goto done;
 
+               if (bss_desc) {
+                       u8 config_bands = 0;
+
+                       if (mwifiex_band_to_radio_type((u8) bss_desc->bss_band)
+                           == HostCmd_SCAN_RADIO_TYPE_BG)
+                               config_bands = BAND_B | BAND_G | BAND_GN;
+                       else
+                               config_bands = BAND_A | BAND_AN;
+
+                       if (!((config_bands | adapter->fw_bands) &
+                             ~adapter->fw_bands))
+                               adapter->config_bands = config_bands;
+               }
+
                ret = mwifiex_check_network_compatibility(priv, bss_desc);
                if (ret)
                        goto done;
@@ -496,8 +506,11 @@ int mwifiex_enable_hs(struct mwifiex_adapter *adapter)
                return false;
        }
 
-       wait_event_interruptible(adapter->hs_activate_wait_q,
-                                adapter->hs_activate_wait_q_woken);
+       if (wait_event_interruptible(adapter->hs_activate_wait_q,
+                                    adapter->hs_activate_wait_q_woken)) {
+               dev_err(adapter->dev, "hs_activate_wait_q terminated\n");
+               return false;
+       }
 
        return true;
 }
index f221b95b90b3c2935c67e9c272d5f1c2a89000d4..83564d36e801884bfb6d9c566653542dbe60cee4 100644 (file)
@@ -4250,9 +4250,11 @@ static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw,
        p->amsdu_enabled = 0;
 
        rc = mwl8k_post_cmd(hw, &cmd->header);
+       if (!rc)
+               rc = p->station_id;
        kfree(cmd);
 
-       return rc ? rc : p->station_id;
+       return rc;
 }
 
 static int mwl8k_cmd_update_stadb_del(struct ieee80211_hw *hw,
index 21b1bbb93a7e41452720a6f9ede17cba3951393e..b80bc4612581857455c2e076de835f66ac5be710 100644 (file)
@@ -57,12 +57,12 @@ config RTL8192CU
 
 config RTLWIFI
        tristate
-       depends on RTL8192CE || RTL8192CU || RTL8192SE || RTL8192DE
+       depends on RTL8192CE || RTL8192CU || RTL8192SE || RTL8192DE || RTL8723AE
        default m
 
 config RTLWIFI_DEBUG
        bool "Additional debugging output"
-       depends on RTL8192CE || RTL8192CU || RTL8192SE || RTL8192DE
+       depends on RTL8192CE || RTL8192CU || RTL8192SE || RTL8192DE || RTL8723AE
        default y
 
 config RTL8192C_COMMON
index 1d5d3604e3e03875f5e9ec4453ad2291c5de3c33..246e5352f2e15a850dbc7971c08bdbbcffbc7172 100644 (file)
@@ -692,7 +692,7 @@ u8 rtl92c_phy_sw_chnl(struct ieee80211_hw *hw)
        if (!(is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) {
                rtl92c_phy_sw_chnl_callback(hw);
                RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD,
-                        "sw_chnl_inprogress false schdule workitem\n");
+                        "sw_chnl_inprogress false schedule workitem\n");
                rtlphy->sw_chnl_inprogress = false;
        } else {
                RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD,
index 39cc7938eedfe44da35ea7a37134a705cfc4db5e..3d8536bb0d2bf5055c181d5d8f4b67d25153488b 100644 (file)
@@ -1106,7 +1106,7 @@ u8 rtl8723ae_phy_sw_chnl(struct ieee80211_hw *hw)
        if (!(is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) {
                rtl8723ae_phy_sw_chnl_callback(hw);
                RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD,
-                        "sw_chnl_inprogress false schdule workitem\n");
+                        "sw_chnl_inprogress false schedule workitem\n");
                rtlphy->sw_chnl_inprogress = false;
        } else {
                RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD,
index 26ffd3e3fb7463f84ce4385691fe19e028a26d0a..2c113de943233db361a41e2bdcd92202c37a32b7 100644 (file)
@@ -44,7 +44,6 @@ extern bool pciehp_poll_mode;
 extern int pciehp_poll_time;
 extern bool pciehp_debug;
 extern bool pciehp_force;
-extern struct workqueue_struct *pciehp_wq;
 
 #define dbg(format, arg...)                                            \
 do {                                                                   \
@@ -78,6 +77,7 @@ struct slot {
        struct hotplug_slot *hotplug_slot;
        struct delayed_work work;       /* work for button event */
        struct mutex lock;
+       struct workqueue_struct *wq;
 };
 
 struct event_info {
index 916bf4f53abadc16d9205ac41dc9e3edc6f839ec..939bd1d4b5b138b8f8bbe319007973579a6c39df 100644 (file)
@@ -42,7 +42,6 @@ bool pciehp_debug;
 bool pciehp_poll_mode;
 int pciehp_poll_time;
 bool pciehp_force;
-struct workqueue_struct *pciehp_wq;
 
 #define DRIVER_VERSION "0.4"
 #define DRIVER_AUTHOR  "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
@@ -340,18 +339,13 @@ static int __init pcied_init(void)
 {
        int retval = 0;
 
-       pciehp_wq = alloc_workqueue("pciehp", 0, 0);
-       if (!pciehp_wq)
-               return -ENOMEM;
-
        pciehp_firmware_init();
        retval = pcie_port_service_register(&hpdriver_portdrv);
        dbg("pcie_port_service_register = %d\n", retval);
        info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
-       if (retval) {
-               destroy_workqueue(pciehp_wq);
+       if (retval)
                dbg("Failure to register service\n");
-       }
+
        return retval;
 }
 
@@ -359,7 +353,6 @@ static void __exit pcied_cleanup(void)
 {
        dbg("unload_pciehpd()\n");
        pcie_port_service_unregister(&hpdriver_portdrv);
-       destroy_workqueue(pciehp_wq);
        info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
 }
 
index 27f44295a65738976b4be0f07f4cd33b04e2cc85..38f01867917521a6402e92dbaaf5add19b27a640 100644 (file)
@@ -49,7 +49,7 @@ static int queue_interrupt_event(struct slot *p_slot, u32 event_type)
        info->p_slot = p_slot;
        INIT_WORK(&info->work, interrupt_event_handler);
 
-       queue_work(pciehp_wq, &info->work);
+       queue_work(p_slot->wq, &info->work);
 
        return 0;
 }
@@ -344,7 +344,7 @@ void pciehp_queue_pushbutton_work(struct work_struct *work)
                kfree(info);
                goto out;
        }
-       queue_work(pciehp_wq, &info->work);
+       queue_work(p_slot->wq, &info->work);
  out:
        mutex_unlock(&p_slot->lock);
 }
@@ -377,7 +377,7 @@ static void handle_button_press_event(struct slot *p_slot)
                if (ATTN_LED(ctrl))
                        pciehp_set_attention_status(p_slot, 0);
 
-               queue_delayed_work(pciehp_wq, &p_slot->work, 5*HZ);
+               queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ);
                break;
        case BLINKINGOFF_STATE:
        case BLINKINGON_STATE:
@@ -439,7 +439,7 @@ static void handle_surprise_event(struct slot *p_slot)
        else
                p_slot->state = POWERON_STATE;
 
-       queue_work(pciehp_wq, &info->work);
+       queue_work(p_slot->wq, &info->work);
 }
 
 static void interrupt_event_handler(struct work_struct *work)
index 13b2eaf7ba434f205896785a17eaf335842fbd66..5127f3f418211496ddf44b335ccc5ab44fbac856 100644 (file)
@@ -773,23 +773,32 @@ static void pcie_shutdown_notification(struct controller *ctrl)
 static int pcie_init_slot(struct controller *ctrl)
 {
        struct slot *slot;
+       char name[32];
 
        slot = kzalloc(sizeof(*slot), GFP_KERNEL);
        if (!slot)
                return -ENOMEM;
 
+       snprintf(name, sizeof(name), "pciehp-%u", PSN(ctrl));
+       slot->wq = alloc_workqueue(name, 0, 0);
+       if (!slot->wq)
+               goto abort;
+
        slot->ctrl = ctrl;
        mutex_init(&slot->lock);
        INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work);
        ctrl->slot = slot;
        return 0;
+abort:
+       kfree(slot);
+       return -ENOMEM;
 }
 
 static void pcie_cleanup_slot(struct controller *ctrl)
 {
        struct slot *slot = ctrl->slot;
        cancel_delayed_work(&slot->work);
-       flush_workqueue(pciehp_wq);
+       destroy_workqueue(slot->wq);
        kfree(slot);
 }
 
index ca64932e658b0c5e981c5bb746663045da9cc7ce..b849f995075a8cd13927ce0ee725279638b5796e 100644 (file)
@@ -46,8 +46,6 @@
 extern bool shpchp_poll_mode;
 extern int shpchp_poll_time;
 extern bool shpchp_debug;
-extern struct workqueue_struct *shpchp_wq;
-extern struct workqueue_struct *shpchp_ordered_wq;
 
 #define dbg(format, arg...)                                            \
 do {                                                                   \
@@ -91,6 +89,7 @@ struct slot {
        struct list_head        slot_list;
        struct delayed_work work;       /* work for button event */
        struct mutex lock;
+       struct workqueue_struct *wq;
        u8 hp_slot;
 };
 
index b6de307248e43cedf52a0a27eacf9211b61efe72..3100c52c837cfceccc7314646898f30846c96b39 100644 (file)
@@ -39,8 +39,6 @@
 bool shpchp_debug;
 bool shpchp_poll_mode;
 int shpchp_poll_time;
-struct workqueue_struct *shpchp_wq;
-struct workqueue_struct *shpchp_ordered_wq;
 
 #define DRIVER_VERSION "0.4"
 #define DRIVER_AUTHOR  "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
@@ -129,6 +127,14 @@ static int init_slots(struct controller *ctrl)
                slot->device = ctrl->slot_device_offset + i;
                slot->hpc_ops = ctrl->hpc_ops;
                slot->number = ctrl->first_slot + (ctrl->slot_num_inc * i);
+
+               snprintf(name, sizeof(name), "shpchp-%d", slot->number);
+               slot->wq = alloc_workqueue(name, 0, 0);
+               if (!slot->wq) {
+                       retval = -ENOMEM;
+                       goto error_info;
+               }
+
                mutex_init(&slot->lock);
                INIT_DELAYED_WORK(&slot->work, shpchp_queue_pushbutton_work);
 
@@ -148,7 +154,7 @@ static int init_slots(struct controller *ctrl)
                if (retval) {
                        ctrl_err(ctrl, "pci_hp_register failed with error %d\n",
                                 retval);
-                       goto error_info;
+                       goto error_slotwq;
                }
 
                get_power_status(hotplug_slot, &info->power_status);
@@ -160,6 +166,8 @@ static int init_slots(struct controller *ctrl)
        }
 
        return 0;
+error_slotwq:
+       destroy_workqueue(slot->wq);
 error_info:
        kfree(info);
 error_hpslot:
@@ -180,8 +188,7 @@ void cleanup_slots(struct controller *ctrl)
                slot = list_entry(tmp, struct slot, slot_list);
                list_del(&slot->slot_list);
                cancel_delayed_work(&slot->work);
-               flush_workqueue(shpchp_wq);
-               flush_workqueue(shpchp_ordered_wq);
+               destroy_workqueue(slot->wq);
                pci_hp_deregister(slot->hotplug_slot);
        }
 }
@@ -364,25 +371,12 @@ static struct pci_driver shpc_driver = {
 
 static int __init shpcd_init(void)
 {
-       int retval = 0;
-
-       shpchp_wq = alloc_ordered_workqueue("shpchp", 0);
-       if (!shpchp_wq)
-               return -ENOMEM;
-
-       shpchp_ordered_wq = alloc_ordered_workqueue("shpchp_ordered", 0);
-       if (!shpchp_ordered_wq) {
-               destroy_workqueue(shpchp_wq);
-               return -ENOMEM;
-       }
+       int retval;
 
        retval = pci_register_driver(&shpc_driver);
        dbg("%s: pci_register_driver = %d\n", __func__, retval);
        info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
-       if (retval) {
-               destroy_workqueue(shpchp_ordered_wq);
-               destroy_workqueue(shpchp_wq);
-       }
+
        return retval;
 }
 
@@ -390,8 +384,6 @@ static void __exit shpcd_cleanup(void)
 {
        dbg("unload_shpchpd()\n");
        pci_unregister_driver(&shpc_driver);
-       destroy_workqueue(shpchp_ordered_wq);
-       destroy_workqueue(shpchp_wq);
        info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
 }
 
index f9b5a52e4115c321367547c30644b545200928a9..58499277903a4ab4a6225982d88876de399120e2 100644 (file)
@@ -51,7 +51,7 @@ static int queue_interrupt_event(struct slot *p_slot, u32 event_type)
        info->p_slot = p_slot;
        INIT_WORK(&info->work, interrupt_event_handler);
 
-       queue_work(shpchp_wq, &info->work);
+       queue_work(p_slot->wq, &info->work);
 
        return 0;
 }
@@ -453,7 +453,7 @@ void shpchp_queue_pushbutton_work(struct work_struct *work)
                kfree(info);
                goto out;
        }
-       queue_work(shpchp_ordered_wq, &info->work);
+       queue_work(p_slot->wq, &info->work);
  out:
        mutex_unlock(&p_slot->lock);
 }
@@ -501,7 +501,7 @@ static void handle_button_press_event(struct slot *p_slot)
                p_slot->hpc_ops->green_led_blink(p_slot);
                p_slot->hpc_ops->set_attention_status(p_slot, 0);
 
-               queue_delayed_work(shpchp_wq, &p_slot->work, 5*HZ);
+               queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ);
                break;
        case BLINKINGOFF_STATE:
        case BLINKINGON_STATE:
index bafd2bbcaf6541d1983fababa4563ae174837e15..c18e5bf444faa693d41c6a9103ef7ab8982b07da 100644 (file)
@@ -739,7 +739,7 @@ EXPORT_SYMBOL_GPL(pci_num_vf);
 /**
  * pci_sriov_set_totalvfs -- reduce the TotalVFs available
  * @dev: the PCI PF device
- * numvfs: number that should be used for TotalVFs supported
+ * @numvfs: number that should be used for TotalVFs supported
  *
  * Should be called from PF driver's probe routine with
  * device's mutex held.
index 6c8bc5809787d7e2a4b82c72c590918c20f49665..fde4a32a0295082a95de76b6ea08dc1d59fdc2a9 100644 (file)
@@ -82,4 +82,4 @@ endchoice
 
 config PCIE_PME
        def_bool y
-       depends on PCIEPORTBUS && PM_RUNTIME && EXPERIMENTAL && ACPI
+       depends on PCIEPORTBUS && PM_RUNTIME && ACPI
index 421bbc5fee324b485f409e7a025338bc524eecf5..564d97f94b6cd4d248b7cdad8b3b4e138144ff97 100644 (file)
@@ -630,6 +630,7 @@ static void aer_recover_work_func(struct work_struct *work)
                        continue;
                }
                do_recovery(pdev, entry.severity);
+               pci_dev_put(pdev);
        }
 }
 #endif
index b52630b8eada26ad35f8304e436c63f3e064edd3..8474b6a4fc9bb714e7ab85cf539353fd151b8125 100644 (file)
@@ -771,6 +771,9 @@ void pcie_clear_aspm(struct pci_bus *bus)
 {
        struct pci_dev *child;
 
+       if (aspm_force)
+               return;
+
        /*
         * Clear any ASPM setup that the firmware has carried out on this bus
         */
index c31aeb01bb0002cc648b603c6c526ad83f9ab9a9..efaecefe3f8cc5fda8326c5dbd647e56076a4984 100644 (file)
@@ -181,7 +181,6 @@ config PINCTRL_COH901
 
 config PINCTRL_SAMSUNG
        bool
-       depends on OF && GPIOLIB
        select PINMUX
        select PINCONF
 
index 69aba3697287d02e749fc5c27155c026518601b7..428ea96a94d356a3caee8b971a1df51d8c9564c3 100644 (file)
@@ -588,7 +588,7 @@ static int dove_pinctrl_probe(struct platform_device *pdev)
 {
        const struct of_device_id *match =
                of_match_device(dove_pinctrl_of_match, &pdev->dev);
-       pdev->dev.platform_data = match->data;
+       pdev->dev.platform_data = (void *)match->data;
 
        /*
         * General MPP Configuration Register is part of pdma registers.
index f12084e180579afe19cb3914ff8d4e4db7573eed..cdd483df673eaf5d38d522ba3e8f5ef26f835c3c 100644 (file)
@@ -66,9 +66,9 @@ static struct mvebu_mpp_mode mv88f6xxx_mpp_modes[] = {
                MPP_VAR_FUNCTION(0x5, "sata0", "act",    V(0, 1, 1, 1, 1, 0)),
                MPP_VAR_FUNCTION(0xb, "lcd", "vsync",    V(0, 0, 0, 0, 1, 0))),
        MPP_MODE(6,
-               MPP_VAR_FUNCTION(0x0, "sysrst", "out",   V(1, 1, 1, 1, 1, 1)),
-               MPP_VAR_FUNCTION(0x1, "spi", "mosi",     V(1, 1, 1, 1, 1, 1)),
-               MPP_VAR_FUNCTION(0x2, "ptp", "trig",     V(1, 1, 1, 1, 0, 0))),
+               MPP_VAR_FUNCTION(0x1, "sysrst", "out",   V(1, 1, 1, 1, 1, 1)),
+               MPP_VAR_FUNCTION(0x2, "spi", "mosi",     V(1, 1, 1, 1, 1, 1)),
+               MPP_VAR_FUNCTION(0x3, "ptp", "trig",     V(1, 1, 1, 1, 0, 0))),
        MPP_MODE(7,
                MPP_VAR_FUNCTION(0x0, "gpo", NULL,       V(1, 1, 1, 1, 1, 1)),
                MPP_VAR_FUNCTION(0x1, "pex", "rsto",     V(1, 1, 1, 1, 0, 1)),
@@ -458,7 +458,7 @@ static int kirkwood_pinctrl_probe(struct platform_device *pdev)
 {
        const struct of_device_id *match =
                of_match_device(kirkwood_pinctrl_of_match, &pdev->dev);
-       pdev->dev.platform_data = match->data;
+       pdev->dev.platform_data = (void *)match->data;
        return mvebu_pinctrl_probe(pdev);
 }
 
index de05b64f0da695708f2c5ef201c5438c96946d88..142729914c347fc305e32449c11f637a44ad654f 100644 (file)
@@ -599,7 +599,7 @@ static int exynos5440_gpio_direction_output(struct gpio_chip *gc, unsigned offse
 }
 
 /* parse the pin numbers listed in the 'samsung,exynos5440-pins' property */
-static int __init exynos5440_pinctrl_parse_dt_pins(struct platform_device *pdev,
+static int exynos5440_pinctrl_parse_dt_pins(struct platform_device *pdev,
                        struct device_node *cfg_np, unsigned int **pin_list,
                        unsigned int *npins)
 {
@@ -630,7 +630,7 @@ static int __init exynos5440_pinctrl_parse_dt_pins(struct platform_device *pdev,
  * Parse the information about all the available pin groups and pin functions
  * from device node of the pin-controller.
  */
-static int __init exynos5440_pinctrl_parse_dt(struct platform_device *pdev,
+static int exynos5440_pinctrl_parse_dt(struct platform_device *pdev,
                                struct exynos5440_pinctrl_priv_data *priv)
 {
        struct device *dev = &pdev->dev;
@@ -723,7 +723,7 @@ static int __init exynos5440_pinctrl_parse_dt(struct platform_device *pdev,
 }
 
 /* register the pinctrl interface with the pinctrl subsystem */
-static int __init exynos5440_pinctrl_register(struct platform_device *pdev,
+static int exynos5440_pinctrl_register(struct platform_device *pdev,
                                struct exynos5440_pinctrl_priv_data *priv)
 {
        struct device *dev = &pdev->dev;
@@ -798,7 +798,7 @@ static int __init exynos5440_pinctrl_register(struct platform_device *pdev,
 }
 
 /* register the gpiolib interface with the gpiolib subsystem */
-static int __init exynos5440_gpiolib_register(struct platform_device *pdev,
+static int exynos5440_gpiolib_register(struct platform_device *pdev,
                                struct exynos5440_pinctrl_priv_data *priv)
 {
        struct gpio_chip *gc;
@@ -831,7 +831,7 @@ static int __init exynos5440_gpiolib_register(struct platform_device *pdev,
 }
 
 /* unregister the gpiolib interface with the gpiolib subsystem */
-static int __init exynos5440_gpiolib_unregister(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);
index dd227d21dcf28563c1d0902d51fa8a3b8140aeed..23af9f1f9c35e6d9d7793075ad7ec2df92070043 100644 (file)
@@ -146,7 +146,7 @@ free:
 static void mxs_dt_free_map(struct pinctrl_dev *pctldev,
                            struct pinctrl_map *map, unsigned num_maps)
 {
-       int i;
+       u32 i;
 
        for (i = 0; i < num_maps; i++) {
                if (map[i].type == PIN_MAP_TYPE_MUX_GROUP)
@@ -203,7 +203,7 @@ static int mxs_pinctrl_enable(struct pinctrl_dev *pctldev, unsigned selector,
        void __iomem *reg;
        u8 bank, shift;
        u16 pin;
-       int i;
+       u32 i;
 
        for (i = 0; i < g->npins; i++) {
                bank = PINID_TO_BANK(g->pins[i]);
@@ -256,7 +256,7 @@ static int mxs_pinconf_group_set(struct pinctrl_dev *pctldev,
        void __iomem *reg;
        u8 ma, vol, pull, bank, shift;
        u16 pin;
-       int i;
+       u32 i;
 
        ma = CONFIG_TO_MA(config);
        vol = CONFIG_TO_VOL(config);
@@ -345,8 +345,7 @@ static int mxs_pinctrl_parse_group(struct platform_device *pdev,
        const char *propname = "fsl,pinmux-ids";
        char *group;
        int length = strlen(np->name) + SUFFIX_LEN;
-       int i;
-       u32 val;
+       u32 val, i;
 
        group = devm_kzalloc(&pdev->dev, length, GFP_KERNEL);
        if (!group)
index 1bb16ffb4e41a9d0f252fc8a563188f97f343642..5767b18ebdff6d1639bd37c6007f9c93553a3f58 100644 (file)
@@ -676,7 +676,7 @@ int nmk_gpio_set_mode(int gpio, int gpio_mode)
 }
 EXPORT_SYMBOL(nmk_gpio_set_mode);
 
-static int nmk_prcm_gpiocr_get_mode(struct pinctrl_dev *pctldev, int gpio)
+static int __maybe_unused nmk_prcm_gpiocr_get_mode(struct pinctrl_dev *pctldev, int gpio)
 {
        int i;
        u16 reg;
index f6a360b86eb6465fef029e777147ccbd3bc95b66..5c32e880bcb24315b4b2db260294106765eb4240 100644 (file)
@@ -30,7 +30,6 @@
 #define PCS_MUX_BITS_NAME              "pinctrl-single,bits"
 #define PCS_REG_NAME_LEN               ((sizeof(unsigned long) * 2) + 1)
 #define PCS_OFF_DISABLED               ~0U
-#define PCS_MAX_GPIO_VALUES            2
 
 /**
  * struct pcs_pingroup - pingroups for a function
@@ -77,16 +76,6 @@ struct pcs_function {
        struct list_head node;
 };
 
-/**
- * struct pcs_gpio_range - pinctrl gpio range
- * @range:     subrange of the GPIO number space
- * @gpio_func: gpio function value in the pinmux register
- */
-struct pcs_gpio_range {
-       struct pinctrl_gpio_range range;
-       int gpio_func;
-};
-
 /**
  * struct pcs_data - wrapper for data needed by pinctrl framework
  * @pa:                pindesc array
@@ -414,26 +403,9 @@ static void pcs_disable(struct pinctrl_dev *pctldev, unsigned fselector,
 }
 
 static int pcs_request_gpio(struct pinctrl_dev *pctldev,
-                           struct pinctrl_gpio_range *range, unsigned pin)
+                       struct pinctrl_gpio_range *range, unsigned offset)
 {
-       struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev);
-       struct pcs_gpio_range *gpio = NULL;
-       int end, mux_bytes;
-       unsigned data;
-
-       gpio = container_of(range, struct pcs_gpio_range, range);
-       end = range->pin_base + range->npins - 1;
-       if (pin < range->pin_base || pin > end) {
-               dev_err(pctldev->dev,
-                       "pin %d isn't in the range of %d to %d\n",
-                       pin, range->pin_base, end);
-               return -EINVAL;
-       }
-       mux_bytes = pcs->width / BITS_PER_BYTE;
-       data = pcs->read(pcs->base + pin * mux_bytes) & ~pcs->fmask;
-       data |= gpio->gpio_func;
-       pcs->write(data, pcs->base + pin * mux_bytes);
-       return 0;
+       return -ENOTSUPP;
 }
 
 static struct pinmux_ops pcs_pinmux_ops = {
@@ -907,49 +879,6 @@ static void pcs_free_resources(struct pcs_device *pcs)
 
 static struct of_device_id pcs_of_match[];
 
-static int pcs_add_gpio_range(struct device_node *node, struct pcs_device *pcs)
-{
-       struct pcs_gpio_range *gpio;
-       struct device_node *child;
-       struct resource r;
-       const char name[] = "pinctrl-single";
-       u32 gpiores[PCS_MAX_GPIO_VALUES];
-       int ret, i = 0, mux_bytes = 0;
-
-       for_each_child_of_node(node, child) {
-               ret = of_address_to_resource(child, 0, &r);
-               if (ret < 0)
-                       continue;
-               memset(gpiores, 0, sizeof(u32) * PCS_MAX_GPIO_VALUES);
-               ret = of_property_read_u32_array(child, "pinctrl-single,gpio",
-                                                gpiores, PCS_MAX_GPIO_VALUES);
-               if (ret < 0)
-                       continue;
-               gpio = devm_kzalloc(pcs->dev, sizeof(*gpio), GFP_KERNEL);
-               if (!gpio) {
-                       dev_err(pcs->dev, "failed to allocate pcs gpio\n");
-                       return -ENOMEM;
-               }
-               gpio->range.name = devm_kzalloc(pcs->dev, sizeof(name),
-                                               GFP_KERNEL);
-               if (!gpio->range.name) {
-                       dev_err(pcs->dev, "failed to allocate range name\n");
-                       return -ENOMEM;
-               }
-               memcpy((char *)gpio->range.name, name, sizeof(name));
-
-               gpio->range.id = i++;
-               gpio->range.base = gpiores[0];
-               gpio->gpio_func = gpiores[1];
-               mux_bytes = pcs->width / BITS_PER_BYTE;
-               gpio->range.pin_base = (r.start - pcs->res->start) / mux_bytes;
-               gpio->range.npins = (r.end - r.start) / mux_bytes + 1;
-
-               pinctrl_add_gpio_range(pcs->pctl, &gpio->range);
-       }
-       return 0;
-}
-
 static int pcs_probe(struct platform_device *pdev)
 {
        struct device_node *np = pdev->dev.of_node;
@@ -1046,10 +975,6 @@ static int pcs_probe(struct platform_device *pdev)
                goto free;
        }
 
-       ret = pcs_add_gpio_range(np, pcs);
-       if (ret < 0)
-               goto free;
-
        dev_info(pcs->dev, "%i pins at pa %p size %u\n",
                 pcs->desc.npins, pcs->base, pcs->size);
 
index 7481146a5b473c1cab6745a7d6ac1ae76e66257e..97c2be195efc36eeb76bd848f2c1327be7f168c2 100644 (file)
@@ -244,7 +244,7 @@ static int __init ibm_rtl_init(void) {
        if (force)
                pr_warn("module loaded by force\n");
        /* first ensure that we are running on IBM HW */
-       else if (efi_enabled || !dmi_check_system(ibm_rtl_dmi_table))
+       else if (efi_enabled(EFI_BOOT) || !dmi_check_system(ibm_rtl_dmi_table))
                return -ENODEV;
 
        /* Get the address for the Extended BIOS Data Area */
index 71623a2ff3e87b0cffe3d5e85ae96480600a7e59..d1f0300531766f64ab741ccbd4d388db0644d427 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/seq_file.h>
 #include <linux/debugfs.h>
 #include <linux/ctype.h>
+#include <linux/efi.h>
 #include <acpi/video.h>
 
 /*
@@ -1544,6 +1545,9 @@ static int __init samsung_init(void)
        struct samsung_laptop *samsung;
        int ret;
 
+       if (efi_enabled(EFI_BOOT))
+               return -ENODEV;
+
        quirks = &samsung_unknown;
        if (!force && !dmi_check_system(samsung_dmi_table))
                return -ENODEV;
index 0f65b246cc0c8d24c559cdeafd340232d1c55e0c..278584302f2d162c56599ad61a62ade3cdcceadd 100644 (file)
@@ -1885,9 +1885,15 @@ int regulator_can_change_voltage(struct regulator *regulator)
        struct regulator_dev    *rdev = regulator->rdev;
 
        if (rdev->constraints &&
-           rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE &&
-           (rdev->desc->n_voltages - rdev->desc->linear_min_sel) > 1)
-               return 1;
+           (rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) {
+               if (rdev->desc->n_voltages - rdev->desc->linear_min_sel > 1)
+                       return 1;
+
+               if (rdev->desc->continuous_voltage_range &&
+                   rdev->constraints->min_uV && rdev->constraints->max_uV &&
+                   rdev->constraints->min_uV != rdev->constraints->max_uV)
+                       return 1;
+       }
 
        return 0;
 }
@@ -3315,7 +3321,8 @@ static void rdev_init_debugfs(struct regulator_dev *rdev)
  * @config: runtime configuration for regulator
  *
  * Called by regulator drivers to register a regulator.
- * Returns 0 on success.
+ * Returns a valid pointer to struct regulator_dev on success
+ * or an ERR_PTR() on error.
  */
 struct regulator_dev *
 regulator_register(const struct regulator_desc *regulator_desc,
index 261f3d2299bc0a5d2d074824211172be7a6b8448..89bd2faaef8cf627cddcd9f1b7a03c9b4cab5fae 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #include <linux/slab.h>
+#include <linux/module.h>
 
 #include "dbx500-prcmu.h"
 
index df0eafb0dc7e63a1f4a9e8bfb989d91caa1d9f63..02be7fcae32f5d88d4a702267121aac93ae1c263 100644 (file)
@@ -71,26 +71,26 @@ struct voltage_map_desc {
        int step;
 };
 
-/* Voltage maps in mV */
+/* Voltage maps in uV */
 static const struct voltage_map_desc ldo_voltage_map_desc = {
-       .min = 800,     .max = 3950,    .step = 50,
+       .min = 800000,  .max = 3950000, .step = 50000,
 }; /* LDO1 ~ 18, 21 all */
 
 static const struct voltage_map_desc buck1245_voltage_map_desc = {
-       .min = 650,     .max = 2225,    .step = 25,
+       .min = 650000,  .max = 2225000, .step = 25000,
 }; /* Buck1, 2, 4, 5 */
 
 static const struct voltage_map_desc buck37_voltage_map_desc = {
-       .min = 750,     .max = 3900,    .step = 50,
+       .min = 750000,  .max = 3900000, .step = 50000,
 }; /* Buck3, 7 */
 
-/* current map in mA */
+/* current map in uA */
 static const struct voltage_map_desc charger_current_map_desc = {
-       .min = 200,     .max = 950,     .step = 50,
+       .min = 200000,  .max = 950000,  .step = 50000,
 };
 
 static const struct voltage_map_desc topoff_current_map_desc = {
-       .min = 50,      .max = 200,     .step = 10,
+       .min = 50000,   .max = 200000,  .step = 10000,
 };
 
 static const struct voltage_map_desc *reg_voltage_map[] = {
@@ -194,7 +194,7 @@ static int max8997_list_voltage(struct regulator_dev *rdev,
        if (val > desc->max)
                return -EINVAL;
 
-       return val * 1000;
+       return val;
 }
 
 static int max8997_get_enable_register(struct regulator_dev *rdev,
@@ -485,7 +485,6 @@ static int max8997_set_voltage_ldobuck(struct regulator_dev *rdev,
 {
        struct max8997_data *max8997 = rdev_get_drvdata(rdev);
        struct i2c_client *i2c = max8997->iodev->i2c;
-       int min_vol = min_uV / 1000, max_vol = max_uV / 1000;
        const struct voltage_map_desc *desc;
        int rid = rdev_get_id(rdev);
        int i, reg, shift, mask, ret;
@@ -509,7 +508,7 @@ static int max8997_set_voltage_ldobuck(struct regulator_dev *rdev,
 
        desc = reg_voltage_map[rid];
 
-       i = max8997_get_voltage_proper_val(desc, min_vol, max_vol);
+       i = max8997_get_voltage_proper_val(desc, min_uV, max_uV);
        if (i < 0)
                return i;
 
@@ -557,7 +556,7 @@ static int max8997_set_voltage_ldobuck_time_sel(struct regulator_dev *rdev,
        case MAX8997_BUCK4:
        case MAX8997_BUCK5:
                return DIV_ROUND_UP(desc->step * (new_selector - old_selector),
-                                   max8997->ramp_delay);
+                                   max8997->ramp_delay * 1000);
        }
 
        return 0;
@@ -656,7 +655,6 @@ static int max8997_set_voltage_buck(struct regulator_dev *rdev,
        const struct voltage_map_desc *desc;
        int new_val, new_idx, damage, tmp_val, tmp_idx, tmp_dmg;
        bool gpio_dvs_mode = false;
-       int min_vol = min_uV / 1000, max_vol = max_uV / 1000;
 
        if (rid < MAX8997_BUCK1 || rid > MAX8997_BUCK7)
                return -EINVAL;
@@ -681,7 +679,7 @@ static int max8997_set_voltage_buck(struct regulator_dev *rdev,
                                                selector);
 
        desc = reg_voltage_map[rid];
-       new_val = max8997_get_voltage_proper_val(desc, min_vol, max_vol);
+       new_val = max8997_get_voltage_proper_val(desc, min_uV, max_uV);
        if (new_val < 0)
                return new_val;
 
@@ -1123,8 +1121,8 @@ static int max8997_pmic_probe(struct platform_device *pdev)
                max8997->buck1_vol[i] = ret =
                        max8997_get_voltage_proper_val(
                                        &buck1245_voltage_map_desc,
-                                       pdata->buck1_voltage[i] / 1000,
-                                       pdata->buck1_voltage[i] / 1000 +
+                                       pdata->buck1_voltage[i],
+                                       pdata->buck1_voltage[i] +
                                        buck1245_voltage_map_desc.step);
                if (ret < 0)
                        goto err_out;
@@ -1132,8 +1130,8 @@ static int max8997_pmic_probe(struct platform_device *pdev)
                max8997->buck2_vol[i] = ret =
                        max8997_get_voltage_proper_val(
                                        &buck1245_voltage_map_desc,
-                                       pdata->buck2_voltage[i] / 1000,
-                                       pdata->buck2_voltage[i] / 1000 +
+                                       pdata->buck2_voltage[i],
+                                       pdata->buck2_voltage[i] +
                                        buck1245_voltage_map_desc.step);
                if (ret < 0)
                        goto err_out;
@@ -1141,8 +1139,8 @@ static int max8997_pmic_probe(struct platform_device *pdev)
                max8997->buck5_vol[i] = ret =
                        max8997_get_voltage_proper_val(
                                        &buck1245_voltage_map_desc,
-                                       pdata->buck5_voltage[i] / 1000,
-                                       pdata->buck5_voltage[i] / 1000 +
+                                       pdata->buck5_voltage[i],
+                                       pdata->buck5_voltage[i] +
                                        buck1245_voltage_map_desc.step);
                if (ret < 0)
                        goto err_out;
index b821d08eb64ae84b643f2cfe93cd0764738d21c9..1f0df4046b86d0a303c229379091d76407f61129 100644 (file)
@@ -51,39 +51,39 @@ struct voltage_map_desc {
        int step;
 };
 
-/* Voltage maps */
+/* Voltage maps in uV*/
 static const struct voltage_map_desc ldo23_voltage_map_desc = {
-       .min = 800,     .step = 50,     .max = 1300,
+       .min = 800000,  .step = 50000,  .max = 1300000,
 };
 static const struct voltage_map_desc ldo456711_voltage_map_desc = {
-       .min = 1600,    .step = 100,    .max = 3600,
+       .min = 1600000, .step = 100000, .max = 3600000,
 };
 static const struct voltage_map_desc ldo8_voltage_map_desc = {
-       .min = 3000,    .step = 100,    .max = 3600,
+       .min = 3000000, .step = 100000, .max = 3600000,
 };
 static const struct voltage_map_desc ldo9_voltage_map_desc = {
-       .min = 2800,    .step = 100,    .max = 3100,
+       .min = 2800000, .step = 100000, .max = 3100000,
 };
 static const struct voltage_map_desc ldo10_voltage_map_desc = {
-       .min = 950,     .step = 50,     .max = 1300,
+       .min = 95000,   .step = 50000,  .max = 1300000,
 };
 static const struct voltage_map_desc ldo1213_voltage_map_desc = {
-       .min = 800,     .step = 100,    .max = 3300,
+       .min = 800000,  .step = 100000, .max = 3300000,
 };
 static const struct voltage_map_desc ldo1415_voltage_map_desc = {
-       .min = 1200,    .step = 100,    .max = 3300,
+       .min = 1200000, .step = 100000, .max = 3300000,
 };
 static const struct voltage_map_desc ldo1617_voltage_map_desc = {
-       .min = 1600,    .step = 100,    .max = 3600,
+       .min = 1600000, .step = 100000, .max = 3600000,
 };
 static const struct voltage_map_desc buck12_voltage_map_desc = {
-       .min = 750,     .step = 25,     .max = 1525,
+       .min = 750000,  .step = 25000,  .max = 1525000,
 };
 static const struct voltage_map_desc buck3_voltage_map_desc = {
-       .min = 1600,    .step = 100,    .max = 3600,
+       .min = 1600000, .step = 100000, .max = 3600000,
 };
 static const struct voltage_map_desc buck4_voltage_map_desc = {
-       .min = 800,     .step = 100,    .max = 2300,
+       .min = 800000,  .step = 100000, .max = 2300000,
 };
 
 static const struct voltage_map_desc *ldo_voltage_map[] = {
@@ -445,9 +445,9 @@ static int max8998_set_voltage_buck_time_sel(struct regulator_dev *rdev,
        if (max8998->iodev->type == TYPE_MAX8998 && !(val & MAX8998_ENRAMP))
                return 0;
 
-       difference = (new_selector - old_selector) * desc->step;
+       difference = (new_selector - old_selector) * desc->step / 1000;
        if (difference > 0)
-               return difference / ((val & 0x0f) + 1);
+               return DIV_ROUND_UP(difference, (val & 0x0f) + 1);
 
        return 0;
 }
@@ -702,7 +702,7 @@ static int max8998_pmic_probe(struct platform_device *pdev)
                i = 0;
                while (buck12_voltage_map_desc.min +
                       buck12_voltage_map_desc.step*i
-                      < (pdata->buck1_voltage1 / 1000))
+                      < pdata->buck1_voltage1)
                        i++;
                max8998->buck1_vol[0] = i;
                ret = max8998_write_reg(i2c, MAX8998_REG_BUCK1_VOLTAGE1, i);
@@ -713,7 +713,7 @@ static int max8998_pmic_probe(struct platform_device *pdev)
                i = 0;
                while (buck12_voltage_map_desc.min +
                       buck12_voltage_map_desc.step*i
-                      < (pdata->buck1_voltage2 / 1000))
+                      < pdata->buck1_voltage2)
                        i++;
 
                max8998->buck1_vol[1] = i;
@@ -725,7 +725,7 @@ static int max8998_pmic_probe(struct platform_device *pdev)
                i = 0;
                while (buck12_voltage_map_desc.min +
                       buck12_voltage_map_desc.step*i
-                      < (pdata->buck1_voltage3 / 1000))
+                      < pdata->buck1_voltage3)
                        i++;
 
                max8998->buck1_vol[2] = i;
@@ -737,7 +737,7 @@ static int max8998_pmic_probe(struct platform_device *pdev)
                i = 0;
                while (buck12_voltage_map_desc.min +
                       buck12_voltage_map_desc.step*i
-                      < (pdata->buck1_voltage4 / 1000))
+                      < pdata->buck1_voltage4)
                        i++;
 
                max8998->buck1_vol[3] = i;
@@ -763,7 +763,7 @@ static int max8998_pmic_probe(struct platform_device *pdev)
                i = 0;
                while (buck12_voltage_map_desc.min +
                       buck12_voltage_map_desc.step*i
-                      < (pdata->buck2_voltage1 / 1000))
+                      < pdata->buck2_voltage1)
                        i++;
                max8998->buck2_vol[0] = i;
                ret = max8998_write_reg(i2c, MAX8998_REG_BUCK2_VOLTAGE1, i);
@@ -774,7 +774,7 @@ static int max8998_pmic_probe(struct platform_device *pdev)
                i = 0;
                while (buck12_voltage_map_desc.min +
                       buck12_voltage_map_desc.step*i
-                      < (pdata->buck2_voltage2 / 1000))
+                      < pdata->buck2_voltage2)
                        i++;
                max8998->buck2_vol[1] = i;
                ret = max8998_write_reg(i2c, MAX8998_REG_BUCK2_VOLTAGE2, i);
@@ -792,8 +792,8 @@ static int max8998_pmic_probe(struct platform_device *pdev)
                        int count = (desc->max - desc->min) / desc->step + 1;
 
                        regulators[index].n_voltages = count;
-                       regulators[index].min_uV = desc->min * 1000;
-                       regulators[index].uV_step = desc->step * 1000;
+                       regulators[index].min_uV = desc->min;
+                       regulators[index].uV_step = desc->step;
                }
 
                config.dev = max8998->dev;
index 9f991f2c525af531ec0b5450523bb2e0e75a5b54..33b65c9ad5d5be4da31d725678faeb7c5dc2b031 100644 (file)
@@ -214,7 +214,7 @@ static int s5m8767_reg_is_enabled(struct regulator_dev *rdev)
        struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev);
        int ret, reg;
        int mask = 0xc0, enable_ctrl;
-       u8 val;
+       unsigned int val;
 
        ret = s5m8767_get_register(rdev, &reg, &enable_ctrl);
        if (ret == -EINVAL)
@@ -306,7 +306,7 @@ static int s5m8767_get_voltage_sel(struct regulator_dev *rdev)
        struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev);
        int reg, mask, ret;
        int reg_id = rdev_get_id(rdev);
-       u8 val;
+       unsigned int val;
 
        ret = s5m8767_get_voltage_register(rdev, &reg);
        if (ret)
index b15d711bc8c66634c9792c26e15885e055aabe11..9019d0e7ecb6ad9868fa96e23fa0f77a1275fc75 100644 (file)
@@ -728,7 +728,7 @@ static int tps80031_regulator_probe(struct platform_device *pdev)
                        }
                }
                rdev = regulator_register(&ri->rinfo->desc, &config);
-               if (IS_ERR_OR_NULL(rdev)) {
+               if (IS_ERR(rdev)) {
                        dev_err(&pdev->dev,
                                "register regulator failed %s\n",
                                        ri->rinfo->desc.name);
index 96bafc5c3bf87c2426892cdc0ffa46756e7baf36..8f0dcfedb83cdfb9c613083574e8e591826beaf5 100644 (file)
@@ -227,7 +227,7 @@ static const struct rtc_class_ops da9055_rtc_ops = {
        .alarm_irq_enable = da9055_rtc_alarm_irq_enable,
 };
 
-static int __init da9055_rtc_device_init(struct da9055 *da9055,
+static int da9055_rtc_device_init(struct da9055 *da9055,
                                        struct da9055_pdata *pdata)
 {
        int ret;
index afb7cfa85ccc04ac637268ee0bba4240f05c12d3..c016ad81767a9d1be4697c267072a124f4398fc6 100644 (file)
@@ -506,6 +506,7 @@ isl1208_rtc_interrupt(int irq, void *data)
 {
        unsigned long timeout = jiffies + msecs_to_jiffies(1000);
        struct i2c_client *client = data;
+       struct rtc_device *rtc = i2c_get_clientdata(client);
        int handled = 0, sr, err;
 
        /*
@@ -528,6 +529,8 @@ isl1208_rtc_interrupt(int irq, void *data)
        if (sr & ISL1208_REG_SR_ALM) {
                dev_dbg(&client->dev, "alarm!\n");
 
+               rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
+
                /* Clear the alarm */
                sr &= ~ISL1208_REG_SR_ALM;
                sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR, sr);
index 08378e3cc21cf8209796d50876ee64ae5c0a4aaa..10c1a3454e48ecce74c595d5cd3a33a7b3f4af1d 100644 (file)
@@ -44,6 +44,7 @@
 #define RTC_YMR                0x34    /* Year match register */
 #define RTC_YLR                0x38    /* Year data load register */
 
+#define RTC_CR_EN      (1 << 0)        /* counter enable bit */
 #define RTC_CR_CWEN    (1 << 26)       /* Clockwatch enable bit */
 
 #define RTC_TCR_EN     (1 << 1) /* Periodic timer enable bit */
@@ -320,7 +321,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
        struct pl031_local *ldata;
        struct pl031_vendor_data *vendor = id->data;
        struct rtc_class_ops *ops = &vendor->ops;
-       unsigned long time;
+       unsigned long time, data;
 
        ret = amba_request_regions(adev, NULL);
        if (ret)
@@ -345,10 +346,11 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
        dev_dbg(&adev->dev, "designer ID = 0x%02x\n", amba_manf(adev));
        dev_dbg(&adev->dev, "revision = 0x%01x\n", amba_rev(adev));
 
+       data = readl(ldata->base + RTC_CR);
        /* Enable the clockwatch on ST Variants */
        if (vendor->clockwatch)
-               writel(readl(ldata->base + RTC_CR) | RTC_CR_CWEN,
-                      ldata->base + RTC_CR);
+               data |= RTC_CR_CWEN;
+       writel(data | RTC_CR_EN, ldata->base + RTC_CR);
 
        /*
         * On ST PL031 variants, the RTC reset value does not provide correct
index 00c930f4b6f322fcfd42b37c8f2e6926f0bdbbfd..2730533e2d2df2da9417882fb6891b5a65a17a1f 100644 (file)
@@ -137,7 +137,7 @@ static int vt8500_rtc_set_time(struct device *dev, struct rtc_time *tm)
                return -EINVAL;
        }
 
-       writel((bin2bcd(tm->tm_year - 100) << DATE_YEAR_S)
+       writel((bin2bcd(tm->tm_year % 100) << DATE_YEAR_S)
                | (bin2bcd(tm->tm_mon + 1) << DATE_MONTH_S)
                | (bin2bcd(tm->tm_mday))
                | ((tm->tm_year >= 200) << DATE_CENTURY_S),
index 68e80e2734a4be89763ca466a48621911570bce4..10729bbceceda4f49432e5eb647e04ae9d1399c4 100644 (file)
@@ -283,7 +283,7 @@ struct chsc_sei_nt2_area {
        u8  ccdf[PAGE_SIZE - 24 - 56];  /* content-code dependent field */
 } __packed;
 
-#define CHSC_SEI_NT0   0ULL
+#define CHSC_SEI_NT0   (1ULL << 63)
 #define CHSC_SEI_NT2   (1ULL << 61)
 
 struct chsc_sei {
@@ -291,7 +291,8 @@ struct chsc_sei {
        u32 reserved1;
        u64 ntsm;                       /* notification type mask */
        struct chsc_header response;
-       u32 reserved2;
+       u32 :24;
+       u8 nt;
        union {
                struct chsc_sei_nt0_area nt0_area;
                struct chsc_sei_nt2_area nt2_area;
@@ -496,17 +497,17 @@ static int __chsc_process_crw(struct chsc_sei *sei, u64 ntsm)
                                css_schedule_eval_all();
                        }
 
-                       switch (sei->ntsm) {
-                       case CHSC_SEI_NT0:
+                       switch (sei->nt) {
+                       case 0:
                                chsc_process_sei_nt0(&sei->u.nt0_area);
-                               return 1;
-                       case CHSC_SEI_NT2:
+                               break;
+                       case 2:
                                chsc_process_sei_nt2(&sei->u.nt2_area);
-                               return 1;
+                               break;
                        default:
-                               CIO_CRW_EVENT(2, "chsc: unhandled nt (nt=%08Lx)\n",
-                                             sei->ntsm);
-                               return 0;
+                               CIO_CRW_EVENT(2, "chsc: unhandled nt=%d\n",
+                                             sei->nt);
+                               break;
                        }
                } else {
                        CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n",
@@ -537,15 +538,7 @@ static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow)
        sei = sei_page;
 
        CIO_TRACE_EVENT(2, "prcss");
-
-       /*
-        * The ntsm does not allow to select NT0 and NT2 together. We need to
-        * first check for NT2, than additionally for NT0...
-        */
-#ifdef CONFIG_PCI
-       if (!__chsc_process_crw(sei, CHSC_SEI_NT2))
-#endif
-               __chsc_process_crw(sei, CHSC_SEI_NT0);
+       __chsc_process_crw(sei, CHSC_SEI_NT0 | CHSC_SEI_NT2);
 }
 
 void chsc_chp_online(struct chp_id chpid)
index d73fdcfeb45a19e1695dc188b045e29c72e57557..2839baa82a5a458d0035d59e8a5666ac07690779 100644 (file)
@@ -633,7 +633,7 @@ static int isci_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                return -ENOMEM;
        pci_set_drvdata(pdev, pci_info);
 
-       if (efi_enabled)
+       if (efi_enabled(EFI_RUNTIME_SERVICES))
                orom = isci_get_efi_var(pdev);
 
        if (!orom)
index 5aedcdf4ac5cd185a2ffac8ebcca2e3dcffd6c27..1ebe67cd18333c3b39f98d0e2badcd7b0a948090 100644 (file)
@@ -126,6 +126,12 @@ static int sh_clk_div_set_rate(struct clk *clk, unsigned long rate)
 
 static int sh_clk_div_enable(struct clk *clk)
 {
+       if (clk->div_mask == SH_CLK_DIV6_MSK) {
+               int ret = sh_clk_div_set_rate(clk, clk->rate);
+               if (ret < 0)
+                       return ret;
+       }
+
        sh_clk_write(sh_clk_read(clk) & ~CPG_CKSTP_BIT, clk);
        return 0;
 }
index 329bdb42109f9a726bec9ebd74359a2f97bfd287..eca907bf8b6d4d4878b0ee3890c5fd4987bb7a3c 100644 (file)
@@ -114,8 +114,6 @@ source "drivers/staging/media/Kconfig"
 
 source "drivers/staging/net/Kconfig"
 
-source "drivers/staging/omapdrm/Kconfig"
-
 source "drivers/staging/android/Kconfig"
 
 source "drivers/staging/ozwpan/Kconfig"
index c7ec486680f7f460226bae0444e16db604d9fc79..d810ed729add5efc30f2045f8b619f5155735329 100644 (file)
@@ -49,7 +49,6 @@ obj-$(CONFIG_SPEAKUP)         += speakup/
 obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_TM1217)      += cptm1217/
 obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4)   += ste_rmi4/
 obj-$(CONFIG_MFD_NVEC)         += nvec/
-obj-$(CONFIG_DRM_OMAP)         += omapdrm/
 obj-$(CONFIG_ANDROID)          += android/
 obj-$(CONFIG_USB_WPAN_HCD)     += ozwpan/
 obj-$(CONFIG_USB_G_CCG)                += ccg/
index 7de2a10213bd9d49294daa57430992e6934168ea..36eec320569c6bd14d077be6328b34ae4fede36f 100644 (file)
@@ -444,6 +444,7 @@ config COMEDI_ADQ12B
 
 config COMEDI_NI_AT_A2150
        tristate "NI AT-A2150 ISA card support"
+       select COMEDI_FC
        depends on VIRT_TO_BUS
        ---help---
          Enable support for National Instruments AT-A2150 cards
index b7bba1790a20005bbb955c50ba9a142a1480ac51..9b038e4a7e711eea1b5bd3873706847b701de2fe 100644 (file)
@@ -1549,6 +1549,9 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
        if (cmd == COMEDI_DEVCONFIG) {
                rc = do_devconfig_ioctl(dev,
                                        (struct comedi_devconfig __user *)arg);
+               if (rc == 0)
+                       /* Evade comedi_auto_unconfig(). */
+                       dev_file_info->hardware_device = NULL;
                goto done;
        }
 
index fb3d09323ba145c2460408971459ee29f39bd63b..01de996239f1e6142f07f9851cc840e8ddc14a75 100644 (file)
@@ -345,7 +345,7 @@ static int waveform_ai_cancel(struct comedi_device *dev,
        struct waveform_private *devpriv = dev->private;
 
        devpriv->timer_running = 0;
-       del_timer(&devpriv->timer);
+       del_timer_sync(&devpriv->timer);
        return 0;
 }
 
index aaac0b2cc9eb7172ab963aa361e8d7e828ceb751..fd1662b4175db8755b8c605ed90f08437c12ba4c 100644 (file)
@@ -963,7 +963,7 @@ static const struct ni_board_struct ni_boards[] = {
         .ao_range_table = &range_ni_M_625x_ao,
         .reg_type = ni_reg_625x,
         .ao_unipolar = 0,
-        .ao_speed = 357,
+        .ao_speed = 350,
         .num_p0_dio_channels = 8,
         .caldac = {caldac_none},
         .has_8255 = 0,
@@ -982,7 +982,7 @@ static const struct ni_board_struct ni_boards[] = {
         .ao_range_table = &range_ni_M_625x_ao,
         .reg_type = ni_reg_625x,
         .ao_unipolar = 0,
-        .ao_speed = 357,
+        .ao_speed = 350,
         .num_p0_dio_channels = 8,
         .caldac = {caldac_none},
         .has_8255 = 0,
@@ -1001,7 +1001,7 @@ static const struct ni_board_struct ni_boards[] = {
         .ao_range_table = &range_ni_M_625x_ao,
         .reg_type = ni_reg_625x,
         .ao_unipolar = 0,
-        .ao_speed = 357,
+        .ao_speed = 350,
         .num_p0_dio_channels = 8,
         .caldac = {caldac_none},
         .has_8255 = 0,
@@ -1037,7 +1037,7 @@ static const struct ni_board_struct ni_boards[] = {
         .ao_range_table = &range_ni_M_625x_ao,
         .reg_type = ni_reg_625x,
         .ao_unipolar = 0,
-        .ao_speed = 357,
+        .ao_speed = 350,
         .num_p0_dio_channels = 32,
         .caldac = {caldac_none},
         .has_8255 = 0,
@@ -1056,7 +1056,7 @@ static const struct ni_board_struct ni_boards[] = {
         .ao_range_table = &range_ni_M_625x_ao,
         .reg_type = ni_reg_625x,
         .ao_unipolar = 0,
-        .ao_speed = 357,
+        .ao_speed = 350,
         .num_p0_dio_channels = 32,
         .caldac = {caldac_none},
         .has_8255 = 0,
@@ -1092,7 +1092,7 @@ static const struct ni_board_struct ni_boards[] = {
         .ao_range_table = &range_ni_M_628x_ao,
         .reg_type = ni_reg_628x,
         .ao_unipolar = 1,
-        .ao_speed = 357,
+        .ao_speed = 350,
         .num_p0_dio_channels = 8,
         .caldac = {caldac_none},
         .has_8255 = 0,
@@ -1111,7 +1111,7 @@ static const struct ni_board_struct ni_boards[] = {
         .ao_range_table = &range_ni_M_628x_ao,
         .reg_type = ni_reg_628x,
         .ao_unipolar = 1,
-        .ao_speed = 357,
+        .ao_speed = 350,
         .num_p0_dio_channels = 8,
         .caldac = {caldac_none},
         .has_8255 = 0,
@@ -1147,7 +1147,7 @@ static const struct ni_board_struct ni_boards[] = {
         .ao_range_table = &range_ni_M_628x_ao,
         .reg_type = ni_reg_628x,
         .ao_unipolar = 1,
-        .ao_speed = 357,
+        .ao_speed = 350,
         .num_p0_dio_channels = 32,
         .caldac = {caldac_none},
         .has_8255 = 0,
index 580406cb1808e55b201aafad0611f62ed536744f..b2f8331e4acf90005887a7f1e5947d0c7b560aea 100644 (file)
@@ -3,7 +3,9 @@ config FIREWIRE_SERIAL
        depends on FIREWIRE
        help
           This enables TTY over IEEE 1394, providing high-speed serial
-         connectivity to cabled peers.
+         connectivity to cabled peers. This driver implements a
+         ad-hoc transport protocol and is currently limited to
+         Linux-to-Linux communication.
 
          To compile this driver as a module, say M here:  the module will
          be called firewire-serial.
index 726900548eae5c8136861588d4bce475cd9349b4..8dae8fb252233905b380927218cb80b8bb96e4cc 100644 (file)
@@ -1,5 +1,5 @@
-TODOs
------
+TODOs prior to this driver moving out of staging
+------------------------------------------------
 1. Implement retries for RCODE_BUSY, RCODE_NO_ACK and RCODE_SEND_ERROR
    - I/O is handled asynchronously which presents some issues when error
      conditions occur.
@@ -11,17 +11,9 @@ TODOs
 -- Issues with firewire stack --
 1. This driver uses the same unregistered vendor id that the firewire core does
      (0xd00d1e). Perhaps this could be exposed as a define in
-     firewire-constants.h?
-2. MAX_ASYNC_PAYLOAD needs to be publicly exposed by core/ohci
-   - otherwise how will this driver know the max size of address window to
-     open for one packet write?
+     firewire.h?
 3. Maybe device_max_receive() and link_speed_to_max_payload() should be
      taken up by the firewire core?
-4. To avoid dropping rx data while still limiting the maximum buffering,
-     the size of the AR context must be known. How to expose this to drivers?
-5. Explore if bigger AR context will reduce RCODE_BUSY responses
-   (or auto-grow to certain max size -- but this would require major surgery
-    as the current AR is contiguously mapped)
 
 -- Issues with TTY core --
   1. Hack for alternate device name scheme
index 61ee29083b268ece51870b1c80c60035acd4fa55..d03a7f57e8d475ceddedb052d42b3077c2a7745d 100644 (file)
@@ -179,7 +179,7 @@ static void dump_profile(struct seq_file *m, struct stats *stats)
 /* Returns the max receive packet size for the given card */
 static inline int device_max_receive(struct fw_device *fw_device)
 {
-       return 1 <<  (clamp_t(int, fw_device->max_rec, 8U, 13U) + 1);
+       return 1 <<  (clamp_t(int, fw_device->max_rec, 8U, 11U) + 1);
 }
 
 static void fwtty_log_tx_error(struct fwtty_port *port, int rcode)
index 8b572edf95634264ce5897938b1bc279a618c243..caa1c1ea82d5f783782efd263f2f60500a68062b 100644 (file)
@@ -374,10 +374,10 @@ static inline void fwtty_bind_console(struct fwtty_port *port,
  */
 static inline int link_speed_to_max_payload(unsigned speed)
 {
-       static const int max_async[] = { 307, 614, 1229, 2458, 4916, 9832, };
-       BUILD_BUG_ON(ARRAY_SIZE(max_async) - 1 != SCODE_3200);
+       static const int max_async[] = { 307, 614, 1229, 2458, };
+       BUILD_BUG_ON(ARRAY_SIZE(max_async) - 1 != SCODE_800);
 
-       speed = clamp(speed, (unsigned) SCODE_100, (unsigned) SCODE_3200);
+       speed = clamp(speed, (unsigned) SCODE_100, (unsigned) SCODE_800);
        if (limit_bw)
                return max_async[speed];
        else
index fb31b457a56a9b2b50fe96982ce083d25d89a032..c5ceb9d90ea83a696a4ca4518dc379a2c6a82831 100644 (file)
@@ -239,7 +239,7 @@ static irqreturn_t mxs_lradc_trigger_handler(int irq, void *p)
        struct mxs_lradc *lradc = iio_priv(iio);
        const uint32_t chan_value = LRADC_CH_ACCUMULATE |
                ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
-       int i, j = 0;
+       unsigned int i, j = 0;
 
        for_each_set_bit(i, iio->active_scan_mask, iio->masklength) {
                lradc->buffer[j] = readl(lradc->base + LRADC_CH(j));
index ea295b25308c470525e9213ad039c46eadc95ca5..87979a0d03a91358d1e892ed133292f855bfad3e 100644 (file)
@@ -27,8 +27,8 @@ config ADIS16130
 config ADIS16260
        tristate "Analog Devices ADIS16260 Digital Gyroscope Sensor SPI driver"
        depends on SPI
-       select IIO_TRIGGER if IIO_BUFFER
-       select IIO_SW_RING if IIO_BUFFER
+       select IIO_ADIS_LIB
+       select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
        help
          Say yes here to build support for Analog Devices ADIS16260 ADIS16265
          ADIS16250 ADIS16255 and ADIS16251 programmable digital gyroscope sensors.
index 3525a68d6a7529e01eb17c83bc00db5841faa33e..41d7350d030f3e9341175e8b4771068b2913a087 100644 (file)
@@ -69,7 +69,7 @@ static int adis16080_spi_read(struct iio_dev *indio_dev,
        ret = spi_read(st->us, st->buf, 2);
 
        if (ret == 0)
-               *val = ((st->buf[0] & 0xF) << 8) | st->buf[1];
+               *val = sign_extend32(((st->buf[0] & 0xF) << 8) | st->buf[1], 11);
        mutex_unlock(&st->buf_lock);
 
        return ret;
index ecf0f44bc70e1daf8dc4133d5d1a56e251560e8e..cec19f1cf56cb261ae5dc4782d266022813e6c15 100644 (file)
@@ -584,7 +584,6 @@ int imx_drm_add_encoder(struct drm_encoder *encoder,
 
        ret = imx_drm_encoder_register(imx_drm_encoder);
        if (ret) {
-               kfree(imx_drm_encoder);
                ret = -ENOMEM;
                goto err_register;
        }
index 677e665ca86d62dc764db90ca7205385cd2166c2..f7059cddd7fdb2da864ff19975d4ca99fbb0664f 100644 (file)
@@ -1104,7 +1104,9 @@ static int ipu_probe(struct platform_device *pdev)
        if (ret)
                goto out_failed_irq;
 
-       ipu_reset(ipu);
+       ret = ipu_reset(ipu);
+       if (ret)
+               goto out_failed_reset;
 
        /* Set MCU_T to divide MCU access window into 2 */
        ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18),
@@ -1129,6 +1131,7 @@ failed_add_clients:
        ipu_submodules_exit(ipu);
 failed_submodules_init:
        ipu_irq_exit(ipu);
+out_failed_reset:
 out_failed_irq:
        clk_disable_unprepare(ipu->clk);
 failed_clk_get:
index 1892006526b52fbf85eb19ca8e943db1b96615da..4b3a019409b5793632efa538cdcf65e0030f217f 100644 (file)
@@ -452,7 +452,7 @@ static int ipu_get_resources(struct ipu_crtc *ipu_crtc,
        int ret;
 
        ipu_crtc->ipu_ch = ipu_idmac_get(ipu, pdata->dma[0]);
-       if (IS_ERR_OR_NULL(ipu_crtc->ipu_ch)) {
+       if (IS_ERR(ipu_crtc->ipu_ch)) {
                ret = PTR_ERR(ipu_crtc->ipu_ch);
                goto err_out;
        }
@@ -472,7 +472,7 @@ static int ipu_get_resources(struct ipu_crtc *ipu_crtc,
        if (pdata->dp >= 0) {
                ipu_crtc->dp = ipu_dp_get(ipu, pdata->dp);
                if (IS_ERR(ipu_crtc->dp)) {
-                       ret = PTR_ERR(ipu_crtc->ipu_ch);
+                       ret = PTR_ERR(ipu_crtc->dp);
                        goto err_out;
                }
        }
@@ -548,6 +548,8 @@ static int ipu_drm_probe(struct platform_device *pdev)
        ipu_crtc->dev = &pdev->dev;
 
        ret = ipu_crtc_init(ipu_crtc, pdata);
+       if (ret)
+               return ret;
 
        platform_set_drvdata(pdev, ipu_crtc);
 
diff --git a/drivers/staging/omapdrm/TODO b/drivers/staging/omapdrm/TODO
deleted file mode 100644 (file)
index 938c788..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-TODO
-. add video decode/encode support (via syslink3 + codec-engine)
-  . NOTE: with dmabuf this probably could be split into different driver
-    so perhaps this TODO doesn't belong here
-. where should we do eviction (detatch_pages())?  We aren't necessarily
-  accessing the pages via a GART, so maybe we need some other threshold
-  to put a cap on the # of pages that can be pin'd.  (It is mostly only
-  of interest in case you have a swap partition/file.. which a lot of
-  these devices do not.. but it doesn't hurt for the driver to do the
-  right thing anyways.)
-  . Use mm_shrinker to trigger unpinning pages.  Need to figure out how
-    to handle next issue first (I think?)
-  . Note TTM already has some mm_shrinker stuff..  maybe an argument to
-    move to TTM?  Or maybe something that could be factored out in common?
-. GEM/shmem backed pages can have existing mappings (kernel linear map,
-  etc..), which isn't really ideal.
-. Revisit GEM sync object infrastructure.. TTM has some framework for this
-  already.  Possibly this could be refactored out and made more common?
-  There should be some way to do this with less wheel-reinvention.
-. Review DSS vs KMS mismatches.  The omap_dss_device is sort of part encoder,
-  part connector.  Which results in a bit of duct tape to fwd calls from
-  encoder to connector.  Possibly this could be done a bit better.
-. Solve PM sequencing on resume.  DMM/TILER must be reloaded before any
-  access is made from any component in the system.  Which means on suspend
-  CRTC's should be disabled, and on resume the LUT should be reprogrammed
-  before CRTC's are re-enabled, to prevent DSS from trying to DMA from a
-  buffer mapped in DMM/TILER before LUT is reloaded.
-
-Userspace:
-. git://github.com/robclark/xf86-video-omap.git
-
-Currently tested on
-. OMAP3530 beagleboard
-. OMAP4430 pandaboard
-. OMAP4460 pandaboard
diff --git a/drivers/staging/omapdrm/omap_crtc.c b/drivers/staging/omapdrm/omap_crtc.c
deleted file mode 100644 (file)
index d87bd84..0000000
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * drivers/staging/omapdrm/omap_crtc.c
- *
- * Copyright (C) 2011 Texas Instruments
- * Author: Rob Clark <rob@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.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 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 "omap_drv.h"
-
-#include <drm/drm_mode.h>
-#include "drm_crtc.h"
-#include "drm_crtc_helper.h"
-
-#define to_omap_crtc(x) container_of(x, struct omap_crtc, base)
-
-struct omap_crtc {
-       struct drm_crtc base;
-       struct drm_plane *plane;
-       const char *name;
-       int id;
-
-       /* if there is a pending flip, these will be non-null: */
-       struct drm_pending_vblank_event *event;
-       struct drm_framebuffer *old_fb;
-};
-
-static void omap_crtc_destroy(struct drm_crtc *crtc)
-{
-       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-       omap_crtc->plane->funcs->destroy(omap_crtc->plane);
-       drm_crtc_cleanup(crtc);
-       kfree(omap_crtc);
-}
-
-static void omap_crtc_dpms(struct drm_crtc *crtc, int mode)
-{
-       struct omap_drm_private *priv = crtc->dev->dev_private;
-       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-       int i;
-
-       WARN_ON(omap_plane_dpms(omap_crtc->plane, mode));
-
-       for (i = 0; i < priv->num_planes; i++) {
-               struct drm_plane *plane = priv->planes[i];
-               if (plane->crtc == crtc)
-                       WARN_ON(omap_plane_dpms(plane, mode));
-       }
-}
-
-static bool omap_crtc_mode_fixup(struct drm_crtc *crtc,
-               const struct drm_display_mode *mode,
-               struct drm_display_mode *adjusted_mode)
-{
-       return true;
-}
-
-static int omap_crtc_mode_set(struct drm_crtc *crtc,
-               struct drm_display_mode *mode,
-               struct drm_display_mode *adjusted_mode,
-               int x, int y,
-               struct drm_framebuffer *old_fb)
-{
-       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-       struct drm_plane *plane = omap_crtc->plane;
-
-       return omap_plane_mode_set(plane, crtc, crtc->fb,
-                       0, 0, mode->hdisplay, mode->vdisplay,
-                       x << 16, y << 16,
-                       mode->hdisplay << 16, mode->vdisplay << 16);
-}
-
-static void omap_crtc_prepare(struct drm_crtc *crtc)
-{
-       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-       DBG("%s", omap_crtc->name);
-       omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
-}
-
-static void omap_crtc_commit(struct drm_crtc *crtc)
-{
-       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-       DBG("%s", omap_crtc->name);
-       omap_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
-}
-
-static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
-               struct drm_framebuffer *old_fb)
-{
-       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-       struct drm_plane *plane = omap_crtc->plane;
-       struct drm_display_mode *mode = &crtc->mode;
-
-       return plane->funcs->update_plane(plane, crtc, crtc->fb,
-                       0, 0, mode->hdisplay, mode->vdisplay,
-                       x << 16, y << 16,
-                       mode->hdisplay << 16, mode->vdisplay << 16);
-}
-
-static void omap_crtc_load_lut(struct drm_crtc *crtc)
-{
-}
-
-static void vblank_cb(void *arg)
-{
-       static uint32_t sequence;
-       struct drm_crtc *crtc = arg;
-       struct drm_device *dev = crtc->dev;
-       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-       struct drm_pending_vblank_event *event = omap_crtc->event;
-       unsigned long flags;
-       struct timeval now;
-
-       WARN_ON(!event);
-
-       omap_crtc->event = NULL;
-
-       /* wakeup userspace */
-       if (event) {
-               do_gettimeofday(&now);
-
-               spin_lock_irqsave(&dev->event_lock, flags);
-               /* TODO: we can't yet use the vblank time accounting,
-                * because omapdss lower layer is the one that knows
-                * the irq # and registers the handler, which more or
-                * less defeats how drm_irq works.. for now just fake
-                * the sequence number and use gettimeofday..
-                *
-               event->event.sequence = drm_vblank_count_and_time(
-                               dev, omap_crtc->id, &now);
-                */
-               event->event.sequence = sequence++;
-               event->event.tv_sec = now.tv_sec;
-               event->event.tv_usec = now.tv_usec;
-               list_add_tail(&event->base.link,
-                               &event->base.file_priv->event_list);
-               wake_up_interruptible(&event->base.file_priv->event_wait);
-               spin_unlock_irqrestore(&dev->event_lock, flags);
-       }
-}
-
-static void page_flip_cb(void *arg)
-{
-       struct drm_crtc *crtc = arg;
-       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-       struct drm_framebuffer *old_fb = omap_crtc->old_fb;
-       struct drm_gem_object *bo;
-
-       omap_crtc->old_fb = NULL;
-
-       omap_crtc_mode_set_base(crtc, crtc->x, crtc->y, old_fb);
-
-       /* really we'd like to setup the callback atomically w/ setting the
-        * new scanout buffer to avoid getting stuck waiting an extra vblank
-        * cycle.. for now go for correctness and later figure out speed..
-        */
-       omap_plane_on_endwin(omap_crtc->plane, vblank_cb, crtc);
-
-       bo = omap_framebuffer_bo(crtc->fb, 0);
-       drm_gem_object_unreference_unlocked(bo);
-}
-
-static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
-                struct drm_framebuffer *fb,
-                struct drm_pending_vblank_event *event)
-{
-       struct drm_device *dev = crtc->dev;
-       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-       struct drm_gem_object *bo;
-
-       DBG("%d -> %d", crtc->fb ? crtc->fb->base.id : -1, fb->base.id);
-
-       if (omap_crtc->event) {
-               dev_err(dev->dev, "already a pending flip\n");
-               return -EINVAL;
-       }
-
-       omap_crtc->old_fb = crtc->fb;
-       omap_crtc->event = event;
-       crtc->fb = fb;
-
-       /*
-        * Hold a reference temporarily until the crtc is updated
-        * and takes the reference to the bo.  This avoids it
-        * getting freed from under us:
-        */
-       bo = omap_framebuffer_bo(fb, 0);
-       drm_gem_object_reference(bo);
-
-       omap_gem_op_async(bo, OMAP_GEM_READ, page_flip_cb, crtc);
-
-       return 0;
-}
-
-static int omap_crtc_set_property(struct drm_crtc *crtc,
-               struct drm_property *property, uint64_t val)
-{
-       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-       struct omap_drm_private *priv = crtc->dev->dev_private;
-
-       if (property == priv->rotation_prop) {
-               crtc->invert_dimensions =
-                               !!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270)));
-       }
-
-       return omap_plane_set_property(omap_crtc->plane, property, val);
-}
-
-static const struct drm_crtc_funcs omap_crtc_funcs = {
-       .set_config = drm_crtc_helper_set_config,
-       .destroy = omap_crtc_destroy,
-       .page_flip = omap_crtc_page_flip_locked,
-       .set_property = omap_crtc_set_property,
-};
-
-static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = {
-       .dpms = omap_crtc_dpms,
-       .mode_fixup = omap_crtc_mode_fixup,
-       .mode_set = omap_crtc_mode_set,
-       .prepare = omap_crtc_prepare,
-       .commit = omap_crtc_commit,
-       .mode_set_base = omap_crtc_mode_set_base,
-       .load_lut = omap_crtc_load_lut,
-};
-
-/* initialize crtc */
-struct drm_crtc *omap_crtc_init(struct drm_device *dev,
-               struct omap_overlay *ovl, int id)
-{
-       struct drm_crtc *crtc = NULL;
-       struct omap_crtc *omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL);
-
-       DBG("%s", ovl->name);
-
-       if (!omap_crtc) {
-               dev_err(dev->dev, "could not allocate CRTC\n");
-               goto fail;
-       }
-
-       crtc = &omap_crtc->base;
-
-       omap_crtc->plane = omap_plane_init(dev, ovl, (1 << id), true);
-       omap_crtc->plane->crtc = crtc;
-       omap_crtc->name = ovl->name;
-       omap_crtc->id = id;
-
-       drm_crtc_init(dev, crtc, &omap_crtc_funcs);
-       drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs);
-
-       omap_plane_install_properties(omap_crtc->plane, &crtc->base);
-
-       return crtc;
-
-fail:
-       if (crtc)
-               omap_crtc_destroy(crtc);
-
-       return NULL;
-}
diff --git a/drivers/staging/omapdrm/omap_plane.c b/drivers/staging/omapdrm/omap_plane.c
deleted file mode 100644 (file)
index 2a8e5ba..0000000
+++ /dev/null
@@ -1,558 +0,0 @@
-/*
- * drivers/staging/omapdrm/omap_plane.c
- *
- * Copyright (C) 2011 Texas Instruments
- * Author: Rob Clark <rob.clark@linaro.org>
- *
- * 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/kfifo.h>
-
-#include "omap_drv.h"
-#include "omap_dmm_tiler.h"
-
-/* some hackery because omapdss has an 'enum omap_plane' (which would be
- * better named omap_plane_id).. and compiler seems unhappy about having
- * both a 'struct omap_plane' and 'enum omap_plane'
- */
-#define omap_plane _omap_plane
-
-/*
- * plane funcs
- */
-
-struct callback {
-       void (*fxn)(void *);
-       void *arg;
-};
-
-#define to_omap_plane(x) container_of(x, struct omap_plane, base)
-
-struct omap_plane {
-       struct drm_plane base;
-       struct omap_overlay *ovl;
-       struct omap_overlay_info info;
-
-       /* position/orientation of scanout within the fb: */
-       struct omap_drm_window win;
-
-
-       /* last fb that we pinned: */
-       struct drm_framebuffer *pinned_fb;
-
-       uint32_t nformats;
-       uint32_t formats[32];
-
-       /* for synchronizing access to unpins fifo */
-       struct mutex unpin_mutex;
-
-       /* set of bo's pending unpin until next END_WIN irq */
-       DECLARE_KFIFO_PTR(unpin_fifo, struct drm_gem_object *);
-       int num_unpins, pending_num_unpins;
-
-       /* for deferred unpin when we need to wait for scanout complete irq */
-       struct work_struct work;
-
-       /* callback on next endwin irq */
-       struct callback endwin;
-};
-
-/* map from ovl->id to the irq we are interested in for scanout-done */
-static const uint32_t id2irq[] = {
-               [OMAP_DSS_GFX]    = DISPC_IRQ_GFX_END_WIN,
-               [OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_END_WIN,
-               [OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_END_WIN,
-               [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_END_WIN,
-};
-
-static void dispc_isr(void *arg, uint32_t mask)
-{
-       struct drm_plane *plane = arg;
-       struct omap_plane *omap_plane = to_omap_plane(plane);
-       struct omap_drm_private *priv = plane->dev->dev_private;
-
-       omap_dispc_unregister_isr(dispc_isr, plane,
-                       id2irq[omap_plane->ovl->id]);
-
-       queue_work(priv->wq, &omap_plane->work);
-}
-
-static void unpin_worker(struct work_struct *work)
-{
-       struct omap_plane *omap_plane =
-                       container_of(work, struct omap_plane, work);
-       struct callback endwin;
-
-       mutex_lock(&omap_plane->unpin_mutex);
-       DBG("unpinning %d of %d", omap_plane->num_unpins,
-                       omap_plane->num_unpins + omap_plane->pending_num_unpins);
-       while (omap_plane->num_unpins > 0) {
-               struct drm_gem_object *bo = NULL;
-               int ret = kfifo_get(&omap_plane->unpin_fifo, &bo);
-               WARN_ON(!ret);
-               omap_gem_put_paddr(bo);
-               drm_gem_object_unreference_unlocked(bo);
-               omap_plane->num_unpins--;
-       }
-       endwin = omap_plane->endwin;
-       omap_plane->endwin.fxn = NULL;
-       mutex_unlock(&omap_plane->unpin_mutex);
-
-       if (endwin.fxn)
-               endwin.fxn(endwin.arg);
-}
-
-static void install_irq(struct drm_plane *plane)
-{
-       struct omap_plane *omap_plane = to_omap_plane(plane);
-       struct omap_overlay *ovl = omap_plane->ovl;
-       int ret;
-
-       ret = omap_dispc_register_isr(dispc_isr, plane, id2irq[ovl->id]);
-
-       /*
-        * omapdss has upper limit on # of registered irq handlers,
-        * which we shouldn't hit.. but if we do the limit should
-        * be raised or bad things happen:
-        */
-       WARN_ON(ret == -EBUSY);
-}
-
-/* push changes down to dss2 */
-static int commit(struct drm_plane *plane)
-{
-       struct drm_device *dev = plane->dev;
-       struct omap_plane *omap_plane = to_omap_plane(plane);
-       struct omap_overlay *ovl = omap_plane->ovl;
-       struct omap_overlay_info *info = &omap_plane->info;
-       int ret;
-
-       DBG("%s", ovl->name);
-       DBG("%dx%d -> %dx%d (%d)", info->width, info->height, info->out_width,
-                       info->out_height, info->screen_width);
-       DBG("%d,%d %08x %08x", info->pos_x, info->pos_y,
-                       info->paddr, info->p_uv_addr);
-
-       /* NOTE: do we want to do this at all here, or just wait
-        * for dpms(ON) since other CRTC's may not have their mode
-        * set yet, so fb dimensions may still change..
-        */
-       ret = ovl->set_overlay_info(ovl, info);
-       if (ret) {
-               dev_err(dev->dev, "could not set overlay info\n");
-               return ret;
-       }
-
-       mutex_lock(&omap_plane->unpin_mutex);
-       omap_plane->num_unpins += omap_plane->pending_num_unpins;
-       omap_plane->pending_num_unpins = 0;
-       mutex_unlock(&omap_plane->unpin_mutex);
-
-       /* our encoder doesn't necessarily get a commit() after this, in
-        * particular in the dpms() and mode_set_base() cases, so force the
-        * manager to update:
-        *
-        * could this be in the encoder somehow?
-        */
-       if (ovl->manager) {
-               ret = ovl->manager->apply(ovl->manager);
-               if (ret) {
-                       dev_err(dev->dev, "could not apply settings\n");
-                       return ret;
-               }
-
-               /*
-                * NOTE: really this should be atomic w/ mgr->apply() but
-                * omapdss does not expose such an API
-                */
-               if (omap_plane->num_unpins > 0)
-                       install_irq(plane);
-
-       } else {
-               struct omap_drm_private *priv = dev->dev_private;
-               queue_work(priv->wq, &omap_plane->work);
-       }
-
-
-       if (ovl->is_enabled(ovl)) {
-               omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y,
-                               info->out_width, info->out_height);
-       }
-
-       return 0;
-}
-
-/* when CRTC that we are attached to has potentially changed, this checks
- * if we are attached to proper manager, and if necessary updates.
- */
-static void update_manager(struct drm_plane *plane)
-{
-       struct omap_drm_private *priv = plane->dev->dev_private;
-       struct omap_plane *omap_plane = to_omap_plane(plane);
-       struct omap_overlay *ovl = omap_plane->ovl;
-       struct omap_overlay_manager *mgr = NULL;
-       int i;
-
-       if (plane->crtc) {
-               for (i = 0; i < priv->num_encoders; i++) {
-                       struct drm_encoder *encoder = priv->encoders[i];
-                       if (encoder->crtc == plane->crtc) {
-                               mgr = omap_encoder_get_manager(encoder);
-                               break;
-                       }
-               }
-       }
-
-       if (ovl->manager != mgr) {
-               bool enabled = ovl->is_enabled(ovl);
-
-               /* don't switch things around with enabled overlays: */
-               if (enabled)
-                       omap_plane_dpms(plane, DRM_MODE_DPMS_OFF);
-
-               if (ovl->manager) {
-                       DBG("disconnecting %s from %s", ovl->name,
-                                       ovl->manager->name);
-                       ovl->unset_manager(ovl);
-               }
-
-               if (mgr) {
-                       DBG("connecting %s to %s", ovl->name, mgr->name);
-                       ovl->set_manager(ovl, mgr);
-               }
-
-               if (enabled && mgr)
-                       omap_plane_dpms(plane, DRM_MODE_DPMS_ON);
-       }
-}
-
-static void unpin(void *arg, struct drm_gem_object *bo)
-{
-       struct drm_plane *plane = arg;
-       struct omap_plane *omap_plane = to_omap_plane(plane);
-
-       if (kfifo_put(&omap_plane->unpin_fifo,
-                       (const struct drm_gem_object **)&bo)) {
-               omap_plane->pending_num_unpins++;
-               /* also hold a ref so it isn't free'd while pinned */
-               drm_gem_object_reference(bo);
-       } else {
-               dev_err(plane->dev->dev, "unpin fifo full!\n");
-               omap_gem_put_paddr(bo);
-       }
-}
-
-/* update which fb (if any) is pinned for scanout */
-static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb)
-{
-       struct omap_plane *omap_plane = to_omap_plane(plane);
-       struct drm_framebuffer *pinned_fb = omap_plane->pinned_fb;
-
-       if (pinned_fb != fb) {
-               int ret;
-
-               DBG("%p -> %p", pinned_fb, fb);
-
-               mutex_lock(&omap_plane->unpin_mutex);
-               ret = omap_framebuffer_replace(pinned_fb, fb, plane, unpin);
-               mutex_unlock(&omap_plane->unpin_mutex);
-
-               if (ret) {
-                       dev_err(plane->dev->dev, "could not swap %p -> %p\n",
-                                       omap_plane->pinned_fb, fb);
-                       omap_plane->pinned_fb = NULL;
-                       return ret;
-               }
-
-               omap_plane->pinned_fb = fb;
-       }
-
-       return 0;
-}
-
-/* update parameters that are dependent on the framebuffer dimensions and
- * position within the fb that this plane scans out from. This is called
- * when framebuffer or x,y base may have changed.
- */
-static void update_scanout(struct drm_plane *plane)
-{
-       struct omap_plane *omap_plane = to_omap_plane(plane);
-       struct omap_overlay_info *info = &omap_plane->info;
-       struct omap_drm_window *win = &omap_plane->win;
-       int ret;
-
-       ret = update_pin(plane, plane->fb);
-       if (ret) {
-               dev_err(plane->dev->dev,
-                       "could not pin fb: %d\n", ret);
-               omap_plane_dpms(plane, DRM_MODE_DPMS_OFF);
-               return;
-       }
-
-       omap_framebuffer_update_scanout(plane->fb, win, info);
-
-       DBG("%s: %d,%d: %08x %08x (%d)", omap_plane->ovl->name,
-                       win->src_x, win->src_y,
-                       (u32)info->paddr, (u32)info->p_uv_addr,
-                       info->screen_width);
-}
-
-int omap_plane_mode_set(struct drm_plane *plane,
-               struct drm_crtc *crtc, struct drm_framebuffer *fb,
-               int crtc_x, int crtc_y,
-               unsigned int crtc_w, unsigned int crtc_h,
-               uint32_t src_x, uint32_t src_y,
-               uint32_t src_w, uint32_t src_h)
-{
-       struct omap_plane *omap_plane = to_omap_plane(plane);
-       struct omap_drm_window *win = &omap_plane->win;
-
-       win->crtc_x = crtc_x;
-       win->crtc_y = crtc_y;
-       win->crtc_w = crtc_w;
-       win->crtc_h = crtc_h;
-
-       /* src values are in Q16 fixed point, convert to integer: */
-       win->src_x = src_x >> 16;
-       win->src_y = src_y >> 16;
-       win->src_w = src_w >> 16;
-       win->src_h = src_h >> 16;
-
-       /* note: this is done after this fxn returns.. but if we need
-        * to do a commit/update_scanout, etc before this returns we
-        * need the current value.
-        */
-       plane->fb = fb;
-       plane->crtc = crtc;
-
-       update_scanout(plane);
-       update_manager(plane);
-
-       return 0;
-}
-
-static int omap_plane_update(struct drm_plane *plane,
-               struct drm_crtc *crtc, struct drm_framebuffer *fb,
-               int crtc_x, int crtc_y,
-               unsigned int crtc_w, unsigned int crtc_h,
-               uint32_t src_x, uint32_t src_y,
-               uint32_t src_w, uint32_t src_h)
-{
-       omap_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h,
-                       src_x, src_y, src_w, src_h);
-       return omap_plane_dpms(plane, DRM_MODE_DPMS_ON);
-}
-
-static int omap_plane_disable(struct drm_plane *plane)
-{
-       struct omap_plane *omap_plane = to_omap_plane(plane);
-       omap_plane->win.rotation = BIT(DRM_ROTATE_0);
-       return omap_plane_dpms(plane, DRM_MODE_DPMS_OFF);
-}
-
-static void omap_plane_destroy(struct drm_plane *plane)
-{
-       struct omap_plane *omap_plane = to_omap_plane(plane);
-       DBG("%s", omap_plane->ovl->name);
-       omap_plane_disable(plane);
-       drm_plane_cleanup(plane);
-       WARN_ON(omap_plane->pending_num_unpins + omap_plane->num_unpins > 0);
-       kfifo_free(&omap_plane->unpin_fifo);
-       kfree(omap_plane);
-}
-
-int omap_plane_dpms(struct drm_plane *plane, int mode)
-{
-       struct omap_plane *omap_plane = to_omap_plane(plane);
-       struct omap_overlay *ovl = omap_plane->ovl;
-       int r;
-
-       DBG("%s: %d", omap_plane->ovl->name, mode);
-
-       if (mode == DRM_MODE_DPMS_ON) {
-               update_scanout(plane);
-               r = commit(plane);
-               if (!r)
-                       r = ovl->enable(ovl);
-       } else {
-               struct omap_drm_private *priv = plane->dev->dev_private;
-               r = ovl->disable(ovl);
-               update_pin(plane, NULL);
-               queue_work(priv->wq, &omap_plane->work);
-       }
-
-       return r;
-}
-
-void omap_plane_on_endwin(struct drm_plane *plane,
-               void (*fxn)(void *), void *arg)
-{
-       struct omap_plane *omap_plane = to_omap_plane(plane);
-
-       mutex_lock(&omap_plane->unpin_mutex);
-       omap_plane->endwin.fxn = fxn;
-       omap_plane->endwin.arg = arg;
-       mutex_unlock(&omap_plane->unpin_mutex);
-
-       install_irq(plane);
-}
-
-/* helper to install properties which are common to planes and crtcs */
-void omap_plane_install_properties(struct drm_plane *plane,
-               struct drm_mode_object *obj)
-{
-       struct drm_device *dev = plane->dev;
-       struct omap_drm_private *priv = dev->dev_private;
-       struct drm_property *prop;
-
-       if (priv->has_dmm) {
-               prop = priv->rotation_prop;
-               if (!prop) {
-                       const struct drm_prop_enum_list props[] = {
-                                       { DRM_ROTATE_0,   "rotate-0" },
-                                       { DRM_ROTATE_90,  "rotate-90" },
-                                       { DRM_ROTATE_180, "rotate-180" },
-                                       { DRM_ROTATE_270, "rotate-270" },
-                                       { DRM_REFLECT_X,  "reflect-x" },
-                                       { DRM_REFLECT_Y,  "reflect-y" },
-                       };
-                       prop = drm_property_create_bitmask(dev, 0, "rotation",
-                                       props, ARRAY_SIZE(props));
-                       if (prop == NULL)
-                               return;
-                       priv->rotation_prop = prop;
-               }
-               drm_object_attach_property(obj, prop, 0);
-       }
-
-       prop = priv->zorder_prop;
-       if (!prop) {
-               prop = drm_property_create_range(dev, 0, "zorder", 0, 3);
-               if (prop == NULL)
-                       return;
-               priv->zorder_prop = prop;
-       }
-       drm_object_attach_property(obj, prop, 0);
-}
-
-int omap_plane_set_property(struct drm_plane *plane,
-               struct drm_property *property, uint64_t val)
-{
-       struct omap_plane *omap_plane = to_omap_plane(plane);
-       struct omap_drm_private *priv = plane->dev->dev_private;
-       int ret = -EINVAL;
-
-       if (property == priv->rotation_prop) {
-               struct omap_overlay *ovl = omap_plane->ovl;
-
-               DBG("%s: rotation: %02x", ovl->name, (uint32_t)val);
-               omap_plane->win.rotation = val;
-
-               if (ovl->is_enabled(ovl))
-                       ret = omap_plane_dpms(plane, DRM_MODE_DPMS_ON);
-               else
-                       ret = 0;
-       } else if (property == priv->zorder_prop) {
-               struct omap_overlay *ovl = omap_plane->ovl;
-
-               DBG("%s: zorder: %d", ovl->name, (uint32_t)val);
-               omap_plane->info.zorder = val;
-
-               if (ovl->is_enabled(ovl))
-                       ret = omap_plane_dpms(plane, DRM_MODE_DPMS_ON);
-               else
-                       ret = 0;
-       }
-
-       return ret;
-}
-
-static const struct drm_plane_funcs omap_plane_funcs = {
-               .update_plane = omap_plane_update,
-               .disable_plane = omap_plane_disable,
-               .destroy = omap_plane_destroy,
-               .set_property = omap_plane_set_property,
-};
-
-/* initialize plane */
-struct drm_plane *omap_plane_init(struct drm_device *dev,
-               struct omap_overlay *ovl, unsigned int possible_crtcs,
-               bool priv)
-{
-       struct drm_plane *plane = NULL;
-       struct omap_plane *omap_plane;
-       int ret;
-
-       DBG("%s: possible_crtcs=%08x, priv=%d", ovl->name,
-                       possible_crtcs, priv);
-
-       /* friendly reminder to update table for future hw: */
-       WARN_ON(ovl->id >= ARRAY_SIZE(id2irq));
-
-       omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
-       if (!omap_plane) {
-               dev_err(dev->dev, "could not allocate plane\n");
-               goto fail;
-       }
-
-       mutex_init(&omap_plane->unpin_mutex);
-
-       ret = kfifo_alloc(&omap_plane->unpin_fifo, 16, GFP_KERNEL);
-       if (ret) {
-               dev_err(dev->dev, "could not allocate unpin FIFO\n");
-               goto fail;
-       }
-
-       INIT_WORK(&omap_plane->work, unpin_worker);
-
-       omap_plane->nformats = omap_framebuffer_get_formats(
-                       omap_plane->formats, ARRAY_SIZE(omap_plane->formats),
-                       ovl->supported_modes);
-       omap_plane->ovl = ovl;
-       plane = &omap_plane->base;
-
-       drm_plane_init(dev, plane, possible_crtcs, &omap_plane_funcs,
-                       omap_plane->formats, omap_plane->nformats, priv);
-
-       omap_plane_install_properties(plane, &plane->base);
-
-       /* get our starting configuration, set defaults for parameters
-        * we don't currently use, etc:
-        */
-       ovl->get_overlay_info(ovl, &omap_plane->info);
-       omap_plane->info.rotation_type = OMAP_DSS_ROT_DMA;
-       omap_plane->info.rotation = OMAP_DSS_ROT_0;
-       omap_plane->info.global_alpha = 0xff;
-       omap_plane->info.mirror = 0;
-
-       /* Set defaults depending on whether we are a CRTC or overlay
-        * layer.
-        * TODO add ioctl to give userspace an API to change this.. this
-        * will come in a subsequent patch.
-        */
-       if (priv)
-               omap_plane->info.zorder = 0;
-       else
-               omap_plane->info.zorder = ovl->id;
-
-       update_manager(plane);
-
-       return plane;
-
-fail:
-       if (plane)
-               omap_plane_destroy(plane);
-
-       return NULL;
-}
index ae38475854b50249f6787b90762cd025c26fef5b..d10d75e8a33fc6775b8901297a6c9de397b064ce 100644 (file)
@@ -937,7 +937,8 @@ short alloc_rx_desc_ring(struct net_device *dev, u16 bufsize, int count)
 
                dma_tmp = pci_map_single(pdev, buf, bufsize * sizeof(u8),
                                         PCI_DMA_FROMDEVICE);
-
+               if (pci_dma_mapping_error(pdev, dma_tmp))
+                       return -1;
                if (-1 == buffer_add(&(priv->rxbuffer), buf, dma_tmp,
                           &(priv->rxbufferhead))) {
                        DMESGE("Unable to allocate mem RX buf");
index 808aab6fa5ef70b23baf41cbe4bbad20ca6d9f9c..a9d78e9651c6006a981c7e26c920f71cd57692d2 100644 (file)
@@ -1183,6 +1183,8 @@ void  rtl8192_tx_fill_desc(struct net_device *dev, struct tx_desc *pdesc,
                                                pTxFwInfo->TxRate,
                                                cb_desc);
 
+       if (pci_dma_mapping_error(priv->pdev, mapping))
+               RT_TRACE(COMP_ERR, "DMA Mapping error\n");;
        if (cb_desc->bAMPDUEnable) {
                pTxFwInfo->AllowAggregation = 1;
                pTxFwInfo->RxMF = cb_desc->ampdu_factor;
@@ -1280,6 +1282,8 @@ void  rtl8192_tx_fill_cmd_desc(struct net_device *dev,
        dma_addr_t mapping = pci_map_single(priv->pdev, skb->data, skb->len,
                         PCI_DMA_TODEVICE);
 
+       if (pci_dma_mapping_error(priv->pdev, mapping))
+               RT_TRACE(COMP_ERR, "DMA Mapping error\n");;
        memset(entry, 0, 12);
        entry->LINIP = cb_desc->bLastIniPkt;
        entry->FirstSeg = 1;
index 1a70f324552f14becf86c43d347b4977b84cd94e..4ebf99b3097543b07cad50a264e700d70b926e27 100644 (file)
@@ -2104,7 +2104,10 @@ static short rtl8192_alloc_rx_desc_ring(struct net_device *dev)
                                                  skb_tail_pointer_rsl(skb),
                                                  priv->rxbuffersize,
                                                  PCI_DMA_FROMDEVICE);
-
+                       if (pci_dma_mapping_error(priv->pdev, *mapping)) {
+                               dev_kfree_skb_any(skb);
+                               return -1;
+                       }
                        entry->BufferAddress = cpu_to_le32(*mapping);
 
                        entry->Length = priv->rxbuffersize;
@@ -2397,7 +2400,11 @@ static void rtl8192_rx_normal(struct net_device *dev)
                                                    skb_tail_pointer_rsl(skb),
                                                    priv->rxbuffersize,
                                                    PCI_DMA_FROMDEVICE);
-
+                       if (pci_dma_mapping_error(priv->pdev,
+                                                 *((dma_addr_t *)skb->cb))) {
+                               dev_kfree_skb_any(skb);
+                               return;
+                       }
                }
 done:
                pdesc->BufferAddress = cpu_to_le32(*((dma_addr_t *)skb->cb));
index 6b73843e580af46e373c7885898b45e97baa9928..a96cd06d69dd7fc9ed78d71ec920763b8c33c022 100644 (file)
@@ -63,6 +63,8 @@ static struct usb_device_id rtl871x_usb_id_tbl[] = {
        {USB_DEVICE(0x0B05, 0x1791)}, /* 11n mode disable */
        /* Belkin */
        {USB_DEVICE(0x050D, 0x945A)},
+       /* ISY IWL - Belkin clone */
+       {USB_DEVICE(0x050D, 0x11F1)},
        /* Corega */
        {USB_DEVICE(0x07AA, 0x0047)},
        /* D-Link */
index ac87c5e38dee05ec7f8e072a3d27c5c346594a13..1facad625554b0b88baf67c70e4a33b46d77560b 100644 (file)
@@ -2,6 +2,7 @@ config SB105X
        tristate "SystemBase PCI Multiport UART"
        select SERIAL_CORE
        depends on PCI
+       depends on X86
        help
          A driver for the SystemBase Multi-2/PCI serial card
 
index edb2a85b9d52512c41b161db44b5f74e93a44cd8..9464f3874346eb141f115c65752b9bbe41fc4c7d 100644 (file)
@@ -3054,6 +3054,7 @@ static int init_mp_dev(struct pci_dev *pcidev, mppcibrd_t brd)
                                sbdev->nr_ports = ((portnum_hex/16)*10) + (portnum_hex % 16);
                        }
                        break;
+#ifdef CONFIG_PARPORT_PC
                case PCI_DEVICE_ID_MP2S1P :
                        sbdev->nr_ports = 2;
 
@@ -3073,6 +3074,7 @@ static int init_mp_dev(struct pci_dev *pcidev, mppcibrd_t brd)
                        /* add PC compatible parallel port */
                        parport_pc_probe_port(pcidev->resource[2].start, pcidev->resource[3].start, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &pcidev->dev, 0);
                        break;
+#endif
        }
 
        ret = request_region(sbdev->uart_access_addr, (8*sbdev->nr_ports), sbdev->name);
index df9533798095cd7b2e2b08a1799f206c5091e07c..7616f058a00b43ab4c1dd91d0384e6a5400b9a65 100644 (file)
@@ -342,7 +342,7 @@ int synth_init(char *synth_name)
 
        mutex_lock(&spk_mutex);
        /* First, check if we already have it loaded. */
-       for (i = 0; synths[i] != NULL && i < MAXSYNTHS; i++)
+       for (i = 0; i < MAXSYNTHS && synths[i] != NULL; i++)
                if (strcmp(synths[i]->name, synth_name) == 0)
                        synth = synths[i];
 
@@ -423,7 +423,7 @@ int synth_add(struct spk_synth *in_synth)
        int i;
        int status = 0;
        mutex_lock(&spk_mutex);
-       for (i = 0; synths[i] != NULL && i < MAXSYNTHS; i++)
+       for (i = 0; i < MAXSYNTHS && synths[i] != NULL; i++)
                /* synth_remove() is responsible for rotating the array down */
                if (in_synth == synths[i]) {
                        mutex_unlock(&spk_mutex);
index 543a127c7d4d63e5b43de81151ec54a382cf03b8..b783bfa59b1cea42f943596f8ddce5c620a08835 100644 (file)
@@ -31,7 +31,7 @@
  * driver should read or write to PRM/CM registers directly; they
  * should rely on OMAP core code to do this.
  */
-#include <mach-omap2/cm2xxx_3xxx.h>
+#include <mach-omap2/cm3xxx.h>
 #include <mach-omap2/prm-regbits-34xx.h>
 #include <mach-omap2/cm-regbits-34xx.h>
 #include <dspbridge/devdefs.h>
index b647207928b14aca75a5e445cce94e5efc56b940..2f084e181d39de2131360677c65c9deb4ed3143f 100644 (file)
@@ -121,9 +121,13 @@ void dsp_clk_exit(void)
        for (i = 0; i < DM_TIMER_CLOCKS; i++)
                omap_dm_timer_free(timer[i]);
 
+       clk_unprepare(iva2_clk);
        clk_put(iva2_clk);
+       clk_unprepare(ssi.sst_fck);
        clk_put(ssi.sst_fck);
+       clk_unprepare(ssi.ssr_fck);
        clk_put(ssi.ssr_fck);
+       clk_unprepare(ssi.ick);
        clk_put(ssi.ick);
 }
 
@@ -145,14 +149,21 @@ void dsp_clk_init(void)
        iva2_clk = clk_get(&dspbridge_device.dev, "iva2_ck");
        if (IS_ERR(iva2_clk))
                dev_err(bridge, "failed to get iva2 clock %p\n", iva2_clk);
+       else
+               clk_prepare(iva2_clk);
 
        ssi.sst_fck = clk_get(&dspbridge_device.dev, "ssi_sst_fck");
        ssi.ssr_fck = clk_get(&dspbridge_device.dev, "ssi_ssr_fck");
        ssi.ick = clk_get(&dspbridge_device.dev, "ssi_ick");
 
-       if (IS_ERR(ssi.sst_fck) || IS_ERR(ssi.ssr_fck) || IS_ERR(ssi.ick))
+       if (IS_ERR(ssi.sst_fck) || IS_ERR(ssi.ssr_fck) || IS_ERR(ssi.ick)) {
                dev_err(bridge, "failed to get ssi: sst %p, ssr %p, ick %p\n",
                                        ssi.sst_fck, ssi.ssr_fck, ssi.ick);
+       } else {
+               clk_prepare(ssi.sst_fck);
+               clk_prepare(ssi.ssr_fck);
+               clk_prepare(ssi.ick);
+       }
 }
 
 /**
index 1dce36fb828f1d24ebcd8ea4d19c5c86d9587c88..7ff0e6c980395c80c9c83fe4355a6eef854a6a79 100644 (file)
@@ -63,11 +63,15 @@ int dsp_wdt_init(void)
        dsp_wdt.fclk = clk_get(NULL, "wdt3_fck");
 
        if (!IS_ERR(dsp_wdt.fclk)) {
+               clk_prepare(dsp_wdt.fclk);
+
                dsp_wdt.iclk = clk_get(NULL, "wdt3_ick");
                if (IS_ERR(dsp_wdt.iclk)) {
                        clk_put(dsp_wdt.fclk);
                        dsp_wdt.fclk = NULL;
                        ret = -EFAULT;
+               } else {
+                       clk_prepare(dsp_wdt.iclk);
                }
        } else
                ret = -EFAULT;
@@ -95,10 +99,14 @@ void dsp_wdt_exit(void)
        free_irq(INT_34XX_WDT3_IRQ, &dsp_wdt);
        tasklet_kill(&dsp_wdt.wdt3_tasklet);
 
-       if (dsp_wdt.fclk)
+       if (dsp_wdt.fclk) {
+               clk_unprepare(dsp_wdt.fclk);
                clk_put(dsp_wdt.fclk);
-       if (dsp_wdt.iclk)
+       }
+       if (dsp_wdt.iclk) {
+               clk_unprepare(dsp_wdt.iclk);
                clk_put(dsp_wdt.iclk);
+       }
 
        dsp_wdt.fclk = NULL;
        dsp_wdt.iclk = NULL;
index 0331178ca3b3c65d54da5a9ea0920118008e87f4..bf73ba26e88aee484b21dc5aeff7b661c07f0c90 100644 (file)
@@ -162,11 +162,9 @@ static struct vme_driver pio2_driver = {
 
 static int __init pio2_init(void)
 {
-       int retval = 0;
-
        if (bus_num == 0) {
                pr_err("No cards, skipping registration\n");
-               goto err_nocard;
+               return -ENODEV;
        }
 
        if (bus_num > PIO2_CARDS_MAX) {
@@ -176,15 +174,7 @@ static int __init pio2_init(void)
        }
 
        /* Register the PIO2 driver */
-       retval = vme_register_driver(&pio2_driver, bus_num);
-       if (retval != 0)
-               goto err_reg;
-
-       return retval;
-
-err_reg:
-err_nocard:
-       return retval;
+       return  vme_register_driver(&pio2_driver, bus_num);
 }
 
 static int pio2_match(struct vme_dev *vdev)
index 6b2ec390e77503e177989ef89ec205011d02b031..806cbf72fb59b6f58eb1419d98af755475fed311 100644 (file)
@@ -90,7 +90,6 @@ typedef struct tagSRSNCapObject {
 } SRSNCapObject, *PSRSNCapObject;
 
 // BSS info(AP)
-#pragma pack(1)
 typedef struct tagKnownBSS {
     // BSS info
     BOOL            bActive;
index 5d8faf9f96ec4aac7f4e4c452b7d83dafb79bd35..e0d2b07ba608d14415f19daee1ae8d0924b6cf65 100644 (file)
@@ -34,7 +34,6 @@
 #include "device.h"
 
 /*---------------------  Export Definitions -------------------------*/
-#pragma pack(1)
 typedef struct tagSINTData {
        BYTE byTSR0;
        BYTE byPkt0;
index 22710cef751d77fe51dc9d760fc98574c6c94b44..ae6e2d237b207250a9ced56d059e590dccb61964 100644 (file)
@@ -95,13 +95,12 @@ typedef enum tagWZONETYPE {
 // Ioctl interface structure
 // Command structure
 //
-#pragma pack(1)
 typedef struct tagSCmdRequest {
        u8 name[16];
        void    *data;
        u16         wResult;
        u16     wCmdCode;
-} SCmdRequest, *PSCmdRequest;
+} __packed SCmdRequest, *PSCmdRequest;
 
 //
 // Scan
@@ -111,7 +110,7 @@ typedef struct tagSCmdScan {
 
     u8     ssid[SSID_MAXLEN + 2];
 
-} SCmdScan, *PSCmdScan;
+} __packed SCmdScan, *PSCmdScan;
 
 //
 // BSS Join
@@ -126,7 +125,7 @@ typedef struct tagSCmdBSSJoin {
     BOOL    bPSEnable;
     BOOL    bShareKeyAuth;
 
-} SCmdBSSJoin, *PSCmdBSSJoin;
+} __packed SCmdBSSJoin, *PSCmdBSSJoin;
 
 //
 // Zonetype Setting
@@ -137,7 +136,7 @@ typedef struct tagSCmdZoneTypeSet {
  BOOL       bWrite;
  WZONETYPE  ZoneType;
 
-} SCmdZoneTypeSet, *PSCmdZoneTypeSet;
+} __packed SCmdZoneTypeSet, *PSCmdZoneTypeSet;
 
 typedef struct tagSWPAResult {
          char  ifname[100];
@@ -145,7 +144,7 @@ typedef struct tagSWPAResult {
        u8 key_mgmt;
        u8 eap_type;
          BOOL authenticated;
-} SWPAResult, *PSWPAResult;
+} __packed SWPAResult, *PSWPAResult;
 
 typedef struct tagSCmdStartAP {
 
@@ -157,7 +156,7 @@ typedef struct tagSCmdStartAP {
     BOOL    bShareKeyAuth;
     u8      byBasicRate;
 
-} SCmdStartAP, *PSCmdStartAP;
+} __packed SCmdStartAP, *PSCmdStartAP;
 
 typedef struct tagSCmdSetWEP {
 
@@ -167,7 +166,7 @@ typedef struct tagSCmdSetWEP {
     BOOL    bWepKeyAvailable[WEP_NKEYS];
     u32     auWepKeyLength[WEP_NKEYS];
 
-} SCmdSetWEP, *PSCmdSetWEP;
+} __packed SCmdSetWEP, *PSCmdSetWEP;
 
 typedef struct tagSBSSIDItem {
 
@@ -180,14 +179,14 @@ typedef struct tagSBSSIDItem {
     BOOL    bWEPOn;
     u32     uRSSI;
 
-} SBSSIDItem;
+} __packed SBSSIDItem;
 
 
 typedef struct tagSBSSIDList {
 
        u32                 uItem;
        SBSSIDItem      sBSSIDList[0];
-} SBSSIDList, *PSBSSIDList;
+} __packed SBSSIDList, *PSBSSIDList;
 
 
 typedef struct tagSNodeItem {
@@ -208,7 +207,7 @@ typedef struct tagSNodeItem {
     u32            uTxAttempts;
     u16            wFailureRatio;
 
-} SNodeItem;
+} __packed SNodeItem;
 
 
 typedef struct tagSNodeList {
@@ -216,7 +215,7 @@ typedef struct tagSNodeList {
        u32                 uItem;
        SNodeItem       sNodeList[0];
 
-} SNodeList, *PSNodeList;
+} __packed SNodeList, *PSNodeList;
 
 
 typedef struct tagSCmdLinkStatus {
@@ -229,7 +228,7 @@ typedef struct tagSCmdLinkStatus {
     u32     uChannel;
     u32     uLinkRate;
 
-} SCmdLinkStatus, *PSCmdLinkStatus;
+} __packed SCmdLinkStatus, *PSCmdLinkStatus;
 
 //
 // 802.11 counter
@@ -247,7 +246,7 @@ typedef struct tagSDot11MIBCount {
     u32 ReceivedFragmentCount;
     u32 MulticastReceivedFrameCount;
     u32 FCSErrorCount;
-} SDot11MIBCount, *PSDot11MIBCount;
+} __packed SDot11MIBCount, *PSDot11MIBCount;
 
 
 
@@ -355,13 +354,13 @@ typedef struct tagSStatMIBCount {
     u32   ullTxBroadcastBytes[2];
     u32   ullTxMulticastBytes[2];
     u32   ullTxDirectedBytes[2];
-} SStatMIBCount, *PSStatMIBCount;
+} __packed SStatMIBCount, *PSStatMIBCount;
 
 typedef struct tagSCmdValue {
 
     u32     dwValue;
 
-} SCmdValue,  *PSCmdValue;
+} __packed SCmdValue,  *PSCmdValue;
 
 //
 // hostapd & viawget ioctl related
@@ -431,7 +430,7 @@ struct viawget_hostapd_param {
                        u8 ssid[32];
                } scan_req;
        } u;
-};
+} __packed;
 
 /*---------------------  Export Classes  ----------------------------*/
 
index 959c8868f6e2add1f86531ae68e96e5b58eb83fa..2522ddec718d3dd3f930097d80841ec2b8bdda88 100644 (file)
@@ -67,12 +67,11 @@ enum {
 
 
 
-#pragma pack(1)
 typedef struct viawget_wpa_header {
        u8 type;
        u16 req_ie_len;
        u16 resp_ie_len;
-} viawget_wpa_header;
+} __packed viawget_wpa_header;
 
 struct viawget_wpa_param {
        u32 cmd;
@@ -113,9 +112,8 @@ struct viawget_wpa_param {
                        u8 *buf;
                } scan_results;
        } u;
-};
+} __packed;
 
-#pragma pack(1)
 struct viawget_scan_result {
        u8 bssid[6];
        u8 ssid[32];
@@ -130,7 +128,7 @@ struct viawget_scan_result {
        int noise;
        int level;
        int maxrate;
-};
+} __packed;
 
 /*---------------------  Export Classes  ----------------------------*/
 
index 18c06a59c091b7bb1928cbac9038f5f0809a2288..1d31eab19d16b2d1ef5c2714162046da08c68500 100644 (file)
@@ -638,8 +638,8 @@ int prism2_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
 }
 
 
-int prism2_set_tx_power(struct wiphy *wiphy, enum nl80211_tx_power_setting type,
-                       int mbm)
+int prism2_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
+                       enum nl80211_tx_power_setting type, int mbm)
 {
        struct prism2_wiphy_private *priv = wiphy_priv(wiphy);
        wlandevice_t *wlandev = priv->wlandev;
@@ -665,7 +665,8 @@ exit:
        return err;
 }
 
-int prism2_get_tx_power(struct wiphy *wiphy, int *dbm)
+int prism2_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
+                       int *dbm)
 {
        struct prism2_wiphy_private *priv = wiphy_priv(wiphy);
        wlandevice_t *wlandev = priv->wlandev;
index 4efa9bc0fcf0d1750152de5342d565144a2427ce..89bfd858bb280949f499c463a6ec26034bddcdad 100644 (file)
@@ -406,7 +406,7 @@ int prism2mgmt_scan_results(wlandevice_t *wlandev, void *msgp)
        /* SSID */
        req->ssid.status = P80211ENUM_msgitem_status_data_ok;
        req->ssid.data.len = le16_to_cpu(item->ssid.len);
-       req->ssid.data.len = min_t(u16, req->ssid.data.len, WLAN_BSSID_LEN);
+       req->ssid.data.len = min_t(u16, req->ssid.data.len, WLAN_SSID_MAXLEN);
        memcpy(req->ssid.data.data, item->ssid.data, req->ssid.data.len);
 
        /* supported rates */
index fb4a7c94aed36f087cb41cc771f947b1d6776678..f2a73bd739fb94b750e66e1ee2c931e3c971a52a 100644 (file)
@@ -265,7 +265,7 @@ out_cleanup:
 static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
                           int offset)
 {
-       int ret;
+       int ret = 0;
        size_t clen;
        unsigned long handle;
        struct page *page;
@@ -286,10 +286,8 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
                        goto out;
                }
                ret = zram_decompress_page(zram, uncmem, index);
-               if (ret) {
-                       kfree(uncmem);
+               if (ret)
                        goto out;
-               }
        }
 
        /*
@@ -302,16 +300,18 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
 
        user_mem = kmap_atomic(page);
 
-       if (is_partial_io(bvec))
+       if (is_partial_io(bvec)) {
                memcpy(uncmem + offset, user_mem + bvec->bv_offset,
                       bvec->bv_len);
-       else
+               kunmap_atomic(user_mem);
+               user_mem = NULL;
+       } else {
                uncmem = user_mem;
+       }
 
        if (page_zero_filled(uncmem)) {
-               kunmap_atomic(user_mem);
-               if (is_partial_io(bvec))
-                       kfree(uncmem);
+               if (!is_partial_io(bvec))
+                       kunmap_atomic(user_mem);
                zram_stat_inc(&zram->stats.pages_zero);
                zram_set_flag(zram, index, ZRAM_ZERO);
                ret = 0;
@@ -321,9 +321,11 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
        ret = lzo1x_1_compress(uncmem, PAGE_SIZE, src, &clen,
                               zram->compress_workmem);
 
-       kunmap_atomic(user_mem);
-       if (is_partial_io(bvec))
-                       kfree(uncmem);
+       if (!is_partial_io(bvec)) {
+               kunmap_atomic(user_mem);
+               user_mem = NULL;
+               uncmem = NULL;
+       }
 
        if (unlikely(ret != LZO_E_OK)) {
                pr_err("Compression failed! err=%d\n", ret);
@@ -332,8 +334,10 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
 
        if (unlikely(clen > max_zpage_size)) {
                zram_stat_inc(&zram->stats.bad_compress);
-               src = uncmem;
                clen = PAGE_SIZE;
+               src = NULL;
+               if (is_partial_io(bvec))
+                       src = uncmem;
        }
 
        handle = zs_malloc(zram->mem_pool, clen);
@@ -345,7 +349,11 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
        }
        cmem = zs_map_object(zram->mem_pool, handle, ZS_MM_WO);
 
+       if ((clen == PAGE_SIZE) && !is_partial_io(bvec))
+               src = kmap_atomic(page);
        memcpy(cmem, src, clen);
+       if ((clen == PAGE_SIZE) && !is_partial_io(bvec))
+               kunmap_atomic(src);
 
        zs_unmap_object(zram->mem_pool, handle);
 
@@ -358,9 +366,10 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
        if (clen <= PAGE_SIZE / 2)
                zram_stat_inc(&zram->stats.good_compress);
 
-       return 0;
-
 out:
+       if (is_partial_io(bvec))
+               kfree(uncmem);
+
        if (ret)
                zram_stat64_inc(zram, &zram->stats.failed_writes);
        return ret;
index 9ac4c151eae43af4efdaac0b0f36c1ef301f068f..ba6091bf93fcde3c1463d6ba54a8ae07f812ddf0 100644 (file)
@@ -372,7 +372,7 @@ int iscsit_prepare_cmds_for_realligance(struct iscsi_conn *conn)
                 * made generic here.
                 */
                if (!(cmd->cmd_flags & ICF_OOO_CMDSN) && !cmd->immediate_cmd &&
-                    iscsi_sna_gte(cmd->stat_sn, conn->sess->exp_cmd_sn)) {
+                    iscsi_sna_gte(cmd->cmd_sn, conn->sess->exp_cmd_sn)) {
                        list_del(&cmd->i_conn_node);
                        spin_unlock_bh(&conn->cmd_lock);
                        iscsit_free_cmd(cmd);
index 85140f7dde1eec83305d4e41cc0eab665fb87df0..7d4ec02e29a9773d3eb1ddbcd1fc404e5ee334e8 100644 (file)
@@ -212,7 +212,7 @@ target_emulate_set_target_port_groups(struct se_cmd *cmd)
        struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem, *l_tg_pt_gp_mem;
        unsigned char *buf;
        unsigned char *ptr;
-       sense_reason_t rc;
+       sense_reason_t rc = TCM_NO_SENSE;
        u32 len = 4; /* Skip over RESERVED area in header */
        int alua_access_state, primary = 0;
        u16 tg_pt_id, rtpi;
index e2695101bb9911f1d02ba03217a3b0f059fbf0ac..f2aa7543d20aca556855c4fbde2ba3aa00b8ffcc 100644 (file)
@@ -941,6 +941,8 @@ int se_dev_set_queue_depth(struct se_device *dev, u32 queue_depth)
 
 int se_dev_set_fabric_max_sectors(struct se_device *dev, u32 fabric_max_sectors)
 {
+       int block_size = dev->dev_attrib.block_size;
+
        if (dev->export_count) {
                pr_err("dev[%p]: Unable to change SE Device"
                        " fabric_max_sectors while export_count is %d\n",
@@ -978,8 +980,12 @@ int se_dev_set_fabric_max_sectors(struct se_device *dev, u32 fabric_max_sectors)
        /*
         * Align max_sectors down to PAGE_SIZE to follow transport_allocate_data_tasks()
         */
+       if (!block_size) {
+               block_size = 512;
+               pr_warn("Defaulting to 512 for zero block_size\n");
+       }
        fabric_max_sectors = se_dev_align_max_sectors(fabric_max_sectors,
-                                                     dev->dev_attrib.block_size);
+                                                     block_size);
 
        dev->dev_attrib.fabric_max_sectors = fabric_max_sectors;
        pr_debug("dev[%p]: SE Device max_sectors changed to %u\n",
index 810263dfa4a1d9d272e16a492c5fb44365967a6c..c57bbbc7a7d10c428d750f577a381cd2689291b3 100644 (file)
@@ -754,6 +754,11 @@ static int target_fabric_port_link(
                return -EFAULT;
        }
 
+       if (!(dev->dev_flags & DF_CONFIGURED)) {
+               pr_err("se_device not configured yet, cannot port link\n");
+               return -ENODEV;
+       }
+
        tpg_ci = &lun_ci->ci_parent->ci_group->cg_item;
        se_tpg = container_of(to_config_group(tpg_ci),
                                struct se_portal_group, tpg_group);
index e35dbf85841fa6067094a336ef23d9edbb3427e6..8e0290b38e431ed070f4a84b8db078cf446fd8e3 100644 (file)
@@ -2053,7 +2053,7 @@ core_scsi3_emulate_pro_register(struct se_cmd *cmd, u64 res_key, u64 sa_res_key,
        /* Used for APTPL metadata w/ UNREGISTER */
        unsigned char *pr_aptpl_buf = NULL;
        unsigned char isid_buf[PR_REG_ISID_LEN], *isid_ptr = NULL;
-       sense_reason_t ret;
+       sense_reason_t ret = TCM_NO_SENSE;
        int pr_holder = 0, type;
 
        if (!se_sess || !se_lun) {
index 26a6d183ccb1c3f1f5e9eb6ec213d150a7ebfa6b..a664c664a31ac6accf24d557b66c7ab12e2d9596 100644 (file)
@@ -58,11 +58,10 @@ sbc_emulate_readcapacity(struct se_cmd *cmd)
        buf[7] = dev->dev_attrib.block_size & 0xff;
 
        rbuf = transport_kmap_data_sg(cmd);
-       if (!rbuf)
-               return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
-
-       memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length));
-       transport_kunmap_data_sg(cmd);
+       if (rbuf) {
+               memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length));
+               transport_kunmap_data_sg(cmd);
+       }
 
        target_complete_cmd(cmd, GOOD);
        return 0;
@@ -97,11 +96,10 @@ sbc_emulate_readcapacity_16(struct se_cmd *cmd)
                buf[14] = 0x80;
 
        rbuf = transport_kmap_data_sg(cmd);
-       if (!rbuf)
-               return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
-
-       memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length));
-       transport_kunmap_data_sg(cmd);
+       if (rbuf) {
+               memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length));
+               transport_kunmap_data_sg(cmd);
+       }
 
        target_complete_cmd(cmd, GOOD);
        return 0;
index 84f9e96e8ace79d41ea71723f8ca92a28c00623a..2d88f087d9616a53ca7dad67cb44c5cf2a3d33e3 100644 (file)
@@ -641,11 +641,10 @@ spc_emulate_inquiry(struct se_cmd *cmd)
 
 out:
        rbuf = transport_kmap_data_sg(cmd);
-       if (!rbuf)
-               return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
-
-       memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length));
-       transport_kunmap_data_sg(cmd);
+       if (rbuf) {
+               memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length));
+               transport_kunmap_data_sg(cmd);
+       }
 
        if (!ret)
                target_complete_cmd(cmd, GOOD);
@@ -851,7 +850,7 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd)
 {
        struct se_device *dev = cmd->se_dev;
        char *cdb = cmd->t_task_cdb;
-       unsigned char *buf, *map_buf;
+       unsigned char buf[SE_MODE_PAGE_BUF], *rbuf;
        int type = dev->transport->get_device_type(dev);
        int ten = (cmd->t_task_cdb[0] == MODE_SENSE_10);
        bool dbd = !!(cdb[1] & 0x08);
@@ -863,26 +862,8 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd)
        int ret;
        int i;
 
-       map_buf = transport_kmap_data_sg(cmd);
-       if (!map_buf)
-               return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
-       /*
-        * If SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC is not set, then we
-        * know we actually allocated a full page.  Otherwise, if the
-        * data buffer is too small, allocate a temporary buffer so we
-        * don't have to worry about overruns in all our INQUIRY
-        * emulation handling.
-        */
-       if (cmd->data_length < SE_MODE_PAGE_BUF &&
-           (cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC)) {
-               buf = kzalloc(SE_MODE_PAGE_BUF, GFP_KERNEL);
-               if (!buf) {
-                       transport_kunmap_data_sg(cmd);
-                       return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
-               }
-       } else {
-               buf = map_buf;
-       }
+       memset(buf, 0, SE_MODE_PAGE_BUF);
+
        /*
         * Skip over MODE DATA LENGTH + MEDIUM TYPE fields to byte 3 for
         * MODE_SENSE_10 and byte 2 for MODE_SENSE (6).
@@ -934,8 +915,6 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd)
        if (page == 0x3f) {
                if (subpage != 0x00 && subpage != 0xff) {
                        pr_warn("MODE_SENSE: Invalid subpage code: 0x%02x\n", subpage);
-                       kfree(buf);
-                       transport_kunmap_data_sg(cmd);
                        return TCM_INVALID_CDB_FIELD;
                }
 
@@ -972,7 +951,6 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd)
                pr_err("MODE SENSE: unimplemented page/subpage: 0x%02x/0x%02x\n",
                       page, subpage);
 
-       transport_kunmap_data_sg(cmd);
        return TCM_UNKNOWN_MODE_PAGE;
 
 set_length:
@@ -981,12 +959,12 @@ set_length:
        else
                buf[0] = length - 1;
 
-       if (buf != map_buf) {
-               memcpy(map_buf, buf, cmd->data_length);
-               kfree(buf);
+       rbuf = transport_kmap_data_sg(cmd);
+       if (rbuf) {
+               memcpy(rbuf, buf, min_t(u32, SE_MODE_PAGE_BUF, cmd->data_length));
+               transport_kunmap_data_sg(cmd);
        }
 
-       transport_kunmap_data_sg(cmd);
        target_complete_cmd(cmd, GOOD);
        return 0;
 }
index c23c76ccef65aa1da973fd2869389a72774daacc..bd587b70661a0ce11f6aaab596f4e46c6ee14c7e 100644 (file)
@@ -541,9 +541,6 @@ static void transport_lun_remove_cmd(struct se_cmd *cmd)
 
 void transport_cmd_finish_abort(struct se_cmd *cmd, int remove)
 {
-       if (!(cmd->se_cmd_flags & SCF_SCSI_TMR_CDB))
-               transport_lun_remove_cmd(cmd);
-
        if (transport_cmd_check_stop_to_fabric(cmd))
                return;
        if (remove)
@@ -1396,6 +1393,8 @@ static void target_complete_tmr_failure(struct work_struct *work)
 
        se_cmd->se_tmr_req->response = TMR_LUN_DOES_NOT_EXIST;
        se_cmd->se_tfo->queue_tm_rsp(se_cmd);
+
+       transport_cmd_check_stop_to_fabric(se_cmd);
 }
 
 /**
@@ -1688,6 +1687,7 @@ void target_execute_cmd(struct se_cmd *cmd)
        }
 
        cmd->t_state = TRANSPORT_PROCESSING;
+       cmd->transport_state |= CMD_T_ACTIVE;
        spin_unlock_irq(&cmd->t_state_lock);
 
        if (!target_handle_task_attr(cmd))
@@ -2597,6 +2597,16 @@ transport_send_check_condition_and_sense(struct se_cmd *cmd,
         * SENSE KEY values from include/scsi/scsi.h
         */
        switch (reason) {
+       case TCM_NO_SENSE:
+               /* CURRENT ERROR */
+               buffer[0] = 0x70;
+               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
+               /* Not Ready */
+               buffer[SPC_SENSE_KEY_OFFSET] = NOT_READY;
+               /* NO ADDITIONAL SENSE INFORMATION */
+               buffer[SPC_ASC_KEY_OFFSET] = 0;
+               buffer[SPC_ASCQ_KEY_OFFSET] = 0;
+               break;
        case TCM_NON_EXISTENT_LUN:
                /* CURRENT ERROR */
                buffer[0] = 0x70;
@@ -2743,7 +2753,7 @@ transport_send_check_condition_and_sense(struct se_cmd *cmd,
                /* ILLEGAL REQUEST */
                buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
                /* LOGICAL UNIT COMMUNICATION FAILURE */
-               buffer[SPC_ASC_KEY_OFFSET] = 0x80;
+               buffer[SPC_ASC_KEY_OFFSET] = 0x08;
                break;
        }
        /*
@@ -2804,6 +2814,8 @@ void transport_send_task_abort(struct se_cmd *cmd)
        }
        cmd->scsi_status = SAM_STAT_TASK_ABORTED;
 
+       transport_lun_remove_cmd(cmd);
+
        pr_debug("Setting SAM_STAT_TASK_ABORTED status for CDB: 0x%02x,"
                " ITT: 0x%08x\n", cmd->t_task_cdb[0],
                cmd->se_tfo->get_task_tag(cmd));
index 12d6fa21e5e19b68631dbf5b2d70f98f7cfbf864..6659dd36e806519af35acb47d3a85cf665bd4a38 100644 (file)
@@ -355,11 +355,11 @@ static int ft_prli_locked(struct fc_rport_priv *rdata, u32 spp_len,
 
        tport = ft_tport_create(rdata->local_port);
        if (!tport)
-               return 0;       /* not a target for this local port */
+               goto not_target;        /* not a target for this local port */
 
        acl = ft_acl_get(tport->tpg, rdata);
        if (!acl)
-               return 0;
+               goto not_target;        /* no target for this remote */
 
        if (!rspp)
                goto fill;
@@ -396,12 +396,18 @@ static int ft_prli_locked(struct fc_rport_priv *rdata, u32 spp_len,
 
        /*
         * OR in our service parameters with other provider (initiator), if any.
-        * TBD XXX - indicate RETRY capability?
         */
 fill:
        fcp_parm = ntohl(spp->spp_params);
+       fcp_parm &= ~FCP_SPPF_RETRY;
        spp->spp_params = htonl(fcp_parm | FCP_SPPF_TARG_FCN);
        return FC_SPP_RESP_ACK;
+
+not_target:
+       fcp_parm = ntohl(spp->spp_params);
+       fcp_parm &= ~FCP_SPPF_TARG_FCN;
+       spp->spp_params = htonl(fcp_parm);
+       return 0;
 }
 
 /**
index be6a373601b783677b876a512854edcfb28f3732..79ff3a5e925d6fdc252c6aa6d2f066e959e4637f 100644 (file)
@@ -441,6 +441,8 @@ static int pty_bsd_ioctl(struct tty_struct *tty,
                return pty_get_pktmode(tty, (int __user *)arg);
        case TIOCSIG:    /* Send signal to other side of pty */
                return pty_signal(tty, (int) arg);
+       case TIOCGPTN: /* TTY returns ENOTTY, but glibc expects EINVAL here */
+               return -EINVAL;
        }
        return -ENOIOCTLCMD;
 }
index d085e3a8ec0644bc9760caa0aed026533905c2a4..f9320437a64971c605b3e153d2a8aa4b160155bf 100644 (file)
@@ -300,6 +300,12 @@ static const struct serial8250_config uart_config[] = {
                                  UART_FCR_R_TRIG_00 | UART_FCR_T_TRIG_00,
                .flags          = UART_CAP_FIFO,
        },
+       [PORT_BRCM_TRUMANAGE] = {
+               .name           = "TruManage",
+               .fifo_size      = 1,
+               .tx_loadsz      = 1024,
+               .flags          = UART_CAP_HFIFO,
+       },
        [PORT_8250_CIR] = {
                .name           = "CIR port"
        }
@@ -1490,6 +1496,11 @@ void serial8250_tx_chars(struct uart_8250_port *up)
                port->icount.tx++;
                if (uart_circ_empty(xmit))
                        break;
+               if (up->capabilities & UART_CAP_HFIFO) {
+                       if ((serial_port_in(port, UART_LSR) & BOTH_EMPTY) !=
+                           BOTH_EMPTY)
+                               break;
+               }
        } while (--count > 0);
 
        if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
index 3b4ea84898c2e719dc233ecddde63dc3c421bd0d..12caa1292b75c20b3d8221d4576a6f97c69db3d4 100644 (file)
@@ -40,6 +40,7 @@ struct serial8250_config {
 #define UART_CAP_AFE   (1 << 11)       /* MCR-based hw flow control */
 #define UART_CAP_UUE   (1 << 12)       /* UART needs IER bit 6 set (Xscale) */
 #define UART_CAP_RTOIE (1 << 13)       /* UART needs IER bit 4 set (Xscale, Tegra) */
+#define UART_CAP_HFIFO (1 << 14)       /* UART has a "hidden" FIFO */
 
 #define UART_BUG_QUOT  (1 << 0)        /* UART has buggy quot LSB */
 #define UART_BUG_TXEN  (1 << 1)        /* UART has buggy TX IIR status */
index 1d0dba2d562d27bb72a3e91f2273176c4cd67f41..096d2ef48b32f6b62abfe5c220a44440545fb0b4 100644 (file)
@@ -79,7 +79,7 @@ static int dw8250_handle_irq(struct uart_port *p)
        } else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) {
                /* Clear the USR and write the LCR again. */
                (void)p->serial_in(p, UART_USR);
-               p->serial_out(p, d->last_lcr, UART_LCR);
+               p->serial_out(p, UART_LCR, d->last_lcr);
 
                return 1;
        }
index 26b9dc012ed0b64ec1f1b3807b573af0f5f82b91..a27a98e1b0667fc8bfd7e379eaaaa1cfa6c9229a 100644 (file)
@@ -1085,6 +1085,18 @@ pci_omegapci_setup(struct serial_private *priv,
        return setup_port(priv, port, 2, idx * 8, 0);
 }
 
+static int
+pci_brcm_trumanage_setup(struct serial_private *priv,
+                        const struct pciserial_board *board,
+                        struct uart_8250_port *port, int idx)
+{
+       int ret = pci_default_setup(priv, board, port, idx);
+
+       port->port.type = PORT_BRCM_TRUMANAGE;
+       port->port.flags = (port->port.flags | UPF_FIXED_PORT | UPF_FIXED_TYPE);
+       return ret;
+}
+
 static int skip_tx_en_setup(struct serial_private *priv,
                        const struct pciserial_board *board,
                        struct uart_8250_port *port, int idx)
@@ -1301,9 +1313,10 @@ pci_wch_ch353_setup(struct serial_private *priv,
 #define PCI_VENDOR_ID_AGESTAR          0x5372
 #define PCI_DEVICE_ID_AGESTAR_9375     0x6872
 #define PCI_VENDOR_ID_ASIX             0x9710
-#define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0019
 #define PCI_DEVICE_ID_COMMTECH_4224PCIE        0x0020
 #define PCI_DEVICE_ID_COMMTECH_4228PCIE        0x0021
+#define PCI_DEVICE_ID_COMMTECH_4222PCIE        0x0022
+#define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a
 
 
 /* Unknown vendors/cards - this should not be in linux/pci_ids.h */
@@ -1953,6 +1966,17 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
                .subdevice      = PCI_ANY_ID,
                .setup          = pci_xr17v35x_setup,
        },
+       /*
+        * Broadcom TruManage (NetXtreme)
+        */
+       {
+               .vendor         = PCI_VENDOR_ID_BROADCOM,
+               .device         = PCI_DEVICE_ID_BROADCOM_TRUMANAGE,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .setup          = pci_brcm_trumanage_setup,
+       },
+
        /*
         * Default "match everything" terminator entry
         */
@@ -2148,6 +2172,7 @@ enum pci_board_num_t {
        pbn_ce4100_1_115200,
        pbn_omegapci,
        pbn_NETMOS9900_2s_115200,
+       pbn_brcm_trumanage,
 };
 
 /*
@@ -2246,7 +2271,7 @@ static struct pciserial_board pci_boards[] = {
 
        [pbn_b0_8_1152000_200] = {
                .flags          = FL_BASE0,
-               .num_ports      = 2,
+               .num_ports      = 8,
                .base_baud      = 1152000,
                .uart_offset    = 0x200,
        },
@@ -2892,6 +2917,12 @@ static struct pciserial_board pci_boards[] = {
                .num_ports      = 2,
                .base_baud      = 115200,
        },
+       [pbn_brcm_trumanage] = {
+               .flags          = FL_BASE0,
+               .num_ports      = 1,
+               .reg_shift      = 2,
+               .base_baud      = 115200,
+       },
 };
 
 static const struct pci_device_id blacklist[] = {
@@ -4470,6 +4501,13 @@ static struct pci_device_id serial_pci_tbl[] = {
                PCI_ANY_ID, PCI_ANY_ID, 0, 0,
                pbn_omegapci },
 
+       /*
+        * Broadcom TruManage
+        */
+       {       PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BROADCOM_TRUMANAGE,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_brcm_trumanage },
+
        /*
         * AgeStar as-prs2-009
         */
index 675d94ab0aff63f97de2f52e7da565b8e5309013..8cb6d8d66a1362b1b2f5566a875c1f56248a3b29 100644 (file)
@@ -637,6 +637,7 @@ static void ifx_port_shutdown(struct tty_port *port)
 
        clear_bit(IFX_SPI_STATE_IO_AVAILABLE, &ifx_dev->flags);
        mrdy_set_low(ifx_dev);
+       del_timer(&ifx_dev->spi_timer);
        clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags);
        tasklet_kill(&ifx_dev->io_work_tasklet);
 }
@@ -810,7 +811,8 @@ static void ifx_spi_io(unsigned long data)
                ifx_dev->spi_xfer.cs_change = 0;
                ifx_dev->spi_xfer.speed_hz = ifx_dev->spi_dev->max_speed_hz;
                /* ifx_dev->spi_xfer.speed_hz = 390625; */
-               ifx_dev->spi_xfer.bits_per_word = spi_bpw;
+               ifx_dev->spi_xfer.bits_per_word =
+                       ifx_dev->spi_dev->bits_per_word;
 
                ifx_dev->spi_xfer.tx_buf = ifx_dev->tx_buffer;
                ifx_dev->spi_xfer.rx_buf = ifx_dev->rx_buffer;
index 6db23b035efe0766dcc99e8bddb0a301dd458217..e55615eb34ad32037cf78dc18aef63a00373e1ee 100644 (file)
@@ -253,7 +253,7 @@ static void mxs_auart_tx_chars(struct mxs_auart_port *s)
        struct circ_buf *xmit = &s->port.state->xmit;
 
        if (auart_dma_enabled(s)) {
-               int i = 0;
+               u32 i = 0;
                int size;
                void *buffer = s->tx_dma_buf;
 
@@ -412,10 +412,12 @@ static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl)
 
        u32 ctrl = readl(u->membase + AUART_CTRL2);
 
-       ctrl &= ~AUART_CTRL2_RTSEN;
+       ctrl &= ~(AUART_CTRL2_RTSEN | AUART_CTRL2_RTS);
        if (mctrl & TIOCM_RTS) {
                if (tty_port_cts_enabled(&u->state->port))
                        ctrl |= AUART_CTRL2_RTSEN;
+               else
+                       ctrl |= AUART_CTRL2_RTS;
        }
 
        s->ctrl = mctrl;
index 12e5249d053e79b8d15991daf27614755c03c3bf..e514b3a4dc572069da4df72cba488f6be9cd1c98 100644 (file)
@@ -1006,7 +1006,6 @@ static void s3c24xx_serial_resetport(struct uart_port *port,
 
        ucon &= ucon_mask;
        wr_regl(port, S3C2410_UCON,  ucon | cfg->ucon);
-       wr_regl(port, S3C2410_ULCON, cfg->ulcon);
 
        /* reset both fifos */
        wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
index 8fd181436a6ba246e2247c5be871ac7f3c71c760..d5ed9f61300562c8febbb8a8095304a1c9955899 100644 (file)
@@ -604,7 +604,7 @@ static int vt8500_serial_probe(struct platform_device *pdev)
        vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
 
        vt8500_port->clk = of_clk_get(pdev->dev.of_node, 0);
-       if (vt8500_port->clk) {
+       if (!IS_ERR(vt8500_port->clk)) {
                vt8500_port->uart.uartclk = clk_get_rate(vt8500_port->clk);
        } else {
                /* use the default of 24Mhz if not specified and warn */
index 8fd89687d068e58b5546a153f8ea894c32782f47..ed43a7f88b4f3aefd882e7001c0e65768c44c73d 100644 (file)
@@ -638,7 +638,7 @@ static inline void save_screen(struct vc_data *vc)
  *     Redrawing of screen
  */
 
-static void clear_buffer_attributes(struct vc_data *vc)
+void clear_buffer_attributes(struct vc_data *vc)
 {
        unsigned short *p = (unsigned short *)vc->vc_origin;
        int count = vc->vc_screenbuf_size / 2;
@@ -2987,7 +2987,7 @@ int __init vty_init(const struct file_operations *console_fops)
 
 static struct class *vtconsole_class;
 
-static int bind_con_driver(const struct consw *csw, int first, int last,
+static int do_bind_con_driver(const struct consw *csw, int first, int last,
                           int deflt)
 {
        struct module *owner = csw->owner;
@@ -2998,7 +2998,7 @@ static int bind_con_driver(const struct consw *csw, int first, int last,
        if (!try_module_get(owner))
                return -ENODEV;
 
-       console_lock();
+       WARN_CONSOLE_UNLOCKED();
 
        /* check if driver is registered */
        for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
@@ -3083,11 +3083,22 @@ static int bind_con_driver(const struct consw *csw, int first, int last,
 
        retval = 0;
 err:
-       console_unlock();
        module_put(owner);
        return retval;
 };
 
+
+static int bind_con_driver(const struct consw *csw, int first, int last,
+                          int deflt)
+{
+       int ret;
+
+       console_lock();
+       ret = do_bind_con_driver(csw, first, last, deflt);
+       console_unlock();
+       return ret;
+}
+
 #ifdef CONFIG_VT_HW_CONSOLE_BINDING
 static int con_is_graphics(const struct consw *csw, int first, int last)
 {
@@ -3123,6 +3134,18 @@ static int con_is_graphics(const struct consw *csw, int first, int last)
  * or 0 on success.
  */
 int unbind_con_driver(const struct consw *csw, int first, int last, int deflt)
+{
+       int retval;
+
+       console_lock();
+       retval = do_unbind_con_driver(csw, first, last, deflt);
+       console_unlock();
+       return retval;
+}
+EXPORT_SYMBOL(unbind_con_driver);
+
+/* unlocked version of unbind_con_driver() */
+int do_unbind_con_driver(const struct consw *csw, int first, int last, int deflt)
 {
        struct module *owner = csw->owner;
        const struct consw *defcsw = NULL;
@@ -3132,7 +3155,7 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt)
        if (!try_module_get(owner))
                return -ENODEV;
 
-       console_lock();
+       WARN_CONSOLE_UNLOCKED();
 
        /* check if driver is registered and if it is unbindable */
        for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
@@ -3145,10 +3168,8 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt)
                }
        }
 
-       if (retval) {
-               console_unlock();
+       if (retval)
                goto err;
-       }
 
        retval = -ENODEV;
 
@@ -3164,15 +3185,11 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt)
                }
        }
 
-       if (retval) {
-               console_unlock();
+       if (retval)
                goto err;
-       }
 
-       if (!con_is_bound(csw)) {
-               console_unlock();
+       if (!con_is_bound(csw))
                goto err;
-       }
 
        first = max(first, con_driver->first);
        last = min(last, con_driver->last);
@@ -3199,15 +3216,14 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt)
        if (!con_is_bound(csw))
                con_driver->flag &= ~CON_DRIVER_FLAG_INIT;
 
-       console_unlock();
        /* ignore return value, binding should not fail */
-       bind_con_driver(defcsw, first, last, deflt);
+       do_bind_con_driver(defcsw, first, last, deflt);
 err:
        module_put(owner);
        return retval;
 
 }
-EXPORT_SYMBOL(unbind_con_driver);
+EXPORT_SYMBOL_GPL(do_unbind_con_driver);
 
 static int vt_bind(struct con_driver *con)
 {
@@ -3492,28 +3508,18 @@ int con_debug_leave(void)
 }
 EXPORT_SYMBOL_GPL(con_debug_leave);
 
-/**
- * register_con_driver - register console driver to console layer
- * @csw: console driver
- * @first: the first console to take over, minimum value is 0
- * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1
- *
- * DESCRIPTION: This function registers a console driver which can later
- * bind to a range of consoles specified by @first and @last. It will
- * also initialize the console driver by calling con_startup().
- */
-int register_con_driver(const struct consw *csw, int first, int last)
+static int do_register_con_driver(const struct consw *csw, int first, int last)
 {
        struct module *owner = csw->owner;
        struct con_driver *con_driver;
        const char *desc;
        int i, retval = 0;
 
+       WARN_CONSOLE_UNLOCKED();
+
        if (!try_module_get(owner))
                return -ENODEV;
 
-       console_lock();
-
        for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
                con_driver = &registered_con_driver[i];
 
@@ -3566,10 +3572,29 @@ int register_con_driver(const struct consw *csw, int first, int last)
        }
 
 err:
-       console_unlock();
        module_put(owner);
        return retval;
 }
+
+/**
+ * register_con_driver - register console driver to console layer
+ * @csw: console driver
+ * @first: the first console to take over, minimum value is 0
+ * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1
+ *
+ * DESCRIPTION: This function registers a console driver which can later
+ * bind to a range of consoles specified by @first and @last. It will
+ * also initialize the console driver by calling con_startup().
+ */
+int register_con_driver(const struct consw *csw, int first, int last)
+{
+       int retval;
+
+       console_lock();
+       retval = do_register_con_driver(csw, first, last);
+       console_unlock();
+       return retval;
+}
 EXPORT_SYMBOL(register_con_driver);
 
 /**
@@ -3585,9 +3610,18 @@ EXPORT_SYMBOL(register_con_driver);
  */
 int unregister_con_driver(const struct consw *csw)
 {
-       int i, retval = -ENODEV;
+       int retval;
 
        console_lock();
+       retval = do_unregister_con_driver(csw);
+       console_unlock();
+       return retval;
+}
+EXPORT_SYMBOL(unregister_con_driver);
+
+int do_unregister_con_driver(const struct consw *csw)
+{
+       int i, retval = -ENODEV;
 
        /* cannot unregister a bound driver */
        if (con_is_bound(csw))
@@ -3613,27 +3647,53 @@ int unregister_con_driver(const struct consw *csw)
                }
        }
 err:
-       console_unlock();
        return retval;
 }
-EXPORT_SYMBOL(unregister_con_driver);
+EXPORT_SYMBOL_GPL(do_unregister_con_driver);
 
 /*
  *     If we support more console drivers, this function is used
  *     when a driver wants to take over some existing consoles
  *     and become default driver for newly opened ones.
  *
- *      take_over_console is basically a register followed by unbind
+ *     take_over_console is basically a register followed by unbind
+ */
+int do_take_over_console(const struct consw *csw, int first, int last, int deflt)
+{
+       int err;
+
+       err = do_register_con_driver(csw, first, last);
+       /*
+        * If we get an busy error we still want to bind the console driver
+        * and return success, as we may have unbound the console driver
+        * but not unregistered it.
+        */
+       if (err == -EBUSY)
+               err = 0;
+       if (!err)
+               do_bind_con_driver(csw, first, last, deflt);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(do_take_over_console);
+
+/*
+ *     If we support more console drivers, this function is used
+ *     when a driver wants to take over some existing consoles
+ *     and become default driver for newly opened ones.
+ *
+ *     take_over_console is basically a register followed by unbind
  */
 int take_over_console(const struct consw *csw, int first, int last, int deflt)
 {
        int err;
 
        err = register_con_driver(csw, first, last);
-       /* if we get an busy error we still want to bind the console driver
+       /*
+        * If we get an busy error we still want to bind the console driver
         * and return success, as we may have unbound the console driver
-       Â * but not unregistered it.
-       */
+        * but not unregistered it.
+        */
        if (err == -EBUSY)
                err = 0;
        if (!err)
index 4c90b510d0160c246f1fd5c6c280a88d091c80f8..640ae6c6d2d2ae67f504cb72582c40a8e85c9216 100644 (file)
@@ -37,6 +37,7 @@ config USB_ARCH_HAS_EHCI
        default y if ARCH_W90X900
        default y if ARCH_AT91
        default y if ARCH_MXC
+       default y if ARCH_MXS
        default y if ARCH_OMAP3
        default y if ARCH_CNS3XXX
        default y if ARCH_VT8500
index caecad9213f5cf14f3ef45827548c77e0648dfb9..8e9d31277c436ecce3fdd61ccb78031148a3e53e 100644 (file)
@@ -70,6 +70,9 @@ static int host_start(struct ci13xxx *ci)
        else
                ci->hcd = hcd;
 
+       if (ci->platdata->flags & CI13XXX_DISABLE_STREAMING)
+               hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS);
+
        return ret;
 }
 
index 8d809a811e169fac9ece6259c542c17c4b16f907..2d92cce260d7fd646d13d69e01f2152bbd43a487 100644 (file)
@@ -1602,6 +1602,9 @@ static const struct usb_device_id acm_ids[] = {
        { USB_DEVICE(0x0572, 0x1340), /* Conexant CX93010-2x UCMxx */
        .driver_info = NO_UNION_NORMAL,
        },
+       { USB_DEVICE(0x05f9, 0x4002), /* PSC Scanning, Magellan 800i */
+       .driver_info = NO_UNION_NORMAL,
+       },
        { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
        .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
        },
index 4225d5e721312a6ec87a96352d05c7db8a682593..8e64adf8e4d56de0409909dd143e9de52f4fefb0 100644 (file)
@@ -39,6 +39,7 @@
 #include <asm/unaligned.h>
 #include <linux/platform_device.h>
 #include <linux/workqueue.h>
+#include <linux/pm_runtime.h>
 
 #include <linux/usb.h>
 #include <linux/usb/hcd.h>
@@ -1025,6 +1026,49 @@ static int register_root_hub(struct usb_hcd *hcd)
        return retval;
 }
 
+/*
+ * usb_hcd_start_port_resume - a root-hub port is sending a resume signal
+ * @bus: the bus which the root hub belongs to
+ * @portnum: the port which is being resumed
+ *
+ * HCDs should call this function when they know that a resume signal is
+ * being sent to a root-hub port.  The root hub will be prevented from
+ * going into autosuspend until usb_hcd_end_port_resume() is called.
+ *
+ * The bus's private lock must be held by the caller.
+ */
+void usb_hcd_start_port_resume(struct usb_bus *bus, int portnum)
+{
+       unsigned bit = 1 << portnum;
+
+       if (!(bus->resuming_ports & bit)) {
+               bus->resuming_ports |= bit;
+               pm_runtime_get_noresume(&bus->root_hub->dev);
+       }
+}
+EXPORT_SYMBOL_GPL(usb_hcd_start_port_resume);
+
+/*
+ * usb_hcd_end_port_resume - a root-hub port has stopped sending a resume signal
+ * @bus: the bus which the root hub belongs to
+ * @portnum: the port which is being resumed
+ *
+ * HCDs should call this function when they know that a resume signal has
+ * stopped being sent to a root-hub port.  The root hub will be allowed to
+ * autosuspend again.
+ *
+ * The bus's private lock must be held by the caller.
+ */
+void usb_hcd_end_port_resume(struct usb_bus *bus, int portnum)
+{
+       unsigned bit = 1 << portnum;
+
+       if (bus->resuming_ports & bit) {
+               bus->resuming_ports &= ~bit;
+               pm_runtime_put_noidle(&bus->root_hub->dev);
+       }
+}
+EXPORT_SYMBOL_GPL(usb_hcd_end_port_resume);
 
 /*-------------------------------------------------------------------------*/
 
index a815fd2cc5e729287fe24b8a4b9bb4fc21082d6b..cbf7168e3ce7062c7610e1dfe0c13b6e49f9ff86 100644 (file)
@@ -877,6 +877,60 @@ static int hub_hub_status(struct usb_hub *hub,
        return ret;
 }
 
+static int hub_set_port_link_state(struct usb_hub *hub, int port1,
+                       unsigned int link_status)
+{
+       return set_port_feature(hub->hdev,
+                       port1 | (link_status << 3),
+                       USB_PORT_FEAT_LINK_STATE);
+}
+
+/*
+ * If USB 3.0 ports are placed into the Disabled state, they will no longer
+ * detect any device connects or disconnects.  This is generally not what the
+ * USB core wants, since it expects a disabled port to produce a port status
+ * change event when a new device connects.
+ *
+ * Instead, set the link state to Disabled, wait for the link to settle into
+ * that state, clear any change bits, and then put the port into the RxDetect
+ * state.
+ */
+static int hub_usb3_port_disable(struct usb_hub *hub, int port1)
+{
+       int ret;
+       int total_time;
+       u16 portchange, portstatus;
+
+       if (!hub_is_superspeed(hub->hdev))
+               return -EINVAL;
+
+       ret = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_SS_DISABLED);
+       if (ret) {
+               dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
+                               port1, ret);
+               return ret;
+       }
+
+       /* Wait for the link to enter the disabled state. */
+       for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
+               ret = hub_port_status(hub, port1, &portstatus, &portchange);
+               if (ret < 0)
+                       return ret;
+
+               if ((portstatus & USB_PORT_STAT_LINK_STATE) ==
+                               USB_SS_PORT_LS_SS_DISABLED)
+                       break;
+               if (total_time >= HUB_DEBOUNCE_TIMEOUT)
+                       break;
+               msleep(HUB_DEBOUNCE_STEP);
+       }
+       if (total_time >= HUB_DEBOUNCE_TIMEOUT)
+               dev_warn(hub->intfdev, "Could not disable port %d after %d ms\n",
+                               port1, total_time);
+
+       return hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_RX_DETECT);
+}
+
 static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
 {
        struct usb_device *hdev = hub->hdev;
@@ -885,8 +939,13 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
        if (hub->ports[port1 - 1]->child && set_state)
                usb_set_device_state(hub->ports[port1 - 1]->child,
                                USB_STATE_NOTATTACHED);
-       if (!hub->error && !hub_is_superspeed(hub->hdev))
-               ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
+       if (!hub->error) {
+               if (hub_is_superspeed(hub->hdev))
+                       ret = hub_usb3_port_disable(hub, port1);
+               else
+                       ret = clear_port_feature(hdev, port1,
+                                       USB_PORT_FEAT_ENABLE);
+       }
        if (ret)
                dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
                                port1, ret);
@@ -2440,7 +2499,7 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
 #define HUB_SHORT_RESET_TIME   10
 #define HUB_BH_RESET_TIME      50
 #define HUB_LONG_RESET_TIME    200
-#define HUB_RESET_TIMEOUT      500
+#define HUB_RESET_TIMEOUT      800
 
 static int hub_port_reset(struct usb_hub *hub, int port1,
                        struct usb_device *udev, unsigned int delay, bool warm);
@@ -2475,6 +2534,10 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
                if (ret < 0)
                        return ret;
 
+               /* The port state is unknown until the reset completes. */
+               if ((portstatus & USB_PORT_STAT_RESET))
+                       goto delay;
+
                /*
                 * Some buggy devices require a warm reset to be issued even
                 * when the port appears not to be connected.
@@ -2520,11 +2583,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
                        if ((portchange & USB_PORT_STAT_C_CONNECTION))
                                return -ENOTCONN;
 
-                       /* if we`ve finished resetting, then break out of
-                        * the loop
-                        */
-                       if (!(portstatus & USB_PORT_STAT_RESET) &&
-                           (portstatus & USB_PORT_STAT_ENABLE)) {
+                       if ((portstatus & USB_PORT_STAT_ENABLE)) {
                                if (hub_is_wusb(hub))
                                        udev->speed = USB_SPEED_WIRELESS;
                                else if (hub_is_superspeed(hub->hdev))
@@ -2538,10 +2597,15 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
                                return 0;
                        }
                } else {
-                       if (portchange & USB_PORT_STAT_C_BH_RESET)
-                               return 0;
+                       if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
+                                       hub_port_warm_reset_required(hub,
+                                               portstatus))
+                               return -ENOTCONN;
+
+                       return 0;
                }
 
+delay:
                /* switch to the long delay after two short delay failures */
                if (delay_time >= 2 * HUB_SHORT_RESET_TIME)
                        delay = HUB_LONG_RESET_TIME;
@@ -2565,14 +2629,11 @@ static void hub_port_finish_reset(struct usb_hub *hub, int port1,
                        msleep(10 + 40);
                        update_devnum(udev, 0);
                        hcd = bus_to_hcd(udev->bus);
-                       if (hcd->driver->reset_device) {
-                               *status = hcd->driver->reset_device(hcd, udev);
-                               if (*status < 0) {
-                                       dev_err(&udev->dev, "Cannot reset "
-                                                       "HCD device state\n");
-                                       break;
-                               }
-                       }
+                       /* The xHC may think the device is already reset,
+                        * so ignore the status.
+                        */
+                       if (hcd->driver->reset_device)
+                               hcd->driver->reset_device(hcd, udev);
                }
                /* FALL THROUGH */
        case -ENOTCONN:
@@ -2580,16 +2641,16 @@ static void hub_port_finish_reset(struct usb_hub *hub, int port1,
                clear_port_feature(hub->hdev,
                                port1, USB_PORT_FEAT_C_RESET);
                /* FIXME need disconnect() for NOTATTACHED device */
-               if (warm) {
+               if (hub_is_superspeed(hub->hdev)) {
                        clear_port_feature(hub->hdev, port1,
                                        USB_PORT_FEAT_C_BH_PORT_RESET);
                        clear_port_feature(hub->hdev, port1,
                                        USB_PORT_FEAT_C_PORT_LINK_STATE);
-               } else {
+               }
+               if (!warm)
                        usb_set_device_state(udev, *status
                                        ? USB_STATE_NOTATTACHED
                                        : USB_STATE_DEFAULT);
-               }
                break;
        }
 }
@@ -2777,6 +2838,23 @@ void usb_enable_ltm(struct usb_device *udev)
 EXPORT_SYMBOL_GPL(usb_enable_ltm);
 
 #ifdef CONFIG_USB_SUSPEND
+/*
+ * usb_disable_function_remotewakeup - disable usb3.0
+ * device's function remote wakeup
+ * @udev: target device
+ *
+ * Assume there's only one function on the USB 3.0
+ * device and disable remote wake for the first
+ * interface. FIXME if the interface association
+ * descriptor shows there's more than one function.
+ */
+static int usb_disable_function_remotewakeup(struct usb_device *udev)
+{
+       return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                               USB_REQ_CLEAR_FEATURE, USB_RECIP_INTERFACE,
+                               USB_INTRF_FUNC_SUSPEND, 0, NULL, 0,
+                               USB_CTRL_SET_TIMEOUT);
+}
 
 /*
  * usb_port_suspend - suspend a usb device's upstream port
@@ -2894,12 +2972,19 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
                dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n",
                                port1, status);
                /* paranoia:  "should not happen" */
-               if (udev->do_remote_wakeup)
-                       (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-                               USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
-                               USB_DEVICE_REMOTE_WAKEUP, 0,
-                               NULL, 0,
-                               USB_CTRL_SET_TIMEOUT);
+               if (udev->do_remote_wakeup) {
+                       if (!hub_is_superspeed(hub->hdev)) {
+                               (void) usb_control_msg(udev,
+                                               usb_sndctrlpipe(udev, 0),
+                                               USB_REQ_CLEAR_FEATURE,
+                                               USB_RECIP_DEVICE,
+                                               USB_DEVICE_REMOTE_WAKEUP, 0,
+                                               NULL, 0,
+                                               USB_CTRL_SET_TIMEOUT);
+                       } else
+                               (void) usb_disable_function_remotewakeup(udev);
+
+               }
 
                /* Try to enable USB2 hardware LPM again */
                if (udev->usb2_hw_lpm_capable == 1)
@@ -2939,7 +3024,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
 static int finish_port_resume(struct usb_device *udev)
 {
        int     status = 0;
-       u16     devstatus;
+       u16     devstatus = 0;
 
        /* caller owns the udev device lock */
        dev_dbg(&udev->dev, "%s\n",
@@ -2984,21 +3069,37 @@ static int finish_port_resume(struct usb_device *udev)
        if (status) {
                dev_dbg(&udev->dev, "gone after usb resume? status %d\n",
                                status);
-       } else if (udev->actconfig) {
-               le16_to_cpus(&devstatus);
-               if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) {
-                       status = usb_control_msg(udev,
-                                       usb_sndctrlpipe(udev, 0),
-                                       USB_REQ_CLEAR_FEATURE,
+       /*
+        * There are a few quirky devices which violate the standard
+        * by claiming to have remote wakeup enabled after a reset,
+        * which crash if the feature is cleared, hence check for
+        * udev->reset_resume
+        */
+       } else if (udev->actconfig && !udev->reset_resume) {
+               if (!hub_is_superspeed(udev->parent)) {
+                       le16_to_cpus(&devstatus);
+                       if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP))
+                               status = usb_control_msg(udev,
+                                               usb_sndctrlpipe(udev, 0),
+                                               USB_REQ_CLEAR_FEATURE,
                                                USB_RECIP_DEVICE,
-                                       USB_DEVICE_REMOTE_WAKEUP, 0,
-                                       NULL, 0,
-                                       USB_CTRL_SET_TIMEOUT);
-                       if (status)
-                               dev_dbg(&udev->dev,
-                                       "disable remote wakeup, status %d\n",
-                                       status);
+                                               USB_DEVICE_REMOTE_WAKEUP, 0,
+                                               NULL, 0,
+                                               USB_CTRL_SET_TIMEOUT);
+               } else {
+                       status = usb_get_status(udev, USB_RECIP_INTERFACE, 0,
+                                       &devstatus);
+                       le16_to_cpus(&devstatus);
+                       if (!status && devstatus & (USB_INTRF_STAT_FUNC_RW_CAP
+                                       | USB_INTRF_STAT_FUNC_RW))
+                               status =
+                                       usb_disable_function_remotewakeup(udev);
                }
+
+               if (status)
+                       dev_dbg(&udev->dev,
+                               "disable remote wakeup, status %d\n",
+                               status);
                status = 0;
        }
        return status;
@@ -4638,9 +4739,14 @@ static void hub_events(void)
                         * SS.Inactive state.
                         */
                        if (hub_port_warm_reset_required(hub, portstatus)) {
+                               int status;
+
                                dev_dbg(hub_dev, "warm reset port %d\n", i);
-                               hub_port_reset(hub, i, NULL,
+                               status = hub_port_reset(hub, i, NULL,
                                                HUB_BH_RESET_TIME, true);
+                               if (status < 0)
+                                       hub_port_disable(hub, i, 1);
+                               connect_change = 0;
                        }
 
                        if (connect_change)
index fdefd9c7f7af089e54428b57625d394c70566561..3113c1d71442953d3f9659afff042064bca8a9b9 100644 (file)
@@ -43,6 +43,9 @@ static const struct usb_device_id usb_quirk_list[] = {
        /* Creative SB Audigy 2 NX */
        { USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME },
 
+       /* Microsoft LifeCam-VX700 v2.0 */
+       { USB_DEVICE(0x045e, 0x0770), .driver_info = USB_QUIRK_RESET_RESUME },
+
        /* Logitech Quickcam Fusion */
        { USB_DEVICE(0x046d, 0x08c1), .driver_info = USB_QUIRK_RESET_RESUME },
 
index 92604b4f9712630e83898c83df01b985e8b5a631..5945aadaa1c99a41df4edff373bbfbad080e34fe 100644 (file)
@@ -56,7 +56,7 @@
 #define dump_register(nm)                              \
 {                                                      \
        .name   = __stringify(nm),                      \
-       .offset = DWC3_ ##nm,                           \
+       .offset = DWC3_ ##nm - DWC3_GLOBALS_REGS_START, \
 }
 
 static const struct debugfs_reg32 dwc3_regs[] = {
index 2e43b332aae8b891a5d0e0988172791e4b7a1f88..2fdd767f8fe890defe831c958d1d723af4b060ae 100644 (file)
@@ -1605,6 +1605,7 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc)
 
                if (epnum == 0 || epnum == 1) {
                        dep->endpoint.maxpacket = 512;
+                       dep->endpoint.maxburst = 1;
                        dep->endpoint.ops = &dwc3_gadget_ep0_ops;
                        if (!epnum)
                                dwc->gadget.ep0 = &dep->endpoint;
index fc0ec5e0d58ef43a11f525148dca69d1cb442188..d9f6b9372491d487a817de021f8bfc660d04cd00 100644 (file)
@@ -3231,7 +3231,7 @@ static int udc_pci_probe(
        }
 
        if (!pdev->irq) {
-               dev_err(&dev->pdev->dev, "irq not set\n");
+               dev_err(&pdev->dev, "irq not set\n");
                kfree(dev);
                dev = NULL;
                retval = -ENODEV;
@@ -3250,7 +3250,7 @@ static int udc_pci_probe(
        dev->txfifo = (u32 __iomem *)(dev->virt_addr + UDC_TXFIFO_ADDR);
 
        if (request_irq(pdev->irq, udc_irq, IRQF_SHARED, name, dev) != 0) {
-               dev_dbg(&dev->pdev->dev, "request_irq(%d) fail\n", pdev->irq);
+               dev_dbg(&pdev->dev, "request_irq(%d) fail\n", pdev->irq);
                kfree(dev);
                dev = NULL;
                retval = -EBUSY;
index 95d584dbed13b08516f23d90d83acb973d989a3a..8cf0c0f6fa1fb83bab5f26ab4d54a24e7bda3875 100644 (file)
@@ -130,10 +130,7 @@ static const char ep0name[] = "ep0";
 static const char *const ep_name[] = {
        ep0name,                                /* everyone has ep0 */
 
-       /* act like a net2280: high speed, six configurable endpoints */
-       "ep-a", "ep-b", "ep-c", "ep-d", "ep-e", "ep-f",
-
-       /* or like pxa250: fifteen fixed function endpoints */
+       /* act like a pxa250: fifteen fixed function endpoints */
        "ep1in-bulk", "ep2out-bulk", "ep3in-iso", "ep4out-iso", "ep5in-int",
        "ep6in-bulk", "ep7out-bulk", "ep8in-iso", "ep9out-iso", "ep10in-int",
        "ep11in-bulk", "ep12out-bulk", "ep13in-iso", "ep14out-iso",
@@ -141,6 +138,10 @@ static const char *const ep_name[] = {
 
        /* or like sa1100: two fixed function endpoints */
        "ep1out-bulk", "ep2in-bulk",
+
+       /* and now some generic EPs so we have enough in multi config */
+       "ep3out", "ep4in", "ep5out", "ep6out", "ep7in", "ep8out", "ep9in",
+       "ep10out", "ep11out", "ep12in", "ep13out", "ep14in", "ep15out",
 };
 #define DUMMY_ENDPOINTS        ARRAY_SIZE(ep_name)
 
index 4a6961c517f261103f006259bab366265dde7e4c..8c2f251211491258c2c83396b558132444e7ec9d 100644 (file)
@@ -1153,15 +1153,15 @@ static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
                                        pr_err("%s: unmapped value: %lu\n", opts, value);
                                        return -EINVAL;
                                }
-                       }
-                       else if (!memcmp(opts, "gid", 3))
+                       } else if (!memcmp(opts, "gid", 3)) {
                                data->perms.gid = make_kgid(current_user_ns(), value);
                                if (!gid_valid(data->perms.gid)) {
                                        pr_err("%s: unmapped value: %lu\n", opts, value);
                                        return -EINVAL;
                                }
-                       else
+                       } else {
                                goto invalid;
+                       }
                        break;
 
                default:
index 1b0f086426bd92648b5b6ad0e743c2530601a2c2..d3bd7b095ba37713dfc3bdf1faaa39ddded393ac 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/io.h>
 
-#include <mach/hardware.h>
-
 static struct clk *mxc_ahb_clk;
 static struct clk *mxc_per_clk;
 static struct clk *mxc_ipg_clk;
 
 /* workaround ENGcm09152 for i.MX35 */
-#define USBPHYCTRL_OTGBASE_OFFSET      0x608
+#define MX35_USBPHYCTRL_OFFSET         0x600
+#define USBPHYCTRL_OTGBASE_OFFSET      0x8
 #define USBPHYCTRL_EVDO                        (1 << 23)
 
 int fsl_udc_clk_init(struct platform_device *pdev)
@@ -59,7 +58,7 @@ int fsl_udc_clk_init(struct platform_device *pdev)
        clk_prepare_enable(mxc_per_clk);
 
        /* make sure USB_CLK is running at 60 MHz +/- 1000 Hz */
-       if (!cpu_is_mx51()) {
+       if (!strcmp(pdev->id_entry->name, "imx-udc-mx27")) {
                freq = clk_get_rate(mxc_per_clk);
                if (pdata->phy_mode != FSL_USB2_PHY_ULPI &&
                    (freq < 59999000 || freq > 60001000)) {
@@ -79,27 +78,40 @@ eclkrate:
        return ret;
 }
 
-void fsl_udc_clk_finalize(struct platform_device *pdev)
+int fsl_udc_clk_finalize(struct platform_device *pdev)
 {
        struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
-       if (cpu_is_mx35()) {
-               unsigned int v;
+       int ret = 0;
 
-               /* workaround ENGcm09152 for i.MX35 */
-               if (pdata->workaround & FLS_USB2_WORKAROUND_ENGCM09152) {
-                       v = readl(MX35_IO_ADDRESS(MX35_USB_BASE_ADDR +
-                                       USBPHYCTRL_OTGBASE_OFFSET));
-                       writel(v | USBPHYCTRL_EVDO,
-                               MX35_IO_ADDRESS(MX35_USB_BASE_ADDR +
-                                       USBPHYCTRL_OTGBASE_OFFSET));
+       /* workaround ENGcm09152 for i.MX35 */
+       if (pdata->workaround & FLS_USB2_WORKAROUND_ENGCM09152) {
+               unsigned int v;
+               struct resource *res = platform_get_resource
+                       (pdev, IORESOURCE_MEM, 0);
+               void __iomem *phy_regs = ioremap(res->start +
+                                               MX35_USBPHYCTRL_OFFSET, 512);
+               if (!phy_regs) {
+                       dev_err(&pdev->dev, "ioremap for phy address fails\n");
+                       ret = -EINVAL;
+                       goto ioremap_err;
                }
+
+               v = readl(phy_regs + USBPHYCTRL_OTGBASE_OFFSET);
+               writel(v | USBPHYCTRL_EVDO,
+                       phy_regs + USBPHYCTRL_OTGBASE_OFFSET);
+
+               iounmap(phy_regs);
        }
 
+
+ioremap_err:
        /* ULPI transceivers don't need usbpll */
        if (pdata->phy_mode == FSL_USB2_PHY_ULPI) {
                clk_disable_unprepare(mxc_per_clk);
                mxc_per_clk = NULL;
        }
+
+       return ret;
 }
 
 void fsl_udc_clk_release(void)
index c19f7f13790bf5be541b5e854beab49c969443ef..667275cb7bad2fba361bc305348c5536f5116f93 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/fsl_devices.h>
 #include <linux/dmapool.h>
 #include <linux/delay.h>
+#include <linux/of_device.h>
 
 #include <asm/byteorder.h>
 #include <asm/io.h>
@@ -2438,11 +2439,6 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
        unsigned int i;
        u32 dccparams;
 
-       if (strcmp(pdev->name, driver_name)) {
-               VDBG("Wrong device");
-               return -ENODEV;
-       }
-
        udc_controller = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL);
        if (udc_controller == NULL) {
                ERR("malloc udc failed\n");
@@ -2547,7 +2543,9 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
                dr_controller_setup(udc_controller);
        }
 
-       fsl_udc_clk_finalize(pdev);
+       ret = fsl_udc_clk_finalize(pdev);
+       if (ret)
+               goto err_free_irq;
 
        /* Setup gadget structure */
        udc_controller->gadget.ops = &fsl_gadget_ops;
@@ -2756,22 +2754,32 @@ static int fsl_udc_otg_resume(struct device *dev)
 
        return fsl_udc_resume(NULL);
 }
-
 /*-------------------------------------------------------------------------
        Register entry point for the peripheral controller driver
 --------------------------------------------------------------------------*/
-
+static const struct platform_device_id fsl_udc_devtype[] = {
+       {
+               .name = "imx-udc-mx27",
+       }, {
+               .name = "imx-udc-mx51",
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(platform, fsl_udc_devtype);
 static struct platform_driver udc_driver = {
-       .remove  = __exit_p(fsl_udc_remove),
+       .remove         = __exit_p(fsl_udc_remove),
+       /* Just for FSL i.mx SoC currently */
+       .id_table       = fsl_udc_devtype,
        /* these suspend and resume are not usb suspend and resume */
-       .suspend = fsl_udc_suspend,
-       .resume  = fsl_udc_resume,
-       .driver  = {
-               .name = (char *)driver_name,
-               .owner = THIS_MODULE,
-               /* udc suspend/resume called from OTG driver */
-               .suspend = fsl_udc_otg_suspend,
-               .resume  = fsl_udc_otg_resume,
+       .suspend        = fsl_udc_suspend,
+       .resume         = fsl_udc_resume,
+       .driver         = {
+                       .name = (char *)driver_name,
+                       .owner = THIS_MODULE,
+                       /* udc suspend/resume called from OTG driver */
+                       .suspend = fsl_udc_otg_suspend,
+                       .resume  = fsl_udc_otg_resume,
        },
 };
 
index f61a967f70828dd21c7c7e401388610e6b62ce74..c6703bb07b23fcd026f450a581cf974a218013ad 100644 (file)
@@ -592,15 +592,16 @@ static inline struct ep_queue_head *get_qh_by_ep(struct fsl_ep *ep)
 struct platform_device;
 #ifdef CONFIG_ARCH_MXC
 int fsl_udc_clk_init(struct platform_device *pdev);
-void fsl_udc_clk_finalize(struct platform_device *pdev);
+int fsl_udc_clk_finalize(struct platform_device *pdev);
 void fsl_udc_clk_release(void);
 #else
 static inline int fsl_udc_clk_init(struct platform_device *pdev)
 {
        return 0;
 }
-static inline void fsl_udc_clk_finalize(struct platform_device *pdev)
+static inline int fsl_udc_clk_finalize(struct platform_device *pdev)
 {
+       return 0;
 }
 static inline void fsl_udc_clk_release(void)
 {
index 379aac7b82fcf62d57b5ef41e1054c2b22e8ae71..6e8b1272ebceb3e1317e092ae7243e584c6eeda4 100644 (file)
@@ -1012,7 +1012,7 @@ static void udc_clock_enable(struct mv_udc *udc)
        unsigned int i;
 
        for (i = 0; i < udc->clknum; i++)
-               clk_enable(udc->clk[i]);
+               clk_prepare_enable(udc->clk[i]);
 }
 
 static void udc_clock_disable(struct mv_udc *udc)
@@ -1020,7 +1020,7 @@ static void udc_clock_disable(struct mv_udc *udc)
        unsigned int i;
 
        for (i = 0; i < udc->clknum; i++)
-               clk_disable(udc->clk[i]);
+               clk_disable_unprepare(udc->clk[i]);
 }
 
 static void udc_stop(struct mv_udc *udc)
index 141971d9051eab7d16dca79f0e652917171336e9..439c3f972f8c28d18fb34c691ab9cf8b51559098 100644 (file)
@@ -3477,12 +3477,11 @@ static void s3c_hsotg_delete_debug(struct s3c_hsotg *hsotg)
 /**
  * s3c_hsotg_release - release callback for hsotg device
  * @dev: Device to for which release is called
+ *
+ * Nothing to do as the resource is allocated using devm_ API.
  */
 static void s3c_hsotg_release(struct device *dev)
 {
-       struct s3c_hsotg *hsotg = dev_get_drvdata(dev);
-
-       kfree(hsotg);
 }
 
 /**
index 4f7f76f00c7452239067a5b5498a500d26093f50..7cacd6ae818e3ef957efe728eeb9641ccbf42071 100644 (file)
@@ -1794,9 +1794,10 @@ static int tcm_usbg_drop_nexus(struct usbg_tpg *tpg)
        tpg->tpg_nexus = NULL;
 
        kfree(tv_nexus);
+       ret = 0;
 out:
        mutex_unlock(&tpg->tpg_mutex);
-       return 0;
+       return ret;
 }
 
 static ssize_t tcm_usbg_tpg_store_nexus(
index d0f95482f40e8058a24f27b161f33beb55bb3c4d..598dcc1212f0bb231830f0022961f67e37b6b0fa 100644 (file)
@@ -887,7 +887,7 @@ static void gs_close(struct tty_struct *tty, struct file *file)
        pr_debug("gs_close: ttyGS%d (%p,%p) done!\n",
                        port->port_num, tty, file);
 
-       wake_up_interruptible(&port->port.close_wait);
+       wake_up(&port->port.close_wait);
 exit:
        spin_unlock_irq(&port->port_lock);
 }
index d6bb128ce21ee424a02dfb7db31c96bc88c9af44..3a21c5d683c04f0380aa1606045b35375d26143c 100644 (file)
@@ -148,7 +148,7 @@ config USB_EHCI_FSL
          Variation of ARC USB block used in some Freescale chips.
 
 config USB_EHCI_MXC
-       bool "Support for Freescale i.MX on-chip EHCI USB controller"
+       tristate "Support for Freescale i.MX on-chip EHCI USB controller"
        depends on USB_EHCI_HCD && ARCH_MXC
        select USB_EHCI_ROOT_HUB_TT
        ---help---
index 1eb4c3006e9eab8b7d2588c0322d38b9ad724327..001fbff2fdefedbb346f71db705f8e687201e454 100644 (file)
@@ -26,6 +26,7 @@ obj-$(CONFIG_PCI)             += pci-quirks.o
 obj-$(CONFIG_USB_EHCI_HCD)     += ehci-hcd.o
 obj-$(CONFIG_USB_EHCI_PCI)     += ehci-pci.o
 obj-$(CONFIG_USB_EHCI_HCD_PLATFORM)    += ehci-platform.o
+obj-$(CONFIG_USB_EHCI_MXC)     += ehci-mxc.o
 
 obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o
 obj-$(CONFIG_USB_ISP116X_HCD)  += isp116x-hcd.o
index fd9b5424b860a20e7d04c54fe3cf4fc76ca3be2d..d81d2fcbff1894d79f0606d831d91b628c44f71e 100644 (file)
@@ -230,7 +230,7 @@ static int ehci_fsl_setup_phy(struct usb_hcd *hcd,
 
        switch (phy_mode) {
        case FSL_USB2_PHY_ULPI:
-               if (pdata->controller_ver) {
+               if (pdata->have_sysif_regs && pdata->controller_ver) {
                        /* controller version 1.6 or above */
                        setbits32(non_ehci + FSL_SOC_USB_CTRL,
                                        ULPI_PHY_CLK_SEL);
@@ -251,7 +251,7 @@ static int ehci_fsl_setup_phy(struct usb_hcd *hcd,
                portsc |= PORT_PTS_PTW;
                /* fall through */
        case FSL_USB2_PHY_UTMI:
-               if (pdata->controller_ver) {
+               if (pdata->have_sysif_regs && pdata->controller_ver) {
                        /* controller version 1.6 or above */
                        setbits32(non_ehci + FSL_SOC_USB_CTRL, UTMI_PHY_EN);
                        mdelay(FSL_UTMI_PHY_DLY);  /* Delay for UTMI PHY CLK to
@@ -267,7 +267,8 @@ static int ehci_fsl_setup_phy(struct usb_hcd *hcd,
                break;
        }
 
-       if (pdata->controller_ver && (phy_mode == FSL_USB2_PHY_ULPI)) {
+       if (pdata->have_sysif_regs && pdata->controller_ver &&
+           (phy_mode == FSL_USB2_PHY_ULPI)) {
                /* check PHY_CLK_VALID to get phy clk valid */
                if (!spin_event_timeout(in_be32(non_ehci + FSL_SOC_USB_CTRL) &
                                PHY_CLK_VALID, FSL_USB_PHY_CLK_TIMEOUT, 0)) {
@@ -278,7 +279,7 @@ static int ehci_fsl_setup_phy(struct usb_hcd *hcd,
 
        ehci_writel(ehci, portsc, &ehci->regs->port_status[port_offset]);
 
-       if (phy_mode != FSL_USB2_PHY_ULPI)
+       if (phy_mode != FSL_USB2_PHY_ULPI && pdata->have_sysif_regs)
                setbits32(non_ehci + FSL_SOC_USB_CTRL, USB_CTRL_USB_EN);
 
        return 0;
index c97503bb0b0e064a027575f9eb0d28094d3fda80..b416a3fc99594f30afbb15995191313f6c9315a6 100644 (file)
@@ -74,10 +74,6 @@ static const char    hcd_name [] = "ehci_hcd";
 #undef VERBOSE_DEBUG
 #undef EHCI_URB_TRACE
 
-#ifdef DEBUG
-#define EHCI_STATS
-#endif
-
 /* magic numbers that can affect system performance */
 #define        EHCI_TUNE_CERR          3       /* 0-3 qtd retries; 0 == don't stop */
 #define        EHCI_TUNE_RL_HS         4       /* nak throttle; see 4.9 */
@@ -801,6 +797,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
                        ehci->reset_done[i] = jiffies + msecs_to_jiffies(25);
                        set_bit(i, &ehci->resuming_ports);
                        ehci_dbg (ehci, "port %d remote wakeup\n", i + 1);
+                       usb_hcd_start_port_resume(&hcd->self, i);
                        mod_timer(&hcd->rh_timer, ehci->reset_done[i]);
                }
        }
@@ -1250,11 +1247,6 @@ MODULE_LICENSE ("GPL");
 #define        PLATFORM_DRIVER         ehci_fsl_driver
 #endif
 
-#ifdef CONFIG_USB_EHCI_MXC
-#include "ehci-mxc.c"
-#define PLATFORM_DRIVER                ehci_mxc_driver
-#endif
-
 #ifdef CONFIG_USB_EHCI_SH
 #include "ehci-sh.c"
 #define PLATFORM_DRIVER                ehci_hcd_sh_driver
@@ -1352,7 +1344,8 @@ MODULE_LICENSE ("GPL");
 
 #if !IS_ENABLED(CONFIG_USB_EHCI_PCI) && \
        !IS_ENABLED(CONFIG_USB_EHCI_HCD_PLATFORM) && \
-       !defined(CONFIG_USB_CHIPIDEA_HOST) && \
+       !IS_ENABLED(CONFIG_USB_CHIPIDEA_HOST) && \
+       !IS_ENABLED(CONFIG_USB_EHCI_MXC) && \
        !defined(PLATFORM_DRIVER) && \
        !defined(PS3_SYSTEM_BUS_DRIVER) && \
        !defined(OF_PLATFORM_DRIVER) && \
index 4ccb97c0678f93fd8379af59584bd79e8b8d10ef..4d3b294f203e3acefe469db340d695786b2ffc3f 100644 (file)
@@ -649,7 +649,11 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
                        status = STS_PCD;
                }
        }
-       /* FIXME autosuspend idle root hubs */
+
+       /* If a resume is in progress, make sure it can finish */
+       if (ehci->resuming_ports)
+               mod_timer(&hcd->rh_timer, jiffies + msecs_to_jiffies(25));
+
        spin_unlock_irqrestore (&ehci->lock, flags);
        return status ? retval : 0;
 }
@@ -851,6 +855,7 @@ static int ehci_hub_control (
                                /* resume signaling for 20 msec */
                                ehci->reset_done[wIndex] = jiffies
                                                + msecs_to_jiffies(20);
+                               usb_hcd_start_port_resume(&hcd->self, wIndex);
                                /* check the port again */
                                mod_timer(&ehci_to_hcd(ehci)->rh_timer,
                                                ehci->reset_done[wIndex]);
@@ -862,6 +867,7 @@ static int ehci_hub_control (
                                clear_bit(wIndex, &ehci->suspended_ports);
                                set_bit(wIndex, &ehci->port_c_suspend);
                                ehci->reset_done[wIndex] = 0;
+                               usb_hcd_end_port_resume(&hcd->self, wIndex);
 
                                /* stop resume signaling */
                                temp = ehci_readl(ehci, status_reg);
@@ -950,6 +956,7 @@ static int ehci_hub_control (
                        ehci->reset_done[wIndex] = 0;
                        if (temp & PORT_PE)
                                set_bit(wIndex, &ehci->port_c_suspend);
+                       usb_hcd_end_port_resume(&hcd->self, wIndex);
                }
 
                if (temp & PORT_OC)
index f7bfc0b898b97a90dfece792fe40ca16ef4a2f73..6c56297ea16b8f59bd887f9025cd68ca46a2be96 100644 (file)
@@ -43,7 +43,7 @@ static void ehci_clock_enable(struct ehci_hcd_mv *ehci_mv)
        unsigned int i;
 
        for (i = 0; i < ehci_mv->clknum; i++)
-               clk_enable(ehci_mv->clk[i]);
+               clk_prepare_enable(ehci_mv->clk[i]);
 }
 
 static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv)
@@ -51,7 +51,7 @@ static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv)
        unsigned int i;
 
        for (i = 0; i < ehci_mv->clknum; i++)
-               clk_disable(ehci_mv->clk[i]);
+               clk_disable_unprepare(ehci_mv->clk[i]);
 }
 
 static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv)
index ec7f5d2c90de3464f66a000583fa151fde29946e..dedb80bb8d40a5c37132fbdb0d0c569534aeb9c8 100644 (file)
  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/usb/otg.h>
 #include <linux/usb/ulpi.h>
 #include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
 
 #include <linux/platform_data/usb-ehci-mxc.h>
 
 #include <asm/mach-types.h>
 
+#include "ehci.h"
+
+#define DRIVER_DESC "Freescale On-Chip EHCI Host driver"
+
+static const char hcd_name[] = "ehci-mxc";
+
 #define ULPI_VIEWPORT_OFFSET   0x170
 
 struct ehci_mxc_priv {
        struct clk *usbclk, *ahbclk, *phyclk;
-       struct usb_hcd *hcd;
 };
 
-/* called during probe() after chip reset completes */
-static int ehci_mxc_setup(struct usb_hcd *hcd)
-{
-       hcd->has_tt = 1;
-
-       return ehci_setup(hcd);
-}
+static struct hc_driver __read_mostly ehci_mxc_hc_driver;
 
-static const struct hc_driver ehci_mxc_hc_driver = {
-       .description = hcd_name,
-       .product_desc = "Freescale On-Chip EHCI Host Controller",
-       .hcd_priv_size = sizeof(struct ehci_hcd),
-
-       /*
-        * generic hardware linkage
-        */
-       .irq = ehci_irq,
-       .flags = HCD_USB2 | HCD_MEMORY,
-
-       /*
-        * basic lifecycle operations
-        */
-       .reset = ehci_mxc_setup,
-       .start = ehci_run,
-       .stop = ehci_stop,
-       .shutdown = ehci_shutdown,
-
-       /*
-        * managing i/o requests and associated device resources
-        */
-       .urb_enqueue = ehci_urb_enqueue,
-       .urb_dequeue = ehci_urb_dequeue,
-       .endpoint_disable = ehci_endpoint_disable,
-       .endpoint_reset = ehci_endpoint_reset,
-
-       /*
-        * scheduling support
-        */
-       .get_frame_number = ehci_get_frame,
-
-       /*
-        * root hub support
-        */
-       .hub_status_data = ehci_hub_status_data,
-       .hub_control = ehci_hub_control,
-       .bus_suspend = ehci_bus_suspend,
-       .bus_resume = ehci_bus_resume,
-       .relinquish_port = ehci_relinquish_port,
-       .port_handed_over = ehci_port_handed_over,
-
-       .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+static const struct ehci_driver_overrides ehci_mxc_overrides __initdata = {
+       .extra_priv_size =      sizeof(struct ehci_mxc_priv),
 };
 
 static int ehci_mxc_drv_probe(struct platform_device *pdev)
@@ -112,12 +75,6 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev)
        if (!hcd)
                return -ENOMEM;
 
-       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
-       if (!priv) {
-               ret = -ENOMEM;
-               goto err_alloc;
-       }
-
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res) {
                dev_err(dev, "Found HC with no register addr. Check setup!\n");
@@ -135,6 +92,10 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev)
                goto err_alloc;
        }
 
+       hcd->has_tt = 1;
+       ehci = hcd_to_ehci(hcd);
+       priv = (struct ehci_mxc_priv *) ehci->priv;
+
        /* enable clocks */
        priv->usbclk = devm_clk_get(&pdev->dev, "ipg");
        if (IS_ERR(priv->usbclk)) {
@@ -169,8 +130,6 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev)
                mdelay(10);
        }
 
-       ehci = hcd_to_ehci(hcd);
-
        /* EHCI registers start at offset 0x100 */
        ehci->caps = hcd->regs + 0x100;
        ehci->regs = hcd->regs + 0x100 +
@@ -198,8 +157,7 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev)
                }
        }
 
-       priv->hcd = hcd;
-       platform_set_drvdata(pdev, priv);
+       platform_set_drvdata(pdev, hcd);
 
        ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
        if (ret)
@@ -244,8 +202,11 @@ err_alloc:
 static int __exit ehci_mxc_drv_remove(struct platform_device *pdev)
 {
        struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data;
-       struct ehci_mxc_priv *priv = platform_get_drvdata(pdev);
-       struct usb_hcd *hcd = priv->hcd;
+       struct usb_hcd *hcd = platform_get_drvdata(pdev);
+       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+       struct ehci_mxc_priv *priv = (struct ehci_mxc_priv *) ehci->priv;
+
+       usb_remove_hcd(hcd);
 
        if (pdata && pdata->exit)
                pdata->exit(pdev);
@@ -253,23 +214,20 @@ static int __exit ehci_mxc_drv_remove(struct platform_device *pdev)
        if (pdata->otg)
                usb_phy_shutdown(pdata->otg);
 
-       usb_remove_hcd(hcd);
-       usb_put_hcd(hcd);
-       platform_set_drvdata(pdev, NULL);
-
        clk_disable_unprepare(priv->usbclk);
        clk_disable_unprepare(priv->ahbclk);
 
        if (priv->phyclk)
                clk_disable_unprepare(priv->phyclk);
 
+       usb_put_hcd(hcd);
+       platform_set_drvdata(pdev, NULL);
        return 0;
 }
 
 static void ehci_mxc_drv_shutdown(struct platform_device *pdev)
 {
-       struct ehci_mxc_priv *priv = platform_get_drvdata(pdev);
-       struct usb_hcd *hcd = priv->hcd;
+       struct usb_hcd *hcd = platform_get_drvdata(pdev);
 
        if (hcd->driver->shutdown)
                hcd->driver->shutdown(hcd);
@@ -279,9 +237,31 @@ MODULE_ALIAS("platform:mxc-ehci");
 
 static struct platform_driver ehci_mxc_driver = {
        .probe = ehci_mxc_drv_probe,
-       .remove = __exit_p(ehci_mxc_drv_remove),
+       .remove = ehci_mxc_drv_remove,
        .shutdown = ehci_mxc_drv_shutdown,
        .driver = {
                   .name = "mxc-ehci",
        },
 };
+
+static int __init ehci_mxc_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+       ehci_init_driver(&ehci_mxc_hc_driver, &ehci_mxc_overrides);
+       return platform_driver_register(&ehci_mxc_driver);
+}
+module_init(ehci_mxc_init);
+
+static void __exit ehci_mxc_cleanup(void)
+{
+       platform_driver_unregister(&ehci_mxc_driver);
+}
+module_exit(ehci_mxc_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Sascha Hauer");
+MODULE_LICENSE("GPL");
index dabb20494826f1be237c9a08b6cea7569be46dd7..170b9399e09f34b42ec798b8e02985c5b0f71190 100644 (file)
@@ -200,6 +200,26 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
                break;
        }
 
+       /* optional debug port, normally in the first BAR */
+       temp = pci_find_capability(pdev, PCI_CAP_ID_DBG);
+       if (temp) {
+               pci_read_config_dword(pdev, temp, &temp);
+               temp >>= 16;
+               if (((temp >> 13) & 7) == 1) {
+                       u32 hcs_params = ehci_readl(ehci,
+                                                   &ehci->caps->hcs_params);
+
+                       temp &= 0x1fff;
+                       ehci->debug = hcd->regs + temp;
+                       temp = ehci_readl(ehci, &ehci->debug->control);
+                       ehci_info(ehci, "debug port %d%s\n",
+                                 HCS_DEBUG_PORT(hcs_params),
+                                 (temp & DBGP_ENABLED) ? " IN USE" : "");
+                       if (!(temp & DBGP_ENABLED))
+                               ehci->debug = NULL;
+               }
+       }
+
        retval = ehci_setup(hcd);
        if (retval)
                return retval;
@@ -228,25 +248,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
                break;
        }
 
-       /* optional debug port, normally in the first BAR */
-       temp = pci_find_capability(pdev, 0x0a);
-       if (temp) {
-               pci_read_config_dword(pdev, temp, &temp);
-               temp >>= 16;
-               if ((temp & (3 << 13)) == (1 << 13)) {
-                       temp &= 0x1fff;
-                       ehci->debug = hcd->regs + temp;
-                       temp = ehci_readl(ehci, &ehci->debug->control);
-                       ehci_info(ehci, "debug port %d%s\n",
-                               HCS_DEBUG_PORT(ehci->hcs_params),
-                               (temp & DBGP_ENABLED)
-                                       ? " IN USE"
-                                       : "");
-                       if (!(temp & DBGP_ENABLED))
-                               ehci->debug = NULL;
-               }
-       }
-
        /* at least the Genesys GL880S needs fixup here */
        temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params);
        temp &= 0x0f;
index 3d989028c8365e6644b86428fea2f24cfba9be6e..fd252f0cfb3a7873ec7bad8226a036a9668b182e 100644 (file)
@@ -1197,17 +1197,26 @@ static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested)
        if (ehci->async_iaa || ehci->async_unlinking)
                return;
 
-       /* Do all the waiting QHs at once */
-       ehci->async_iaa = ehci->async_unlink;
-       ehci->async_unlink = NULL;
-
        /* If the controller isn't running, we don't have to wait for it */
        if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) {
+
+               /* Do all the waiting QHs */
+               ehci->async_iaa = ehci->async_unlink;
+               ehci->async_unlink = NULL;
+
                if (!nested)            /* Avoid recursion */
                        end_unlink_async(ehci);
 
        /* Otherwise start a new IAA cycle */
        } else if (likely(ehci->rh_state == EHCI_RH_RUNNING)) {
+               struct ehci_qh          *qh;
+
+               /* Do only the first waiting QH (nVidia bug?) */
+               qh = ehci->async_unlink;
+               ehci->async_iaa = qh;
+               ehci->async_unlink = qh->unlink_next;
+               qh->unlink_next = NULL;
+
                /* Make sure the unlinks are all visible to the hardware */
                wmb();
 
@@ -1255,34 +1264,35 @@ static void end_unlink_async(struct ehci_hcd *ehci)
        }
 }
 
+static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh);
+
 static void unlink_empty_async(struct ehci_hcd *ehci)
 {
-       struct ehci_qh          *qh, *next;
-       bool                    stopped = (ehci->rh_state < EHCI_RH_RUNNING);
+       struct ehci_qh          *qh;
+       struct ehci_qh          *qh_to_unlink = NULL;
        bool                    check_unlinks_later = false;
+       int                     count = 0;
 
-       /* Unlink all the async QHs that have been empty for a timer cycle */
-       next = ehci->async->qh_next.qh;
-       while (next) {
-               qh = next;
-               next = qh->qh_next.qh;
-
+       /* Find the last async QH which has been empty for a timer cycle */
+       for (qh = ehci->async->qh_next.qh; qh; qh = qh->qh_next.qh) {
                if (list_empty(&qh->qtd_list) &&
                                qh->qh_state == QH_STATE_LINKED) {
-                       if (!stopped && qh->unlink_cycle ==
-                                       ehci->async_unlink_cycle)
+                       ++count;
+                       if (qh->unlink_cycle == ehci->async_unlink_cycle)
                                check_unlinks_later = true;
                        else
-                               single_unlink_async(ehci, qh);
+                               qh_to_unlink = qh;
                }
        }
 
-       /* Start a new IAA cycle if any QHs are waiting for it */
-       if (ehci->async_unlink)
-               start_iaa_cycle(ehci, false);
+       /* If nothing else is being unlinked, unlink the last empty QH */
+       if (!ehci->async_iaa && !ehci->async_unlink && qh_to_unlink) {
+               start_unlink_async(ehci, qh_to_unlink);
+               --count;
+       }
 
-       /* QHs that haven't been empty for long enough will be handled later */
-       if (check_unlinks_later) {
+       /* Other QHs will be handled later */
+       if (count > 0) {
                ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true);
                ++ehci->async_unlink_cycle;
        }
index 69ebee73c0c153581895dbd7860ab75024784394..b476daf49f6f3c6cf226b920f5c8ff01ca5d23aa 100644 (file)
@@ -213,7 +213,7 @@ static inline unsigned char tt_start_uframe(struct ehci_hcd *ehci, __hc32 mask)
 }
 
 static const unsigned char
-max_tt_usecs[] = { 125, 125, 125, 125, 125, 125, 30, 0 };
+max_tt_usecs[] = { 125, 125, 125, 125, 125, 125, 125, 25 };
 
 /* carryover low/fullspeed bandwidth that crosses uframe boundries */
 static inline void carryover_tt_bandwidth(unsigned short tt_usecs[8])
@@ -2212,11 +2212,11 @@ static void scan_isoc(struct ehci_hcd *ehci)
        }
        ehci->now_frame = now_frame;
 
+       frame = ehci->last_iso_frame;
        for (;;) {
                union ehci_shadow       q, *q_p;
                __hc32                  type, *hw_p;
 
-               frame = ehci->last_iso_frame;
 restart:
                /* scan each element in frame's queue for completions */
                q_p = &ehci->pshadow [frame];
@@ -2321,6 +2321,9 @@ restart:
                /* Stop when we have reached the current frame */
                if (frame == now_frame)
                        break;
-               ehci->last_iso_frame = (frame + 1) & fmask;
+
+               /* The last frame may still have active siTDs */
+               ehci->last_iso_frame = frame;
+               frame = (frame + 1) & fmask;
        }
 }
index 20dbdcbe9b0fc1fb1330da115bebb3546e45b8f2..f904071d70df2d1cb2d46710c44820ffbd3a90dc 100644 (file)
@@ -113,14 +113,15 @@ static void ehci_poll_ASS(struct ehci_hcd *ehci)
 
        if (want != actual) {
 
-               /* Poll again later, but give up after about 20 ms */
-               if (ehci->ASS_poll_count++ < 20) {
-                       ehci_enable_event(ehci, EHCI_HRTIMER_POLL_ASS, true);
-                       return;
-               }
-               ehci_dbg(ehci, "Waited too long for the async schedule status (%x/%x), giving up\n",
-                               want, actual);
+               /* Poll again later */
+               ehci_enable_event(ehci, EHCI_HRTIMER_POLL_ASS, true);
+               ++ehci->ASS_poll_count;
+               return;
        }
+
+       if (ehci->ASS_poll_count > 20)
+               ehci_dbg(ehci, "ASS poll count reached %d\n",
+                               ehci->ASS_poll_count);
        ehci->ASS_poll_count = 0;
 
        /* The status is up-to-date; restart or stop the schedule as needed */
@@ -159,14 +160,14 @@ static void ehci_poll_PSS(struct ehci_hcd *ehci)
 
        if (want != actual) {
 
-               /* Poll again later, but give up after about 20 ms */
-               if (ehci->PSS_poll_count++ < 20) {
-                       ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true);
-                       return;
-               }
-               ehci_dbg(ehci, "Waited too long for the periodic schedule status (%x/%x), giving up\n",
-                               want, actual);
+               /* Poll again later */
+               ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true);
+               return;
        }
+
+       if (ehci->PSS_poll_count > 20)
+               ehci_dbg(ehci, "PSS poll count reached %d\n",
+                               ehci->PSS_poll_count);
        ehci->PSS_poll_count = 0;
 
        /* The status is up-to-date; restart or stop the schedule as needed */
index 9dadc7118d68e13a84e793a10171a500e6d52fad..36c3a82105953ba2433d12845e7e02a293fa6d35 100644 (file)
@@ -38,6 +38,10 @@ typedef __u16 __bitwise __hc16;
 #endif
 
 /* statistics can be kept for tuning/monitoring */
+#ifdef DEBUG
+#define EHCI_STATS
+#endif
+
 struct ehci_stats {
        /* irq usage */
        unsigned long           normal;
@@ -221,6 +225,9 @@ struct ehci_hcd {                   /* one per controller */
 #ifdef DEBUG
        struct dentry           *debug_dir;
 #endif
+
+       /* platform-specific data -- must come last */
+       unsigned long           priv[0] __aligned(sizeof(s64));
 };
 
 /* convert between an HCD pointer and the corresponding EHCI_HCD */
index 5105127c1d4b3e4c3ac06953c25ac959fdc24a6c..11e0b79ff9d52a8d703c3b2134b8960768123246 100644 (file)
@@ -142,6 +142,9 @@ static int usb_get_ver_info(struct device_node *np)
                        return ver;
        }
 
+       if (of_device_is_compatible(np, "fsl,mpc5121-usb2-dr"))
+               return FSL_USB_VER_OLD;
+
        if (of_device_is_compatible(np, "fsl-usb2-mph")) {
                if (of_device_is_compatible(np, "fsl-usb2-mph-v1.6"))
                        ver = FSL_USB_VER_1_6;
index bd6a7447ccc9efc41cd28e9a2c39b576c92fb6a3..f0ebe8e7c58b4dced4d50617b7a6235d90143cac 100644 (file)
@@ -58,6 +58,7 @@
 #include <linux/usb.h>
 #include <linux/usb/hcd.h>
 #include <linux/dma-mapping.h>
+#include <linux/module.h>
 
 #include "imx21-hcd.h"
 
index d370245a4ee22af7716983da3b48a9791251a4ff..5e3a6deb62b1e7d37b30f0f8a4f534f9e740fda9 100644 (file)
@@ -128,7 +128,8 @@ static void tmio_start_hc(struct platform_device *dev)
        tmio_iowrite8(2, tmio->ccr + CCR_INTC);
 
        dev_info(&dev->dev, "revision %d @ 0x%08llx, irq %d\n",
-                       tmio_ioread8(tmio->ccr + CCR_REVID), hcd->rsrc_start, hcd->irq);
+                       tmio_ioread8(tmio->ccr + CCR_REVID),
+                       (u64) hcd->rsrc_start, hcd->irq);
 }
 
 static int ohci_tmio_start(struct usb_hcd *hcd)
index a3b6d7104ae237f17846fdd7d41f4f14fcc7e4b5..4c338ec03a07d1bfa72ceea172f07e19f151f54b 100644 (file)
@@ -780,6 +780,7 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev)
                                "defaulting to EHCI.\n");
                dev_warn(&xhci_pdev->dev,
                                "USB 3.0 devices will work at USB 2.0 speeds.\n");
+               usb_disable_xhci_ports(xhci_pdev);
                return;
        }
 
index 4b9e9aba26654e3f70f5801bf2ada2438a739c94..4f64d24eebc82b7927afbf1d7bace3d3eb433160 100644 (file)
@@ -447,6 +447,10 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd)
                return IRQ_NONE;
        uhci_writew(uhci, status, USBSTS);              /* Clear it */
 
+       spin_lock(&uhci->lock);
+       if (unlikely(!uhci->is_initialized))    /* not yet configured */
+               goto done;
+
        if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) {
                if (status & USBSTS_HSE)
                        dev_err(uhci_dev(uhci), "host system error, "
@@ -455,7 +459,6 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd)
                        dev_err(uhci_dev(uhci), "host controller process "
                                        "error, something bad happened!\n");
                if (status & USBSTS_HCH) {
-                       spin_lock(&uhci->lock);
                        if (uhci->rh_state >= UHCI_RH_RUNNING) {
                                dev_err(uhci_dev(uhci),
                                        "host controller halted, "
@@ -473,15 +476,15 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd)
                                 * pending unlinks */
                                mod_timer(&hcd->rh_timer, jiffies);
                        }
-                       spin_unlock(&uhci->lock);
                }
        }
 
-       if (status & USBSTS_RD)
+       if (status & USBSTS_RD) {
+               spin_unlock(&uhci->lock);
                usb_hcd_poll_rh_status(hcd);
-       else {
-               spin_lock(&uhci->lock);
+       } else {
                uhci_scan_schedule(uhci);
+ done:
                spin_unlock(&uhci->lock);
        }
 
@@ -662,9 +665,9 @@ static int uhci_start(struct usb_hcd *hcd)
         */
        mb();
 
+       spin_lock_irq(&uhci->lock);
        configure_hc(uhci);
        uhci->is_initialized = 1;
-       spin_lock_irq(&uhci->lock);
        start_rh(uhci);
        spin_unlock_irq(&uhci->lock);
        return 0;
index 768d54295a20742a8f38d6224ce1a9bb1de85556..15d13229ddbb6860d41e706bfaf716fe49b93067 100644 (file)
@@ -116,6 +116,7 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
                }
        }
        clear_bit(port, &uhci->resuming_ports);
+       usb_hcd_end_port_resume(&uhci_to_hcd(uhci)->self, port);
 }
 
 /* Wait for the UHCI controller in HP's iLO2 server management chip.
@@ -167,6 +168,8 @@ static void uhci_check_ports(struct uhci_hcd *uhci)
                                set_bit(port, &uhci->resuming_ports);
                                uhci->ports_timeout = jiffies +
                                                msecs_to_jiffies(25);
+                               usb_hcd_start_port_resume(
+                                               &uhci_to_hcd(uhci)->self, port);
 
                                /* Make sure we see the port again
                                 * after the resuming period is over. */
index a686cf4905bb80ae3e4c7b206c05d7cd2b960920..68914429482f30ff4afed47180aae563d07c5784 100644 (file)
@@ -761,12 +761,39 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                        break;
                case USB_PORT_FEAT_LINK_STATE:
                        temp = xhci_readl(xhci, port_array[wIndex]);
+
+                       /* Disable port */
+                       if (link_state == USB_SS_PORT_LS_SS_DISABLED) {
+                               xhci_dbg(xhci, "Disable port %d\n", wIndex);
+                               temp = xhci_port_state_to_neutral(temp);
+                               /*
+                                * Clear all change bits, so that we get a new
+                                * connection event.
+                                */
+                               temp |= PORT_CSC | PORT_PEC | PORT_WRC |
+                                       PORT_OCC | PORT_RC | PORT_PLC |
+                                       PORT_CEC;
+                               xhci_writel(xhci, temp | PORT_PE,
+                                       port_array[wIndex]);
+                               temp = xhci_readl(xhci, port_array[wIndex]);
+                               break;
+                       }
+
+                       /* Put link in RxDetect (enable port) */
+                       if (link_state == USB_SS_PORT_LS_RX_DETECT) {
+                               xhci_dbg(xhci, "Enable port %d\n", wIndex);
+                               xhci_set_link_state(xhci, port_array, wIndex,
+                                               link_state);
+                               temp = xhci_readl(xhci, port_array[wIndex]);
+                               break;
+                       }
+
                        /* Software should not attempt to set
-                        * port link state above '5' (Rx.Detect) and the port
+                        * port link state above '3' (U3) and the port
                         * must be enabled.
                         */
                        if ((temp & PORT_PE) == 0 ||
-                               (link_state > USB_SS_PORT_LS_RX_DETECT)) {
+                               (link_state > USB_SS_PORT_LS_U3)) {
                                xhci_warn(xhci, "Cannot set link state.\n");
                                goto error;
                        }
@@ -957,6 +984,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
        int max_ports;
        __le32 __iomem **port_array;
        struct xhci_bus_state *bus_state;
+       bool reset_change = false;
 
        max_ports = xhci_get_ports(hcd, &port_array);
        bus_state = &xhci->bus_state[hcd_index(hcd)];
@@ -988,6 +1016,12 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
                        buf[(i + 1) / 8] |= 1 << (i + 1) % 8;
                        status = 1;
                }
+               if ((temp & PORT_RC))
+                       reset_change = true;
+       }
+       if (!status && !reset_change) {
+               xhci_dbg(xhci, "%s: stopping port polling.\n", __func__);
+               clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
        }
        spin_unlock_irqrestore(&xhci->lock, flags);
        return status ? retval : 0;
index fb51c7085ad0d19c7159afd9a069c82846afd405..35616ffbe3ae7435155bf29196d295f35a719b71 100644 (file)
@@ -1250,6 +1250,8 @@ static unsigned int xhci_microframes_to_exponent(struct usb_device *udev,
 static unsigned int xhci_parse_microframe_interval(struct usb_device *udev,
                struct usb_host_endpoint *ep)
 {
+       if (ep->desc.bInterval == 0)
+               return 0;
        return xhci_microframes_to_exponent(udev, ep,
                        ep->desc.bInterval, 0, 15);
 }
index cbb44b7b9d6553e01f9c2f45da23c8fad1b7fe92..7f76a49e90d384fdee83d0f87607113c9ebcf272 100644 (file)
@@ -1698,7 +1698,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
                                faked_port_index + 1);
                if (slot_id && xhci->devs[slot_id])
                        xhci_ring_device(xhci, slot_id);
-               if (bus_state->port_remote_wakeup && (1 << faked_port_index)) {
+               if (bus_state->port_remote_wakeup & (1 << faked_port_index)) {
                        bus_state->port_remote_wakeup &=
                                ~(1 << faked_port_index);
                        xhci_test_and_clear_bit(xhci, port_array,
@@ -1725,6 +1725,15 @@ cleanup:
        if (bogus_port_status)
                return;
 
+       /*
+        * xHCI port-status-change events occur when the "or" of all the
+        * status-change bits in the portsc register changes from 0 to 1.
+        * New status changes won't cause an event if any other change
+        * bits are still set.  When an event occurs, switch over to
+        * polling to avoid losing status changes.
+        */
+       xhci_dbg(xhci, "%s: starting port polling.\n", __func__);
+       set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
        spin_unlock(&xhci->lock);
        /* Pass this up to the core */
        usb_hcd_poll_rh_status(hcd);
@@ -2580,6 +2589,8 @@ cleanup:
                                (trb_comp_code != COMP_STALL &&
                                        trb_comp_code != COMP_BABBLE))
                                xhci_urb_free_priv(xhci, urb_priv);
+                       else
+                               kfree(urb_priv);
 
                        usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb);
                        if ((urb->actual_length != urb->transfer_buffer_length &&
@@ -3099,7 +3110,7 @@ static u32 xhci_v1_0_td_remainder(int running_total, int trb_buff_len,
         * running_total.
         */
        packets_transferred = (running_total + trb_buff_len) /
-               usb_endpoint_maxp(&urb->ep->desc);
+               GET_MAX_PACKET(usb_endpoint_maxp(&urb->ep->desc));
 
        if ((total_packet_count - packets_transferred) > 31)
                return 31 << 17;
@@ -3633,7 +3644,8 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
                td_len = urb->iso_frame_desc[i].length;
                td_remain_len = td_len;
                total_packet_count = DIV_ROUND_UP(td_len,
-                               usb_endpoint_maxp(&urb->ep->desc));
+                               GET_MAX_PACKET(
+                                       usb_endpoint_maxp(&urb->ep->desc)));
                /* A zero-length transfer still involves at least one packet. */
                if (total_packet_count == 0)
                        total_packet_count++;
@@ -3655,9 +3667,11 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
                td = urb_priv->td[i];
                for (j = 0; j < trbs_per_td; j++) {
                        u32 remainder = 0;
-                       field = TRB_TBC(burst_count) | TRB_TLBPC(residue);
+                       field = 0;
 
                        if (first_trb) {
+                               field = TRB_TBC(burst_count) |
+                                       TRB_TLBPC(residue);
                                /* Queue the isoc TRB */
                                field |= TRB_TYPE(TRB_ISOC);
                                /* Assume URB_ISO_ASAP is set */
index 5c72c431bab18da941ec9a1a235c134086065482..f1f01a834ba792cee0935c5ffb9373d53fb368bd 100644 (file)
@@ -884,6 +884,11 @@ int xhci_suspend(struct xhci_hcd *xhci)
                        xhci->shared_hcd->state != HC_STATE_SUSPENDED)
                return -EINVAL;
 
+       /* Don't poll the roothubs on bus suspend. */
+       xhci_dbg(xhci, "%s: stopping port polling.\n", __func__);
+       clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+       del_timer_sync(&hcd->rh_timer);
+
        spin_lock_irq(&xhci->lock);
        clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
        clear_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags);
@@ -1069,6 +1074,11 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
        if (xhci->quirks & XHCI_COMP_MODE_QUIRK)
                compliance_mode_recovery_timer_init(xhci);
 
+       /* Re-enable port polling. */
+       xhci_dbg(xhci, "%s: starting port polling.\n", __func__);
+       set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+       usb_hcd_poll_rh_status(hcd);
+
        return retval;
 }
 #endif /* CONFIG_PM */
index 7667b12f2ff5b0a0a32cd236d6d6e5d970e6cc47..268148de97147d7f77652914cd46660954c83525 100644 (file)
@@ -2179,7 +2179,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
                if (dev->out_pipe == 0 || !param->length || param->sglen < 4)
                        break;
                retval = 0;
-               dev_info(&intf->dev, "TEST 17:  unlink from %d queues of "
+               dev_info(&intf->dev, "TEST 24:  unlink from %d queues of "
                                "%d %d-byte writes\n",
                                param->iterations, param->sglen, param->length);
                for (i = param->iterations; retval == 0 && i > 0; --i) {
index 0968dd7a859def7e04496d525bc51bdecb87cea0..f522000e8f0659b11124f48ef94f6f0c1bdb104c 100644 (file)
@@ -105,7 +105,7 @@ static void cppi_reset_tx(struct cppi_tx_stateram __iomem *tx, u32 ptr)
        musb_writel(&tx->tx_complete, 0, ptr);
 }
 
-static void __init cppi_pool_init(struct cppi *cppi, struct cppi_channel *c)
+static void cppi_pool_init(struct cppi *cppi, struct cppi_channel *c)
 {
        int     j;
 
@@ -150,7 +150,7 @@ static void cppi_pool_free(struct cppi_channel *c)
        c->last_processed = NULL;
 }
 
-static int __init cppi_controller_start(struct dma_controller *c)
+static int cppi_controller_start(struct dma_controller *c)
 {
        struct cppi     *controller;
        void __iomem    *tibase;
index f1c6c5470b92550831cbe6551cf8cb090b2d491d..fd3486745e64bcdecd3aeea4843971e5e1795c22 100644 (file)
@@ -2298,10 +2298,7 @@ static int __init musb_init(void)
        if (usb_disabled())
                return 0;
 
-       pr_info("%s: version " MUSB_VERSION ", "
-               "?dma?"
-               ", "
-               "otg (peripheral+host)",
+       pr_info("%s: version " MUSB_VERSION ", ?dma?, otg (peripheral+host)\n",
                musb_driver_name);
        return platform_driver_register(&musb_driver);
 }
index e6f2ae8368bb8b046b34716698b553156df30a99..f7d764de6fdab988bde6124a8a9cd8657a0ea0ff 100644 (file)
@@ -134,6 +134,11 @@ static const resource_size_t dsps_control_module_phys[] = {
        DSPS_AM33XX_CONTROL_MODULE_PHYS_1,
 };
 
+#define USBPHY_CM_PWRDN                (1 << 0)
+#define USBPHY_OTG_PWRDN       (1 << 1)
+#define USBPHY_OTGVDET_EN      (1 << 19)
+#define USBPHY_OTGSESSEND_EN   (1 << 20)
+
 /**
  * musb_dsps_phy_control - phy on/off
  * @glue: struct dsps_glue *
index 6223062d5d1b5487534dc29724508d2e5df68793..37962c99ff1e9ace79bd89fdadfe4103649909e6 100644 (file)
@@ -110,7 +110,7 @@ config AB8500_USB
 
 config FSL_USB2_OTG
        bool "Freescale USB OTG Transceiver Driver"
-       depends on USB_EHCI_FSL && USB_GADGET_FSL_USB2 && USB_SUSPEND
+       depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_SUSPEND
        select USB_OTG
        select USB_OTG_UTILS
        help
index 1dd57504186db5225b28b353527001b4e1914b2e..eace975991a873a938983da7c3dbb5abc2ce2df5 100644 (file)
@@ -240,7 +240,7 @@ static void otg_clock_enable(struct mv_otg *mvotg)
        unsigned int i;
 
        for (i = 0; i < mvotg->clknum; i++)
-               clk_enable(mvotg->clk[i]);
+               clk_prepare_enable(mvotg->clk[i]);
 }
 
 static void otg_clock_disable(struct mv_otg *mvotg)
@@ -248,7 +248,7 @@ static void otg_clock_disable(struct mv_otg *mvotg)
        unsigned int i;
 
        for (i = 0; i < mvotg->clknum; i++)
-               clk_disable(mvotg->clk[i]);
+               clk_disable_unprepare(mvotg->clk[i]);
 }
 
 static int mv_otg_enable_internal(struct mv_otg *mvotg)
index dd41f61893ef87484fbfc233dc338427bcfcede6..f2985cd88021c0bd52178ee48cc64c4c7c97bc00 100644 (file)
@@ -545,15 +545,6 @@ static int usbhsg_pipe_disable(struct usbhsg_uep *uep)
        return 0;
 }
 
-static void usbhsg_uep_init(struct usbhsg_gpriv *gpriv)
-{
-       int i;
-       struct usbhsg_uep *uep;
-
-       usbhsg_for_each_uep_with_dcp(uep, gpriv, i)
-               uep->pipe = NULL;
-}
-
 /*
  *
  *             usb_ep_ops
@@ -610,7 +601,12 @@ static int usbhsg_ep_disable(struct usb_ep *ep)
 {
        struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
 
-       return usbhsg_pipe_disable(uep);
+       usbhsg_pipe_disable(uep);
+
+       uep->pipe->mod_private  = NULL;
+       uep->pipe               = NULL;
+
+       return 0;
 }
 
 static struct usb_request *usbhsg_ep_alloc_request(struct usb_ep *ep,
@@ -761,9 +757,8 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status)
        usbhs_pipe_init(priv,
                        usbhsg_dma_map_ctrl);
        usbhs_fifo_init(priv);
-       usbhsg_uep_init(gpriv);
 
-       /* dcp init */
+       /* dcp init instead of usbhsg_ep_enable() */
        dcp->pipe               = usbhs_dcp_malloc(priv);
        dcp->pipe->mod_private  = dcp;
        usbhs_pipe_config_update(dcp->pipe, 0, 0, 64);
@@ -825,7 +820,7 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status)
        usbhs_sys_set_test_mode(priv, 0);
        usbhs_sys_function_ctrl(priv, 0);
 
-       usbhsg_pipe_disable(dcp);
+       usbhsg_ep_disable(&dcp->ep);
 
        dev_dbg(dev, "stop gadget\n");
 
@@ -998,6 +993,7 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
         */
        usbhsg_for_each_uep_with_dcp(uep, gpriv, i) {
                uep->gpriv      = gpriv;
+               uep->pipe       = NULL;
                snprintf(uep->ep_name, EP_NAME_SIZE, "ep%d", i);
 
                uep->ep.name            = uep->ep_name;
index 3d3cd6ca2689894a497efefeb6ffc1edf6002cba..b86815421c8d4b6bbf86a54f39bd0e83c82d8bda 100644 (file)
@@ -661,9 +661,10 @@ static void usbhsh_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt)
                status = -ESHUTDOWN;
 
        urb->actual_length = pkt->actual;
-       usbhsh_ureq_free(hpriv, ureq);
 
        usbhsh_endpoint_sequence_save(hpriv, urb, pkt);
+       usbhsh_ureq_free(hpriv, ureq);
+
        usbhsh_pipe_detach(hpriv, usbhsh_ep_to_uep(urb->ep));
 
        usb_hcd_unlink_urb_from_ep(hcd, urb);
index f14736f647ff28ca31e583e08c1914879836978d..edc0f0dcad8378f4298d68815d2935404d8fedac 100644 (file)
@@ -60,6 +60,7 @@ static const struct usb_device_id id_table[] = {
        { USB_DEVICE(0x0FCF, 0x1003) }, /* Dynastream ANT development board */
        { USB_DEVICE(0x0FCF, 0x1004) }, /* Dynastream ANT2USB */
        { USB_DEVICE(0x0FCF, 0x1006) }, /* Dynastream ANT development board */
+       { USB_DEVICE(0x0FDE, 0xCA05) }, /* OWL Wireless Electricity Monitor CM-160 */
        { USB_DEVICE(0x10A6, 0xAA26) }, /* Knock-off DCU-11 cable */
        { USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */
        { USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */
index 0a373b3ae96a77c5f43afbc316c5faaba82e0609..90ceef1776c34197c65fb58988e0fc5771184a35 100644 (file)
@@ -584,6 +584,7 @@ static struct usb_device_id id_table_combined [] = {
        /*
         * ELV devices:
         */
+       { USB_DEVICE(FTDI_ELV_VID, FTDI_ELV_WS300_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_ELV_USR_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_ELV_MSM1_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_ELV_KL100_PID) },
@@ -670,6 +671,7 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_5_PID) },
        { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_6_PID) },
        { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_7_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_OMNI1509) },
        { USB_DEVICE(MOBILITY_VID, MOBILITY_USB_SERIAL_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_ACTIVE_ROBOTS_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_MHAM_KW_PID) },
@@ -875,6 +877,8 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_DISTORTEC_JTAG_LOCK_PICK_PID),
                .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
        { USB_DEVICE(FTDI_VID, FTDI_LUMEL_PD12_PID) },
+       /* Crucible Devices */
+       { USB_DEVICE(FTDI_VID, FTDI_CT_COMET_PID) },
        { },                                    /* Optional parameter entry */
        { }                                     /* Terminating entry */
 };
index 049b6e715fa470921748a9d29a236946e148ec7a..9d359e189a645f7dcd13222433b9377787f81a34 100644 (file)
 #define XSENS_CONVERTER_6_PID  0xD38E
 #define XSENS_CONVERTER_7_PID  0xD38F
 
+/**
+ * Zolix (www.zolix.com.cb) product ids
+ */
+#define FTDI_OMNI1509                  0xD491  /* Omni1509 embedded USB-serial */
+
 /*
  * NDI (www.ndigital.com) product ids
  */
 
 /*
  * ELV USB devices submitted by Christian Abt of ELV (www.elv.de).
- * All of these devices use FTDI's vendor ID (0x0403).
+ * Almost all of these devices use FTDI's vendor ID (0x0403).
  * Further IDs taken from ELV Windows .inf file.
  *
  * The previously included PID for the UO 100 module was incorrect.
  *
  * Armin Laeuger originally sent the PID for the UM 100 module.
  */
+#define FTDI_ELV_VID   0x1B1F  /* ELV AG */
+#define FTDI_ELV_WS300_PID     0xC006  /* eQ3 WS 300 PC II */
 #define FTDI_ELV_USR_PID       0xE000  /* ELV Universal-Sound-Recorder */
 #define FTDI_ELV_MSM1_PID      0xE001  /* ELV Mini-Sound-Modul */
 #define FTDI_ELV_KL100_PID     0xE002  /* ELV Kfz-Leistungsmesser KL 100 */
  * ATI command output: Cinterion MC55i
  */
 #define FTDI_CINTERION_MC55I_PID       0xA951
+
+/*
+ * Product: Comet Caller ID decoder
+ * Manufacturer: Crucible Technologies
+ */
+#define FTDI_CT_COMET_PID      0x8e08
index 58184f3de6867bfa9ebbbc6d6afbe074d80a8f12..82afc4d6a327d6bdbebc4707507684c966f11f9f 100644 (file)
@@ -530,6 +530,9 @@ static void chase_port(struct edgeport_port *port, unsigned long timeout,
        wait_queue_t wait;
        unsigned long flags;
 
+       if (!tty)
+               return;
+
        if (!timeout)
                timeout = (HZ * EDGE_CLOSING_WAIT)/100;
 
index e6f87b76c7156914dac281684e6130fde3b85ac0..567bc77d63970357b2dc9224f04c635ab727fc85 100644 (file)
@@ -242,6 +242,7 @@ static void option_instat_callback(struct urb *urb);
 #define TELIT_PRODUCT_CC864_DUAL               0x1005
 #define TELIT_PRODUCT_CC864_SINGLE             0x1006
 #define TELIT_PRODUCT_DE910_DUAL               0x1010
+#define TELIT_PRODUCT_LE920                    0x1200
 
 /* ZTE PRODUCTS */
 #define ZTE_VENDOR_ID                          0x19d2
@@ -288,6 +289,7 @@ static void option_instat_callback(struct urb *urb);
 #define ALCATEL_VENDOR_ID                      0x1bbb
 #define ALCATEL_PRODUCT_X060S_X200             0x0000
 #define ALCATEL_PRODUCT_X220_X500D             0x0017
+#define ALCATEL_PRODUCT_L100V                  0x011e
 
 #define PIRELLI_VENDOR_ID                      0x1266
 #define PIRELLI_PRODUCT_C100_1                 0x1002
@@ -429,9 +431,12 @@ static void option_instat_callback(struct urb *urb);
 #define MEDIATEK_VENDOR_ID                     0x0e8d
 #define MEDIATEK_PRODUCT_DC_1COM               0x00a0
 #define MEDIATEK_PRODUCT_DC_4COM               0x00a5
+#define MEDIATEK_PRODUCT_DC_4COM2              0x00a7
 #define MEDIATEK_PRODUCT_DC_5COM               0x00a4
 #define MEDIATEK_PRODUCT_7208_1COM             0x7101
 #define MEDIATEK_PRODUCT_7208_2COM             0x7102
+#define MEDIATEK_PRODUCT_7103_2COM             0x7103
+#define MEDIATEK_PRODUCT_7106_2COM             0x7106
 #define MEDIATEK_PRODUCT_FP_1COM               0x0003
 #define MEDIATEK_PRODUCT_FP_2COM               0x0023
 #define MEDIATEK_PRODUCT_FPDC_1COM             0x0043
@@ -441,6 +446,18 @@ static void option_instat_callback(struct urb *urb);
 #define CELLIENT_VENDOR_ID                     0x2692
 #define CELLIENT_PRODUCT_MEN200                        0x9005
 
+/* Hyundai Petatel Inc. products */
+#define PETATEL_VENDOR_ID                      0x1ff4
+#define PETATEL_PRODUCT_NP10T                  0x600e
+
+/* TP-LINK Incorporated products */
+#define TPLINK_VENDOR_ID                       0x2357
+#define TPLINK_PRODUCT_MA180                   0x0201
+
+/* Changhong products */
+#define CHANGHONG_VENDOR_ID                    0x2077
+#define CHANGHONG_PRODUCT_CH690                        0x7001
+
 /* some devices interfaces need special handling due to a number of reasons */
 enum option_blacklist_reason {
                OPTION_BLACKLIST_NONE = 0,
@@ -522,6 +539,11 @@ static const struct option_blacklist_info zte_1255_blacklist = {
        .reserved = BIT(3) | BIT(4),
 };
 
+static const struct option_blacklist_info telit_le920_blacklist = {
+       .sendsetup = BIT(0),
+       .reserved = BIT(1) | BIT(5),
+};
+
 static const struct usb_device_id option_ids[] = {
        { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
        { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
@@ -772,6 +794,8 @@ static const struct usb_device_id option_ids[] = {
        { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_CC864_DUAL) },
        { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_CC864_SINGLE) },
        { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_DE910_DUAL) },
+       { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920),
+               .driver_info = (kernel_ulong_t)&telit_le920_blacklist },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF622, 0xff, 0xff, 0xff) }, /* ZTE WCDMA products */
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff),
                .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
@@ -922,8 +946,10 @@ static const struct usb_device_id option_ids[] = {
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0254, 0xff, 0xff, 0xff) },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0257, 0xff, 0xff, 0xff), /* ZTE MF821 */
          .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
-       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0265, 0xff, 0xff, 0xff) },
-       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0284, 0xff, 0xff, 0xff) },
+       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0265, 0xff, 0xff, 0xff), /* ONDA MT8205 */
+         .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0284, 0xff, 0xff, 0xff), /* ZTE MF880 */
+         .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0317, 0xff, 0xff, 0xff) },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0326, 0xff, 0xff, 0xff),
          .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
@@ -1190,6 +1216,8 @@ static const struct usb_device_id option_ids[] = {
          .driver_info = (kernel_ulong_t)&alcatel_x200_blacklist
        },
        { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X220_X500D) },
+       { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_L100V),
+         .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
        { USB_DEVICE(AIRPLUS_VENDOR_ID, AIRPLUS_PRODUCT_MCD650) },
        { USB_DEVICE(TLAYTECH_VENDOR_ID, TLAYTECH_PRODUCT_TEU800) },
        { USB_DEVICE(LONGCHEER_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W14),
@@ -1294,7 +1322,15 @@ static const struct usb_device_id option_ids[] = {
        { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_FP_2COM, 0x0a, 0x00, 0x00) },
        { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_FPDC_1COM, 0x0a, 0x00, 0x00) },
        { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_FPDC_2COM, 0x0a, 0x00, 0x00) },
+       { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_7103_2COM, 0xff, 0x00, 0x00) },
+       { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_7106_2COM, 0x02, 0x02, 0x01) },
+       { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_4COM2, 0xff, 0x02, 0x01) },
+       { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_4COM2, 0xff, 0x00, 0x00) },
        { USB_DEVICE(CELLIENT_VENDOR_ID, CELLIENT_PRODUCT_MEN200) },
+       { USB_DEVICE(PETATEL_VENDOR_ID, PETATEL_PRODUCT_NP10T) },
+       { USB_DEVICE(TPLINK_VENDOR_ID, TPLINK_PRODUCT_MA180),
+         .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+       { USB_DEVICE(CHANGHONG_VENDOR_ID, CHANGHONG_PRODUCT_CH690) },
        { } /* Terminating entry */
 };
 MODULE_DEVICE_TABLE(usb, option_ids);
index aa148c21ea40e82a6f49b58b855ed2278f53cf3a..24662547dc5b2e60ffc51abc846e930e0afc1182 100644 (file)
@@ -53,6 +53,7 @@ static const struct usb_device_id id_table[] = {
        {DEVICE_G1K(0x05c6, 0x9221)},   /* Generic Gobi QDL device */
        {DEVICE_G1K(0x05c6, 0x9231)},   /* Generic Gobi QDL device */
        {DEVICE_G1K(0x1f45, 0x0001)},   /* Unknown Gobi QDL device */
+       {DEVICE_G1K(0x1bc7, 0x900e)},   /* Telit Gobi QDL device */
 
        /* Gobi 2000 devices */
        {USB_DEVICE(0x1410, 0xa010)},   /* Novatel Gobi 2000 QDL device */
index 105d900150c1a80155c2afcec6a61f4fb07fd0b4..16b0bf055eeb08427436841fc784b07554dd27cc 100644 (file)
@@ -92,8 +92,8 @@ int usb_stor_ucr61s2b_init(struct us_data *us)
        return 0;
 }
 
-/* This places the HUAWEI E220 devices in multi-port mode */
-int usb_stor_huawei_e220_init(struct us_data *us)
+/* This places the HUAWEI usb dongles in multi-port mode */
+static int usb_stor_huawei_feature_init(struct us_data *us)
 {
        int result;
 
@@ -104,3 +104,75 @@ int usb_stor_huawei_e220_init(struct us_data *us)
        US_DEBUGP("Huawei mode set result is %d\n", result);
        return 0;
 }
+
+/*
+ * It will send a scsi switch command called rewind' to huawei dongle.
+ * When the dongle receives this command at the first time,
+ * it will reboot immediately. After rebooted, it will ignore this command.
+ * So it is  unnecessary to read its response.
+ */
+static int usb_stor_huawei_scsi_init(struct us_data *us)
+{
+       int result = 0;
+       int act_len = 0;
+       struct bulk_cb_wrap *bcbw = (struct bulk_cb_wrap *) us->iobuf;
+       char rewind_cmd[] = {0x11, 0x06, 0x20, 0x00, 0x00, 0x01, 0x01, 0x00,
+                       0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+       bcbw->Signature = cpu_to_le32(US_BULK_CB_SIGN);
+       bcbw->Tag = 0;
+       bcbw->DataTransferLength = 0;
+       bcbw->Flags = bcbw->Lun = 0;
+       bcbw->Length = sizeof(rewind_cmd);
+       memset(bcbw->CDB, 0, sizeof(bcbw->CDB));
+       memcpy(bcbw->CDB, rewind_cmd, sizeof(rewind_cmd));
+
+       result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, bcbw,
+                                       US_BULK_CB_WRAP_LEN, &act_len);
+       US_DEBUGP("transfer actual length=%d, result=%d\n", act_len, result);
+       return result;
+}
+
+/*
+ * It tries to find the supported Huawei USB dongles.
+ * In Huawei, they assign the following product IDs
+ * for all of their mobile broadband dongles,
+ * including the new dongles in the future.
+ * So if the product ID is not included in this list,
+ * it means it is not Huawei's mobile broadband dongles.
+ */
+static int usb_stor_huawei_dongles_pid(struct us_data *us)
+{
+       struct usb_interface_descriptor *idesc;
+       int idProduct;
+
+       idesc = &us->pusb_intf->cur_altsetting->desc;
+       idProduct = us->pusb_dev->descriptor.idProduct;
+       /* The first port is CDROM,
+        * means the dongle in the single port mode,
+        * and a switch command is required to be sent. */
+       if (idesc && idesc->bInterfaceNumber == 0) {
+               if ((idProduct == 0x1001)
+                       || (idProduct == 0x1003)
+                       || (idProduct == 0x1004)
+                       || (idProduct >= 0x1401 && idProduct <= 0x1500)
+                       || (idProduct >= 0x1505 && idProduct <= 0x1600)
+                       || (idProduct >= 0x1c02 && idProduct <= 0x2202)) {
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+int usb_stor_huawei_init(struct us_data *us)
+{
+       int result = 0;
+
+       if (usb_stor_huawei_dongles_pid(us)) {
+               if (us->pusb_dev->descriptor.idProduct >= 0x1446)
+                       result = usb_stor_huawei_scsi_init(us);
+               else
+                       result = usb_stor_huawei_feature_init(us);
+       }
+       return result;
+}
index 529327fbb06be1b6876e8802b84bd281287291c3..5376d4fc76f04c25092244765237f94f44456ba1 100644 (file)
@@ -46,5 +46,5 @@ int usb_stor_euscsi_init(struct us_data *us);
  * flash reader */
 int usb_stor_ucr61s2b_init(struct us_data *us);
 
-/* This places the HUAWEI E220 devices in multi-port mode */
-int usb_stor_huawei_e220_init(struct us_data *us);
+/* This places the HUAWEI usb dongles in multi-port mode */
+int usb_stor_huawei_init(struct us_data *us);
index d305a5aa3a5d7683a5bcd1c2e60c4a0262f35f65..72923b56bbf6b871a6da926168d43cf8db3e6383 100644 (file)
@@ -1527,335 +1527,10 @@ UNUSUAL_DEV(  0x1210, 0x0003, 0x0100, 0x0100,
 /* Reported by fangxiaozhi <huananhu@huawei.com>
  * This brings the HUAWEI data card devices into multi-port mode
  */
-UNUSUAL_DEV(  0x12d1, 0x1001, 0x0000, 0x0000,
+UNUSUAL_VENDOR_INTF(0x12d1, 0x08, 0x06, 0x50,
                "HUAWEI MOBILE",
                "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1003, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1004, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1401, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1402, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1403, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1404, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1405, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1406, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1407, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1408, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1409, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x140A, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x140B, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x140C, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x140D, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x140E, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x140F, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1410, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1411, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1412, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1413, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1414, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1415, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1416, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1417, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1418, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1419, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x141A, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x141B, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x141C, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x141D, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x141E, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x141F, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1420, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1421, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1422, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1423, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1424, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1425, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1426, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1427, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1428, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1429, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x142A, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x142B, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x142C, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x142D, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x142E, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x142F, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1430, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1431, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1432, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1433, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1434, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1435, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1436, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1437, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1438, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x1439, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x143A, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x143B, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x143C, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x143D, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x143E, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
-               0),
-UNUSUAL_DEV(  0x12d1, 0x143F, 0x0000, 0x0000,
-               "HUAWEI MOBILE",
-               "Mass Storage",
-               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,
+               USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_init,
                0),
 
 /* Reported by Vilius Bilinkevicius <vilisas AT xxx DOT lt) */
index 31b3e1a61bbdda035d5ce1e5ef63ac05e36b5b26..cf09b6ba71ff49f5be0e367811f72e3794630f32 100644 (file)
@@ -120,6 +120,17 @@ MODULE_PARM_DESC(quirks, "supplemental list of device IDs and their quirks");
        .useTransport = use_transport,  \
 }
 
+#define UNUSUAL_VENDOR_INTF(idVendor, cl, sc, pr, \
+               vendor_name, product_name, use_protocol, use_transport, \
+               init_function, Flags) \
+{ \
+       .vendorName = vendor_name,      \
+       .productName = product_name,    \
+       .useProtocol = use_protocol,    \
+       .useTransport = use_transport,  \
+       .initFunction = init_function,  \
+}
+
 static struct us_unusual_dev us_unusual_dev_list[] = {
 #      include "unusual_devs.h"
        { }             /* Terminating entry */
@@ -131,6 +142,7 @@ static struct us_unusual_dev for_dynamic_ids =
 #undef UNUSUAL_DEV
 #undef COMPLIANT_DEV
 #undef USUAL_DEV
+#undef UNUSUAL_VENDOR_INTF
 
 #ifdef CONFIG_LOCKDEP
 
index b78a526910fb31ac257a13800c593236c0e21a27..5ef8ce74aae456c741bdff0856b564e9edb156a0 100644 (file)
 #define USUAL_DEV(useProto, useTrans) \
 { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, useProto, useTrans) }
 
+/* Define the device is matched with Vendor ID and interface descriptors */
+#define UNUSUAL_VENDOR_INTF(id_vendor, cl, sc, pr, \
+                       vendorName, productName, useProtocol, useTransport, \
+                       initFunction, flags) \
+{ \
+       .match_flags = USB_DEVICE_ID_MATCH_INT_INFO \
+                               | USB_DEVICE_ID_MATCH_VENDOR, \
+       .idVendor    = (id_vendor), \
+       .bInterfaceClass = (cl), \
+       .bInterfaceSubClass = (sc), \
+       .bInterfaceProtocol = (pr), \
+       .driver_info = (flags) \
+}
+
 struct usb_device_id usb_storage_usb_ids[] = {
 #      include "unusual_devs.h"
        { }             /* Terminating entry */
@@ -50,6 +64,7 @@ MODULE_DEVICE_TABLE(usb, usb_storage_usb_ids);
 #undef UNUSUAL_DEV
 #undef COMPLIANT_DEV
 #undef USUAL_DEV
+#undef UNUSUAL_VENDOR_INTF
 
 /*
  * The table of devices to ignore
index 4362d9e7baa355a971ed5935a486757b49699154..f72323ef618fbd99ce88d0c99480d165b94e2f42 100644 (file)
@@ -240,17 +240,17 @@ ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, char __user *buf,
                        filled = 1;
                } else {
                        /* Drop writes, fill reads with FF */
+                       filled = min((size_t)(x_end - pos), count);
                        if (!iswrite) {
                                char val = 0xFF;
                                size_t i;
 
-                               for (i = 0; i < x_end - pos; i++) {
+                               for (i = 0; i < filled; i++) {
                                        if (put_user(val, buf + i))
                                                goto out;
                                }
                        }
 
-                       filled = x_end - pos;
                }
 
                count -= filled;
index b20df5c829f5036042008d37f6ab20d424e977cd..22321cf84fbe2ce4c19090928ba5ecb83b8e5486 100644 (file)
@@ -575,10 +575,8 @@ static void vhost_scsi_handle_vq(struct vhost_scsi *vs)
 
        /* Must use ioctl VHOST_SCSI_SET_ENDPOINT */
        tv_tpg = vs->vs_tpg;
-       if (unlikely(!tv_tpg)) {
-               pr_err("%s endpoint not set\n", __func__);
+       if (unlikely(!tv_tpg))
                return;
-       }
 
        mutex_lock(&vq->mutex);
        vhost_disable_notify(&vs->dev, vq);
index e7068c508800ce502bff34055da63ae71f25f0f6..09f1a18c1adf61614adb8687d441047e426b730e 100644 (file)
@@ -21,8 +21,6 @@ source "drivers/gpu/vga/Kconfig"
 
 source "drivers/gpu/drm/Kconfig"
 
-source "drivers/gpu/stub/Kconfig"
-
 config VGASTATE
        tristate
        default n
@@ -33,6 +31,27 @@ config VIDEO_OUTPUT_CONTROL
          This framework adds support for low-level control of the video 
          output switch.
 
+config DISPLAY_TIMING
+       bool
+
+config VIDEOMODE
+       bool
+
+config OF_DISPLAY_TIMING
+       bool "Enable device tree display timing support"
+       depends on OF
+       select DISPLAY_TIMING
+       help
+         helper to parse display timings from the devicetree
+
+config OF_VIDEOMODE
+       bool "Enable device tree videomode support"
+       depends on OF
+       select VIDEOMODE
+       select OF_DISPLAY_TIMING
+       help
+         helper to get videomodes from the devicetree
+
 menuconfig FB
        tristate "Support for frame buffer devices"
        ---help---
index 768a137a1bacd3f8eca7dfde548cce68c41176bb..f592f3b32ec76d4782503c864a50f1fa11d230a6 100644 (file)
@@ -168,3 +168,7 @@ obj-$(CONFIG_FB_VIRTUAL)          += vfb.o
 
 #video output switch sysfs driver
 obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o
+obj-$(CONFIG_DISPLAY_TIMING) += display_timing.o
+obj-$(CONFIG_OF_DISPLAY_TIMING) += of_display_timing.o
+obj-$(CONFIG_VIDEOMODE) += videomode.o
+obj-$(CONFIG_OF_VIDEOMODE) += of_videomode.o
index fdefa8fd72c4da070787d95260b2744d6a9a7dab..501c599e75493b2424877617dc09a8c9fbaa7b12 100644 (file)
@@ -529,6 +529,33 @@ static int search_for_mapped_con(void)
        return retval;
 }
 
+static int do_fbcon_takeover(int show_logo)
+{
+       int err, i;
+
+       if (!num_registered_fb)
+               return -ENODEV;
+
+       if (!show_logo)
+               logo_shown = FBCON_LOGO_DONTSHOW;
+
+       for (i = first_fb_vc; i <= last_fb_vc; i++)
+               con2fb_map[i] = info_idx;
+
+       err = do_take_over_console(&fb_con, first_fb_vc, last_fb_vc,
+                               fbcon_is_default);
+
+       if (err) {
+               for (i = first_fb_vc; i <= last_fb_vc; i++)
+                       con2fb_map[i] = -1;
+               info_idx = -1;
+       } else {
+               fbcon_has_console_bind = 1;
+       }
+
+       return err;
+}
+
 static int fbcon_takeover(int show_logo)
 {
        int err, i;
@@ -815,6 +842,8 @@ static void con2fb_init_display(struct vc_data *vc, struct fb_info *info,
  *
  *     Maps a virtual console @unit to a frame buffer device
  *     @newidx.
+ *
+ *     This should be called with the console lock held.
  */
 static int set_con2fb_map(int unit, int newidx, int user)
 {
@@ -832,7 +861,7 @@ static int set_con2fb_map(int unit, int newidx, int user)
 
        if (!search_for_mapped_con() || !con_is_bound(&fb_con)) {
                info_idx = newidx;
-               return fbcon_takeover(0);
+               return do_fbcon_takeover(0);
        }
 
        if (oldidx != -1)
@@ -840,7 +869,6 @@ static int set_con2fb_map(int unit, int newidx, int user)
 
        found = search_fb_in_map(newidx);
 
-       console_lock();
        con2fb_map[unit] = newidx;
        if (!err && !found)
                err = con2fb_acquire_newinfo(vc, info, unit, oldidx);
@@ -867,7 +895,6 @@ static int set_con2fb_map(int unit, int newidx, int user)
        if (!search_fb_in_map(info_idx))
                info_idx = newidx;
 
-       console_unlock();
        return err;
 }
 
@@ -990,7 +1017,7 @@ static const char *fbcon_startup(void)
        }
 
        /* Setup default font */
-       if (!p->fontdata) {
+       if (!p->fontdata && !vc->vc_font.data) {
                if (!fontname[0] || !(font = find_font(fontname)))
                        font = get_default_font(info->var.xres,
                                                info->var.yres,
@@ -1000,6 +1027,8 @@ static const char *fbcon_startup(void)
                vc->vc_font.height = font->height;
                vc->vc_font.data = (void *)(p->fontdata = font->data);
                vc->vc_font.charcount = 256; /* FIXME  Need to support more fonts */
+       } else {
+               p->fontdata = vc->vc_font.data;
        }
 
        cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
@@ -1159,9 +1188,9 @@ static void fbcon_init(struct vc_data *vc, int init)
        ops->p = &fb_display[fg_console];
 }
 
-static void fbcon_free_font(struct display *p)
+static void fbcon_free_font(struct display *p, bool freefont)
 {
-       if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
+       if (freefont && p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
                kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
        p->fontdata = NULL;
        p->userfont = 0;
@@ -1173,8 +1202,8 @@ static void fbcon_deinit(struct vc_data *vc)
        struct fb_info *info;
        struct fbcon_ops *ops;
        int idx;
+       bool free_font = true;
 
-       fbcon_free_font(p);
        idx = con2fb_map[vc->vc_num];
 
        if (idx == -1)
@@ -1185,6 +1214,8 @@ static void fbcon_deinit(struct vc_data *vc)
        if (!info)
                goto finished;
 
+       if (info->flags & FBINFO_MISC_FIRMWARE)
+               free_font = false;
        ops = info->fbcon_par;
 
        if (!ops)
@@ -1196,6 +1227,8 @@ static void fbcon_deinit(struct vc_data *vc)
        ops->flags &= ~FBCON_FLAGS_INIT;
 finished:
 
+       fbcon_free_font(p, free_font);
+
        if (!con_is_bound(&fb_con))
                fbcon_exit();
 
@@ -2977,7 +3010,7 @@ static int fbcon_unbind(void)
 {
        int ret;
 
-       ret = unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc,
+       ret = do_unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc,
                                fbcon_is_default);
 
        if (!ret)
@@ -2992,6 +3025,7 @@ static inline int fbcon_unbind(void)
 }
 #endif /* CONFIG_VT_HW_CONSOLE_BINDING */
 
+/* called with console_lock held */
 static int fbcon_fb_unbind(int idx)
 {
        int i, new_idx = -1, ret = 0;
@@ -3018,6 +3052,7 @@ static int fbcon_fb_unbind(int idx)
        return ret;
 }
 
+/* called with console_lock held */
 static int fbcon_fb_unregistered(struct fb_info *info)
 {
        int i, idx;
@@ -3050,11 +3085,12 @@ static int fbcon_fb_unregistered(struct fb_info *info)
                primary_device = -1;
 
        if (!num_registered_fb)
-               unregister_con_driver(&fb_con);
+               do_unregister_con_driver(&fb_con);
 
        return 0;
 }
 
+/* called with console_lock held */
 static void fbcon_remap_all(int idx)
 {
        int i;
@@ -3099,6 +3135,7 @@ static inline void fbcon_select_primary(struct fb_info *info)
 }
 #endif /* CONFIG_FRAMEBUFFER_DETECT_PRIMARY */
 
+/* called with console_lock held */
 static int fbcon_fb_registered(struct fb_info *info)
 {
        int ret = 0, i, idx;
@@ -3115,7 +3152,7 @@ static int fbcon_fb_registered(struct fb_info *info)
                }
 
                if (info_idx != -1)
-                       ret = fbcon_takeover(1);
+                       ret = do_fbcon_takeover(1);
        } else {
                for (i = first_fb_vc; i <= last_fb_vc; i++) {
                        if (con2fb_map_boot[i] == idx)
@@ -3251,6 +3288,7 @@ static int fbcon_event_notify(struct notifier_block *self,
                ret = fbcon_fb_unregistered(info);
                break;
        case FB_EVENT_SET_CONSOLE_MAP:
+               /* called with console lock held */
                con2fb = event->data;
                ret = set_con2fb_map(con2fb->console - 1,
                                     con2fb->framebuffer, 1);
index d449a74d4a31297b48b25b9a0d2931ad127f4b96..5855d17d19ac9b8d493c13d7f0dd879ccf0710e4 100644 (file)
@@ -1064,7 +1064,7 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512)
        unsigned short video_port_status = vga_video_port_reg + 6;
        int font_select = 0x00, beg, i;
        char *charmap;
-       
+       bool clear_attribs = false;
        if (vga_video_type != VIDEO_TYPE_EGAM) {
                charmap = (char *) VGA_MAP_MEM(colourmap, 0);
                beg = 0x0e;
@@ -1169,12 +1169,6 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512)
 
        /* if 512 char mode is already enabled don't re-enable it. */
        if ((set) && (ch512 != vga_512_chars)) {
-               /* attribute controller */
-               for (i = 0; i < MAX_NR_CONSOLES; i++) {
-                       struct vc_data *c = vc_cons[i].d;
-                       if (c && c->vc_sw == &vga_con)
-                               c->vc_hi_font_mask = ch512 ? 0x0800 : 0;
-               }
                vga_512_chars = ch512;
                /* 256-char: enable intensity bit
                   512-char: disable intensity bit */
@@ -1185,8 +1179,22 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512)
                   it means, but it works, and it appears necessary */
                inb_p(video_port_status);
                vga_wattr(state->vgabase, VGA_AR_ENABLE_DISPLAY, 0);    
+               clear_attribs = true;
        }
        raw_spin_unlock_irq(&vga_lock);
+
+       if (clear_attribs) {
+               for (i = 0; i < MAX_NR_CONSOLES; i++) {
+                       struct vc_data *c = vc_cons[i].d;
+                       if (c && c->vc_sw == &vga_con) {
+                               /* force hi font mask to 0, so we always clear
+                                  the bit on either transition */
+                               c->vc_hi_font_mask = 0x00;
+                               clear_buffer_attributes(c);
+                               c->vc_hi_font_mask = ch512 ? 0x0800 : 0;
+                       }
+               }
+       }
        return 0;
 }
 
diff --git a/drivers/video/display_timing.c b/drivers/video/display_timing.c
new file mode 100644 (file)
index 0000000..5e1822c
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * generic display timing functions
+ *
+ * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
+ *
+ * This file is released under the GPLv2
+ */
+
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <video/display_timing.h>
+
+void display_timings_release(struct display_timings *disp)
+{
+       if (disp->timings) {
+               unsigned int i;
+
+               for (i = 0; i < disp->num_timings; i++)
+                       kfree(disp->timings[i]);
+               kfree(disp->timings);
+       }
+       kfree(disp);
+}
+EXPORT_SYMBOL_GPL(display_timings_release);
index 3ff0105a496a592cc9c1136c585efc463aa9de49..dc61c12ecf8ca516e7910fc5156e81e9701bb788 100644 (file)
@@ -1177,8 +1177,10 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
                event.data = &con2fb;
                if (!lock_fb_info(info))
                        return -ENODEV;
+               console_lock();
                event.info = info;
                ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);
+               console_unlock();
                unlock_fb_info(info);
                break;
        case FBIOBLANK:
@@ -1650,7 +1652,9 @@ static int do_register_framebuffer(struct fb_info *fb_info)
        event.info = fb_info;
        if (!lock_fb_info(fb_info))
                return -ENODEV;
+       console_lock();
        fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
+       console_unlock();
        unlock_fb_info(fb_info);
        return 0;
 }
@@ -1666,8 +1670,10 @@ static int do_unregister_framebuffer(struct fb_info *fb_info)
 
        if (!lock_fb_info(fb_info))
                return -ENODEV;
+       console_lock();
        event.info = fb_info;
        ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event);
+       console_unlock();
        unlock_fb_info(fb_info);
 
        if (ret)
@@ -1682,7 +1688,9 @@ static int do_unregister_framebuffer(struct fb_info *fb_info)
        num_registered_fb--;
        fb_cleanup_device(fb_info);
        event.info = fb_info;
+       console_lock();
        fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);
+       console_unlock();
 
        /* this may free fb info */
        put_fb_info(fb_info);
@@ -1853,11 +1861,8 @@ int fb_new_modelist(struct fb_info *info)
        err = 1;
 
        if (!list_empty(&info->modelist)) {
-               if (!lock_fb_info(info))
-                       return -ENODEV;
                event.info = info;
                err = fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event);
-               unlock_fb_info(info);
        }
 
        return err;
index cef65574db6c09bd906d3ab2eac8c235f208dedf..94ad0f71383c7fa71563332b3ab0fad9276dd2ab 100644 (file)
@@ -31,6 +31,8 @@
 #include <linux/pci.h>
 #include <linux/slab.h>
 #include <video/edid.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
 #ifdef CONFIG_PPC_OF
 #include <asm/prom.h>
 #include <asm/pci-bridge.h>
@@ -1373,6 +1375,98 @@ int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_inf
        kfree(timings);
        return err;
 }
+
+#if IS_ENABLED(CONFIG_VIDEOMODE)
+int fb_videomode_from_videomode(const struct videomode *vm,
+                               struct fb_videomode *fbmode)
+{
+       unsigned int htotal, vtotal;
+
+       fbmode->xres = vm->hactive;
+       fbmode->left_margin = vm->hback_porch;
+       fbmode->right_margin = vm->hfront_porch;
+       fbmode->hsync_len = vm->hsync_len;
+
+       fbmode->yres = vm->vactive;
+       fbmode->upper_margin = vm->vback_porch;
+       fbmode->lower_margin = vm->vfront_porch;
+       fbmode->vsync_len = vm->vsync_len;
+
+       /* prevent division by zero in KHZ2PICOS macro */
+       fbmode->pixclock = vm->pixelclock ?
+                       KHZ2PICOS(vm->pixelclock / 1000) : 0;
+
+       fbmode->sync = 0;
+       fbmode->vmode = 0;
+       if (vm->dmt_flags & VESA_DMT_HSYNC_HIGH)
+               fbmode->sync |= FB_SYNC_HOR_HIGH_ACT;
+       if (vm->dmt_flags & VESA_DMT_HSYNC_HIGH)
+               fbmode->sync |= FB_SYNC_VERT_HIGH_ACT;
+       if (vm->data_flags & DISPLAY_FLAGS_INTERLACED)
+               fbmode->vmode |= FB_VMODE_INTERLACED;
+       if (vm->data_flags & DISPLAY_FLAGS_DOUBLESCAN)
+               fbmode->vmode |= FB_VMODE_DOUBLE;
+       fbmode->flag = 0;
+
+       htotal = vm->hactive + vm->hfront_porch + vm->hback_porch +
+                vm->hsync_len;
+       vtotal = vm->vactive + vm->vfront_porch + vm->vback_porch +
+                vm->vsync_len;
+       /* prevent division by zero */
+       if (htotal && vtotal) {
+               fbmode->refresh = vm->pixelclock / (htotal * vtotal);
+       /* a mode must have htotal and vtotal != 0 or it is invalid */
+       } else {
+               fbmode->refresh = 0;
+               return -EINVAL;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(fb_videomode_from_videomode);
+#endif
+
+#if IS_ENABLED(CONFIG_OF_VIDEOMODE)
+static inline void dump_fb_videomode(const struct fb_videomode *m)
+{
+       pr_debug("fb_videomode = %ux%u@%uHz (%ukHz) %u %u %u %u %u %u %u %u %u\n",
+                m->xres, m->yres, m->refresh, m->pixclock, m->left_margin,
+                m->right_margin, m->upper_margin, m->lower_margin,
+                m->hsync_len, m->vsync_len, m->sync, m->vmode, m->flag);
+}
+
+/**
+ * of_get_fb_videomode - get a fb_videomode from devicetree
+ * @np: device_node with the timing specification
+ * @fb: will be set to the return value
+ * @index: index into the list of display timings in devicetree
+ *
+ * DESCRIPTION:
+ * This function is expensive and should only be used, if only one mode is to be
+ * read from DT. To get multiple modes start with of_get_display_timings ond
+ * work with that instead.
+ */
+int of_get_fb_videomode(struct device_node *np, struct fb_videomode *fb,
+                       int index)
+{
+       struct videomode vm;
+       int ret;
+
+       ret = of_get_videomode(np, &vm, index);
+       if (ret)
+               return ret;
+
+       fb_videomode_from_videomode(&vm, fb);
+
+       pr_debug("%s: got %dx%d display mode from %s\n",
+               of_node_full_name(np), vm.hactive, vm.vactive, np->name);
+       dump_fb_videomode(fb);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(of_get_fb_videomode);
+#endif
+
 #else
 int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
 {
index a55e3669d1352f387c29e1342d4d54db429c2b3f..ef476b02fbe5ca7d91aed2b72710fff5ef806938 100644 (file)
@@ -177,6 +177,8 @@ static ssize_t store_modes(struct device *device,
        if (i * sizeof(struct fb_videomode) != count)
                return -EINVAL;
 
+       if (!lock_fb_info(fb_info))
+               return -ENODEV;
        console_lock();
        list_splice(&fb_info->modelist, &old_list);
        fb_videomode_to_modelist((const struct fb_videomode *)buf, i,
@@ -188,6 +190,7 @@ static ssize_t store_modes(struct device *device,
                fb_destroy_modelist(&old_list);
 
        console_unlock();
+       unlock_fb_info(fb_info);
 
        return 0;
 }
index 12526787a7c741e3b4eb2601dc9f2f4b008d251b..0abf2bf2083604c48006a63682c410c7125109fb 100644 (file)
@@ -139,6 +139,7 @@ struct imxfb_info {
        struct clk              *clk_ahb;
        struct clk              *clk_per;
        enum imxfb_type         devtype;
+       bool                    enabled;
 
        /*
         * These are the addresses we mapped
@@ -536,6 +537,10 @@ static void imxfb_exit_backlight(struct imxfb_info *fbi)
 
 static void imxfb_enable_controller(struct imxfb_info *fbi)
 {
+
+       if (fbi->enabled)
+               return;
+
        pr_debug("Enabling LCD controller\n");
 
        writel(fbi->screen_dma, fbi->regs + LCDC_SSA);
@@ -556,6 +561,7 @@ static void imxfb_enable_controller(struct imxfb_info *fbi)
        clk_prepare_enable(fbi->clk_ipg);
        clk_prepare_enable(fbi->clk_ahb);
        clk_prepare_enable(fbi->clk_per);
+       fbi->enabled = true;
 
        if (fbi->backlight_power)
                fbi->backlight_power(1);
@@ -565,6 +571,9 @@ static void imxfb_enable_controller(struct imxfb_info *fbi)
 
 static void imxfb_disable_controller(struct imxfb_info *fbi)
 {
+       if (!fbi->enabled)
+               return;
+
        pr_debug("Disabling LCD controller\n");
 
        if (fbi->backlight_power)
@@ -575,6 +584,7 @@ static void imxfb_disable_controller(struct imxfb_info *fbi)
        clk_disable_unprepare(fbi->clk_per);
        clk_disable_unprepare(fbi->clk_ipg);
        clk_disable_unprepare(fbi->clk_ahb);
+       fbi->enabled = false;
 
        writel(0, fbi->regs + LCDC_RMCR);
 }
@@ -729,6 +739,8 @@ static int __init imxfb_init_fbinfo(struct platform_device *pdev)
 
        memset(fbi, 0, sizeof(struct imxfb_info));
 
+       fbi->devtype = pdev->id_entry->driver_data;
+
        strlcpy(info->fix.id, IMX_NAME, sizeof(info->fix.id));
 
        info->fix.type                  = FB_TYPE_PACKED_PIXELS;
@@ -789,7 +801,6 @@ static int __init imxfb_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        fbi = info->par;
-       fbi->devtype = pdev->id_entry->driver_data;
 
        if (!fb_mode)
                fb_mode = pdata->mode[0].mode.name;
diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c
new file mode 100644 (file)
index 0000000..13ecd98
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * OF helpers for parsing display timings
+ *
+ * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
+ *
+ * based on of_videomode.c by Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * This file is released under the GPLv2
+ */
+#include <linux/export.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <video/display_timing.h>
+#include <video/of_display_timing.h>
+
+/**
+ * parse_timing_property - parse timing_entry from device_node
+ * @np: device_node with the property
+ * @name: name of the property
+ * @result: will be set to the return value
+ *
+ * DESCRIPTION:
+ * Every display_timing can be specified with either just the typical value or
+ * a range consisting of min/typ/max. This function helps handling this
+ **/
+static int parse_timing_property(struct device_node *np, const char *name,
+                         struct timing_entry *result)
+{
+       struct property *prop;
+       int length, cells, ret;
+
+       prop = of_find_property(np, name, &length);
+       if (!prop) {
+               pr_err("%s: could not find property %s\n",
+                       of_node_full_name(np), name);
+               return -EINVAL;
+       }
+
+       cells = length / sizeof(u32);
+       if (cells == 1) {
+               ret = of_property_read_u32(np, name, &result->typ);
+               result->min = result->typ;
+               result->max = result->typ;
+       } else if (cells == 3) {
+               ret = of_property_read_u32_array(np, name, &result->min, cells);
+       } else {
+               pr_err("%s: illegal timing specification in %s\n",
+                       of_node_full_name(np), name);
+               return -EINVAL;
+       }
+
+       return ret;
+}
+
+/**
+ * of_get_display_timing - parse display_timing entry from device_node
+ * @np: device_node with the properties
+ **/
+static struct display_timing *of_get_display_timing(struct device_node *np)
+{
+       struct display_timing *dt;
+       u32 val = 0;
+       int ret = 0;
+
+       dt = kzalloc(sizeof(*dt), GFP_KERNEL);
+       if (!dt) {
+               pr_err("%s: could not allocate display_timing struct\n",
+                       of_node_full_name(np));
+               return NULL;
+       }
+
+       ret |= parse_timing_property(np, "hback-porch", &dt->hback_porch);
+       ret |= parse_timing_property(np, "hfront-porch", &dt->hfront_porch);
+       ret |= parse_timing_property(np, "hactive", &dt->hactive);
+       ret |= parse_timing_property(np, "hsync-len", &dt->hsync_len);
+       ret |= parse_timing_property(np, "vback-porch", &dt->vback_porch);
+       ret |= parse_timing_property(np, "vfront-porch", &dt->vfront_porch);
+       ret |= parse_timing_property(np, "vactive", &dt->vactive);
+       ret |= parse_timing_property(np, "vsync-len", &dt->vsync_len);
+       ret |= parse_timing_property(np, "clock-frequency", &dt->pixelclock);
+
+       dt->dmt_flags = 0;
+       dt->data_flags = 0;
+       if (!of_property_read_u32(np, "vsync-active", &val))
+               dt->dmt_flags |= val ? VESA_DMT_VSYNC_HIGH :
+                               VESA_DMT_VSYNC_LOW;
+       if (!of_property_read_u32(np, "hsync-active", &val))
+               dt->dmt_flags |= val ? VESA_DMT_HSYNC_HIGH :
+                               VESA_DMT_HSYNC_LOW;
+       if (!of_property_read_u32(np, "de-active", &val))
+               dt->data_flags |= val ? DISPLAY_FLAGS_DE_HIGH :
+                               DISPLAY_FLAGS_DE_LOW;
+       if (!of_property_read_u32(np, "pixelclk-active", &val))
+               dt->data_flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE :
+                               DISPLAY_FLAGS_PIXDATA_NEGEDGE;
+
+       if (of_property_read_bool(np, "interlaced"))
+               dt->data_flags |= DISPLAY_FLAGS_INTERLACED;
+       if (of_property_read_bool(np, "doublescan"))
+               dt->data_flags |= DISPLAY_FLAGS_DOUBLESCAN;
+
+       if (ret) {
+               pr_err("%s: error reading timing properties\n",
+                       of_node_full_name(np));
+               kfree(dt);
+               return NULL;
+       }
+
+       return dt;
+}
+
+/**
+ * of_get_display_timings - parse all display_timing entries from a device_node
+ * @np: device_node with the subnodes
+ **/
+struct display_timings *of_get_display_timings(struct device_node *np)
+{
+       struct device_node *timings_np;
+       struct device_node *entry;
+       struct device_node *native_mode;
+       struct display_timings *disp;
+
+       if (!np) {
+               pr_err("%s: no devicenode given\n", of_node_full_name(np));
+               return NULL;
+       }
+
+       timings_np = of_find_node_by_name(np, "display-timings");
+       if (!timings_np) {
+               pr_err("%s: could not find display-timings node\n",
+                       of_node_full_name(np));
+               return NULL;
+       }
+
+       disp = kzalloc(sizeof(*disp), GFP_KERNEL);
+       if (!disp) {
+               pr_err("%s: could not allocate struct disp'\n",
+                       of_node_full_name(np));
+               goto dispfail;
+       }
+
+       entry = of_parse_phandle(timings_np, "native-mode", 0);
+       /* assume first child as native mode if none provided */
+       if (!entry)
+               entry = of_get_next_child(np, NULL);
+       /* if there is no child, it is useless to go on */
+       if (!entry) {
+               pr_err("%s: no timing specifications given\n",
+                       of_node_full_name(np));
+               goto entryfail;
+       }
+
+       pr_debug("%s: using %s as default timing\n",
+               of_node_full_name(np), entry->name);
+
+       native_mode = entry;
+
+       disp->num_timings = of_get_child_count(timings_np);
+       if (disp->num_timings == 0) {
+               /* should never happen, as entry was already found above */
+               pr_err("%s: no timings specified\n", of_node_full_name(np));
+               goto entryfail;
+       }
+
+       disp->timings = kzalloc(sizeof(struct display_timing *) *
+                               disp->num_timings, GFP_KERNEL);
+       if (!disp->timings) {
+               pr_err("%s: could not allocate timings array\n",
+                       of_node_full_name(np));
+               goto entryfail;
+       }
+
+       disp->num_timings = 0;
+       disp->native_mode = 0;
+
+       for_each_child_of_node(timings_np, entry) {
+               struct display_timing *dt;
+
+               dt = of_get_display_timing(entry);
+               if (!dt) {
+                       /*
+                        * to not encourage wrong devicetrees, fail in case of
+                        * an error
+                        */
+                       pr_err("%s: error in timing %d\n",
+                               of_node_full_name(np), disp->num_timings + 1);
+                       goto timingfail;
+               }
+
+               if (native_mode == entry)
+                       disp->native_mode = disp->num_timings;
+
+               disp->timings[disp->num_timings] = dt;
+               disp->num_timings++;
+       }
+       of_node_put(timings_np);
+       /*
+        * native_mode points to the device_node returned by of_parse_phandle
+        * therefore call of_node_put on it
+        */
+       of_node_put(native_mode);
+
+       pr_debug("%s: got %d timings. Using timing #%d as default\n",
+               of_node_full_name(np), disp->num_timings,
+               disp->native_mode + 1);
+
+       return disp;
+
+timingfail:
+       if (native_mode)
+               of_node_put(native_mode);
+       display_timings_release(disp);
+entryfail:
+       kfree(disp);
+dispfail:
+       of_node_put(timings_np);
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(of_get_display_timings);
+
+/**
+ * of_display_timings_exist - check if a display-timings node is provided
+ * @np: device_node with the timing
+ **/
+int of_display_timings_exist(struct device_node *np)
+{
+       struct device_node *timings_np;
+
+       if (!np)
+               return -EINVAL;
+
+       timings_np = of_parse_phandle(np, "display-timings", 0);
+       if (!timings_np)
+               return -EINVAL;
+
+       of_node_put(timings_np);
+       return 1;
+}
+EXPORT_SYMBOL_GPL(of_display_timings_exist);
diff --git a/drivers/video/of_videomode.c b/drivers/video/of_videomode.c
new file mode 100644 (file)
index 0000000..5b8066c
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * generic videomode helper
+ *
+ * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
+ *
+ * This file is released under the GPLv2
+ */
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/of.h>
+#include <video/display_timing.h>
+#include <video/of_display_timing.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+/**
+ * of_get_videomode - get the videomode #<index> from devicetree
+ * @np - devicenode with the display_timings
+ * @vm - set to return value
+ * @index - index into list of display_timings
+ *         (Set this to OF_USE_NATIVE_MODE to use whatever mode is
+ *          specified as native mode in the DT.)
+ *
+ * DESCRIPTION:
+ * Get a list of all display timings and put the one
+ * specified by index into *vm. This function should only be used, if
+ * only one videomode is to be retrieved. A driver that needs to work
+ * with multiple/all videomodes should work with
+ * of_get_display_timings instead.
+ **/
+int of_get_videomode(struct device_node *np, struct videomode *vm,
+                    int index)
+{
+       struct display_timings *disp;
+       int ret;
+
+       disp = of_get_display_timings(np);
+       if (!disp) {
+               pr_err("%s: no timings specified\n", of_node_full_name(np));
+               return -EINVAL;
+       }
+
+       if (index == OF_USE_NATIVE_MODE)
+               index = disp->native_mode;
+
+       ret = videomode_from_timing(disp, vm, index);
+       if (ret)
+               return ret;
+
+       display_timings_release(disp);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(of_get_videomode);
index 4d99dd7a6831f4be0e866bcc552f62225faa526e..395cb6a8d8f3a86e3507be05686218ebee6f7d6f 100644 (file)
@@ -145,8 +145,8 @@ static void ssd1307fb_update_display(struct ssd1307fb_par *par)
                                u32 page_length = SSD1307FB_WIDTH * i;
                                u32 index = page_length + (SSD1307FB_WIDTH * k + j) / 8;
                                u8 byte = *(vmem + index);
-                               u8 bit = byte & (1 << (7 - (j % 8)));
-                               bit = bit >> (7 - (j % 8));
+                               u8 bit = byte & (1 << (j % 8));
+                               bit = bit >> (j % 8);
                                buf |= bit << k;
                        }
                        ssd1307fb_write_data(par->client, buf);
index 80233dae358ae1e1784d647e41382f90603bd23c..22450908306c3f77b454a6396d82c6363f60943d 100644 (file)
@@ -1467,10 +1467,10 @@ void viafb_set_vclock(u32 clk, int set_iga)
        via_write_misc_reg_mask(0x0C, 0x0C); /* select external clock */
 }
 
-struct display_timing var_to_timing(const struct fb_var_screeninfo *var,
+struct via_display_timing var_to_timing(const struct fb_var_screeninfo *var,
        u16 cxres, u16 cyres)
 {
-       struct display_timing timing;
+       struct via_display_timing timing;
        u16 dx = (var->xres - cxres) / 2, dy = (var->yres - cyres) / 2;
 
        timing.hor_addr = cxres;
@@ -1491,7 +1491,7 @@ struct display_timing var_to_timing(const struct fb_var_screeninfo *var,
 void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var,
        u16 cxres, u16 cyres, int iga)
 {
-       struct display_timing crt_reg = var_to_timing(var,
+       struct via_display_timing crt_reg = var_to_timing(var,
                cxres ? cxres : var->xres, cyres ? cyres : var->yres);
 
        if (iga == IGA1)
index a8205754c7361ff2d68662892cbc581abaad2706..3be073c58b03b6b5e1c1c64d16298fa317053da5 100644 (file)
@@ -637,7 +637,7 @@ extern int viafb_LCD_ON;
 extern int viafb_DVI_ON;
 extern int viafb_hotplug;
 
-struct display_timing var_to_timing(const struct fb_var_screeninfo *var,
+struct via_display_timing var_to_timing(const struct fb_var_screeninfo *var,
        u16 cxres, u16 cyres);
 void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var,
        u16 cxres, u16 cyres, int iga);
index 980ee1b1dcf313b9dc98a6689b7cac48c702d887..5d21ff436ec80a313dc9133a22b1db08ab7006ae 100644 (file)
@@ -549,7 +549,7 @@ void viafb_lcd_set_mode(const struct fb_var_screeninfo *var, u16 cxres,
        int panel_hres = plvds_setting_info->lcd_panel_hres;
        int panel_vres = plvds_setting_info->lcd_panel_vres;
        u32 clock;
-       struct display_timing timing;
+       struct via_display_timing timing;
        struct fb_var_screeninfo panel_var;
        const struct fb_videomode *mode_crt_table, *panel_crt_table;
 
index 3158dfc90bed9126ae6fbd0b2dc034c6f6f0cfa3..65c65c611e0aa583bec01abff384d28d1d6d1dc7 100644 (file)
@@ -319,7 +319,7 @@ struct crt_mode_table {
        int refresh_rate;
        int h_sync_polarity;
        int v_sync_polarity;
-       struct display_timing crtc;
+       struct via_display_timing crtc;
 };
 
 struct io_reg {
index 0e431aee17bbcc83a330ffdda6623d477a84ac8f..0b414b09b9b455320dd4d338a946faa3e3cf6ee1 100644 (file)
@@ -30,9 +30,9 @@
 #include "debug.h"
 
 
-void via_set_primary_timing(const struct display_timing *timing)
+void via_set_primary_timing(const struct via_display_timing *timing)
 {
-       struct display_timing raw;
+       struct via_display_timing raw;
 
        raw.hor_total = timing->hor_total / 8 - 5;
        raw.hor_addr = timing->hor_addr / 8 - 1;
@@ -88,9 +88,9 @@ void via_set_primary_timing(const struct display_timing *timing)
        via_write_reg_mask(VIACR, 0x17, 0x80, 0x80);
 }
 
-void via_set_secondary_timing(const struct display_timing *timing)
+void via_set_secondary_timing(const struct via_display_timing *timing)
 {
-       struct display_timing raw;
+       struct via_display_timing raw;
 
        raw.hor_total = timing->hor_total - 1;
        raw.hor_addr = timing->hor_addr - 1;
index 06e09fe351ae86a209b139e88687d1f8f8f9d7e2..f6a6503da3b38b1d37c3c3d92e28d7ef2abd1b72 100644 (file)
@@ -33,7 +33,7 @@
 #define VIA_PITCH_MAX  0x3FF8
 
 
-struct display_timing {
+struct via_display_timing {
        u16 hor_total;
        u16 hor_addr;
        u16 hor_blank_start;
@@ -49,8 +49,8 @@ struct display_timing {
 };
 
 
-void via_set_primary_timing(const struct display_timing *timing);
-void via_set_secondary_timing(const struct display_timing *timing);
+void via_set_primary_timing(const struct via_display_timing *timing);
+void via_set_secondary_timing(const struct via_display_timing *timing);
 void via_set_primary_address(u32 addr);
 void via_set_secondary_address(u32 addr);
 void via_set_primary_pitch(u32 pitch);
diff --git a/drivers/video/videomode.c b/drivers/video/videomode.c
new file mode 100644 (file)
index 0000000..21c47a2
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * generic display timing functions
+ *
+ * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
+ *
+ * This file is released under the GPLv2
+ */
+
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <video/display_timing.h>
+#include <video/videomode.h>
+
+int videomode_from_timing(const struct display_timings *disp,
+                         struct videomode *vm, unsigned int index)
+{
+       struct display_timing *dt;
+
+       dt = display_timings_get(disp, index);
+       if (!dt)
+               return -EINVAL;
+
+       vm->pixelclock = display_timing_get_value(&dt->pixelclock, TE_TYP);
+       vm->hactive = display_timing_get_value(&dt->hactive, TE_TYP);
+       vm->hfront_porch = display_timing_get_value(&dt->hfront_porch, TE_TYP);
+       vm->hback_porch = display_timing_get_value(&dt->hback_porch, TE_TYP);
+       vm->hsync_len = display_timing_get_value(&dt->hsync_len, TE_TYP);
+
+       vm->vactive = display_timing_get_value(&dt->vactive, TE_TYP);
+       vm->vfront_porch = display_timing_get_value(&dt->vfront_porch, TE_TYP);
+       vm->vback_porch = display_timing_get_value(&dt->vback_porch, TE_TYP);
+       vm->vsync_len = display_timing_get_value(&dt->vsync_len, TE_TYP);
+
+       vm->dmt_flags = dt->dmt_flags;
+       vm->data_flags = dt->data_flags;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(videomode_from_timing);
index 4dcfced107f50e41c518405b28747064d62d78bc..084041d42c9adabfc40251546d361ad76d8987e5 100644 (file)
@@ -25,10 +25,10 @@ static void disable_hotplug_cpu(int cpu)
 static int vcpu_online(unsigned int cpu)
 {
        int err;
-       char dir[32], state[32];
+       char dir[16], state[16];
 
        sprintf(dir, "cpu/%u", cpu);
-       err = xenbus_scanf(XBT_NIL, dir, "availability", "%s", state);
+       err = xenbus_scanf(XBT_NIL, dir, "availability", "%15s", state);
        if (err != 1) {
                if (!xen_initial_domain())
                        printk(KERN_ERR "XENBUS: Unable to read cpu state\n");
index 2e22df2f7a3f8b57fe44ce8842b825602a012658..3c8803feba26cdcba8bd890225ac7995ef72810e 100644 (file)
@@ -56,10 +56,15 @@ MODULE_PARM_DESC(limit, "Maximum number of grants that may be mapped by "
 static atomic_t pages_mapped = ATOMIC_INIT(0);
 
 static int use_ptemod;
+#define populate_freeable_maps use_ptemod
 
 struct gntdev_priv {
+       /* maps with visible offsets in the file descriptor */
        struct list_head maps;
-       /* lock protects maps from concurrent changes */
+       /* maps that are not visible; will be freed on munmap.
+        * Only populated if populate_freeable_maps == 1 */
+       struct list_head freeable_maps;
+       /* lock protects maps and freeable_maps */
        spinlock_t lock;
        struct mm_struct *mm;
        struct mmu_notifier mn;
@@ -193,7 +198,7 @@ static struct grant_map *gntdev_find_map_index(struct gntdev_priv *priv,
        return NULL;
 }
 
-static void gntdev_put_map(struct grant_map *map)
+static void gntdev_put_map(struct gntdev_priv *priv, struct grant_map *map)
 {
        if (!map)
                return;
@@ -208,6 +213,12 @@ static void gntdev_put_map(struct grant_map *map)
                evtchn_put(map->notify.event);
        }
 
+       if (populate_freeable_maps && priv) {
+               spin_lock(&priv->lock);
+               list_del(&map->next);
+               spin_unlock(&priv->lock);
+       }
+
        if (map->pages && !use_ptemod)
                unmap_grant_pages(map, 0, map->count);
        gntdev_free_map(map);
@@ -301,17 +312,10 @@ static int __unmap_grant_pages(struct grant_map *map, int offset, int pages)
 
        if (map->notify.flags & UNMAP_NOTIFY_CLEAR_BYTE) {
                int pgno = (map->notify.addr >> PAGE_SHIFT);
-               if (pgno >= offset && pgno < offset + pages && use_ptemod) {
-                       void __user *tmp = (void __user *)
-                               map->vma->vm_start + map->notify.addr;
-                       err = copy_to_user(tmp, &err, 1);
-                       if (err)
-                               return -EFAULT;
-                       map->notify.flags &= ~UNMAP_NOTIFY_CLEAR_BYTE;
-               } else if (pgno >= offset && pgno < offset + pages) {
-                       uint8_t *tmp = kmap(map->pages[pgno]);
+               if (pgno >= offset && pgno < offset + pages) {
+                       /* No need for kmap, pages are in lowmem */
+                       uint8_t *tmp = pfn_to_kaddr(page_to_pfn(map->pages[pgno]));
                        tmp[map->notify.addr & (PAGE_SIZE-1)] = 0;
-                       kunmap(map->pages[pgno]);
                        map->notify.flags &= ~UNMAP_NOTIFY_CLEAR_BYTE;
                }
        }
@@ -376,11 +380,24 @@ static void gntdev_vma_open(struct vm_area_struct *vma)
 static void gntdev_vma_close(struct vm_area_struct *vma)
 {
        struct grant_map *map = vma->vm_private_data;
+       struct file *file = vma->vm_file;
+       struct gntdev_priv *priv = file->private_data;
 
        pr_debug("gntdev_vma_close %p\n", vma);
-       map->vma = NULL;
+       if (use_ptemod) {
+               /* It is possible that an mmu notifier could be running
+                * concurrently, so take priv->lock to ensure that the vma won't
+                * vanishing during the unmap_grant_pages call, since we will
+                * spin here until that completes. Such a concurrent call will
+                * not do any unmapping, since that has been done prior to
+                * closing the vma, but it may still iterate the unmap_ops list.
+                */
+               spin_lock(&priv->lock);
+               map->vma = NULL;
+               spin_unlock(&priv->lock);
+       }
        vma->vm_private_data = NULL;
-       gntdev_put_map(map);
+       gntdev_put_map(priv, map);
 }
 
 static struct vm_operations_struct gntdev_vmops = {
@@ -390,33 +407,43 @@ static struct vm_operations_struct gntdev_vmops = {
 
 /* ------------------------------------------------------------------ */
 
+static void unmap_if_in_range(struct grant_map *map,
+                             unsigned long start, unsigned long end)
+{
+       unsigned long mstart, mend;
+       int err;
+
+       if (!map->vma)
+               return;
+       if (map->vma->vm_start >= end)
+               return;
+       if (map->vma->vm_end <= start)
+               return;
+       mstart = max(start, map->vma->vm_start);
+       mend   = min(end,   map->vma->vm_end);
+       pr_debug("map %d+%d (%lx %lx), range %lx %lx, mrange %lx %lx\n",
+                       map->index, map->count,
+                       map->vma->vm_start, map->vma->vm_end,
+                       start, end, mstart, mend);
+       err = unmap_grant_pages(map,
+                               (mstart - map->vma->vm_start) >> PAGE_SHIFT,
+                               (mend - mstart) >> PAGE_SHIFT);
+       WARN_ON(err);
+}
+
 static void mn_invl_range_start(struct mmu_notifier *mn,
                                struct mm_struct *mm,
                                unsigned long start, unsigned long end)
 {
        struct gntdev_priv *priv = container_of(mn, struct gntdev_priv, mn);
        struct grant_map *map;
-       unsigned long mstart, mend;
-       int err;
 
        spin_lock(&priv->lock);
        list_for_each_entry(map, &priv->maps, next) {
-               if (!map->vma)
-                       continue;
-               if (map->vma->vm_start >= end)
-                       continue;
-               if (map->vma->vm_end <= start)
-                       continue;
-               mstart = max(start, map->vma->vm_start);
-               mend   = min(end,   map->vma->vm_end);
-               pr_debug("map %d+%d (%lx %lx), range %lx %lx, mrange %lx %lx\n",
-                               map->index, map->count,
-                               map->vma->vm_start, map->vma->vm_end,
-                               start, end, mstart, mend);
-               err = unmap_grant_pages(map,
-                                       (mstart - map->vma->vm_start) >> PAGE_SHIFT,
-                                       (mend - mstart) >> PAGE_SHIFT);
-               WARN_ON(err);
+               unmap_if_in_range(map, start, end);
+       }
+       list_for_each_entry(map, &priv->freeable_maps, next) {
+               unmap_if_in_range(map, start, end);
        }
        spin_unlock(&priv->lock);
 }
@@ -445,6 +472,15 @@ static void mn_release(struct mmu_notifier *mn,
                err = unmap_grant_pages(map, /* offset */ 0, map->count);
                WARN_ON(err);
        }
+       list_for_each_entry(map, &priv->freeable_maps, next) {
+               if (!map->vma)
+                       continue;
+               pr_debug("map %d+%d (%lx %lx)\n",
+                               map->index, map->count,
+                               map->vma->vm_start, map->vma->vm_end);
+               err = unmap_grant_pages(map, /* offset */ 0, map->count);
+               WARN_ON(err);
+       }
        spin_unlock(&priv->lock);
 }
 
@@ -466,6 +502,7 @@ static int gntdev_open(struct inode *inode, struct file *flip)
                return -ENOMEM;
 
        INIT_LIST_HEAD(&priv->maps);
+       INIT_LIST_HEAD(&priv->freeable_maps);
        spin_lock_init(&priv->lock);
 
        if (use_ptemod) {
@@ -500,8 +537,9 @@ static int gntdev_release(struct inode *inode, struct file *flip)
        while (!list_empty(&priv->maps)) {
                map = list_entry(priv->maps.next, struct grant_map, next);
                list_del(&map->next);
-               gntdev_put_map(map);
+               gntdev_put_map(NULL /* already removed */, map);
        }
+       WARN_ON(!list_empty(&priv->freeable_maps));
 
        if (use_ptemod)
                mmu_notifier_unregister(&priv->mn, priv->mm);
@@ -529,14 +567,14 @@ static long gntdev_ioctl_map_grant_ref(struct gntdev_priv *priv,
 
        if (unlikely(atomic_add_return(op.count, &pages_mapped) > limit)) {
                pr_debug("can't map: over limit\n");
-               gntdev_put_map(map);
+               gntdev_put_map(NULL, map);
                return err;
        }
 
        if (copy_from_user(map->grants, &u->refs,
                           sizeof(map->grants[0]) * op.count) != 0) {
-               gntdev_put_map(map);
-               return err;
+               gntdev_put_map(NULL, map);
+               return -EFAULT;
        }
 
        spin_lock(&priv->lock);
@@ -565,11 +603,13 @@ static long gntdev_ioctl_unmap_grant_ref(struct gntdev_priv *priv,
        map = gntdev_find_map_index(priv, op.index >> PAGE_SHIFT, op.count);
        if (map) {
                list_del(&map->next);
+               if (populate_freeable_maps)
+                       list_add_tail(&map->next, &priv->freeable_maps);
                err = 0;
        }
        spin_unlock(&priv->lock);
        if (map)
-               gntdev_put_map(map);
+               gntdev_put_map(priv, map);
        return err;
 }
 
@@ -579,25 +619,31 @@ static long gntdev_ioctl_get_offset_for_vaddr(struct gntdev_priv *priv,
        struct ioctl_gntdev_get_offset_for_vaddr op;
        struct vm_area_struct *vma;
        struct grant_map *map;
+       int rv = -EINVAL;
 
        if (copy_from_user(&op, u, sizeof(op)) != 0)
                return -EFAULT;
        pr_debug("priv %p, offset for vaddr %lx\n", priv, (unsigned long)op.vaddr);
 
+       down_read(&current->mm->mmap_sem);
        vma = find_vma(current->mm, op.vaddr);
        if (!vma || vma->vm_ops != &gntdev_vmops)
-               return -EINVAL;
+               goto out_unlock;
 
        map = vma->vm_private_data;
        if (!map)
-               return -EINVAL;
+               goto out_unlock;
 
        op.offset = map->index << PAGE_SHIFT;
        op.count = map->count;
+       rv = 0;
 
-       if (copy_to_user(u, &op, sizeof(op)) != 0)
+ out_unlock:
+       up_read(&current->mm->mmap_sem);
+
+       if (rv == 0 && copy_to_user(u, &op, sizeof(op)) != 0)
                return -EFAULT;
-       return 0;
+       return rv;
 }
 
 static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u)
@@ -778,7 +824,7 @@ out_unlock_put:
 out_put_map:
        if (use_ptemod)
                map->vma = NULL;
-       gntdev_put_map(map);
+       gntdev_put_map(priv, map);
        return err;
 }
 
index 7038de53652b3cd8999ac7d642fd84441c79dcd5..157c0ccda3efdce0e8a3c4b9ab0937ade1d932ec 100644 (file)
 /* External tools reserve first few grant table entries. */
 #define NR_RESERVED_ENTRIES 8
 #define GNTTAB_LIST_END 0xffffffff
-#define GREFS_PER_GRANT_FRAME \
-(grant_table_version == 1 ?                      \
-(PAGE_SIZE / sizeof(struct grant_entry_v1)) :   \
-(PAGE_SIZE / sizeof(union grant_entry_v2)))
 
 static grant_ref_t **gnttab_list;
 static unsigned int nr_grant_frames;
@@ -154,6 +150,7 @@ static struct gnttab_ops *gnttab_interface;
 static grant_status_t *grstatus;
 
 static int grant_table_version;
+static int grefs_per_grant_frame;
 
 static struct gnttab_free_callback *gnttab_free_callback_list;
 
@@ -767,12 +764,14 @@ static int grow_gnttab_list(unsigned int more_frames)
        unsigned int new_nr_grant_frames, extra_entries, i;
        unsigned int nr_glist_frames, new_nr_glist_frames;
 
+       BUG_ON(grefs_per_grant_frame == 0);
+
        new_nr_grant_frames = nr_grant_frames + more_frames;
-       extra_entries       = more_frames * GREFS_PER_GRANT_FRAME;
+       extra_entries       = more_frames * grefs_per_grant_frame;
 
-       nr_glist_frames = (nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP;
+       nr_glist_frames = (nr_grant_frames * grefs_per_grant_frame + RPP - 1) / RPP;
        new_nr_glist_frames =
-               (new_nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP;
+               (new_nr_grant_frames * grefs_per_grant_frame + RPP - 1) / RPP;
        for (i = nr_glist_frames; i < new_nr_glist_frames; i++) {
                gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_ATOMIC);
                if (!gnttab_list[i])
@@ -780,12 +779,12 @@ static int grow_gnttab_list(unsigned int more_frames)
        }
 
 
-       for (i = GREFS_PER_GRANT_FRAME * nr_grant_frames;
-            i < GREFS_PER_GRANT_FRAME * new_nr_grant_frames - 1; i++)
+       for (i = grefs_per_grant_frame * nr_grant_frames;
+            i < grefs_per_grant_frame * new_nr_grant_frames - 1; i++)
                gnttab_entry(i) = i + 1;
 
        gnttab_entry(i) = gnttab_free_head;
-       gnttab_free_head = GREFS_PER_GRANT_FRAME * nr_grant_frames;
+       gnttab_free_head = grefs_per_grant_frame * nr_grant_frames;
        gnttab_free_count += extra_entries;
 
        nr_grant_frames = new_nr_grant_frames;
@@ -957,7 +956,8 @@ EXPORT_SYMBOL_GPL(gnttab_unmap_refs);
 
 static unsigned nr_status_frames(unsigned nr_grant_frames)
 {
-       return (nr_grant_frames * GREFS_PER_GRANT_FRAME + SPP - 1) / SPP;
+       BUG_ON(grefs_per_grant_frame == 0);
+       return (nr_grant_frames * grefs_per_grant_frame + SPP - 1) / SPP;
 }
 
 static int gnttab_map_frames_v1(xen_pfn_t *frames, unsigned int nr_gframes)
@@ -1115,6 +1115,7 @@ static void gnttab_request_version(void)
        rc = HYPERVISOR_grant_table_op(GNTTABOP_set_version, &gsv, 1);
        if (rc == 0 && gsv.version == 2) {
                grant_table_version = 2;
+               grefs_per_grant_frame = PAGE_SIZE / sizeof(union grant_entry_v2);
                gnttab_interface = &gnttab_v2_ops;
        } else if (grant_table_version == 2) {
                /*
@@ -1127,17 +1128,17 @@ static void gnttab_request_version(void)
                panic("we need grant tables version 2, but only version 1 is available");
        } else {
                grant_table_version = 1;
+               grefs_per_grant_frame = PAGE_SIZE / sizeof(struct grant_entry_v1);
                gnttab_interface = &gnttab_v1_ops;
        }
        printk(KERN_INFO "Grant tables using version %d layout.\n",
                grant_table_version);
 }
 
-int gnttab_resume(void)
+static int gnttab_setup(void)
 {
        unsigned int max_nr_gframes;
 
-       gnttab_request_version();
        max_nr_gframes = gnttab_max_grant_frames();
        if (max_nr_gframes < nr_grant_frames)
                return -ENOSYS;
@@ -1160,6 +1161,12 @@ int gnttab_resume(void)
        return 0;
 }
 
+int gnttab_resume(void)
+{
+       gnttab_request_version();
+       return gnttab_setup();
+}
+
 int gnttab_suspend(void)
 {
        gnttab_interface->unmap_frames();
@@ -1171,9 +1178,10 @@ static int gnttab_expand(unsigned int req_entries)
        int rc;
        unsigned int cur, extra;
 
+       BUG_ON(grefs_per_grant_frame == 0);
        cur = nr_grant_frames;
-       extra = ((req_entries + (GREFS_PER_GRANT_FRAME-1)) /
-                GREFS_PER_GRANT_FRAME);
+       extra = ((req_entries + (grefs_per_grant_frame-1)) /
+                grefs_per_grant_frame);
        if (cur + extra > gnttab_max_grant_frames())
                return -ENOSPC;
 
@@ -1191,21 +1199,23 @@ int gnttab_init(void)
        unsigned int nr_init_grefs;
        int ret;
 
+       gnttab_request_version();
        nr_grant_frames = 1;
        boot_max_nr_grant_frames = __max_nr_grant_frames();
 
        /* Determine the maximum number of frames required for the
         * grant reference free list on the current hypervisor.
         */
+       BUG_ON(grefs_per_grant_frame == 0);
        max_nr_glist_frames = (boot_max_nr_grant_frames *
-                              GREFS_PER_GRANT_FRAME / RPP);
+                              grefs_per_grant_frame / RPP);
 
        gnttab_list = kmalloc(max_nr_glist_frames * sizeof(grant_ref_t *),
                              GFP_KERNEL);
        if (gnttab_list == NULL)
                return -ENOMEM;
 
-       nr_glist_frames = (nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP;
+       nr_glist_frames = (nr_grant_frames * grefs_per_grant_frame + RPP - 1) / RPP;
        for (i = 0; i < nr_glist_frames; i++) {
                gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_KERNEL);
                if (gnttab_list[i] == NULL) {
@@ -1214,12 +1224,12 @@ int gnttab_init(void)
                }
        }
 
-       if (gnttab_resume() < 0) {
+       if (gnttab_setup() < 0) {
                ret = -ENODEV;
                goto ini_nomem;
        }
 
-       nr_init_grefs = nr_grant_frames * GREFS_PER_GRANT_FRAME;
+       nr_init_grefs = nr_grant_frames * grefs_per_grant_frame;
 
        for (i = NR_RESERVED_ENTRIES; i < nr_init_grefs - 1; i++)
                gnttab_entry(i) = i + 1;
index 0bbbccbb1f1296ea305414795b6915679b26d94d..ca2b00e9d558c8fea579f4667106eaccee0adb84 100644 (file)
@@ -199,9 +199,6 @@ static long privcmd_ioctl_mmap(void __user *udata)
        LIST_HEAD(pagelist);
        struct mmap_mfn_state state;
 
-       if (!xen_initial_domain())
-               return -EPERM;
-
        /* We only support privcmd_ioctl_mmap_batch for auto translated. */
        if (xen_feature(XENFEAT_auto_translated_physmap))
                return -ENOSYS;
@@ -261,11 +258,12 @@ struct mmap_batch_state {
         *      -ENOENT if at least 1 -ENOENT has happened.
         */
        int global_error;
-       /* An array for individual errors */
-       int *err;
+       int version;
 
        /* User-space mfn array to store errors in the second pass for V1. */
        xen_pfn_t __user *user_mfn;
+       /* User-space int array to store errors in the second pass for V2. */
+       int __user *user_err;
 };
 
 /* auto translated dom0 note: if domU being created is PV, then mfn is
@@ -288,7 +286,19 @@ static int mmap_batch_fn(void *data, void *state)
                                         &cur_page);
 
        /* Store error code for second pass. */
-       *(st->err++) = ret;
+       if (st->version == 1) {
+               if (ret < 0) {
+                       /*
+                        * V1 encodes the error codes in the 32bit top nibble of the
+                        * mfn (with its known limitations vis-a-vis 64 bit callers).
+                        */
+                       *mfnp |= (ret == -ENOENT) ?
+                                               PRIVCMD_MMAPBATCH_PAGED_ERROR :
+                                               PRIVCMD_MMAPBATCH_MFN_ERROR;
+               }
+       } else { /* st->version == 2 */
+               *((int *) mfnp) = ret;
+       }
 
        /* And see if it affects the global_error. */
        if (ret < 0) {
@@ -305,20 +315,25 @@ static int mmap_batch_fn(void *data, void *state)
        return 0;
 }
 
-static int mmap_return_errors_v1(void *data, void *state)
+static int mmap_return_errors(void *data, void *state)
 {
-       xen_pfn_t *mfnp = data;
        struct mmap_batch_state *st = state;
-       int err = *(st->err++);
 
-       /*
-        * V1 encodes the error codes in the 32bit top nibble of the
-        * mfn (with its known limitations vis-a-vis 64 bit callers).
-        */
-       *mfnp |= (err == -ENOENT) ?
-                               PRIVCMD_MMAPBATCH_PAGED_ERROR :
-                               PRIVCMD_MMAPBATCH_MFN_ERROR;
-       return __put_user(*mfnp, st->user_mfn++);
+       if (st->version == 1) {
+               xen_pfn_t mfnp = *((xen_pfn_t *) data);
+               if (mfnp & PRIVCMD_MMAPBATCH_MFN_ERROR)
+                       return __put_user(mfnp, st->user_mfn++);
+               else
+                       st->user_mfn++;
+       } else { /* st->version == 2 */
+               int err = *((int *) data);
+               if (err)
+                       return __put_user(err, st->user_err++);
+               else
+                       st->user_err++;
+       }
+
+       return 0;
 }
 
 /* Allocate pfns that are then mapped with gmfns from foreign domid. Update
@@ -357,12 +372,8 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version)
        struct vm_area_struct *vma;
        unsigned long nr_pages;
        LIST_HEAD(pagelist);
-       int *err_array = NULL;
        struct mmap_batch_state state;
 
-       if (!xen_initial_domain())
-               return -EPERM;
-
        switch (version) {
        case 1:
                if (copy_from_user(&m, udata, sizeof(struct privcmd_mmapbatch)))
@@ -396,10 +407,12 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version)
                goto out;
        }
 
-       err_array = kcalloc(m.num, sizeof(int), GFP_KERNEL);
-       if (err_array == NULL) {
-               ret = -ENOMEM;
-               goto out;
+       if (version == 2) {
+               /* Zero error array now to only copy back actual errors. */
+               if (clear_user(m.err, sizeof(int) * m.num)) {
+                       ret = -EFAULT;
+                       goto out;
+               }
        }
 
        down_write(&mm->mmap_sem);
@@ -427,7 +440,7 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version)
        state.va            = m.addr;
        state.index         = 0;
        state.global_error  = 0;
-       state.err           = err_array;
+       state.version       = version;
 
        /* mmap_batch_fn guarantees ret == 0 */
        BUG_ON(traverse_pages(m.num, sizeof(xen_pfn_t),
@@ -435,21 +448,14 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version)
 
        up_write(&mm->mmap_sem);
 
-       if (version == 1) {
-               if (state.global_error) {
-                       /* Write back errors in second pass. */
-                       state.user_mfn = (xen_pfn_t *)m.arr;
-                       state.err      = err_array;
-                       ret = traverse_pages(m.num, sizeof(xen_pfn_t),
-                                            &pagelist, mmap_return_errors_v1, &state);
-               } else
-                       ret = 0;
-
-       } else if (version == 2) {
-               ret = __copy_to_user(m.err, err_array, m.num * sizeof(int));
-               if (ret)
-                       ret = -EFAULT;
-       }
+       if (state.global_error) {
+               /* Write back errors in second pass. */
+               state.user_mfn = (xen_pfn_t *)m.arr;
+               state.user_err = m.err;
+               ret = traverse_pages(m.num, sizeof(xen_pfn_t),
+                                                        &pagelist, mmap_return_errors, &state);
+       } else
+               ret = 0;
 
        /* If we have not had any EFAULT-like global errors then set the global
         * error to -ENOENT if necessary. */
@@ -457,7 +463,6 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version)
                ret = -ENOENT;
 
 out:
-       kfree(err_array);
        free_page_list(&pagelist);
 
        return ret;
index a7def010eba3116ac73375f7704c06165c0e0699..f72af87640e076a429af64d050cd23e397e5ff3c 100644 (file)
@@ -124,7 +124,7 @@ static inline int xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev,
 static inline void xen_pcibk_release_pci_dev(struct xen_pcibk_device *pdev,
                                             struct pci_dev *dev)
 {
-       if (xen_pcibk_backend && xen_pcibk_backend->free)
+       if (xen_pcibk_backend && xen_pcibk_backend->release)
                return xen_pcibk_backend->release(pdev, dev);
 }
 
index cfe512fd1caffd8829d0c3f6ce1314cc8e873714..780725a463b1b9001dd089ecb4fddce87b35c51a 100644 (file)
@@ -68,16 +68,6 @@ source "fs/quota/Kconfig"
 source "fs/autofs4/Kconfig"
 source "fs/fuse/Kconfig"
 
-config CUSE
-       tristate "Character device in Userspace support"
-       depends on FUSE_FS
-       help
-         This FUSE extension allows character devices to be
-         implemented in userspace.
-
-         If you want to develop or use userspace character device
-         based on CUSE, answer Y or M.
-
 config GENERIC_ACL
        bool
        select FS_POSIX_ACL
index 521e9d4424f64f81d69d98709031a342d5ee808f..a8b8adc0507059137898cae2e058570e11843291 100644 (file)
@@ -3997,7 +3997,7 @@ again:
         * We make the other tasks wait for the flush only when we can flush
         * all things.
         */
-       if (ret && flush == BTRFS_RESERVE_FLUSH_ALL) {
+       if (ret && flush != BTRFS_RESERVE_NO_FLUSH) {
                flushing = true;
                space_info->flush = 1;
        }
@@ -5560,7 +5560,7 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans,
        int empty_cluster = 2 * 1024 * 1024;
        struct btrfs_space_info *space_info;
        int loop = 0;
-       int index = 0;
+       int index = __get_raid_index(data);
        int alloc_type = (data & BTRFS_BLOCK_GROUP_DATA) ?
                RESERVE_ALLOC_NO_ACCOUNT : RESERVE_ALLOC;
        bool found_uncached_bg = false;
@@ -6788,11 +6788,13 @@ static noinline int walk_up_proc(struct btrfs_trans_handle *trans,
                                                       &wc->flags[level]);
                        if (ret < 0) {
                                btrfs_tree_unlock_rw(eb, path->locks[level]);
+                               path->locks[level] = 0;
                                return ret;
                        }
                        BUG_ON(wc->refs[level] == 0);
                        if (wc->refs[level] == 1) {
                                btrfs_tree_unlock_rw(eb, path->locks[level]);
+                               path->locks[level] = 0;
                                return 1;
                        }
                }
index f169d6b11d7f6a093158428352a44f35d4bae952..2e8cae63d247929613ddc77e6d01761e35b04753 100644 (file)
@@ -171,6 +171,10 @@ static int mergable_maps(struct extent_map *prev, struct extent_map *next)
        if (test_bit(EXTENT_FLAG_COMPRESSED, &prev->flags))
                return 0;
 
+       if (test_bit(EXTENT_FLAG_LOGGING, &prev->flags) ||
+           test_bit(EXTENT_FLAG_LOGGING, &next->flags))
+               return 0;
+
        if (extent_map_end(prev) == next->start &&
            prev->flags == next->flags &&
            prev->bdev == next->bdev &&
@@ -255,7 +259,8 @@ int unpin_extent_cache(struct extent_map_tree *tree, u64 start, u64 len,
        if (!em)
                goto out;
 
-       list_move(&em->list, &tree->modified_extents);
+       if (!test_bit(EXTENT_FLAG_LOGGING, &em->flags))
+               list_move(&em->list, &tree->modified_extents);
        em->generation = gen;
        clear_bit(EXTENT_FLAG_PINNED, &em->flags);
        em->mod_start = em->start;
@@ -280,6 +285,12 @@ out:
 
 }
 
+void clear_em_logging(struct extent_map_tree *tree, struct extent_map *em)
+{
+       clear_bit(EXTENT_FLAG_LOGGING, &em->flags);
+       try_merge_map(tree, em);
+}
+
 /**
  * add_extent_mapping - add new extent map to the extent tree
  * @tree:      tree to insert new map in
index 922943ce29e8caebe0a69feda2a7e481bed0d556..c6598c89cff8c48676dd6179f649ef6f0638acad 100644 (file)
@@ -69,6 +69,7 @@ void free_extent_map(struct extent_map *em);
 int __init extent_map_init(void);
 void extent_map_exit(void);
 int unpin_extent_cache(struct extent_map_tree *tree, u64 start, u64 len, u64 gen);
+void clear_em_logging(struct extent_map_tree *tree, struct extent_map *em);
 struct extent_map *search_extent_mapping(struct extent_map_tree *tree,
                                         u64 start, u64 len);
 #endif
index bd38cef4235882425c08eb6a5d10306736a14237..94aa53b387213bbfc13181163368e5e168574d77 100644 (file)
@@ -460,8 +460,8 @@ int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode,
                if (!contig)
                        offset = page_offset(bvec->bv_page) + bvec->bv_offset;
 
-               if (!contig && (offset >= ordered->file_offset + ordered->len ||
-                   offset < ordered->file_offset)) {
+               if (offset >= ordered->file_offset + ordered->len ||
+                   offset < ordered->file_offset) {
                        unsigned long bytes_left;
                        sums->len = this_sum_bytes;
                        this_sum_bytes = 0;
index 77061bf43edbae995bf55ce73dcc89bf73012400..f76b1fd160d446fb4c92fd56b8cc0ea14acc6f9a 100644 (file)
@@ -2241,6 +2241,7 @@ static int find_desired_extent(struct inode *inode, loff_t *offset, int whence)
        if (lockend <= lockstart)
                lockend = lockstart + root->sectorsize;
 
+       lockend--;
        len = lockend - lockstart + 1;
 
        len = max_t(u64, len, root->sectorsize);
@@ -2307,9 +2308,12 @@ static int find_desired_extent(struct inode *inode, loff_t *offset, int whence)
                                        }
                                }
 
-                               *offset = start;
-                               free_extent_map(em);
-                               break;
+                               if (!test_bit(EXTENT_FLAG_PREALLOC,
+                                             &em->flags)) {
+                                       *offset = start;
+                                       free_extent_map(em);
+                                       break;
+                               }
                        }
                }
 
index 59ea2e4349c9cdbecb105a3a384376f52b0a5da6..0be7a8742a43bb502c9b9294661baaef6e5514e1 100644 (file)
@@ -1862,11 +1862,13 @@ int btrfs_remove_free_space(struct btrfs_block_group_cache *block_group,
 {
        struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl;
        struct btrfs_free_space *info;
-       int ret = 0;
+       int ret;
+       bool re_search = false;
 
        spin_lock(&ctl->tree_lock);
 
 again:
+       ret = 0;
        if (!bytes)
                goto out_lock;
 
@@ -1879,17 +1881,17 @@ again:
                info = tree_search_offset(ctl, offset_to_bitmap(ctl, offset),
                                          1, 0);
                if (!info) {
-                       /* the tree logging code might be calling us before we
-                        * have fully loaded the free space rbtree for this
-                        * block group.  So it is possible the entry won't
-                        * be in the rbtree yet at all.  The caching code
-                        * will make sure not to put it in the rbtree if
-                        * the logging code has pinned it.
+                       /*
+                        * If we found a partial bit of our free space in a
+                        * bitmap but then couldn't find the other part this may
+                        * be a problem, so WARN about it.
                         */
+                       WARN_ON(re_search);
                        goto out_lock;
                }
        }
 
+       re_search = false;
        if (!info->bitmap) {
                unlink_free_space(ctl, info);
                if (offset == info->offset) {
@@ -1935,8 +1937,10 @@ again:
        }
 
        ret = remove_from_bitmap(ctl, info, &offset, &bytes);
-       if (ret == -EAGAIN)
+       if (ret == -EAGAIN) {
+               re_search = true;
                goto again;
+       }
        BUG_ON(ret); /* logic error */
 out_lock:
        spin_unlock(&ctl->tree_lock);
index 16d9e8e191e6ac34d92da08fe90b46e2de42ebf8..cc93b23ca3520c9ad7bf2919e77f61cf400c2e4c 100644 (file)
@@ -88,7 +88,7 @@ static unsigned char btrfs_type_by_mode[S_IFMT >> S_SHIFT] = {
        [S_IFLNK >> S_SHIFT]    = BTRFS_FT_SYMLINK,
 };
 
-static int btrfs_setsize(struct inode *inode, loff_t newsize);
+static int btrfs_setsize(struct inode *inode, struct iattr *attr);
 static int btrfs_truncate(struct inode *inode);
 static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent);
 static noinline int cow_file_range(struct inode *inode,
@@ -2478,6 +2478,18 @@ int btrfs_orphan_cleanup(struct btrfs_root *root)
                                continue;
                        }
                        nr_truncate++;
+
+                       /* 1 for the orphan item deletion. */
+                       trans = btrfs_start_transaction(root, 1);
+                       if (IS_ERR(trans)) {
+                               ret = PTR_ERR(trans);
+                               goto out;
+                       }
+                       ret = btrfs_orphan_add(trans, inode);
+                       btrfs_end_transaction(trans, root);
+                       if (ret)
+                               goto out;
+
                        ret = btrfs_truncate(inode);
                } else {
                        nr_unlink++;
@@ -3665,6 +3677,7 @@ int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size)
                                block_end - cur_offset, 0);
                if (IS_ERR(em)) {
                        err = PTR_ERR(em);
+                       em = NULL;
                        break;
                }
                last_byte = min(extent_map_end(em), block_end);
@@ -3748,16 +3761,27 @@ next:
        return err;
 }
 
-static int btrfs_setsize(struct inode *inode, loff_t newsize)
+static int btrfs_setsize(struct inode *inode, struct iattr *attr)
 {
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct btrfs_trans_handle *trans;
        loff_t oldsize = i_size_read(inode);
+       loff_t newsize = attr->ia_size;
+       int mask = attr->ia_valid;
        int ret;
 
        if (newsize == oldsize)
                return 0;
 
+       /*
+        * The regular truncate() case without ATTR_CTIME and ATTR_MTIME is a
+        * special case where we need to update the times despite not having
+        * these flags set.  For all other operations the VFS set these flags
+        * explicitly if it wants a timestamp update.
+        */
+       if (newsize != oldsize && (!(mask & (ATTR_CTIME | ATTR_MTIME))))
+               inode->i_ctime = inode->i_mtime = current_fs_time(inode->i_sb);
+
        if (newsize > oldsize) {
                truncate_pagecache(inode, oldsize, newsize);
                ret = btrfs_cont_expand(inode, oldsize, newsize);
@@ -3783,9 +3807,34 @@ static int btrfs_setsize(struct inode *inode, loff_t newsize)
                        set_bit(BTRFS_INODE_ORDERED_DATA_CLOSE,
                                &BTRFS_I(inode)->runtime_flags);
 
+               /*
+                * 1 for the orphan item we're going to add
+                * 1 for the orphan item deletion.
+                */
+               trans = btrfs_start_transaction(root, 2);
+               if (IS_ERR(trans))
+                       return PTR_ERR(trans);
+
+               /*
+                * We need to do this in case we fail at _any_ point during the
+                * actual truncate.  Once we do the truncate_setsize we could
+                * invalidate pages which forces any outstanding ordered io to
+                * be instantly completed which will give us extents that need
+                * to be truncated.  If we fail to get an orphan inode down we
+                * could have left over extents that were never meant to live,
+                * so we need to garuntee from this point on that everything
+                * will be consistent.
+                */
+               ret = btrfs_orphan_add(trans, inode);
+               btrfs_end_transaction(trans, root);
+               if (ret)
+                       return ret;
+
                /* we don't support swapfiles, so vmtruncate shouldn't fail */
                truncate_setsize(inode, newsize);
                ret = btrfs_truncate(inode);
+               if (ret && inode->i_nlink)
+                       btrfs_orphan_del(NULL, inode);
        }
 
        return ret;
@@ -3805,7 +3854,7 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
                return err;
 
        if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) {
-               err = btrfs_setsize(inode, attr->ia_size);
+               err = btrfs_setsize(inode, attr);
                if (err)
                        return err;
        }
@@ -5572,10 +5621,13 @@ struct extent_map *btrfs_get_extent_fiemap(struct inode *inode, struct page *pag
                return em;
        if (em) {
                /*
-                * if our em maps to a hole, there might
-                * actually be delalloc bytes behind it
+                * if our em maps to
+                * -  a hole or
+                * -  a pre-alloc extent,
+                * there might actually be delalloc bytes behind it.
                 */
-               if (em->block_start != EXTENT_MAP_HOLE)
+               if (em->block_start != EXTENT_MAP_HOLE &&
+                   !test_bit(EXTENT_FLAG_PREALLOC, &em->flags))
                        return em;
                else
                        hole_em = em;
@@ -5657,6 +5709,8 @@ struct extent_map *btrfs_get_extent_fiemap(struct inode *inode, struct page *pag
                         */
                        em->block_start = hole_em->block_start;
                        em->block_len = hole_len;
+                       if (test_bit(EXTENT_FLAG_PREALLOC, &hole_em->flags))
+                               set_bit(EXTENT_FLAG_PREALLOC, &em->flags);
                } else {
                        em->start = range_start;
                        em->len = found;
@@ -6915,11 +6969,9 @@ static int btrfs_truncate(struct inode *inode)
 
        /*
         * 1 for the truncate slack space
-        * 1 for the orphan item we're going to add
-        * 1 for the orphan item deletion
         * 1 for updating the inode.
         */
-       trans = btrfs_start_transaction(root, 4);
+       trans = btrfs_start_transaction(root, 2);
        if (IS_ERR(trans)) {
                err = PTR_ERR(trans);
                goto out;
@@ -6930,12 +6982,6 @@ static int btrfs_truncate(struct inode *inode)
                                      min_size);
        BUG_ON(ret);
 
-       ret = btrfs_orphan_add(trans, inode);
-       if (ret) {
-               btrfs_end_transaction(trans, root);
-               goto out;
-       }
-
        /*
         * setattr is responsible for setting the ordered_data_close flag,
         * but that is only tested during the last file release.  That
@@ -7004,12 +7050,6 @@ static int btrfs_truncate(struct inode *inode)
                ret = btrfs_orphan_del(trans, inode);
                if (ret)
                        err = ret;
-       } else if (ret && inode->i_nlink > 0) {
-               /*
-                * Failed to do the truncate, remove us from the in memory
-                * orphan list.
-                */
-               ret = btrfs_orphan_del(NULL, inode);
        }
 
        if (trans) {
@@ -7531,41 +7571,61 @@ void btrfs_wait_and_free_delalloc_work(struct btrfs_delalloc_work *work)
  */
 int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput)
 {
-       struct list_head *head = &root->fs_info->delalloc_inodes;
        struct btrfs_inode *binode;
        struct inode *inode;
        struct btrfs_delalloc_work *work, *next;
        struct list_head works;
+       struct list_head splice;
        int ret = 0;
 
        if (root->fs_info->sb->s_flags & MS_RDONLY)
                return -EROFS;
 
        INIT_LIST_HEAD(&works);
-
+       INIT_LIST_HEAD(&splice);
+again:
        spin_lock(&root->fs_info->delalloc_lock);
-       while (!list_empty(head)) {
-               binode = list_entry(head->next, struct btrfs_inode,
+       list_splice_init(&root->fs_info->delalloc_inodes, &splice);
+       while (!list_empty(&splice)) {
+               binode = list_entry(splice.next, struct btrfs_inode,
                                    delalloc_inodes);
+
+               list_del_init(&binode->delalloc_inodes);
+
                inode = igrab(&binode->vfs_inode);
                if (!inode)
-                       list_del_init(&binode->delalloc_inodes);
+                       continue;
+
+               list_add_tail(&binode->delalloc_inodes,
+                             &root->fs_info->delalloc_inodes);
                spin_unlock(&root->fs_info->delalloc_lock);
-               if (inode) {
-                       work = btrfs_alloc_delalloc_work(inode, 0, delay_iput);
-                       if (!work) {
-                               ret = -ENOMEM;
-                               goto out;
-                       }
-                       list_add_tail(&work->list, &works);
-                       btrfs_queue_worker(&root->fs_info->flush_workers,
-                                          &work->work);
+
+               work = btrfs_alloc_delalloc_work(inode, 0, delay_iput);
+               if (unlikely(!work)) {
+                       ret = -ENOMEM;
+                       goto out;
                }
+               list_add_tail(&work->list, &works);
+               btrfs_queue_worker(&root->fs_info->flush_workers,
+                                  &work->work);
+
                cond_resched();
                spin_lock(&root->fs_info->delalloc_lock);
        }
        spin_unlock(&root->fs_info->delalloc_lock);
 
+       list_for_each_entry_safe(work, next, &works, list) {
+               list_del_init(&work->list);
+               btrfs_wait_and_free_delalloc_work(work);
+       }
+
+       spin_lock(&root->fs_info->delalloc_lock);
+       if (!list_empty(&root->fs_info->delalloc_inodes)) {
+               spin_unlock(&root->fs_info->delalloc_lock);
+               goto again;
+       }
+       spin_unlock(&root->fs_info->delalloc_lock);
+
        /* the filemap_flush will queue IO into the worker threads, but
         * we have to make sure the IO is actually started and that
         * ordered extents get created before we return
@@ -7578,11 +7638,18 @@ int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput)
                    atomic_read(&root->fs_info->async_delalloc_pages) == 0));
        }
        atomic_dec(&root->fs_info->async_submit_draining);
+       return 0;
 out:
        list_for_each_entry_safe(work, next, &works, list) {
                list_del_init(&work->list);
                btrfs_wait_and_free_delalloc_work(work);
        }
+
+       if (!list_empty_careful(&splice)) {
+               spin_lock(&root->fs_info->delalloc_lock);
+               list_splice_tail(&splice, &root->fs_info->delalloc_inodes);
+               spin_unlock(&root->fs_info->delalloc_lock);
+       }
        return ret;
 }
 
index 4b4516770f055432964da1e82fe591e1e2128ef8..5b22d45d3c6a232aeed49b24cf34bb374a96ecc9 100644 (file)
@@ -1339,7 +1339,8 @@ static noinline int btrfs_ioctl_resize(struct file *file,
        if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running,
                        1)) {
                pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n");
-               return -EINPROGRESS;
+               mnt_drop_write_file(file);
+               return -EINVAL;
        }
 
        mutex_lock(&root->fs_info->volume_mutex);
@@ -1362,6 +1363,7 @@ static noinline int btrfs_ioctl_resize(struct file *file,
                printk(KERN_INFO "btrfs: resizing devid %llu\n",
                       (unsigned long long)devid);
        }
+
        device = btrfs_find_device(root->fs_info, devid, NULL, NULL);
        if (!device) {
                printk(KERN_INFO "btrfs: resizer unable to find device %llu\n",
@@ -1369,9 +1371,10 @@ static noinline int btrfs_ioctl_resize(struct file *file,
                ret = -EINVAL;
                goto out_free;
        }
-       if (device->fs_devices && device->fs_devices->seeding) {
+
+       if (!device->writeable) {
                printk(KERN_INFO "btrfs: resizer unable to apply on "
-                      "seeding device %llu\n",
+                      "readonly device %llu\n",
                       (unsigned long long)devid);
                ret = -EINVAL;
                goto out_free;
@@ -1443,8 +1446,8 @@ out_free:
        kfree(vol_args);
 out:
        mutex_unlock(&root->fs_info->volume_mutex);
-       mnt_drop_write_file(file);
        atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0);
+       mnt_drop_write_file(file);
        return ret;
 }
 
@@ -2095,13 +2098,13 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
                err = inode_permission(inode, MAY_WRITE | MAY_EXEC);
                if (err)
                        goto out_dput;
-
-               /* check if subvolume may be deleted by a non-root user */
-               err = btrfs_may_delete(dir, dentry, 1);
-               if (err)
-                       goto out_dput;
        }
 
+       /* check if subvolume may be deleted by a user */
+       err = btrfs_may_delete(dir, dentry, 1);
+       if (err)
+               goto out_dput;
+
        if (btrfs_ino(inode) != BTRFS_FIRST_FREE_OBJECTID) {
                err = -EINVAL;
                goto out_dput;
@@ -2183,19 +2186,20 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp)
        struct btrfs_ioctl_defrag_range_args *range;
        int ret;
 
-       if (btrfs_root_readonly(root))
-               return -EROFS;
+       ret = mnt_want_write_file(file);
+       if (ret)
+               return ret;
 
        if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running,
                        1)) {
                pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n");
-               return -EINPROGRESS;
+               mnt_drop_write_file(file);
+               return -EINVAL;
        }
-       ret = mnt_want_write_file(file);
-       if (ret) {
-               atomic_set(&root->fs_info->mutually_exclusive_operation_running,
-                          0);
-               return ret;
+
+       if (btrfs_root_readonly(root)) {
+               ret = -EROFS;
+               goto out;
        }
 
        switch (inode->i_mode & S_IFMT) {
@@ -2247,8 +2251,8 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp)
                ret = -EINVAL;
        }
 out:
-       mnt_drop_write_file(file);
        atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0);
+       mnt_drop_write_file(file);
        return ret;
 }
 
@@ -2263,7 +2267,7 @@ static long btrfs_ioctl_add_dev(struct btrfs_root *root, void __user *arg)
        if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running,
                        1)) {
                pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n");
-               return -EINPROGRESS;
+               return -EINVAL;
        }
 
        mutex_lock(&root->fs_info->volume_mutex);
@@ -2300,7 +2304,7 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)
                        1)) {
                pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n");
                mnt_drop_write_file(file);
-               return -EINPROGRESS;
+               return -EINVAL;
        }
 
        mutex_lock(&root->fs_info->volume_mutex);
@@ -2316,8 +2320,8 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)
        kfree(vol_args);
 out:
        mutex_unlock(&root->fs_info->volume_mutex);
-       mnt_drop_write_file(file);
        atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0);
+       mnt_drop_write_file(file);
        return ret;
 }
 
@@ -3437,8 +3441,8 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg)
        struct btrfs_fs_info *fs_info = root->fs_info;
        struct btrfs_ioctl_balance_args *bargs;
        struct btrfs_balance_control *bctl;
+       bool need_unlock; /* for mut. excl. ops lock */
        int ret;
-       int need_to_clear_lock = 0;
 
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
@@ -3447,14 +3451,61 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg)
        if (ret)
                return ret;
 
-       mutex_lock(&fs_info->volume_mutex);
+again:
+       if (!atomic_xchg(&fs_info->mutually_exclusive_operation_running, 1)) {
+               mutex_lock(&fs_info->volume_mutex);
+               mutex_lock(&fs_info->balance_mutex);
+               need_unlock = true;
+               goto locked;
+       }
+
+       /*
+        * mut. excl. ops lock is locked.  Three possibilites:
+        *   (1) some other op is running
+        *   (2) balance is running
+        *   (3) balance is paused -- special case (think resume)
+        */
        mutex_lock(&fs_info->balance_mutex);
+       if (fs_info->balance_ctl) {
+               /* this is either (2) or (3) */
+               if (!atomic_read(&fs_info->balance_running)) {
+                       mutex_unlock(&fs_info->balance_mutex);
+                       if (!mutex_trylock(&fs_info->volume_mutex))
+                               goto again;
+                       mutex_lock(&fs_info->balance_mutex);
+
+                       if (fs_info->balance_ctl &&
+                           !atomic_read(&fs_info->balance_running)) {
+                               /* this is (3) */
+                               need_unlock = false;
+                               goto locked;
+                       }
+
+                       mutex_unlock(&fs_info->balance_mutex);
+                       mutex_unlock(&fs_info->volume_mutex);
+                       goto again;
+               } else {
+                       /* this is (2) */
+                       mutex_unlock(&fs_info->balance_mutex);
+                       ret = -EINPROGRESS;
+                       goto out;
+               }
+       } else {
+               /* this is (1) */
+               mutex_unlock(&fs_info->balance_mutex);
+               pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+locked:
+       BUG_ON(!atomic_read(&fs_info->mutually_exclusive_operation_running));
 
        if (arg) {
                bargs = memdup_user(arg, sizeof(*bargs));
                if (IS_ERR(bargs)) {
                        ret = PTR_ERR(bargs);
-                       goto out;
+                       goto out_unlock;
                }
 
                if (bargs->flags & BTRFS_BALANCE_RESUME) {
@@ -3474,13 +3525,10 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg)
                bargs = NULL;
        }
 
-       if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running,
-                       1)) {
-               pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n");
+       if (fs_info->balance_ctl) {
                ret = -EINPROGRESS;
                goto out_bargs;
        }
-       need_to_clear_lock = 1;
 
        bctl = kzalloc(sizeof(*bctl), GFP_NOFS);
        if (!bctl) {
@@ -3501,11 +3549,17 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg)
        }
 
 do_balance:
-       ret = btrfs_balance(bctl, bargs);
        /*
-        * bctl is freed in __cancel_balance or in free_fs_info if
-        * restriper was paused all the way until unmount
+        * Ownership of bctl and mutually_exclusive_operation_running
+        * goes to to btrfs_balance.  bctl is freed in __cancel_balance,
+        * or, if restriper was paused all the way until unmount, in
+        * free_fs_info.  mutually_exclusive_operation_running is
+        * cleared in __cancel_balance.
         */
+       need_unlock = false;
+
+       ret = btrfs_balance(bctl, bargs);
+
        if (arg) {
                if (copy_to_user(arg, bargs, sizeof(*bargs)))
                        ret = -EFAULT;
@@ -3513,12 +3567,12 @@ do_balance:
 
 out_bargs:
        kfree(bargs);
-out:
-       if (need_to_clear_lock)
-               atomic_set(&root->fs_info->mutually_exclusive_operation_running,
-                          0);
+out_unlock:
        mutex_unlock(&fs_info->balance_mutex);
        mutex_unlock(&fs_info->volume_mutex);
+       if (need_unlock)
+               atomic_set(&fs_info->mutually_exclusive_operation_running, 0);
+out:
        mnt_drop_write_file(file);
        return ret;
 }
@@ -3698,6 +3752,11 @@ static long btrfs_ioctl_qgroup_create(struct file *file, void __user *arg)
                goto drop_write;
        }
 
+       if (!sa->qgroupid) {
+               ret = -EINVAL;
+               goto out;
+       }
+
        trans = btrfs_join_transaction(root);
        if (IS_ERR(trans)) {
                ret = PTR_ERR(trans);
index fe9d02c45f8e521f87b44d6deff3e5f8d99aa3a9..a5c856234323241c22dde18e1ee072c8bed3ccaa 100644 (file)
@@ -379,6 +379,13 @@ next1:
 
                ret = add_relation_rb(fs_info, found_key.objectid,
                                      found_key.offset);
+               if (ret == -ENOENT) {
+                       printk(KERN_WARNING
+                               "btrfs: orphan qgroup relation 0x%llx->0x%llx\n",
+                               (unsigned long long)found_key.objectid,
+                               (unsigned long long)found_key.offset);
+                       ret = 0;        /* ignore the error */
+               }
                if (ret)
                        goto out;
 next2:
@@ -956,17 +963,28 @@ int btrfs_remove_qgroup(struct btrfs_trans_handle *trans,
                        struct btrfs_fs_info *fs_info, u64 qgroupid)
 {
        struct btrfs_root *quota_root;
+       struct btrfs_qgroup *qgroup;
        int ret = 0;
 
        quota_root = fs_info->quota_root;
        if (!quota_root)
                return -EINVAL;
 
+       /* check if there are no relations to this qgroup */
+       spin_lock(&fs_info->qgroup_lock);
+       qgroup = find_qgroup_rb(fs_info, qgroupid);
+       if (qgroup) {
+               if (!list_empty(&qgroup->groups) || !list_empty(&qgroup->members)) {
+                       spin_unlock(&fs_info->qgroup_lock);
+                       return -EBUSY;
+               }
+       }
+       spin_unlock(&fs_info->qgroup_lock);
+
        ret = del_qgroup_item(trans, quota_root, qgroupid);
 
        spin_lock(&fs_info->qgroup_lock);
        del_qgroup_rb(quota_root->fs_info, qgroupid);
-
        spin_unlock(&fs_info->qgroup_lock);
 
        return ret;
index 54454542ad4073352ddcabf27fd9e0bc528e2136..321b7fb4e4417573e9c404069c6b249afb8ab488 100644 (file)
@@ -1814,8 +1814,10 @@ static int name_cache_insert(struct send_ctx *sctx,
                        (unsigned long)nce->ino);
        if (!nce_head) {
                nce_head = kmalloc(sizeof(*nce_head), GFP_NOFS);
-               if (!nce_head)
+               if (!nce_head) {
+                       kfree(nce);
                        return -ENOMEM;
+               }
                INIT_LIST_HEAD(nce_head);
 
                ret = radix_tree_insert(&sctx->name_cache, nce->ino, nce_head);
index 99545df1b86c18071fed0cbf68dcbb8e745d5524..d8982e9601d37862e6405df95df15c1ba34eecb9 100644 (file)
@@ -267,7 +267,7 @@ void __btrfs_abort_transaction(struct btrfs_trans_handle *trans,
                             function, line, errstr);
                return;
        }
-       trans->transaction->aborted = errno;
+       ACCESS_ONCE(trans->transaction->aborted) = errno;
        __btrfs_std_error(root->fs_info, function, line, errno, NULL);
 }
 /*
index 87fac9a21ea56578625536ac1229678e854ec5f7..f15494699f3b649b1fd743b6bb5e5c7764e334d6 100644 (file)
@@ -1468,7 +1468,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
                goto cleanup_transaction;
        }
 
-       if (cur_trans->aborted) {
+       /* Stop the commit early if ->aborted is set */
+       if (unlikely(ACCESS_ONCE(cur_trans->aborted))) {
                ret = cur_trans->aborted;
                goto cleanup_transaction;
        }
@@ -1574,6 +1575,11 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
        wait_event(cur_trans->writer_wait,
                   atomic_read(&cur_trans->num_writers) == 1);
 
+       /* ->aborted might be set after the previous check, so check it */
+       if (unlikely(ACCESS_ONCE(cur_trans->aborted))) {
+               ret = cur_trans->aborted;
+               goto cleanup_transaction;
+       }
        /*
         * the reloc mutex makes sure that we stop
         * the balancing code from coming in and moving
@@ -1657,6 +1663,17 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
                goto cleanup_transaction;
        }
 
+       /*
+        * The tasks which save the space cache and inode cache may also
+        * update ->aborted, check it.
+        */
+       if (unlikely(ACCESS_ONCE(cur_trans->aborted))) {
+               ret = cur_trans->aborted;
+               mutex_unlock(&root->fs_info->tree_log_mutex);
+               mutex_unlock(&root->fs_info->reloc_mutex);
+               goto cleanup_transaction;
+       }
+
        btrfs_prepare_extent_commit(trans, root);
 
        cur_trans = root->fs_info->running_transaction;
index 83186c7e45d40db89192abad9da56628874cf5d1..9027bb1e74660758328a3d133fe58b59ddc460e6 100644 (file)
@@ -3357,6 +3357,11 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
        if (skip_csum)
                return 0;
 
+       if (em->compress_type) {
+               csum_offset = 0;
+               csum_len = block_len;
+       }
+
        /* block start is already adjusted for the file extent offset. */
        ret = btrfs_lookup_csums_range(log->fs_info->csum_root,
                                       em->block_start + csum_offset,
@@ -3410,13 +3415,13 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans,
                em = list_entry(extents.next, struct extent_map, list);
 
                list_del_init(&em->list);
-               clear_bit(EXTENT_FLAG_LOGGING, &em->flags);
 
                /*
                 * If we had an error we just need to delete everybody from our
                 * private list.
                 */
                if (ret) {
+                       clear_em_logging(tree, em);
                        free_extent_map(em);
                        continue;
                }
@@ -3424,8 +3429,9 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans,
                write_unlock(&tree->lock);
 
                ret = log_one_extent(trans, inode, root, em, path);
-               free_extent_map(em);
                write_lock(&tree->lock);
+               clear_em_logging(tree, em);
+               free_extent_map(em);
        }
        WARN_ON(!list_empty(&extents));
        write_unlock(&tree->lock);
index 5cce6aa7401287322c31673cf039f0e32918c6c4..15f6efdf6463589531f8d1507b74c9d8afd35a3d 100644 (file)
@@ -1431,7 +1431,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
                }
        } else {
                ret = btrfs_get_bdev_and_sb(device_path,
-                                           FMODE_READ | FMODE_EXCL,
+                                           FMODE_WRITE | FMODE_EXCL,
                                            root->fs_info->bdev_holder, 0,
                                            &bdev, &bh);
                if (ret)
@@ -2614,7 +2614,14 @@ static int chunk_usage_filter(struct btrfs_fs_info *fs_info, u64 chunk_offset,
        cache = btrfs_lookup_block_group(fs_info, chunk_offset);
        chunk_used = btrfs_block_group_used(&cache->item);
 
-       user_thresh = div_factor_fine(cache->key.offset, bargs->usage);
+       if (bargs->usage == 0)
+               user_thresh = 0;
+       else if (bargs->usage > 100)
+               user_thresh = cache->key.offset;
+       else
+               user_thresh = div_factor_fine(cache->key.offset,
+                                             bargs->usage);
+
        if (chunk_used < user_thresh)
                ret = 0;
 
@@ -2959,6 +2966,8 @@ static void __cancel_balance(struct btrfs_fs_info *fs_info)
        unset_balance_control(fs_info);
        ret = del_balance_item(fs_info->tree_root);
        BUG_ON(ret);
+
+       atomic_set(&fs_info->mutually_exclusive_operation_running, 0);
 }
 
 void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, int lock,
@@ -3138,8 +3147,10 @@ int btrfs_balance(struct btrfs_balance_control *bctl,
 out:
        if (bctl->flags & BTRFS_BALANCE_RESUME)
                __cancel_balance(fs_info);
-       else
+       else {
                kfree(bctl);
+               atomic_set(&fs_info->mutually_exclusive_operation_running, 0);
+       }
        return ret;
 }
 
@@ -3156,7 +3167,6 @@ static int balance_kthread(void *data)
                ret = btrfs_balance(fs_info->balance_ctl, NULL);
        }
 
-       atomic_set(&fs_info->mutually_exclusive_operation_running, 0);
        mutex_unlock(&fs_info->balance_mutex);
        mutex_unlock(&fs_info->volume_mutex);
 
@@ -3179,7 +3189,6 @@ int btrfs_resume_balance_async(struct btrfs_fs_info *fs_info)
                return 0;
        }
 
-       WARN_ON(atomic_xchg(&fs_info->mutually_exclusive_operation_running, 1));
        tsk = kthread_run(balance_kthread, fs_info, "btrfs-balance");
        if (IS_ERR(tsk))
                return PTR_ERR(tsk);
@@ -3233,6 +3242,8 @@ int btrfs_recover_balance(struct btrfs_fs_info *fs_info)
        btrfs_balance_sys(leaf, item, &disk_bargs);
        btrfs_disk_balance_args_to_cpu(&bctl->sys, &disk_bargs);
 
+       WARN_ON(atomic_xchg(&fs_info->mutually_exclusive_operation_running, 1));
+
        mutex_lock(&fs_info->volume_mutex);
        mutex_lock(&fs_info->balance_mutex);
 
@@ -3496,7 +3507,7 @@ struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = {
        { 1, 1, 2, 2, 2, 2 /* raid1 */ },
        { 1, 2, 1, 1, 1, 2 /* dup */ },
        { 1, 1, 0, 2, 1, 1 /* raid0 */ },
-       { 1, 1, 0, 1, 1, 1 /* single */ },
+       { 1, 1, 1, 1, 1, 1 /* single */ },
 };
 
 static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
index c017a2dfb9097e73837230d244ffb14f73dd0e1c..7a75c3e0fd5896b7fc59595fff859c41f23f65b1 100644 (file)
@@ -2935,6 +2935,7 @@ static void guard_bh_eod(int rw, struct bio *bio, struct buffer_head *bh)
                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);
        }
 }
 
index ce5cbd717bfc54be99e746089efe1fd17a8d593c..210fce2df3087a3738291f714e9ac99c18f390f8 100644 (file)
@@ -226,6 +226,8 @@ compose_mount_options_out:
 compose_mount_options_err:
        kfree(mountdata);
        mountdata = ERR_PTR(rc);
+       kfree(*devname);
+       *devname = NULL;
        goto compose_mount_options_out;
 }
 
index 17c3643e595095597e90026ea6a337edb7bd8b0b..12b3da39733b2bc180cd1ac4a164d9f0d071e99a 100644 (file)
@@ -1917,7 +1917,7 @@ srcip_matches(struct sockaddr *srcaddr, struct sockaddr *rhs)
        }
        case AF_INET6: {
                struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr;
-               struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)&rhs;
+               struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)rhs;
                return ipv6_addr_equal(&saddr6->sin6_addr, &vaddr6->sin6_addr);
        }
        default:
index 153bb1e42e631e07b41c5d87d82fd660388ed22e..a5f12b7e228d487029ea83ab72482cdfc8bb2a61 100644 (file)
@@ -176,7 +176,7 @@ static int debugfs_parse_options(char *data, struct debugfs_mount_opts *opts)
                        opts->uid = uid;
                        break;
                case Opt_gid:
-                       if (match_octal(&args[0], &option))
+                       if (match_int(&args[0], &option))
                                return -EINVAL;
                        gid = make_kgid(current_user_ns(), option);
                        if (!gid_valid(gid))
index 7ff49852b0cb75163ff9646dfb16195678971899..911649a47dd5ae5b000bfabb2b73b0c71a95ec52 100644 (file)
@@ -503,11 +503,11 @@ static ssize_t device_write(struct file *file, const char __user *buf,
 #endif
                return -EINVAL;
 
-#ifdef CONFIG_COMPAT
-       if (count > sizeof(struct dlm_write_request32) + DLM_RESNAME_MAXLEN)
-#else
+       /*
+        * can't compare against COMPAT/dlm_write_request32 because
+        * we don't yet know if is64bit is zero
+        */
        if (count > sizeof(struct dlm_write_request) + DLM_RESNAME_MAXLEN)
-#endif
                return -EINVAL;
 
        kbuf = kzalloc(count + 1, GFP_NOFS);
index 18c45cac368fe3ec830c7f0c8433e5cd2db0fc2d..20df02c1cc70190b04802493d5a25b802e0adcba 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -434,8 +434,9 @@ static int count(struct user_arg_ptr argv, int max)
                        if (IS_ERR(p))
                                return -EFAULT;
 
-                       if (i++ >= max)
+                       if (i >= max)
                                return -E2BIG;
+                       ++i;
 
                        if (fatal_signal_pending(current))
                                return -ERESTARTNOHAND;
index e95b94945d5f4e8d93413c77c23d1db4d414b85b..137af4255da6dfab591e4d640821539663d01a7c 100644 (file)
@@ -191,15 +191,14 @@ struct posix_acl *f2fs_get_acl(struct inode *inode, int type)
                retval = f2fs_getxattr(inode, name_index, "", value, retval);
        }
 
-       if (retval < 0) {
-               if (retval == -ENODATA)
-                       acl = NULL;
-               else
-                       acl = ERR_PTR(retval);
-       } else {
+       if (retval > 0)
                acl = f2fs_acl_from_disk(value, retval);
-       }
+       else if (retval == -ENODATA)
+               acl = NULL;
+       else
+               acl = ERR_PTR(retval);
        kfree(value);
+
        if (!IS_ERR(acl))
                set_cached_acl(inode, type, acl);
 
index 6ef36c37e2be22395335b2467706c1255ed9ecac..ff3c8439af874e059e8474cb8025f5583dea3149 100644 (file)
@@ -214,7 +214,6 @@ retry:
                goto retry;
        }
        new->ino = ino;
-       INIT_LIST_HEAD(&new->list);
 
        /* add new_oentry into list which is sorted by inode number */
        if (orphan) {
@@ -772,7 +771,7 @@ void init_orphan_info(struct f2fs_sb_info *sbi)
        sbi->n_orphans = 0;
 }
 
-int create_checkpoint_caches(void)
+int __init create_checkpoint_caches(void)
 {
        orphan_entry_slab = f2fs_kmem_cache_create("f2fs_orphan_entry",
                        sizeof(struct orphan_inode_entry), NULL);
index 3aa5ce7cab83d30012e86942bfed4317ac0b2b99..7bd22a201125636397c0d79d15a2822e83c130f4 100644 (file)
@@ -547,6 +547,15 @@ redirty_out:
 
 #define MAX_DESIRED_PAGES_WP   4096
 
+static int __f2fs_writepage(struct page *page, struct writeback_control *wbc,
+                       void *data)
+{
+       struct address_space *mapping = data;
+       int ret = mapping->a_ops->writepage(page, wbc);
+       mapping_set_error(mapping, ret);
+       return ret;
+}
+
 static int f2fs_write_data_pages(struct address_space *mapping,
                            struct writeback_control *wbc)
 {
@@ -563,7 +572,7 @@ static int f2fs_write_data_pages(struct address_space *mapping,
 
        if (!S_ISDIR(inode->i_mode))
                mutex_lock(&sbi->writepages);
-       ret = generic_writepages(mapping, wbc);
+       ret = write_cache_pages(mapping, wbc, __f2fs_writepage, mapping);
        if (!S_ISDIR(inode->i_mode))
                mutex_unlock(&sbi->writepages);
        f2fs_submit_bio(sbi, DATA, (wbc->sync_mode == WB_SYNC_ALL));
@@ -689,6 +698,11 @@ static int f2fs_set_data_page_dirty(struct page *page)
        return 0;
 }
 
+static sector_t f2fs_bmap(struct address_space *mapping, sector_t block)
+{
+       return generic_block_bmap(mapping, block, get_data_block_ro);
+}
+
 const struct address_space_operations f2fs_dblock_aops = {
        .readpage       = f2fs_read_data_page,
        .readpages      = f2fs_read_data_pages,
@@ -700,4 +714,5 @@ const struct address_space_operations f2fs_dblock_aops = {
        .invalidatepage = f2fs_invalidate_data_page,
        .releasepage    = f2fs_release_data_page,
        .direct_IO      = f2fs_direct_IO,
+       .bmap           = f2fs_bmap,
 };
index 0e0380a588ad1502a32633343d2af26d8f075da3..c8c37307b326c5e1e5d25428f85e78cd3a62fa69 100644 (file)
@@ -26,6 +26,7 @@
 
 static LIST_HEAD(f2fs_stat_list);
 static struct dentry *debugfs_root;
+static DEFINE_MUTEX(f2fs_stat_mutex);
 
 static void update_general_status(struct f2fs_sb_info *sbi)
 {
@@ -180,18 +181,14 @@ static int stat_show(struct seq_file *s, void *v)
        int i = 0;
        int j;
 
+       mutex_lock(&f2fs_stat_mutex);
        list_for_each_entry_safe(si, next, &f2fs_stat_list, stat_list) {
 
-               mutex_lock(&si->stat_lock);
-               if (!si->sbi) {
-                       mutex_unlock(&si->stat_lock);
-                       continue;
-               }
                update_general_status(si->sbi);
 
                seq_printf(s, "\n=====[ partition info. #%d ]=====\n", i++);
-               seq_printf(s, "[SB: 1] [CP: 2] [NAT: %d] [SIT: %d] ",
-                          si->nat_area_segs, si->sit_area_segs);
+               seq_printf(s, "[SB: 1] [CP: 2] [SIT: %d] [NAT: %d] ",
+                          si->sit_area_segs, si->nat_area_segs);
                seq_printf(s, "[SSA: %d] [MAIN: %d",
                           si->ssa_area_segs, si->main_area_segs);
                seq_printf(s, "(OverProv:%d Resv:%d)]\n\n",
@@ -286,8 +283,8 @@ static int stat_show(struct seq_file *s, void *v)
                seq_printf(s, "\nMemory: %u KB = static: %u + cached: %u\n",
                                (si->base_mem + si->cache_mem) >> 10,
                                si->base_mem >> 10, si->cache_mem >> 10);
-               mutex_unlock(&si->stat_lock);
        }
+       mutex_unlock(&f2fs_stat_mutex);
        return 0;
 }
 
@@ -303,7 +300,7 @@ static const struct file_operations stat_fops = {
        .release = single_release,
 };
 
-static int init_stats(struct f2fs_sb_info *sbi)
+int f2fs_build_stats(struct f2fs_sb_info *sbi)
 {
        struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
        struct f2fs_stat_info *si;
@@ -313,9 +310,6 @@ static int init_stats(struct f2fs_sb_info *sbi)
                return -ENOMEM;
 
        si = sbi->stat_info;
-       mutex_init(&si->stat_lock);
-       list_add_tail(&si->stat_list, &f2fs_stat_list);
-
        si->all_area_segs = le32_to_cpu(raw_super->segment_count);
        si->sit_area_segs = le32_to_cpu(raw_super->segment_count_sit);
        si->nat_area_segs = le32_to_cpu(raw_super->segment_count_nat);
@@ -325,21 +319,11 @@ static int init_stats(struct f2fs_sb_info *sbi)
        si->main_area_zones = si->main_area_sections /
                                le32_to_cpu(raw_super->secs_per_zone);
        si->sbi = sbi;
-       return 0;
-}
 
-int f2fs_build_stats(struct f2fs_sb_info *sbi)
-{
-       int retval;
-
-       retval = init_stats(sbi);
-       if (retval)
-               return retval;
-
-       if (!debugfs_root)
-               debugfs_root = debugfs_create_dir("f2fs", NULL);
+       mutex_lock(&f2fs_stat_mutex);
+       list_add_tail(&si->stat_list, &f2fs_stat_list);
+       mutex_unlock(&f2fs_stat_mutex);
 
-       debugfs_create_file("status", S_IRUGO, debugfs_root, NULL, &stat_fops);
        return 0;
 }
 
@@ -347,14 +331,22 @@ void f2fs_destroy_stats(struct f2fs_sb_info *sbi)
 {
        struct f2fs_stat_info *si = sbi->stat_info;
 
+       mutex_lock(&f2fs_stat_mutex);
        list_del(&si->stat_list);
-       mutex_lock(&si->stat_lock);
-       si->sbi = NULL;
-       mutex_unlock(&si->stat_lock);
+       mutex_unlock(&f2fs_stat_mutex);
+
        kfree(sbi->stat_info);
 }
 
-void destroy_root_stats(void)
+void __init f2fs_create_root_stats(void)
+{
+       debugfs_root = debugfs_create_dir("f2fs", NULL);
+       if (debugfs_root)
+               debugfs_create_file("status", S_IRUGO, debugfs_root,
+                                        NULL, &stat_fops);
+}
+
+void f2fs_destroy_root_stats(void)
 {
        debugfs_remove_recursive(debugfs_root);
        debugfs_root = NULL;
index 951ed52748f6171a3bc1097a3d806f0df37c778c..989980e16d0b03da6174582518f9fe10f25ca811 100644 (file)
@@ -503,7 +503,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
        }
 
        if (inode) {
-               inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+               inode->i_ctime = CURRENT_TIME;
                drop_nlink(inode);
                if (S_ISDIR(inode->i_mode)) {
                        drop_nlink(inode);
index 13c6dfbb71833e202ffec05a21ee8f0dceb5c046..c8e2d751ef9cd53a614cb875c1f8a9fe8bcdb241 100644 (file)
@@ -211,11 +211,11 @@ struct dnode_of_data {
 static inline void set_new_dnode(struct dnode_of_data *dn, struct inode *inode,
                struct page *ipage, struct page *npage, nid_t nid)
 {
+       memset(dn, 0, sizeof(*dn));
        dn->inode = inode;
        dn->inode_page = ipage;
        dn->node_page = npage;
        dn->nid = nid;
-       dn->inode_page_locked = 0;
 }
 
 /*
@@ -877,6 +877,8 @@ bool f2fs_empty_dir(struct inode *);
  * super.c
  */
 int f2fs_sync_fs(struct super_block *, int);
+extern __printf(3, 4)
+void f2fs_msg(struct super_block *, const char *, const char *, ...);
 
 /*
  * hash.c
@@ -912,7 +914,7 @@ int restore_node_summary(struct f2fs_sb_info *, unsigned int,
 void flush_nat_entries(struct f2fs_sb_info *);
 int build_node_manager(struct f2fs_sb_info *);
 void destroy_node_manager(struct f2fs_sb_info *);
-int create_node_manager_caches(void);
+int __init create_node_manager_caches(void);
 void destroy_node_manager_caches(void);
 
 /*
@@ -964,7 +966,7 @@ void sync_dirty_dir_inodes(struct f2fs_sb_info *);
 void block_operations(struct f2fs_sb_info *);
 void write_checkpoint(struct f2fs_sb_info *, bool, bool);
 void init_orphan_info(struct f2fs_sb_info *);
-int create_checkpoint_caches(void);
+int __init create_checkpoint_caches(void);
 void destroy_checkpoint_caches(void);
 
 /*
@@ -984,9 +986,9 @@ int do_write_data_page(struct page *);
 int start_gc_thread(struct f2fs_sb_info *);
 void stop_gc_thread(struct f2fs_sb_info *);
 block_t start_bidx_of_node(unsigned int);
-int f2fs_gc(struct f2fs_sb_info *, int);
+int f2fs_gc(struct f2fs_sb_info *);
 void build_gc_manager(struct f2fs_sb_info *);
-int create_gc_caches(void);
+int __init create_gc_caches(void);
 void destroy_gc_caches(void);
 
 /*
@@ -1058,7 +1060,8 @@ struct f2fs_stat_info {
 
 int f2fs_build_stats(struct f2fs_sb_info *);
 void f2fs_destroy_stats(struct f2fs_sb_info *);
-void destroy_root_stats(void);
+void __init f2fs_create_root_stats(void);
+void f2fs_destroy_root_stats(void);
 #else
 #define stat_inc_call_count(si)
 #define stat_inc_seg_count(si, type)
@@ -1068,7 +1071,8 @@ void destroy_root_stats(void);
 
 static inline int f2fs_build_stats(struct f2fs_sb_info *sbi) { return 0; }
 static inline void f2fs_destroy_stats(struct f2fs_sb_info *sbi) { }
-static inline void destroy_root_stats(void) { }
+static inline void __init f2fs_create_root_stats(void) { }
+static inline void f2fs_destroy_root_stats(void) { }
 #endif
 
 extern const struct file_operations f2fs_dir_operations;
index 7f9ea9271ebeaa4e1da9440758c5fe6c2cee62f3..3191b52aafb04cdce0ead1a5dbdb4a1ed6c7b082 100644 (file)
@@ -96,8 +96,9 @@ out:
 }
 
 static const struct vm_operations_struct f2fs_file_vm_ops = {
-       .fault        = filemap_fault,
-       .page_mkwrite = f2fs_vm_page_mkwrite,
+       .fault          = filemap_fault,
+       .page_mkwrite   = f2fs_vm_page_mkwrite,
+       .remap_pages    = generic_file_remap_pages,
 };
 
 static int need_to_sync_dir(struct f2fs_sb_info *sbi, struct inode *inode)
@@ -137,6 +138,9 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
        if (ret)
                return ret;
 
+       /* guarantee free sections for fsync */
+       f2fs_balance_fs(sbi);
+
        mutex_lock(&inode->i_mutex);
 
        if (datasync && !(inode->i_state & I_DIRTY_DATASYNC))
@@ -407,6 +411,8 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end)
                struct dnode_of_data dn;
                struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
 
+               f2fs_balance_fs(sbi);
+
                mutex_lock_op(sbi, DATA_TRUNC);
                set_new_dnode(&dn, inode, NULL, NULL, 0);
                err = get_dnode_of_data(&dn, index, RDONLY_NODE);
@@ -534,7 +540,6 @@ static long f2fs_fallocate(struct file *file, int mode,
                                loff_t offset, loff_t len)
 {
        struct inode *inode = file->f_path.dentry->d_inode;
-       struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
        long ret;
 
        if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
@@ -545,7 +550,10 @@ static long f2fs_fallocate(struct file *file, int mode,
        else
                ret = expand_inode_data(inode, offset, len, mode);
 
-       f2fs_balance_fs(sbi);
+       if (!ret) {
+               inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+               mark_inode_dirty(inode);
+       }
        return ret;
 }
 
index b0ec721e984a324e3e239d4cdfe946cb0a5b8df0..c386910dacc53b5300f019eec517fa88a04461bc 100644 (file)
@@ -78,7 +78,7 @@ static int gc_thread_func(void *data)
 
                sbi->bg_gc++;
 
-               if (f2fs_gc(sbi, 1) == GC_NONE)
+               if (f2fs_gc(sbi) == GC_NONE)
                        wait_ms = GC_THREAD_NOGC_SLEEP_TIME;
                else if (wait_ms == GC_THREAD_NOGC_SLEEP_TIME)
                        wait_ms = GC_THREAD_MAX_SLEEP_TIME;
@@ -424,7 +424,11 @@ next_step:
 }
 
 /*
- * Calculate start block index that this node page contains
+ * Calculate start block index indicating the given node offset.
+ * Be careful, caller should give this node offset only indicating direct node
+ * blocks. If any node offsets, which point the other types of node blocks such
+ * as indirect or double indirect node blocks, are given, it must be a caller's
+ * bug.
  */
 block_t start_bidx_of_node(unsigned int node_ofs)
 {
@@ -651,62 +655,44 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno,
        return ret;
 }
 
-int f2fs_gc(struct f2fs_sb_info *sbi, int nGC)
+int f2fs_gc(struct f2fs_sb_info *sbi)
 {
-       unsigned int segno;
-       int old_free_secs, cur_free_secs;
-       int gc_status, nfree;
        struct list_head ilist;
+       unsigned int segno, i;
        int gc_type = BG_GC;
+       int gc_status = GC_NONE;
 
        INIT_LIST_HEAD(&ilist);
 gc_more:
-       nfree = 0;
-       gc_status = GC_NONE;
+       if (!(sbi->sb->s_flags & MS_ACTIVE))
+               goto stop;
 
        if (has_not_enough_free_secs(sbi))
-               old_free_secs = reserved_sections(sbi);
-       else
-               old_free_secs = free_sections(sbi);
-
-       while (sbi->sb->s_flags & MS_ACTIVE) {
-               int i;
-               if (has_not_enough_free_secs(sbi))
-                       gc_type = FG_GC;
+               gc_type = FG_GC;
 
-               cur_free_secs = free_sections(sbi) + nfree;
+       if (!__get_victim(sbi, &segno, gc_type, NO_CHECK_TYPE))
+               goto stop;
 
-               /* We got free space successfully. */
-               if (nGC < cur_free_secs - old_free_secs)
-                       break;
-
-               if (!__get_victim(sbi, &segno, gc_type, NO_CHECK_TYPE))
+       for (i = 0; i < sbi->segs_per_sec; i++) {
+               /*
+                * do_garbage_collect will give us three gc_status:
+                * GC_ERROR, GC_DONE, and GC_BLOCKED.
+                * If GC is finished uncleanly, we have to return
+                * the victim to dirty segment list.
+                */
+               gc_status = do_garbage_collect(sbi, segno + i, &ilist, gc_type);
+               if (gc_status != GC_DONE)
                        break;
-
-               for (i = 0; i < sbi->segs_per_sec; i++) {
-                       /*
-                        * do_garbage_collect will give us three gc_status:
-                        * GC_ERROR, GC_DONE, and GC_BLOCKED.
-                        * If GC is finished uncleanly, we have to return
-                        * the victim to dirty segment list.
-                        */
-                       gc_status = do_garbage_collect(sbi, segno + i,
-                                       &ilist, gc_type);
-                       if (gc_status != GC_DONE)
-                               goto stop;
-                       nfree++;
-               }
        }
-stop:
-       if (has_not_enough_free_secs(sbi) || gc_status == GC_BLOCKED) {
+       if (has_not_enough_free_secs(sbi)) {
                write_checkpoint(sbi, (gc_status == GC_BLOCKED), false);
-               if (nfree)
+               if (has_not_enough_free_secs(sbi))
                        goto gc_more;
        }
+stop:
        mutex_unlock(&sbi->gc_mutex);
 
        put_gc_inode(&ilist);
-       BUG_ON(!list_empty(&ilist));
        return gc_status;
 }
 
@@ -715,7 +701,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
        DIRTY_I(sbi)->v_ops = &default_v_ops;
 }
 
-int create_gc_caches(void)
+int __init create_gc_caches(void)
 {
        winode_slab = f2fs_kmem_cache_create("f2fs_gc_inodes",
                        sizeof(struct inode_entry), NULL);
index bf20b4d03214f01c2a2fa3b278612dc52d73896a..79424177732209f207ce78aee69c5020d90ede31 100644 (file)
@@ -217,6 +217,9 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
                        inode->i_ino == F2FS_META_INO(sbi))
                return 0;
 
+       if (wbc)
+               f2fs_balance_fs(sbi);
+
        node_page = get_node_page(sbi, inode->i_ino);
        if (IS_ERR(node_page))
                return PTR_ERR(node_page);
index 5066bfd256c973657ec28795e2d87af557b13b89..9bda63c9c166a886c1585cc34de06dfef3f59cc2 100644 (file)
@@ -1124,6 +1124,12 @@ static int f2fs_write_node_page(struct page *page,
        return 0;
 }
 
+/*
+ * It is very important to gather dirty pages and write at once, so that we can
+ * submit a big bio without interfering other data writes.
+ * Be default, 512 pages (2MB), a segment size, is quite reasonable.
+ */
+#define COLLECT_DIRTY_NODES    512
 static int f2fs_write_node_pages(struct address_space *mapping,
                            struct writeback_control *wbc)
 {
@@ -1131,17 +1137,16 @@ static int f2fs_write_node_pages(struct address_space *mapping,
        struct block_device *bdev = sbi->sb->s_bdev;
        long nr_to_write = wbc->nr_to_write;
 
-       if (wbc->for_kupdate)
-               return 0;
-
-       if (get_pages(sbi, F2FS_DIRTY_NODES) == 0)
-               return 0;
-
+       /* First check balancing cached NAT entries */
        if (try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK)) {
                write_checkpoint(sbi, false, false);
                return 0;
        }
 
+       /* collect a number of dirty node pages and write together */
+       if (get_pages(sbi, F2FS_DIRTY_NODES) < COLLECT_DIRTY_NODES)
+               return 0;
+
        /* if mounting is failed, skip writing node pages */
        wbc->nr_to_write = bio_get_nr_vecs(bdev);
        sync_node_pages(sbi, 0, wbc);
@@ -1732,7 +1737,7 @@ void destroy_node_manager(struct f2fs_sb_info *sbi)
        kfree(nm_i);
 }
 
-int create_node_manager_caches(void)
+int __init create_node_manager_caches(void)
 {
        nat_entry_slab = f2fs_kmem_cache_create("nat_entry",
                        sizeof(struct nat_entry), NULL);
index b571fee677d5837e4481c805f82f3f38f8975281..f42e4060b3991c140fd7e3e3de58d40d30dfcbc4 100644 (file)
@@ -67,7 +67,7 @@ static int recover_dentry(struct page *ipage, struct inode *inode)
                kunmap(page);
                f2fs_put_page(page, 0);
        } else {
-               f2fs_add_link(&dent, inode);
+               err = f2fs_add_link(&dent, inode);
        }
        iput(dir);
 out:
@@ -151,7 +151,6 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
                                goto out;
                        }
 
-                       INIT_LIST_HEAD(&entry->list);
                        list_add_tail(&entry->list, head);
                        entry->blkaddr = blkaddr;
                }
@@ -174,10 +173,9 @@ out:
 static void destroy_fsync_dnodes(struct f2fs_sb_info *sbi,
                                        struct list_head *head)
 {
-       struct list_head *this;
-       struct fsync_inode_entry *entry;
-       list_for_each(this, head) {
-               entry = list_entry(this, struct fsync_inode_entry, list);
+       struct fsync_inode_entry *entry, *tmp;
+
+       list_for_each_entry_safe(entry, tmp, head, list) {
                iput(entry->inode);
                list_del(&entry->list);
                kmem_cache_free(fsync_entry_slab, entry);
index de6240922b0a8f14d4a17109013d09e432b0fb6e..4b009906658235d428f5455ca8da2f038e61997e 100644 (file)
@@ -31,7 +31,7 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi)
         */
        if (has_not_enough_free_secs(sbi)) {
                mutex_lock(&sbi->gc_mutex);
-               f2fs_gc(sbi, 1);
+               f2fs_gc(sbi);
        }
 }
 
index 08a94c814bdc9a9a5047e632863ac1d03bdfcd8d..37fad04c866907969a128876aaedd8b34c3209ef 100644 (file)
@@ -53,6 +53,18 @@ static match_table_t f2fs_tokens = {
        {Opt_err, NULL},
 };
 
+void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...)
+{
+       struct va_format vaf;
+       va_list args;
+
+       va_start(args, fmt);
+       vaf.fmt = fmt;
+       vaf.va = &args;
+       printk("%sF2FS-fs (%s): %pV\n", level, sb->s_id, &vaf);
+       va_end(args);
+}
+
 static void init_once(void *foo)
 {
        struct f2fs_inode_info *fi = (struct f2fs_inode_info *) foo;
@@ -125,6 +137,8 @@ int f2fs_sync_fs(struct super_block *sb, int sync)
 
        if (sync)
                write_checkpoint(sbi, false, false);
+       else
+               f2fs_balance_fs(sbi);
 
        return 0;
 }
@@ -247,7 +261,8 @@ static const struct export_operations f2fs_export_ops = {
        .get_parent = f2fs_get_parent,
 };
 
-static int parse_options(struct f2fs_sb_info *sbi, char *options)
+static int parse_options(struct super_block *sb, struct f2fs_sb_info *sbi,
+                               char *options)
 {
        substring_t args[MAX_OPT_ARGS];
        char *p;
@@ -286,7 +301,8 @@ static int parse_options(struct f2fs_sb_info *sbi, char *options)
                        break;
 #else
                case Opt_nouser_xattr:
-                       pr_info("nouser_xattr options not supported\n");
+                       f2fs_msg(sb, KERN_INFO,
+                               "nouser_xattr options not supported");
                        break;
 #endif
 #ifdef CONFIG_F2FS_FS_POSIX_ACL
@@ -295,7 +311,7 @@ static int parse_options(struct f2fs_sb_info *sbi, char *options)
                        break;
 #else
                case Opt_noacl:
-                       pr_info("noacl options not supported\n");
+                       f2fs_msg(sb, KERN_INFO, "noacl options not supported");
                        break;
 #endif
                case Opt_active_logs:
@@ -309,8 +325,9 @@ static int parse_options(struct f2fs_sb_info *sbi, char *options)
                        set_opt(sbi, DISABLE_EXT_IDENTIFY);
                        break;
                default:
-                       pr_err("Unrecognized mount option \"%s\" or missing value\n",
-                                       p);
+                       f2fs_msg(sb, KERN_ERR,
+                               "Unrecognized mount option \"%s\" or missing value",
+                               p);
                        return -EINVAL;
                }
        }
@@ -337,23 +354,36 @@ static loff_t max_file_size(unsigned bits)
        return result;
 }
 
-static int sanity_check_raw_super(struct f2fs_super_block *raw_super)
+static int sanity_check_raw_super(struct super_block *sb,
+                       struct f2fs_super_block *raw_super)
 {
        unsigned int blocksize;
 
-       if (F2FS_SUPER_MAGIC != le32_to_cpu(raw_super->magic))
+       if (F2FS_SUPER_MAGIC != le32_to_cpu(raw_super->magic)) {
+               f2fs_msg(sb, KERN_INFO,
+                       "Magic Mismatch, valid(0x%x) - read(0x%x)",
+                       F2FS_SUPER_MAGIC, le32_to_cpu(raw_super->magic));
                return 1;
+       }
 
        /* Currently, support only 4KB block size */
        blocksize = 1 << le32_to_cpu(raw_super->log_blocksize);
-       if (blocksize != PAGE_CACHE_SIZE)
+       if (blocksize != PAGE_CACHE_SIZE) {
+               f2fs_msg(sb, KERN_INFO,
+                       "Invalid blocksize (%u), supports only 4KB\n",
+                       blocksize);
                return 1;
+       }
        if (le32_to_cpu(raw_super->log_sectorsize) !=
-                                       F2FS_LOG_SECTOR_SIZE)
+                                       F2FS_LOG_SECTOR_SIZE) {
+               f2fs_msg(sb, KERN_INFO, "Invalid log sectorsize");
                return 1;
+       }
        if (le32_to_cpu(raw_super->log_sectors_per_block) !=
-                                       F2FS_LOG_SECTORS_PER_BLOCK)
+                                       F2FS_LOG_SECTORS_PER_BLOCK) {
+               f2fs_msg(sb, KERN_INFO, "Invalid log sectors per block");
                return 1;
+       }
        return 0;
 }
 
@@ -413,14 +443,17 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
        if (!sbi)
                return -ENOMEM;
 
-       /* set a temporary block size */
-       if (!sb_set_blocksize(sb, F2FS_BLKSIZE))
+       /* set a block size */
+       if (!sb_set_blocksize(sb, F2FS_BLKSIZE)) {
+               f2fs_msg(sb, KERN_ERR, "unable to set blocksize");
                goto free_sbi;
+       }
 
        /* read f2fs raw super block */
        raw_super_buf = sb_bread(sb, 0);
        if (!raw_super_buf) {
                err = -EIO;
+               f2fs_msg(sb, KERN_ERR, "unable to read superblock");
                goto free_sbi;
        }
        raw_super = (struct f2fs_super_block *)
@@ -438,12 +471,14 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
        set_opt(sbi, POSIX_ACL);
 #endif
        /* parse mount options */
-       if (parse_options(sbi, (char *)data))
+       if (parse_options(sb, sbi, (char *)data))
                goto free_sb_buf;
 
        /* sanity checking of raw super */
-       if (sanity_check_raw_super(raw_super))
+       if (sanity_check_raw_super(sb, raw_super)) {
+               f2fs_msg(sb, KERN_ERR, "Can't find a valid F2FS filesystem");
                goto free_sb_buf;
+       }
 
        sb->s_maxbytes = max_file_size(le32_to_cpu(raw_super->log_blocksize));
        sb->s_max_links = F2FS_LINK_MAX;
@@ -477,18 +512,23 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
        /* get an inode for meta space */
        sbi->meta_inode = f2fs_iget(sb, F2FS_META_INO(sbi));
        if (IS_ERR(sbi->meta_inode)) {
+               f2fs_msg(sb, KERN_ERR, "Failed to read F2FS meta data inode");
                err = PTR_ERR(sbi->meta_inode);
                goto free_sb_buf;
        }
 
        err = get_valid_checkpoint(sbi);
-       if (err)
+       if (err) {
+               f2fs_msg(sb, KERN_ERR, "Failed to get valid F2FS checkpoint");
                goto free_meta_inode;
+       }
 
        /* sanity checking of checkpoint */
        err = -EINVAL;
-       if (sanity_check_ckpt(raw_super, sbi->ckpt))
+       if (sanity_check_ckpt(raw_super, sbi->ckpt)) {
+               f2fs_msg(sb, KERN_ERR, "Invalid F2FS checkpoint");
                goto free_cp;
+       }
 
        sbi->total_valid_node_count =
                                le32_to_cpu(sbi->ckpt->valid_node_count);
@@ -502,25 +542,28 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
        INIT_LIST_HEAD(&sbi->dir_inode_list);
        spin_lock_init(&sbi->dir_inode_lock);
 
-       /* init super block */
-       if (!sb_set_blocksize(sb, sbi->blocksize))
-               goto free_cp;
-
        init_orphan_info(sbi);
 
        /* setup f2fs internal modules */
        err = build_segment_manager(sbi);
-       if (err)
+       if (err) {
+               f2fs_msg(sb, KERN_ERR,
+                       "Failed to initialize F2FS segment manager");
                goto free_sm;
+       }
        err = build_node_manager(sbi);
-       if (err)
+       if (err) {
+               f2fs_msg(sb, KERN_ERR,
+                       "Failed to initialize F2FS node manager");
                goto free_nm;
+       }
 
        build_gc_manager(sbi);
 
        /* get an inode for node space */
        sbi->node_inode = f2fs_iget(sb, F2FS_NODE_INO(sbi));
        if (IS_ERR(sbi->node_inode)) {
+               f2fs_msg(sb, KERN_ERR, "Failed to read node inode");
                err = PTR_ERR(sbi->node_inode);
                goto free_nm;
        }
@@ -533,6 +576,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
        /* read root inode and dentry */
        root = f2fs_iget(sb, F2FS_ROOT_INO(sbi));
        if (IS_ERR(root)) {
+               f2fs_msg(sb, KERN_ERR, "Failed to read root inode");
                err = PTR_ERR(root);
                goto free_node_inode;
        }
@@ -596,7 +640,7 @@ static struct file_system_type f2fs_fs_type = {
        .fs_flags       = FS_REQUIRES_DEV,
 };
 
-static int init_inodecache(void)
+static int __init init_inodecache(void)
 {
        f2fs_inode_cachep = f2fs_kmem_cache_create("f2fs_inode_cache",
                        sizeof(struct f2fs_inode_info), NULL);
@@ -631,14 +675,17 @@ static int __init init_f2fs_fs(void)
        err = create_checkpoint_caches();
        if (err)
                goto fail;
-       return register_filesystem(&f2fs_fs_type);
+       err = register_filesystem(&f2fs_fs_type);
+       if (err)
+               goto fail;
+       f2fs_create_root_stats();
 fail:
        return err;
 }
 
 static void __exit exit_f2fs_fs(void)
 {
-       destroy_root_stats();
+       f2fs_destroy_root_stats();
        unregister_filesystem(&f2fs_fs_type);
        destroy_checkpoint_caches();
        destroy_gc_caches();
index 940136a3d3a61a05494f9163c5cbd9cf19874a37..8038c049650473bd260d50006ca75175ec90fa62 100644 (file)
@@ -318,6 +318,8 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name,
        if (name_len > 255 || value_len > MAX_VALUE_LEN)
                return -ERANGE;
 
+       f2fs_balance_fs(sbi);
+
        mutex_lock_op(sbi, NODE_NEW);
        if (!fi->i_xattr_nid) {
                /* Allocate new attribute block */
index 0cf160a94eda06afe6055d3c9e0aa806337121e3..1b2f6c2c3aaff697d2909cb88953e61b9d57b82b 100644 (file)
@@ -4,12 +4,24 @@ config FUSE_FS
          With FUSE it is possible to implement a fully functional filesystem
          in a userspace program.
 
-         There's also companion library: libfuse.  This library along with
-         utilities is available from the FUSE homepage:
+         There's also a companion library: libfuse2.  This library is available
+         from the FUSE homepage:
          <http://fuse.sourceforge.net/>
+         although chances are your distribution already has that library
+         installed if you've installed the "fuse" package itself.
 
          See <file:Documentation/filesystems/fuse.txt> for more information.
          See <file:Documentation/Changes> for needed library/utility version.
 
          If you want to develop a userspace FS, or if you want to use
          a filesystem based on FUSE, answer Y or M.
+
+config CUSE
+       tristate "Character device in Userspace support"
+       depends on FUSE_FS
+       help
+         This FUSE extension allows character devices to be
+         implemented in userspace.
+
+         If you want to develop or use a userspace character device
+         based on CUSE, answer Y or M.
index ee8d55042298272f6ac6c76982f4ecd5efb745ca..e397b675b029a6e9f2d6f7f59a0d73acec412fbe 100644 (file)
@@ -45,7 +45,6 @@
 #include <linux/miscdevice.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
-#include <linux/spinlock.h>
 #include <linux/stat.h>
 #include <linux/module.h>
 
@@ -63,7 +62,7 @@ struct cuse_conn {
        bool                    unrestricted_ioctl;
 };
 
-static DEFINE_SPINLOCK(cuse_lock);             /* protects cuse_conntbl */
+static DEFINE_MUTEX(cuse_lock);                /* protects registration */
 static struct list_head cuse_conntbl[CUSE_CONNTBL_LEN];
 static struct class *cuse_class;
 
@@ -114,14 +113,14 @@ static int cuse_open(struct inode *inode, struct file *file)
        int rc;
 
        /* look up and get the connection */
-       spin_lock(&cuse_lock);
+       mutex_lock(&cuse_lock);
        list_for_each_entry(pos, cuse_conntbl_head(devt), list)
                if (pos->dev->devt == devt) {
                        fuse_conn_get(&pos->fc);
                        cc = pos;
                        break;
                }
-       spin_unlock(&cuse_lock);
+       mutex_unlock(&cuse_lock);
 
        /* dead? */
        if (!cc)
@@ -267,7 +266,7 @@ static int cuse_parse_one(char **pp, char *end, char **keyp, char **valp)
 static int cuse_parse_devinfo(char *p, size_t len, struct cuse_devinfo *devinfo)
 {
        char *end = p + len;
-       char *key, *val;
+       char *uninitialized_var(key), *uninitialized_var(val);
        int rc;
 
        while (true) {
@@ -305,14 +304,14 @@ static void cuse_gendev_release(struct device *dev)
  */
 static void cuse_process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
 {
-       struct cuse_conn *cc = fc_to_cc(fc);
+       struct cuse_conn *cc = fc_to_cc(fc), *pos;
        struct cuse_init_out *arg = req->out.args[0].value;
        struct page *page = req->pages[0];
        struct cuse_devinfo devinfo = { };
        struct device *dev;
        struct cdev *cdev;
        dev_t devt;
-       int rc;
+       int rc, i;
 
        if (req->out.h.error ||
            arg->major != FUSE_KERNEL_VERSION || arg->minor < 11) {
@@ -356,15 +355,24 @@ static void cuse_process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
        dev_set_drvdata(dev, cc);
        dev_set_name(dev, "%s", devinfo.name);
 
+       mutex_lock(&cuse_lock);
+
+       /* make sure the device-name is unique */
+       for (i = 0; i < CUSE_CONNTBL_LEN; ++i) {
+               list_for_each_entry(pos, &cuse_conntbl[i], list)
+                       if (!strcmp(dev_name(pos->dev), dev_name(dev)))
+                               goto err_unlock;
+       }
+
        rc = device_add(dev);
        if (rc)
-               goto err_device;
+               goto err_unlock;
 
        /* register cdev */
        rc = -ENOMEM;
        cdev = cdev_alloc();
        if (!cdev)
-               goto err_device;
+               goto err_unlock;
 
        cdev->owner = THIS_MODULE;
        cdev->ops = &cuse_frontend_fops;
@@ -377,9 +385,8 @@ static void cuse_process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
        cc->cdev = cdev;
 
        /* make the device available */
-       spin_lock(&cuse_lock);
        list_add(&cc->list, cuse_conntbl_head(devt));
-       spin_unlock(&cuse_lock);
+       mutex_unlock(&cuse_lock);
 
        /* announce device availability */
        dev_set_uevent_suppress(dev, 0);
@@ -391,7 +398,8 @@ out:
 
 err_cdev:
        cdev_del(cdev);
-err_device:
+err_unlock:
+       mutex_unlock(&cuse_lock);
        put_device(dev);
 err_region:
        unregister_chrdev_region(devt, 1);
@@ -520,9 +528,9 @@ static int cuse_channel_release(struct inode *inode, struct file *file)
        int rc;
 
        /* remove from the conntbl, no more access from this point on */
-       spin_lock(&cuse_lock);
+       mutex_lock(&cuse_lock);
        list_del_init(&cc->list);
-       spin_unlock(&cuse_lock);
+       mutex_unlock(&cuse_lock);
 
        /* remove device */
        if (cc->dev)
index c16335315e5da8843a41dc9de6ea5f27cae6df12..e83351aa5baddedc2d8000c3ac82936a25da28b7 100644 (file)
@@ -692,8 +692,6 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)
        struct page *oldpage = *pagep;
        struct page *newpage;
        struct pipe_buffer *buf = cs->pipebufs;
-       struct address_space *mapping;
-       pgoff_t index;
 
        unlock_request(cs->fc, cs->req);
        fuse_copy_finish(cs);
@@ -724,9 +722,6 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)
        if (fuse_check_page(newpage) != 0)
                goto out_fallback_unlock;
 
-       mapping = oldpage->mapping;
-       index = oldpage->index;
-
        /*
         * This is a new and locked page, it shouldn't be mapped or
         * have any special flags on it
index e21d4d8f87e36e45daf71f044712710f12b23322..f3ab824fa302bcae5909adf5c676c4bbb9417743 100644 (file)
@@ -2177,8 +2177,8 @@ fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
        return ret;
 }
 
-long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
-                           loff_t length)
+static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
+                               loff_t length)
 {
        struct fuse_file *ff = file->private_data;
        struct fuse_conn *fc = ff->fc;
@@ -2213,7 +2213,6 @@ long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
 
        return err;
 }
-EXPORT_SYMBOL_GPL(fuse_file_fallocate);
 
 static const struct file_operations fuse_file_operations = {
        .llseek         = fuse_file_llseek,
index b906ed17a8391a99ffbc15d8ea62c6861be90689..9802de0f85e61fe061ea150d15177c39b4128ca3 100644 (file)
@@ -281,6 +281,7 @@ static void gdlm_put_lock(struct gfs2_glock *gl)
 {
        struct gfs2_sbd *sdp = gl->gl_sbd;
        struct lm_lockstruct *ls = &sdp->sd_lockstruct;
+       int lvb_needs_unlock = 0;
        int error;
 
        if (gl->gl_lksb.sb_lkid == 0) {
@@ -294,8 +295,12 @@ static void gdlm_put_lock(struct gfs2_glock *gl)
        gfs2_update_request_times(gl);
 
        /* don't want to skip dlm_unlock writing the lvb when lock is ex */
+
+       if (gl->gl_lksb.sb_lvbptr && (gl->gl_state == LM_ST_EXCLUSIVE))
+               lvb_needs_unlock = 1;
+
        if (test_bit(SDF_SKIP_DLM_UNLOCK, &sdp->sd_flags) &&
-           gl->gl_lksb.sb_lvbptr && (gl->gl_state != LM_ST_EXCLUSIVE)) {
+           !lvb_needs_unlock) {
                gfs2_glock_free(gl);
                return;
        }
index a2862339323b2a5f1e08dd7e25a779e1b683b828..81cc7eaff86321c2788c0aa83c236778e6fb4b70 100644 (file)
@@ -446,7 +446,8 @@ int __log_start_commit(journal_t *journal, tid_t target)
         * currently running transaction (if it exists).  Otherwise,
         * the target tid must be an old one.
         */
-       if (journal->j_running_transaction &&
+       if (journal->j_commit_request != target &&
+           journal->j_running_transaction &&
            journal->j_running_transaction->t_tid == target) {
                /*
                 * We want a new commit: OK, mark the request and wakeup the
index dd057bc6b65b28d3822229e0c0d5d0777b7955b9..fc8dc20fdeb9c90274d5acb8ef2bb831af297425 100644 (file)
@@ -177,11 +177,31 @@ out_nofree:
        return mnt;
 }
 
+static int
+nfs_namespace_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
+{
+       if (NFS_FH(dentry->d_inode)->size != 0)
+               return nfs_getattr(mnt, dentry, stat);
+       generic_fillattr(dentry->d_inode, stat);
+       return 0;
+}
+
+static int
+nfs_namespace_setattr(struct dentry *dentry, struct iattr *attr)
+{
+       if (NFS_FH(dentry->d_inode)->size != 0)
+               return nfs_setattr(dentry, attr);
+       return -EACCES;
+}
+
 const struct inode_operations nfs_mountpoint_inode_operations = {
        .getattr        = nfs_getattr,
+       .setattr        = nfs_setattr,
 };
 
 const struct inode_operations nfs_referral_inode_operations = {
+       .getattr        = nfs_namespace_getattr,
+       .setattr        = nfs_namespace_setattr,
 };
 
 static void nfs_expire_automounts(struct work_struct *work)
index acc3472681244d004f743a198e802709a38927b4..2e9779b58b7af6f3d143e60af3fba6abc8902a11 100644 (file)
@@ -236,11 +236,10 @@ struct nfs_client *nfs4_init_client(struct nfs_client *clp,
        error = nfs4_discover_server_trunking(clp, &old);
        if (error < 0)
                goto error;
+       nfs_put_client(clp);
        if (clp != old) {
                clp->cl_preserve_clid = true;
-               nfs_put_client(clp);
                clp = old;
-               atomic_inc(&clp->cl_count);
        }
 
        return clp;
@@ -306,7 +305,7 @@ int nfs40_walk_client_list(struct nfs_client *new,
                .clientid       = new->cl_clientid,
                .confirm        = new->cl_confirm,
        };
-       int status;
+       int status = -NFS4ERR_STALE_CLIENTID;
 
        spin_lock(&nn->nfs_client_lock);
        list_for_each_entry_safe(pos, n, &nn->nfs_client_list, cl_share_link) {
@@ -332,40 +331,33 @@ int nfs40_walk_client_list(struct nfs_client *new,
 
                if (prev)
                        nfs_put_client(prev);
+               prev = pos;
 
                status = nfs4_proc_setclientid_confirm(pos, &clid, cred);
-               if (status == 0) {
+               switch (status) {
+               case -NFS4ERR_STALE_CLIENTID:
+                       break;
+               case 0:
                        nfs4_swap_callback_idents(pos, new);
 
-                       nfs_put_client(pos);
+                       prev = NULL;
                        *result = pos;
                        dprintk("NFS: <-- %s using nfs_client = %p ({%d})\n",
                                __func__, pos, atomic_read(&pos->cl_count));
-                       return 0;
-               }
-               if (status != -NFS4ERR_STALE_CLIENTID) {
-                       nfs_put_client(pos);
-                       dprintk("NFS: <-- %s status = %d, no result\n",
-                               __func__, status);
-                       return status;
+               default:
+                       goto out;
                }
 
                spin_lock(&nn->nfs_client_lock);
-               prev = pos;
        }
+       spin_unlock(&nn->nfs_client_lock);
 
-       /*
-        * No matching nfs_client found.  This should be impossible,
-        * because the new nfs_client has already been added to
-        * nfs_client_list by nfs_get_client().
-        *
-        * Don't BUG(), since the caller is holding a mutex.
-        */
+       /* No match found. The server lost our clientid */
+out:
        if (prev)
                nfs_put_client(prev);
-       spin_unlock(&nn->nfs_client_lock);
-       pr_err("NFS: %s Error: no matching nfs_client found\n", __func__);
-       return -NFS4ERR_STALE_CLIENTID;
+       dprintk("NFS: <-- %s status = %d\n", __func__, status);
+       return status;
 }
 
 #ifdef CONFIG_NFS_V4_1
@@ -432,7 +424,7 @@ int nfs41_walk_client_list(struct nfs_client *new,
 {
        struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id);
        struct nfs_client *pos, *n, *prev = NULL;
-       int error;
+       int status = -NFS4ERR_STALE_CLIENTID;
 
        spin_lock(&nn->nfs_client_lock);
        list_for_each_entry_safe(pos, n, &nn->nfs_client_list, cl_share_link) {
@@ -448,14 +440,17 @@ int nfs41_walk_client_list(struct nfs_client *new,
                                nfs_put_client(prev);
                        prev = pos;
 
-                       error = nfs_wait_client_init_complete(pos);
-                       if (error < 0) {
+                       nfs4_schedule_lease_recovery(pos);
+                       status = nfs_wait_client_init_complete(pos);
+                       if (status < 0) {
                                nfs_put_client(pos);
                                spin_lock(&nn->nfs_client_lock);
                                continue;
                        }
-
+                       status = pos->cl_cons_state;
                        spin_lock(&nn->nfs_client_lock);
+                       if (status < 0)
+                               continue;
                }
 
                if (pos->rpc_ops != new->rpc_ops)
@@ -473,6 +468,7 @@ int nfs41_walk_client_list(struct nfs_client *new,
                if (!nfs4_match_serverowners(pos, new))
                        continue;
 
+               atomic_inc(&pos->cl_count);
                spin_unlock(&nn->nfs_client_lock);
                dprintk("NFS: <-- %s using nfs_client = %p ({%d})\n",
                        __func__, pos, atomic_read(&pos->cl_count));
@@ -481,16 +477,10 @@ int nfs41_walk_client_list(struct nfs_client *new,
                return 0;
        }
 
-       /*
-        * No matching nfs_client found.  This should be impossible,
-        * because the new nfs_client has already been added to
-        * nfs_client_list by nfs_get_client().
-        *
-        * Don't BUG(), since the caller is holding a mutex.
-        */
+       /* No matching nfs_client found. */
        spin_unlock(&nn->nfs_client_lock);
-       pr_err("NFS: %s Error: no matching nfs_client found\n", __func__);
-       return -NFS4ERR_STALE_CLIENTID;
+       dprintk("NFS: <-- %s status = %d\n", __func__, status);
+       return status;
 }
 #endif /* CONFIG_NFS_V4_1 */
 
index 9448c579d41a40068897f99cbc5db09bccf3e063..e61f68d5ef218dd64cdfec220bab02069cf1d72e 100644 (file)
@@ -136,16 +136,11 @@ int nfs40_discover_server_trunking(struct nfs_client *clp,
        clp->cl_confirm = clid.confirm;
 
        status = nfs40_walk_client_list(clp, result, cred);
-       switch (status) {
-       case -NFS4ERR_STALE_CLIENTID:
-               set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
-       case 0:
+       if (status == 0) {
                /* Sustain the lease, even if it's empty.  If the clientid4
                 * goes stale it's of no use for trunking discovery. */
                nfs4_schedule_state_renewal(*result);
-               break;
        }
-
 out:
        return status;
 }
@@ -1863,6 +1858,7 @@ again:
        case -ETIMEDOUT:
        case -EAGAIN:
                ssleep(1);
+       case -NFS4ERR_STALE_CLIENTID:
                dprintk("NFS: %s after status %d, retrying\n",
                        __func__, status);
                goto again;
@@ -2022,8 +2018,18 @@ static int nfs4_reset_session(struct nfs_client *clp)
        nfs4_begin_drain_session(clp);
        cred = nfs4_get_exchange_id_cred(clp);
        status = nfs4_proc_destroy_session(clp->cl_session, cred);
-       if (status && status != -NFS4ERR_BADSESSION &&
-           status != -NFS4ERR_DEADSESSION) {
+       switch (status) {
+       case 0:
+       case -NFS4ERR_BADSESSION:
+       case -NFS4ERR_DEADSESSION:
+               break;
+       case -NFS4ERR_BACK_CHAN_BUSY:
+       case -NFS4ERR_DELAY:
+               set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
+               status = 0;
+               ssleep(1);
+               goto out;
+       default:
                status = nfs4_recovery_handle_error(clp, status);
                goto out;
        }
index 2e7e8c878e5d157418e91a3ce742b8810168c5d3..b056b1628722218bbbadce33e86255e6953c8994 100644 (file)
@@ -2589,27 +2589,23 @@ nfs_xdev_mount(struct file_system_type *fs_type, int flags,
        struct nfs_server *server;
        struct dentry *mntroot = ERR_PTR(-ENOMEM);
        struct nfs_subversion *nfs_mod = NFS_SB(data->sb)->nfs_client->cl_nfs_mod;
-       int error;
 
-       dprintk("--> nfs_xdev_mount_common()\n");
+       dprintk("--> nfs_xdev_mount()\n");
 
        mount_info.mntfh = mount_info.cloned->fh;
 
        /* create a new volume representation */
        server = nfs_mod->rpc_ops->clone_server(NFS_SB(data->sb), data->fh, data->fattr, data->authflavor);
-       if (IS_ERR(server)) {
-               error = PTR_ERR(server);
-               goto out_err;
-       }
 
-       mntroot = nfs_fs_mount_common(server, flags, dev_name, &mount_info, nfs_mod);
-       dprintk("<-- nfs_xdev_mount_common() = 0\n");
-out:
-       return mntroot;
+       if (IS_ERR(server))
+               mntroot = ERR_CAST(server);
+       else
+               mntroot = nfs_fs_mount_common(server, flags,
+                               dev_name, &mount_info, nfs_mod);
 
-out_err:
-       dprintk("<-- nfs_xdev_mount_common() = %d [error]\n", error);
-       goto out;
+       dprintk("<-- nfs_xdev_mount() = %ld\n",
+                       IS_ERR(mntroot) ? PTR_ERR(mntroot) : 0L);
+       return mntroot;
 }
 
 #if IS_ENABLED(CONFIG_NFS_V4)
index fdb180769485f95c78a0a5b5f33d5752baf0590f..f3859354e41a97afcdb1c4f8fdec4d9e46f09666 100644 (file)
@@ -664,8 +664,11 @@ static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp,
        if (ret < 0)
                printk(KERN_ERR "NILFS: GC failed during preparation: "
                        "cannot read source blocks: err=%d\n", ret);
-       else
+       else {
+               if (nilfs_sb_need_update(nilfs))
+                       set_nilfs_discontinued(nilfs);
                ret = nilfs_clean_segments(inode->i_sb, argv, kbufs);
+       }
 
        nilfs_remove_all_gcinodes(nilfs);
        clear_nilfs_gc_running(nilfs);
index 9d863fb501f92ab9a9207fcce7d8b2087c637c0e..f2bc3dfd0b883585e1a2afe6734de2db6be77004 100644 (file)
@@ -296,7 +296,7 @@ EXPORT_SYMBOL(seq_read);
  *     seq_lseek -     ->llseek() method for sequential files.
  *     @file: the file in question
  *     @offset: new position
- *     @origin: 0 for absolute, 1 for relative position
+ *     @whence: 0 for absolute, 1 for relative position
  *
  *     Ready-made ->f_op->llseek()
  */
index d44fb568abe1a945c014a03c88648cd847906cc0..e9be396a558d944fde7086cce64d22c672cdc5b7 100644 (file)
@@ -307,7 +307,8 @@ static void udf_sb_free_partitions(struct super_block *sb)
 {
        struct udf_sb_info *sbi = UDF_SB(sb);
        int i;
-
+       if (sbi->s_partmaps == NULL)
+               return;
        for (i = 0; i < sbi->s_partitions; i++)
                udf_free_partition(&sbi->s_partmaps[i]);
        kfree(sbi->s_partmaps);
index 4111a40ebe1a17dde31f89e5154c0d257c6e5d6c..5f707e5371717a331131f72f5babff24670d9bef 100644 (file)
@@ -86,11 +86,11 @@ xfs_destroy_ioend(
        }
 
        if (ioend->io_iocb) {
+               inode_dio_done(ioend->io_inode);
                if (ioend->io_isasync) {
                        aio_complete(ioend->io_iocb, ioend->io_error ?
                                        ioend->io_error : ioend->io_result, 0);
                }
-               inode_dio_done(ioend->io_inode);
        }
 
        mempool_free(ioend, xfs_ioend_pool);
index 0e92d12765d2670146b0325cd1215b449fb60dfe..cdb2d33485837bcd314f380f6d0c6737424626ec 100644 (file)
@@ -4680,9 +4680,6 @@ __xfs_bmapi_allocate(
                        return error;
        }
 
-       if (bma->flags & XFS_BMAPI_STACK_SWITCH)
-               bma->stack_switch = 1;
-
        error = xfs_bmap_alloc(bma);
        if (error)
                return error;
@@ -4956,6 +4953,9 @@ xfs_bmapi_write(
        bma.flist = flist;
        bma.firstblock = firstblock;
 
+       if (flags & XFS_BMAPI_STACK_SWITCH)
+               bma.stack_switch = 1;
+
        while (bno < end && n < *nmap) {
                inhole = eof || bma.got.br_startoff > bno;
                wasdelay = !inhole && isnullstartblock(bma.got.br_startblock);
index 26673a0b20e7249a149b22357c0961f86a6ac903..fbbb9eb92e32a7b38ec32c954313cffdb39a8508 100644 (file)
@@ -175,7 +175,7 @@ xfs_buf_get_maps(
        bp->b_map_count = map_count;
 
        if (map_count == 1) {
-               bp->b_maps = &bp->b_map;
+               bp->b_maps = &bp->__b_map;
                return 0;
        }
 
@@ -193,7 +193,7 @@ static void
 xfs_buf_free_maps(
        struct xfs_buf  *bp)
 {
-       if (bp->b_maps != &bp->b_map) {
+       if (bp->b_maps != &bp->__b_map) {
                kmem_free(bp->b_maps);
                bp->b_maps = NULL;
        }
@@ -377,8 +377,8 @@ xfs_buf_allocate_memory(
        }
 
 use_alloc_page:
-       start = BBTOB(bp->b_map.bm_bn) >> PAGE_SHIFT;
-       end = (BBTOB(bp->b_map.bm_bn + bp->b_length) + PAGE_SIZE - 1)
+       start = BBTOB(bp->b_maps[0].bm_bn) >> PAGE_SHIFT;
+       end = (BBTOB(bp->b_maps[0].bm_bn + bp->b_length) + PAGE_SIZE - 1)
                                                                >> PAGE_SHIFT;
        page_count = end - start;
        error = _xfs_buf_get_pages(bp, page_count, flags);
@@ -487,6 +487,7 @@ _xfs_buf_find(
        struct rb_node          *parent;
        xfs_buf_t               *bp;
        xfs_daddr_t             blkno = map[0].bm_bn;
+       xfs_daddr_t             eofs;
        int                     numblks = 0;
        int                     i;
 
@@ -498,6 +499,23 @@ _xfs_buf_find(
        ASSERT(!(numbytes < (1 << btp->bt_sshift)));
        ASSERT(!(BBTOB(blkno) & (xfs_off_t)btp->bt_smask));
 
+       /*
+        * Corrupted block numbers can get through to here, unfortunately, so we
+        * have to check that the buffer falls within the filesystem bounds.
+        */
+       eofs = XFS_FSB_TO_BB(btp->bt_mount, btp->bt_mount->m_sb.sb_dblocks);
+       if (blkno >= eofs) {
+               /*
+                * XXX (dgc): we should really be returning EFSCORRUPTED here,
+                * but none of the higher level infrastructure supports
+                * returning a specific error on buffer lookup failures.
+                */
+               xfs_alert(btp->bt_mount,
+                         "%s: Block out of range: block 0x%llx, EOFS 0x%llx ",
+                         __func__, blkno, eofs);
+               return NULL;
+       }
+
        /* get tree root */
        pag = xfs_perag_get(btp->bt_mount,
                                xfs_daddr_to_agno(btp->bt_mount, blkno));
@@ -640,7 +658,7 @@ _xfs_buf_read(
        xfs_buf_flags_t         flags)
 {
        ASSERT(!(flags & XBF_WRITE));
-       ASSERT(bp->b_map.bm_bn != XFS_BUF_DADDR_NULL);
+       ASSERT(bp->b_maps[0].bm_bn != XFS_BUF_DADDR_NULL);
 
        bp->b_flags &= ~(XBF_WRITE | XBF_ASYNC | XBF_READ_AHEAD);
        bp->b_flags |= flags & (XBF_READ | XBF_ASYNC | XBF_READ_AHEAD);
@@ -1487,6 +1505,8 @@ restart:
        while (!list_empty(&btp->bt_lru)) {
                bp = list_first_entry(&btp->bt_lru, struct xfs_buf, b_lru);
                if (atomic_read(&bp->b_hold) > 1) {
+                       trace_xfs_buf_wait_buftarg(bp, _RET_IP_);
+                       list_move_tail(&bp->b_lru, &btp->bt_lru);
                        spin_unlock(&btp->bt_lru_lock);
                        delay(100);
                        goto restart;
@@ -1709,7 +1729,7 @@ xfs_buf_cmp(
        struct xfs_buf  *bp = container_of(b, struct xfs_buf, b_list);
        xfs_daddr_t             diff;
 
-       diff = ap->b_map.bm_bn - bp->b_map.bm_bn;
+       diff = ap->b_maps[0].bm_bn - bp->b_maps[0].bm_bn;
        if (diff < 0)
                return -1;
        if (diff > 0)
index 23f5642480bb1ea5f0f8a42423c607e7d61e9b83..433a12ed7b179e44ef784e8a144346af4faa9852 100644 (file)
@@ -151,7 +151,7 @@ typedef struct xfs_buf {
        struct page             **b_pages;      /* array of page pointers */
        struct page             *b_page_array[XB_PAGES]; /* inline pages */
        struct xfs_buf_map      *b_maps;        /* compound buffer map */
-       struct xfs_buf_map      b_map;          /* inline compound buffer map */
+       struct xfs_buf_map      __b_map;        /* inline compound buffer map */
        int                     b_map_count;
        int                     b_io_length;    /* IO size in BBs */
        atomic_t                b_pin_count;    /* pin count */
@@ -330,8 +330,8 @@ void xfs_buf_stale(struct xfs_buf *bp);
  * In future, uncached buffers will pass the block number directly to the io
  * request function and hence these macros will go away at that point.
  */
-#define XFS_BUF_ADDR(bp)               ((bp)->b_map.bm_bn)
-#define XFS_BUF_SET_ADDR(bp, bno)      ((bp)->b_map.bm_bn = (xfs_daddr_t)(bno))
+#define XFS_BUF_ADDR(bp)               ((bp)->b_maps[0].bm_bn)
+#define XFS_BUF_SET_ADDR(bp, bno)      ((bp)->b_maps[0].bm_bn = (xfs_daddr_t)(bno))
 
 static inline void xfs_buf_set_ref(struct xfs_buf *bp, int lru_ref)
 {
index becf4a97efc65c95240e45c1b05b751919d51317..3f9949fee391b11cdfd73c12799cccba4ce1b56b 100644 (file)
@@ -71,7 +71,7 @@ xfs_buf_item_log_debug(
                chunk_num = byte >> XFS_BLF_SHIFT;
                word_num = chunk_num >> BIT_TO_WORD_SHIFT;
                bit_num = chunk_num & (NBWORD - 1);
-               wordp = &(bip->bli_format.blf_data_map[word_num]);
+               wordp = &(bip->__bli_format.blf_data_map[word_num]);
                bit_set = *wordp & (1 << bit_num);
                ASSERT(bit_set);
                byte++;
@@ -237,7 +237,7 @@ xfs_buf_item_size(
                 * cancel flag in it.
                 */
                trace_xfs_buf_item_size_stale(bip);
-               ASSERT(bip->bli_format.blf_flags & XFS_BLF_CANCEL);
+               ASSERT(bip->__bli_format.blf_flags & XFS_BLF_CANCEL);
                return bip->bli_format_count;
        }
 
@@ -278,7 +278,7 @@ xfs_buf_item_format_segment(
        uint            buffer_offset;
 
        /* copy the flags across from the base format item */
-       blfp->blf_flags = bip->bli_format.blf_flags;
+       blfp->blf_flags = bip->__bli_format.blf_flags;
 
        /*
         * Base size is the actual size of the ondisk structure - it reflects
@@ -287,6 +287,17 @@ xfs_buf_item_format_segment(
         */
        base_size = offsetof(struct xfs_buf_log_format, blf_data_map) +
                        (blfp->blf_map_size * sizeof(blfp->blf_data_map[0]));
+
+       nvecs = 0;
+       first_bit = xfs_next_bit(blfp->blf_data_map, blfp->blf_map_size, 0);
+       if (!(bip->bli_flags & XFS_BLI_STALE) && first_bit == -1) {
+               /*
+                * If the map is not be dirty in the transaction, mark
+                * the size as zero and do not advance the vector pointer.
+                */
+               goto out;
+       }
+
        vecp->i_addr = blfp;
        vecp->i_len = base_size;
        vecp->i_type = XLOG_REG_TYPE_BFORMAT;
@@ -301,15 +312,13 @@ xfs_buf_item_format_segment(
                 */
                trace_xfs_buf_item_format_stale(bip);
                ASSERT(blfp->blf_flags & XFS_BLF_CANCEL);
-               blfp->blf_size = nvecs;
-               return vecp;
+               goto out;
        }
 
        /*
         * Fill in an iovec for each set of contiguous chunks.
         */
-       first_bit = xfs_next_bit(blfp->blf_data_map, blfp->blf_map_size, 0);
-       ASSERT(first_bit != -1);
+
        last_bit = first_bit;
        nbits = 1;
        for (;;) {
@@ -371,7 +380,8 @@ xfs_buf_item_format_segment(
                        nbits++;
                }
        }
-       bip->bli_format.blf_size = nvecs;
+out:
+       blfp->blf_size = nvecs;
        return vecp;
 }
 
@@ -405,7 +415,7 @@ xfs_buf_item_format(
        if (bip->bli_flags & XFS_BLI_INODE_BUF) {
                if (!((bip->bli_flags & XFS_BLI_INODE_ALLOC_BUF) &&
                      xfs_log_item_in_current_chkpt(lip)))
-                       bip->bli_format.blf_flags |= XFS_BLF_INODE_BUF;
+                       bip->__bli_format.blf_flags |= XFS_BLF_INODE_BUF;
                bip->bli_flags &= ~XFS_BLI_INODE_BUF;
        }
 
@@ -485,7 +495,7 @@ xfs_buf_item_unpin(
                ASSERT(bip->bli_flags & XFS_BLI_STALE);
                ASSERT(xfs_buf_islocked(bp));
                ASSERT(XFS_BUF_ISSTALE(bp));
-               ASSERT(bip->bli_format.blf_flags & XFS_BLF_CANCEL);
+               ASSERT(bip->__bli_format.blf_flags & XFS_BLF_CANCEL);
 
                trace_xfs_buf_item_unpin_stale(bip);
 
@@ -601,7 +611,7 @@ xfs_buf_item_unlock(
 {
        struct xfs_buf_log_item *bip = BUF_ITEM(lip);
        struct xfs_buf          *bp = bip->bli_buf;
-       int                     aborted;
+       int                     aborted, clean, i;
        uint                    hold;
 
        /* Clear the buffer's association with this transaction. */
@@ -631,7 +641,7 @@ xfs_buf_item_unlock(
         */
        if (bip->bli_flags & XFS_BLI_STALE) {
                trace_xfs_buf_item_unlock_stale(bip);
-               ASSERT(bip->bli_format.blf_flags & XFS_BLF_CANCEL);
+               ASSERT(bip->__bli_format.blf_flags & XFS_BLF_CANCEL);
                if (!aborted) {
                        atomic_dec(&bip->bli_refcount);
                        return;
@@ -642,12 +652,27 @@ xfs_buf_item_unlock(
 
        /*
         * If the buf item isn't tracking any data, free it, otherwise drop the
-        * reference we hold to it.
+        * reference we hold to it. If we are aborting the transaction, this may
+        * be the only reference to the buf item, so we free it anyway
+        * regardless of whether it is dirty or not. A dirty abort implies a
+        * shutdown, anyway.
         */
-       if (xfs_bitmap_empty(bip->bli_format.blf_data_map,
-                            bip->bli_format.blf_map_size))
+       clean = 1;
+       for (i = 0; i < bip->bli_format_count; i++) {
+               if (!xfs_bitmap_empty(bip->bli_formats[i].blf_data_map,
+                            bip->bli_formats[i].blf_map_size)) {
+                       clean = 0;
+                       break;
+               }
+       }
+       if (clean)
                xfs_buf_item_relse(bp);
-       else
+       else if (aborted) {
+               if (atomic_dec_and_test(&bip->bli_refcount)) {
+                       ASSERT(XFS_FORCED_SHUTDOWN(lip->li_mountp));
+                       xfs_buf_item_relse(bp);
+               }
+       } else
                atomic_dec(&bip->bli_refcount);
 
        if (!hold)
@@ -716,7 +741,7 @@ xfs_buf_item_get_format(
        bip->bli_format_count = count;
 
        if (count == 1) {
-               bip->bli_formats = &bip->bli_format;
+               bip->bli_formats = &bip->__bli_format;
                return 0;
        }
 
@@ -731,7 +756,7 @@ STATIC void
 xfs_buf_item_free_format(
        struct xfs_buf_log_item *bip)
 {
-       if (bip->bli_formats != &bip->bli_format) {
+       if (bip->bli_formats != &bip->__bli_format) {
                kmem_free(bip->bli_formats);
                bip->bli_formats = NULL;
        }
index 6850f49f4af3f60c0a5a8ef9508f7bac9af00e4c..16def435944ac79063cd3f48bcf8067cc2d270c5 100644 (file)
@@ -104,7 +104,7 @@ typedef struct xfs_buf_log_item {
 #endif
        int                     bli_format_count;       /* count of headers */
        struct xfs_buf_log_format *bli_formats; /* array of in-log header ptrs */
-       struct xfs_buf_log_format bli_format;   /* embedded in-log header */
+       struct xfs_buf_log_format __bli_format; /* embedded in-log header */
 } xfs_buf_log_item_t;
 
 void   xfs_buf_item_init(struct xfs_buf *, struct xfs_mount *);
index d0e9c74d3d96a75c18d40f175efd51eec85aab18..a8bd26b82ecb00dd14063da4c62c1a64011f0caa 100644 (file)
@@ -246,10 +246,10 @@ xfs_swap_extents(
                goto out_unlock;
        }
 
-       error = -filemap_write_and_wait(VFS_I(ip)->i_mapping);
+       error = -filemap_write_and_wait(VFS_I(tip)->i_mapping);
        if (error)
                goto out_unlock;
-       truncate_pagecache_range(VFS_I(ip), 0, -1);
+       truncate_pagecache_range(VFS_I(tip), 0, -1);
 
        /* Verify O_DIRECT for ftmp */
        if (VN_CACHED(VFS_I(tip)) != 0) {
index 7536faaa61e7852175f7b7c20f1e4b0c566c24db..12afe07a91d71ddc003a763674d7d1ee45a992d3 100644 (file)
@@ -355,10 +355,12 @@ xfs_dir2_block_addname(
        /*
         * If need to compact the leaf entries, do it now.
         */
-       if (compact)
+       if (compact) {
                xfs_dir2_block_compact(tp, bp, hdr, btp, blp, &needlog,
                                      &lfloghigh, &lfloglow);
-       else if (btp->stale) {
+               /* recalculate blp post-compaction */
+               blp = xfs_dir2_block_leaf_p(btp);
+       } else if (btp->stale) {
                /*
                 * Set leaf logging boundaries to impossible state.
                 * For the no-stale case they're set explicitly.
index add06b4e9a635511afc3e2716836e210ff794c46..364818eef40e55720a6a4509afbde63a1b48cd05 100644 (file)
@@ -351,6 +351,15 @@ xfs_iomap_prealloc_size(
                }
                if (shift)
                        alloc_blocks >>= shift;
+
+               /*
+                * If we are still trying to allocate more space than is
+                * available, squash the prealloc hard. This can happen if we
+                * have a large file on a small filesystem and the above
+                * lowspace thresholds are smaller than MAXEXTLEN.
+                */
+               while (alloc_blocks >= freesp)
+                       alloc_blocks >>= 4;
        }
 
        if (alloc_blocks < mp->m_writeio_blocks)
index da508463ff1006b7b4b5371d5f2c19495cb5d44b..7d6df7c00c36fb22e8f864285c6d2352b44a7cdd 100644 (file)
@@ -658,7 +658,7 @@ xfs_sb_quiet_read_verify(
                return;
        }
        /* quietly fail */
-       xfs_buf_ioerror(bp, EFSCORRUPTED);
+       xfs_buf_ioerror(bp, EWRONGFS);
 }
 
 static void
index 5f53e75409b8f45ad919aae17b24cf92c5218a06..8a59f8546552e9577b3a93c44b8cfb7251e3561a 100644 (file)
@@ -784,11 +784,11 @@ xfs_qm_scall_getquota(
             (XFS_IS_OQUOTA_ENFORCED(mp) &&
                        (dst->d_flags & (FS_PROJ_QUOTA | FS_GROUP_QUOTA)))) &&
            dst->d_id != 0) {
-               if (((int) dst->d_bcount > (int) dst->d_blk_softlimit) &&
+               if ((dst->d_bcount > dst->d_blk_softlimit) &&
                    (dst->d_blk_softlimit > 0)) {
                        ASSERT(dst->d_btimer != 0);
                }
-               if (((int) dst->d_icount > (int) dst->d_ino_softlimit) &&
+               if ((dst->d_icount > dst->d_ino_softlimit) &&
                    (dst->d_ino_softlimit > 0)) {
                        ASSERT(dst->d_itimer != 0);
                }
index 2e137d4a85ae66bc3a9172a4d93f734ba7aabb99..16a812977eab3d8fcf001e0175e6cfd773dcfe67 100644 (file)
@@ -341,6 +341,7 @@ DEFINE_BUF_EVENT(xfs_buf_item_relse);
 DEFINE_BUF_EVENT(xfs_buf_item_iodone);
 DEFINE_BUF_EVENT(xfs_buf_item_iodone_async);
 DEFINE_BUF_EVENT(xfs_buf_error_relse);
+DEFINE_BUF_EVENT(xfs_buf_wait_buftarg);
 DEFINE_BUF_EVENT(xfs_trans_read_buf_io);
 DEFINE_BUF_EVENT(xfs_trans_read_buf_shut);
 
index 4fc17d479d42301d13cc86bfd2a8aec194fecea0..3edf5dbee001c60239a11254dbd697c7602e9b06 100644 (file)
@@ -93,7 +93,7 @@ _xfs_trans_bjoin(
        xfs_buf_item_init(bp, tp->t_mountp);
        bip = bp->b_fspriv;
        ASSERT(!(bip->bli_flags & XFS_BLI_STALE));
-       ASSERT(!(bip->bli_format.blf_flags & XFS_BLF_CANCEL));
+       ASSERT(!(bip->__bli_format.blf_flags & XFS_BLF_CANCEL));
        ASSERT(!(bip->bli_flags & XFS_BLI_LOGGED));
        if (reset_recur)
                bip->bli_recur = 0;
@@ -432,7 +432,7 @@ xfs_trans_brelse(xfs_trans_t        *tp,
        bip = bp->b_fspriv;
        ASSERT(bip->bli_item.li_type == XFS_LI_BUF);
        ASSERT(!(bip->bli_flags & XFS_BLI_STALE));
-       ASSERT(!(bip->bli_format.blf_flags & XFS_BLF_CANCEL));
+       ASSERT(!(bip->__bli_format.blf_flags & XFS_BLF_CANCEL));
        ASSERT(atomic_read(&bip->bli_refcount) > 0);
 
        trace_xfs_trans_brelse(bip);
@@ -519,7 +519,7 @@ xfs_trans_bhold(xfs_trans_t *tp,
        ASSERT(bp->b_transp == tp);
        ASSERT(bip != NULL);
        ASSERT(!(bip->bli_flags & XFS_BLI_STALE));
-       ASSERT(!(bip->bli_format.blf_flags & XFS_BLF_CANCEL));
+       ASSERT(!(bip->__bli_format.blf_flags & XFS_BLF_CANCEL));
        ASSERT(atomic_read(&bip->bli_refcount) > 0);
 
        bip->bli_flags |= XFS_BLI_HOLD;
@@ -539,7 +539,7 @@ xfs_trans_bhold_release(xfs_trans_t *tp,
        ASSERT(bp->b_transp == tp);
        ASSERT(bip != NULL);
        ASSERT(!(bip->bli_flags & XFS_BLI_STALE));
-       ASSERT(!(bip->bli_format.blf_flags & XFS_BLF_CANCEL));
+       ASSERT(!(bip->__bli_format.blf_flags & XFS_BLF_CANCEL));
        ASSERT(atomic_read(&bip->bli_refcount) > 0);
        ASSERT(bip->bli_flags & XFS_BLI_HOLD);
 
@@ -598,7 +598,7 @@ xfs_trans_log_buf(xfs_trans_t       *tp,
                bip->bli_flags &= ~XFS_BLI_STALE;
                ASSERT(XFS_BUF_ISSTALE(bp));
                XFS_BUF_UNSTALE(bp);
-               bip->bli_format.blf_flags &= ~XFS_BLF_CANCEL;
+               bip->__bli_format.blf_flags &= ~XFS_BLF_CANCEL;
        }
 
        tp->t_flags |= XFS_TRANS_DIRTY;
@@ -643,6 +643,7 @@ xfs_trans_binval(
        xfs_buf_t       *bp)
 {
        xfs_buf_log_item_t      *bip = bp->b_fspriv;
+       int                     i;
 
        ASSERT(bp->b_transp == tp);
        ASSERT(bip != NULL);
@@ -657,8 +658,8 @@ xfs_trans_binval(
                 */
                ASSERT(XFS_BUF_ISSTALE(bp));
                ASSERT(!(bip->bli_flags & (XFS_BLI_LOGGED | XFS_BLI_DIRTY)));
-               ASSERT(!(bip->bli_format.blf_flags & XFS_BLF_INODE_BUF));
-               ASSERT(bip->bli_format.blf_flags & XFS_BLF_CANCEL);
+               ASSERT(!(bip->__bli_format.blf_flags & XFS_BLF_INODE_BUF));
+               ASSERT(bip->__bli_format.blf_flags & XFS_BLF_CANCEL);
                ASSERT(bip->bli_item.li_desc->lid_flags & XFS_LID_DIRTY);
                ASSERT(tp->t_flags & XFS_TRANS_DIRTY);
                return;
@@ -668,10 +669,12 @@ xfs_trans_binval(
 
        bip->bli_flags |= XFS_BLI_STALE;
        bip->bli_flags &= ~(XFS_BLI_INODE_BUF | XFS_BLI_LOGGED | XFS_BLI_DIRTY);
-       bip->bli_format.blf_flags &= ~XFS_BLF_INODE_BUF;
-       bip->bli_format.blf_flags |= XFS_BLF_CANCEL;
-       memset((char *)(bip->bli_format.blf_data_map), 0,
-             (bip->bli_format.blf_map_size * sizeof(uint)));
+       bip->__bli_format.blf_flags &= ~XFS_BLF_INODE_BUF;
+       bip->__bli_format.blf_flags |= XFS_BLF_CANCEL;
+       for (i = 0; i < bip->bli_format_count; i++) {
+               memset(bip->bli_formats[i].blf_data_map, 0,
+                      (bip->bli_formats[i].blf_map_size * sizeof(uint)));
+       }
        bip->bli_item.li_desc->lid_flags |= XFS_LID_DIRTY;
        tp->t_flags |= XFS_TRANS_DIRTY;
 }
@@ -775,5 +778,5 @@ xfs_trans_dquot_buf(
               type == XFS_BLF_GDQUOT_BUF);
        ASSERT(atomic_read(&bip->bli_refcount) > 0);
 
-       bip->bli_format.blf_flags |= type;
+       bip->__bli_format.blf_flags |= type;
 }
index ccf7b4f34a3c4e070fb44cd7456c859ee5c3c645..6c32af918c2f1dc257fea3613bcf5bca8137d9ec 100644 (file)
@@ -16,6 +16,22 @@ extern void
 dma_free_coherent(struct device *dev, size_t size, void *cpu_addr,
                    dma_addr_t dma_handle);
 
+static inline void *dma_alloc_attrs(struct device *dev, size_t size,
+                                   dma_addr_t *dma_handle, gfp_t flag,
+                                   struct dma_attrs *attrs)
+{
+       /* attrs is not supported and ignored */
+       return dma_alloc_coherent(dev, size, dma_handle, flag);
+}
+
+static inline void dma_free_attrs(struct device *dev, size_t size,
+                                 void *cpu_addr, dma_addr_t dma_handle,
+                                 struct dma_attrs *attrs)
+{
+       /* attrs is not supported and ignored */
+       dma_free_coherent(dev, size, cpu_addr, dma_handle);
+}
+
 #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
 #define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
 
index 701beab27aab71c94031151d9daaf6090f570b99..5cf680a98f9bcb7494a2c0a4bc557fca00b84296 100644 (file)
@@ -461,10 +461,8 @@ static inline int is_zero_pfn(unsigned long pfn)
        return offset_from_zero_pfn <= (zero_page_mask >> PAGE_SHIFT);
 }
 
-static inline unsigned long my_zero_pfn(unsigned long addr)
-{
-       return page_to_pfn(ZERO_PAGE(addr));
-}
+#define my_zero_pfn(addr)      page_to_pfn(ZERO_PAGE(addr))
+
 #else
 static inline int is_zero_pfn(unsigned long pfn)
 {
index 58f466ff00d363701155427cb7968515ef60c935..1db51b8524e9fb86fcc71e797f95a6518c259bfc 100644 (file)
@@ -21,10 +21,12 @@ asmlinkage long sys_mmap(unsigned long addr, unsigned long len,
                        unsigned long fd, off_t pgoff);
 #endif
 
+#ifndef CONFIG_GENERIC_SIGALTSTACK
 #ifndef sys_sigaltstack
 asmlinkage long sys_sigaltstack(const stack_t __user *, stack_t __user *,
                        struct pt_regs *);
 #endif
+#endif
 
 #ifndef sys_rt_sigreturn
 asmlinkage long sys_rt_sigreturn(struct pt_regs *regs);
index fad21c927a3884e7b7c230f8cea24263f2ed73a9..2d94d7413d71cc02483bae57219007bb6f30dd28 100644 (file)
@@ -85,6 +85,9 @@ struct module;
 struct drm_file;
 struct drm_device;
 
+struct device_node;
+struct videomode;
+
 #include <drm/drm_os_linux.h>
 #include <drm/drm_hashtab.h>
 #include <drm/drm_mm.h>
@@ -446,7 +449,15 @@ struct drm_file {
        int is_master; /* this file private is a master for a minor */
        struct drm_master *master; /* master this node is currently associated with
                                      N.B. not always minor->master */
+
+       /**
+        * fbs - List of framebuffers associated with this file.
+        *
+        * Protected by fbs_lock. Note that the fbs list holds a reference on
+        * the fb object to prevent it from untimely disappearing.
+        */
        struct list_head fbs;
+       struct mutex fbs_lock;
 
        wait_queue_head_t event_wait;
        struct list_head event_list;
@@ -919,6 +930,14 @@ struct drm_driver {
        /* import dmabuf -> GEM */
        struct drm_gem_object * (*gem_prime_import)(struct drm_device *dev,
                                struct dma_buf *dma_buf);
+       /* low-level interface used by drm_gem_prime_{import,export} */
+       int (*gem_prime_pin)(struct drm_gem_object *obj);
+       struct sg_table *(*gem_prime_get_sg_table)(struct drm_gem_object *obj);
+       struct drm_gem_object *(*gem_prime_import_sg_table)(
+                               struct drm_device *dev, size_t size,
+                               struct sg_table *sgt);
+       void *(*gem_prime_vmap)(struct drm_gem_object *obj);
+       void (*gem_prime_vunmap)(struct drm_gem_object *obj, void *vaddr);
 
        /* vga arb irq handler */
        void (*vgaarb_irq)(struct drm_device *dev, bool state);
@@ -1276,6 +1295,11 @@ static inline int drm_device_is_unplugged(struct drm_device *dev)
        return ret;
 }
 
+static inline bool drm_modeset_is_locked(struct drm_device *dev)
+{
+       return mutex_is_locked(&dev->mode_config.mutex);
+}
+
 /******************************************************************/
 /** \name Internal function definitions */
 /*@{*/
@@ -1456,6 +1480,12 @@ extern struct drm_display_mode *
 drm_mode_create_from_cmdline_mode(struct drm_device *dev,
                                  struct drm_cmdline_mode *cmd);
 
+extern int drm_display_mode_from_videomode(const struct videomode *vm,
+                                          struct drm_display_mode *dmode);
+extern int of_get_drm_display_mode(struct device_node *np,
+                                  struct drm_display_mode *dmode,
+                                  int index);
+
 /* Modesetting support */
 extern void drm_vblank_pre_modeset(struct drm_device *dev, int crtc);
 extern void drm_vblank_post_modeset(struct drm_device *dev, int crtc);
@@ -1540,9 +1570,13 @@ extern int drm_clients_info(struct seq_file *m, void* data);
 extern int drm_gem_name_info(struct seq_file *m, void *data);
 
 
+extern struct dma_buf *drm_gem_prime_export(struct drm_device *dev,
+               struct drm_gem_object *obj, int flags);
 extern int drm_gem_prime_handle_to_fd(struct drm_device *dev,
                struct drm_file *file_priv, uint32_t handle, uint32_t flags,
                int *prime_fd);
+extern struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev,
+               struct dma_buf *dma_buf);
 extern int drm_gem_prime_fd_to_handle(struct drm_device *dev,
                struct drm_file *file_priv, int prime_fd, uint32_t *handle);
 
index 00d78b5161c025b51fc131cf2cdb98a6359fe24c..8b7762728639ad7e3a5ec0f8ba4ab5c1bac1ea3e 100644 (file)
@@ -254,6 +254,10 @@ struct drm_framebuffer {
         * userspace perspective.
         */
        struct kref refcount;
+       /*
+        * Place on the dev->mode_config.fb_list, access protected by
+        * dev->mode_config.fb_lock.
+        */
        struct list_head head;
        struct drm_mode_object base;
        const struct drm_framebuffer_funcs *funcs;
@@ -390,6 +394,15 @@ struct drm_crtc {
        struct drm_device *dev;
        struct list_head head;
 
+       /**
+        * crtc mutex
+        *
+        * This provides a read lock for the overall crtc state (mode, dpms
+        * state, ...) and a write lock for everything which can be update
+        * without a full modeset (fb, cursor data, ...)
+        */
+       struct mutex mutex;
+
        struct drm_mode_object base;
 
        /* framebuffer the connector is currently bound to */
@@ -771,8 +784,18 @@ struct drm_mode_config {
        struct mutex idr_mutex; /* for IDR management */
        struct idr crtc_idr; /* use this idr for all IDs, fb, crtc, connector, modes - just makes life easier */
        /* this is limited to one for now */
+
+
+       /**
+        * fb_lock - mutex to protect fb state
+        *
+        * Besides the global fb list his also protects the fbs list in the
+        * file_priv
+        */
+       struct mutex fb_lock;
        int num_fb;
        struct list_head fb_list;
+
        int num_connector;
        struct list_head connector_list;
        int num_encoder;
@@ -842,6 +865,10 @@ struct drm_prop_enum_list {
        char *name;
 };
 
+extern void drm_modeset_lock_all(struct drm_device *dev);
+extern void drm_modeset_unlock_all(struct drm_device *dev);
+extern void drm_warn_on_modeset_not_all_locked(struct drm_device *dev);
+
 extern int drm_crtc_init(struct drm_device *dev,
                         struct drm_crtc *crtc,
                         const struct drm_crtc_funcs *funcs);
@@ -932,10 +959,13 @@ extern void drm_framebuffer_set_object(struct drm_device *dev,
 extern int drm_framebuffer_init(struct drm_device *dev,
                                struct drm_framebuffer *fb,
                                const struct drm_framebuffer_funcs *funcs);
+extern struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
+                                                     uint32_t id);
 extern void drm_framebuffer_unreference(struct drm_framebuffer *fb);
 extern void drm_framebuffer_reference(struct drm_framebuffer *fb);
 extern void drm_framebuffer_remove(struct drm_framebuffer *fb);
 extern void drm_framebuffer_cleanup(struct drm_framebuffer *fb);
+extern void drm_framebuffer_unregister_private(struct drm_framebuffer *fb);
 extern int drmfb_probe(struct drm_device *dev, struct drm_crtc *crtc);
 extern int drmfb_remove(struct drm_device *dev, struct drm_framebuffer *fb);
 extern void drm_crtc_probe_connector_modes(struct drm_device *dev, int maxX, int maxY);
@@ -985,6 +1015,7 @@ extern int drm_mode_getcrtc(struct drm_device *dev,
                            void *data, struct drm_file *file_priv);
 extern int drm_mode_getconnector(struct drm_device *dev,
                              void *data, struct drm_file *file_priv);
+extern int drm_mode_set_config_internal(struct drm_mode_set *set);
 extern int drm_mode_setcrtc(struct drm_device *dev,
                            void *data, struct drm_file *file_priv);
 extern int drm_mode_getplane(struct drm_device *dev,
@@ -1033,6 +1064,7 @@ extern u8 *drm_find_cea_extension(struct edid *edid);
 extern u8 drm_match_cea_mode(struct drm_display_mode *to_match);
 extern bool drm_detect_hdmi_monitor(struct edid *edid);
 extern bool drm_detect_monitor_audio(struct edid *edid);
+extern bool drm_rgb_quant_range_selectable(struct edid *edid);
 extern int drm_mode_page_flip_ioctl(struct drm_device *dev,
                                    void *data, struct drm_file *file_priv);
 extern struct drm_display_mode *drm_cvt_mode(struct drm_device *dev,
index 5120b01c2eebb86809729ec8945bc8bf68f62b97..c09511625a11244f8a07a9114952b9352f619f8d 100644 (file)
@@ -48,6 +48,18 @@ struct drm_fb_helper_surface_size {
        u32 surface_depth;
 };
 
+/**
+ * struct drm_fb_helper_funcs - driver callbacks for the fbdev emulation library
+ * @gamma_set: - Set the given gamma lut register on the given crtc.
+ * @gamma_get: - Read the given gamma lut register on the given crtc, used to
+ *              save the current lut when force-restoring the fbdev for e.g.
+ *              kdbg.
+ * @fb_probe: - Driver callback to allocate and initialize the fbdev info
+ *             structure. Futhermore it also needs to allocate the drm
+ *             framebuffer used to back the fbdev.
+ *
+ * Driver callbacks used by the fbdev emulation helper library.
+ */
 struct drm_fb_helper_funcs {
        void (*gamma_set)(struct drm_crtc *crtc, u16 red, u16 green,
                          u16 blue, int regno);
@@ -65,9 +77,7 @@ struct drm_fb_helper_connector {
 
 struct drm_fb_helper {
        struct drm_framebuffer *fb;
-       struct drm_framebuffer *saved_fb;
        struct drm_device *dev;
-       struct drm_display_mode *mode;
        int crtc_count;
        struct drm_fb_helper_crtc *crtc_info;
        int connector_count;
@@ -82,9 +92,6 @@ struct drm_fb_helper {
        bool delayed_hotplug;
 };
 
-int drm_fb_helper_single_fb_probe(struct drm_fb_helper *helper,
-                                 int preferred_bpp);
-
 int drm_fb_helper_init(struct drm_device *dev,
                       struct drm_fb_helper *helper, int crtc_count,
                       int max_conn);
@@ -103,7 +110,6 @@ int drm_fb_helper_setcolreg(unsigned regno,
                            struct fb_info *info);
 
 bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper);
-void drm_fb_helper_restore(void);
 void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper,
                            uint32_t fb_width, uint32_t fb_height);
 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
index 3527fb3f75bbe82c408e8614f5cfbb8c1a88dff9..88591ef8fa24f0cf8653a278d6bed086279691f0 100644 (file)
@@ -89,6 +89,29 @@ static inline bool drm_mm_initialized(struct drm_mm *mm)
 {
        return mm->hole_stack.next;
 }
+
+static inline unsigned long __drm_mm_hole_node_start(struct drm_mm_node *hole_node)
+{
+       return hole_node->start + hole_node->size;
+}
+
+static inline unsigned long drm_mm_hole_node_start(struct drm_mm_node *hole_node)
+{
+       BUG_ON(!hole_node->hole_follows);
+       return __drm_mm_hole_node_start(hole_node);
+}
+
+static inline unsigned long __drm_mm_hole_node_end(struct drm_mm_node *hole_node)
+{
+       return list_entry(hole_node->node_list.next,
+                         struct drm_mm_node, node_list)->start;
+}
+
+static inline unsigned long drm_mm_hole_node_end(struct drm_mm_node *hole_node)
+{
+       return __drm_mm_hole_node_end(hole_node);
+}
+
 #define drm_mm_for_each_node(entry, mm) list_for_each_entry(entry, \
                                                &(mm)->head_node.node_list, \
                                                node_list)
@@ -99,9 +122,26 @@ static inline bool drm_mm_initialized(struct drm_mm *mm)
             entry != NULL; entry = next, \
                next = entry ? list_entry(entry->node_list.next, \
                        struct drm_mm_node, node_list) : NULL) \
+
+/* Note that we need to unroll list_for_each_entry in order to inline
+ * setting hole_start and hole_end on each iteration and keep the
+ * macro sane.
+ */
+#define drm_mm_for_each_hole(entry, mm, hole_start, hole_end) \
+       for (entry = list_entry((mm)->hole_stack.next, struct drm_mm_node, hole_stack); \
+            &entry->hole_stack != &(mm)->hole_stack ? \
+            hole_start = drm_mm_hole_node_start(entry), \
+            hole_end = drm_mm_hole_node_end(entry), \
+            1 : 0; \
+            entry = list_entry(entry->hole_stack.next, struct drm_mm_node, hole_stack))
+
 /*
  * Basic range manager support (drm_mm.c)
  */
+extern struct drm_mm_node *drm_mm_create_block(struct drm_mm *mm,
+                                              unsigned long start,
+                                              unsigned long size,
+                                              bool atomic);
 extern struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *node,
                                                    unsigned long size,
                                                    unsigned alignment,
index 6eb76a1f11ab5bfe912d69c5b3dcd3ec4ae4d4c9..b08bdade60025679eeecff76746d98cd51f2e7df 100644 (file)
@@ -3,26 +3,8 @@
 #ifndef _DRM_INTEL_GTT_H
 #define        _DRM_INTEL_GTT_H
 
-struct intel_gtt {
-       /* Size of memory reserved for graphics by the BIOS */
-       unsigned int stolen_size;
-       /* Total number of gtt entries. */
-       unsigned int gtt_total_entries;
-       /* Part of the gtt that is mappable by the cpu, for those chips where
-        * this is not the full gtt. */
-       unsigned int gtt_mappable_entries;
-       /* Whether i915 needs to use the dmar apis or not. */
-       unsigned int needs_dmar : 1;
-       /* Whether we idle the gpu before mapping/unmapping */
-       unsigned int do_idle_maps : 1;
-       /* Share the scratch page dma with ppgtts. */
-       dma_addr_t scratch_page_dma;
-       struct page *scratch_page;
-       /* for ppgtt PDE access */
-       u32 __iomem *gtt;
-       /* needed for ioremap in drm/i915 */
-       phys_addr_t gma_bus_addr;
-} *intel_gtt_get(void);
+void intel_gtt_get(size_t *gtt_total, size_t *stolen_size,
+                  phys_addr_t *mappable_base, unsigned long *mappable_end);
 
 int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev,
                     struct agp_bridge_data *bridge);
index e3a43a47d78c51754ae01c46511b0ed56ddf2fc0..0fbd046e7c93653b0c862623e4716fdcfef39465 100644 (file)
@@ -790,16 +790,7 @@ extern void ttm_mem_io_unlock(struct ttm_mem_type_manager *man);
  * to make room for a buffer already reserved. (Buffers are reserved before
  * they are evicted). The following algorithm prevents such deadlocks from
  * occurring:
- * 1) Buffers are reserved with the lru spinlock held. Upon successful
- * reservation they are removed from the lru list. This stops a reserved buffer
- * from being evicted. However the lru spinlock is released between the time
- * a buffer is selected for eviction and the time it is reserved.
- * Therefore a check is made when a buffer is reserved for eviction, that it
- * is still the first buffer in the lru list, before it is removed from the
- * list. @check_lru == 1 forces this check. If it fails, the function returns
- * -EINVAL, and the caller should then choose a new buffer to evict and repeat
- * the procedure.
- * 2) Processes attempting to reserve multiple buffers other than for eviction,
+ * Processes attempting to reserve multiple buffers other than for eviction,
  * (typically execbuf), should first obtain a unique 32-bit
  * validation sequence number,
  * and call this function with @use_sequence == 1 and @sequence == the unique
@@ -830,9 +821,39 @@ extern int ttm_bo_reserve(struct ttm_buffer_object *bo,
                          bool interruptible,
                          bool no_wait, bool use_sequence, uint32_t sequence);
 
+/**
+ * ttm_bo_reserve_slowpath_nolru:
+ * @bo: A pointer to a struct ttm_buffer_object.
+ * @interruptible: Sleep interruptible if waiting.
+ * @sequence: Set (@bo)->sequence to this value after lock
+ *
+ * This is called after ttm_bo_reserve returns -EAGAIN and we backed off
+ * from all our other reservations. Because there are no other reservations
+ * held by us, this function cannot deadlock any more.
+ *
+ * Will not remove reserved buffers from the lru lists.
+ * Otherwise identical to ttm_bo_reserve_slowpath.
+ */
+extern int ttm_bo_reserve_slowpath_nolru(struct ttm_buffer_object *bo,
+                                        bool interruptible,
+                                        uint32_t sequence);
+
 
 /**
- * ttm_bo_reserve_locked:
+ * ttm_bo_reserve_slowpath:
+ * @bo: A pointer to a struct ttm_buffer_object.
+ * @interruptible: Sleep interruptible if waiting.
+ * @sequence: Set (@bo)->sequence to this value after lock
+ *
+ * This is called after ttm_bo_reserve returns -EAGAIN and we backed off
+ * from all our other reservations. Because there are no other reservations
+ * held by us, this function cannot deadlock any more.
+ */
+extern int ttm_bo_reserve_slowpath(struct ttm_buffer_object *bo,
+                                  bool interruptible, uint32_t sequence);
+
+/**
+ * ttm_bo_reserve_nolru:
  *
  * @bo: A pointer to a struct ttm_buffer_object.
  * @interruptible: Sleep interruptible if waiting.
@@ -840,9 +861,7 @@ extern int ttm_bo_reserve(struct ttm_buffer_object *bo,
  * @use_sequence: If @bo is already reserved, Only sleep waiting for
  * it to become unreserved if @sequence < (@bo)->sequence.
  *
- * Must be called with struct ttm_bo_global::lru_lock held,
- * and will not remove reserved buffers from the lru lists.
- * The function may release the LRU spinlock if it needs to sleep.
+ * Will not remove reserved buffers from the lru lists.
  * Otherwise identical to ttm_bo_reserve.
  *
  * Returns:
@@ -855,7 +874,7 @@ extern int ttm_bo_reserve(struct ttm_buffer_object *bo,
  * -EDEADLK: Bo already reserved using @sequence. This error code will only
  * be returned if @use_sequence is set to true.
  */
-extern int ttm_bo_reserve_locked(struct ttm_buffer_object *bo,
+extern int ttm_bo_reserve_nolru(struct ttm_buffer_object *bo,
                                 bool interruptible,
                                 bool no_wait, bool use_sequence,
                                 uint32_t sequence);
@@ -879,18 +898,6 @@ extern void ttm_bo_unreserve(struct ttm_buffer_object *bo);
  */
 extern void ttm_bo_unreserve_locked(struct ttm_buffer_object *bo);
 
-/**
- * ttm_bo_wait_unreserved
- *
- * @bo: A pointer to a struct ttm_buffer_object.
- *
- * Wait for a struct ttm_buffer_object to become unreserved.
- * This is typically used in the execbuf code to relax cpu-usage when
- * a potential deadlock condition backoff.
- */
-extern int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo,
-                                 bool interruptible);
-
 /*
  * ttm_bo_util.c
  */
index 408da9502177bd18a3ba88e2831701e916e800aa..8f7a3d68371a37314ab8263c078674fd79a8c5b5 100644 (file)
@@ -297,10 +297,12 @@ enum {
        ATA_LOG_SATA_NCQ        = 0x10,
        ATA_LOG_SATA_ID_DEV_DATA  = 0x30,
        ATA_LOG_SATA_SETTINGS     = 0x08,
-       ATA_LOG_DEVSLP_MDAT       = 0x30,
+       ATA_LOG_DEVSLP_OFFSET     = 0x30,
+       ATA_LOG_DEVSLP_SIZE       = 0x08,
+       ATA_LOG_DEVSLP_MDAT       = 0x00,
        ATA_LOG_DEVSLP_MDAT_MASK  = 0x1F,
-       ATA_LOG_DEVSLP_DETO       = 0x31,
-       ATA_LOG_DEVSLP_VALID      = 0x37,
+       ATA_LOG_DEVSLP_DETO       = 0x01,
+       ATA_LOG_DEVSLP_VALID      = 0x07,
        ATA_LOG_DEVSLP_VALID_MASK = 0x80,
 
        /* READ/WRITE LONG (obsolete) */
index bce729afbcf9a5e184e70edc1bebf9f65a0176a3..5a6d718adf34825eb11bc4dd20cbcb8f1dced0ad 100644 (file)
@@ -24,6 +24,7 @@
 #define _LINUX_AUDIT_H_
 
 #include <linux/sched.h>
+#include <linux/ptrace.h>
 #include <uapi/linux/audit.h>
 
 struct audit_sig_info {
@@ -157,7 +158,8 @@ void audit_core_dumps(long signr);
 
 static inline void audit_seccomp(unsigned long syscall, long signr, int code)
 {
-       if (unlikely(!audit_dummy_context()))
+       /* Force a record to be reported if a signal was delivered. */
+       if (signr || unlikely(!audit_dummy_context()))
                __audit_seccomp(syscall, signr, code);
 }
 
index 6ecb6dc2f3033fc50ba1a8f14eb4b2b12b8ec044..cc7bddeaf553b334e6aa1250fbf74924358e5de4 100644 (file)
@@ -22,7 +22,7 @@ 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,
-                       bool sync, bool *contended, struct page **page);
+                       bool sync, bool *contended);
 extern int 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);
@@ -75,7 +75,7 @@ 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,
-                       bool sync, bool *contended, struct page **page)
+                       bool sync, bool *contended)
 {
        return COMPACT_CONTINUE;
 }
index dedb082fe50fde9e62190569b0c362238a46f1b0..47b858cffc478ce21bd4fc89bd0ac740dfa5cb50 100644 (file)
@@ -77,7 +77,9 @@ extern const struct consw prom_con;   /* SPARC PROM console */
 int con_is_bound(const struct consw *csw);
 int register_con_driver(const struct consw *csw, int first, int last);
 int unregister_con_driver(const struct consw *csw);
+int do_unregister_con_driver(const struct consw *csw);
 int take_over_console(const struct consw *sw, int first, int last, int deflt);
+int do_take_over_console(const struct consw *sw, int first, int last, int deflt);
 void give_up_console(const struct consw *sw);
 #ifdef CONFIG_HW_CONSOLE
 int con_debug_enter(struct vc_data *vc);
index ac3bbb5b95029caeea6546372ad2b6f35c0b83ad..1739510d89943df23f193d5fb04332f47cdaf958 100644 (file)
 #include <linux/cpumask.h>
 #include <linux/gfp.h>
 #include <linux/slab.h>
+#include <linux/kref.h>
 
 /**
  * struct cpu_rmap - CPU affinity reverse-map
+ * @refcount: kref for object
  * @size: Number of objects to be reverse-mapped
  * @used: Number of objects added
  * @obj: Pointer to array of object pointers
@@ -23,6 +25,7 @@
  *      based on affinity masks
  */
 struct cpu_rmap {
+       struct kref     refcount;
        u16             size, used;
        void            **obj;
        struct {
@@ -33,15 +36,7 @@ struct cpu_rmap {
 #define CPU_RMAP_DIST_INF 0xffff
 
 extern struct cpu_rmap *alloc_cpu_rmap(unsigned int size, gfp_t flags);
-
-/**
- * free_cpu_rmap - free CPU affinity reverse-map
- * @rmap: Reverse-map allocated with alloc_cpu_rmap(), or %NULL
- */
-static inline void free_cpu_rmap(struct cpu_rmap *rmap)
-{
-       kfree(rmap);
-}
+extern int cpu_rmap_put(struct cpu_rmap *rmap);
 
 extern int cpu_rmap_add(struct cpu_rmap *rmap, void *obj);
 extern int cpu_rmap_update(struct cpu_rmap *rmap, u16 index,
index 3711b34dc4f9fd8cafab9691bce55e617326dbb6..24cd1037b6d6f487d93ce79dfe0844887f949b9f 100644 (file)
@@ -126,9 +126,9 @@ struct cpuidle_driver {
        struct module           *owner;
        int                     refcnt;
 
-       unsigned int            power_specified:1;
        /* set to 1 to use the core cpuidle time keeping (for all states). */
        unsigned int            en_core_tk_irqen:1;
+       /* states array must be ordered in decreasing power consumption */
        struct cpuidle_state    states[CPUIDLE_STATE_MAX];
        int                     state_count;
        int                     safe_state_index;
index 8b84916dc6719ce46430fa791aa27c0bf8b16835..7a9498ab3c2d10dedf8af5c5dae3b2fd56753684 100644 (file)
@@ -618,18 +618,30 @@ extern int __init efi_setup_pcdp_console(char *);
 #endif
 
 /*
- * We play games with efi_enabled so that the compiler will, if possible, remove
- * EFI-related code altogether.
+ * We play games with efi_enabled so that the compiler will, if
+ * possible, remove EFI-related code altogether.
  */
+#define EFI_BOOT               0       /* Were we booted from EFI? */
+#define EFI_SYSTEM_TABLES      1       /* Can we use EFI system tables? */
+#define EFI_CONFIG_TABLES      2       /* Can we use EFI config tables? */
+#define EFI_RUNTIME_SERVICES   3       /* Can we use runtime services? */
+#define EFI_MEMMAP             4       /* Can we use EFI memory map? */
+#define EFI_64BIT              5       /* Is the firmware 64-bit? */
+
 #ifdef CONFIG_EFI
 # ifdef CONFIG_X86
-   extern int efi_enabled;
-   extern bool efi_64bit;
+extern int efi_enabled(int facility);
 # else
-#  define efi_enabled 1
+static inline int efi_enabled(int facility)
+{
+       return 1;
+}
 # endif
 #else
-# define efi_enabled 0
+static inline int efi_enabled(int facility)
+{
+       return 0;
+}
 #endif
 
 /*
index c7a95714b1fe68e7f9b67e990a384cac0f490044..58b98606ac266fa86fdfe3a63c42b235a984e7b8 100644 (file)
@@ -19,6 +19,8 @@ struct vm_area_struct;
 struct fb_info;
 struct device;
 struct file;
+struct videomode;
+struct device_node;
 
 /* Definitions below are used in the parsed monitor specs */
 #define FB_DPMS_ACTIVE_OFF     1
@@ -714,6 +716,12 @@ extern void fb_destroy_modedb(struct fb_videomode *modedb);
 extern int fb_find_mode_cvt(struct fb_videomode *mode, int margins, int rb);
 extern unsigned char *fb_ddc_read(struct i2c_adapter *adapter);
 
+extern int of_get_fb_videomode(struct device_node *np,
+                              struct fb_videomode *fb,
+                              int index);
+extern int fb_videomode_from_videomode(const struct videomode *vm,
+                                      struct fb_videomode *fbmode);
+
 /* drivers/video/modedb.c */
 #define VESA_MODEDB_SIZE 34
 extern void fb_var_to_videomode(struct fb_videomode *mode,
index a799273714accff40c0bf54850389b095746ff83..10ed4f4364589d5d810385e144ab7138fefc13ba 100644 (file)
 
 #define __exit          __section(.exit.text) __exitused __cold notrace
 
-/* Used for HOTPLUG, but that is always enabled now, so just make them noops */
-#define __devinit
-#define __devinitdata
-#define __devinitconst
-#define __devexit
-#define __devexitdata
-#define __devexitconst
-
 /* Used for HOTPLUG_CPU */
 #define __cpuinit        __section(.cpuinit.text) __cold notrace
 #define __cpuinitdata    __section(.cpuinit.data)
@@ -337,18 +329,6 @@ void __init parse_early_options(char *cmdline);
 #define __INITRODATA_OR_MODULE __INITRODATA
 #endif /*CONFIG_MODULES*/
 
-/* Functions marked as __devexit may be discarded at kernel link time, depending
-   on config options.  Newer versions of binutils detect references from
-   retained sections to discarded sections and flag an error.  Pointers to
-   __devexit functions must use __devexit_p(function_name), the wrapper will
-   insert either the function_name or NULL, depending on the config options.
- */
-#if defined(MODULE) || defined(CONFIG_HOTPLUG)
-#define __devexit_p(x) x
-#else
-#define __devexit_p(x) NULL
-#endif
-
 #ifdef MODULE
 #define __exit_p(x) x
 #else
index 5e4e6170f43a5ef672ddd0d28694849669e310cb..5fa5afeeb7599d6c0f05d7fb59796cf153a614a9 100644 (file)
@@ -268,11 +268,6 @@ struct irq_affinity_notify {
 extern int
 irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify);
 
-static inline void irq_run_affinity_notifiers(void)
-{
-       flush_scheduled_work();
-}
-
 #else /* CONFIG_SMP */
 
 static inline int irq_set_affinity(unsigned int irq, const struct cpumask *m)
index 83ba0ab2c915c1bdb66fc7c922343b3f027ad120..649e5f86b5f00928b94e9f8515bf18f286e6b6e4 100644 (file)
@@ -652,8 +652,8 @@ struct ata_device {
                u32             gscr[SATA_PMP_GSCR_DWORDS]; /* PMP GSCR block */
        };
 
-       /* Identify Device Data Log (30h), SATA Settings (page 08h) */
-       u8                      sata_settings[ATA_SECT_SIZE];
+       /* DEVSLP Timing Variables from Identify Device Data Log */
+       u8                      devslp_timing[ATA_LOG_DEVSLP_SIZE];
 
        /* error history */
        int                     spdn_cnt;
index 00e46376e28f73d33bdfbf56480932dfa12eee9f..2bca44b0893c787bdb463a8f42e7306e02edf87f 100644 (file)
@@ -524,14 +524,17 @@ static inline void print_irqtrace_events(struct task_struct *curr)
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 # ifdef CONFIG_PROVE_LOCKING
 #  define rwsem_acquire(l, s, t, i)            lock_acquire(l, s, t, 0, 2, NULL, i)
+#  define rwsem_acquire_nest(l, s, t, n, i)    lock_acquire(l, s, t, 0, 2, n, i)
 #  define rwsem_acquire_read(l, s, t, i)       lock_acquire(l, s, t, 1, 2, NULL, i)
 # else
 #  define rwsem_acquire(l, s, t, i)            lock_acquire(l, s, t, 0, 1, NULL, i)
+#  define rwsem_acquire_nest(l, s, t, n, i)    lock_acquire(l, s, t, 0, 1, n, i)
 #  define rwsem_acquire_read(l, s, t, i)       lock_acquire(l, s, t, 1, 1, NULL, i)
 # endif
 # define rwsem_release(l, n, i)                        lock_release(l, n, i)
 #else
 # define rwsem_acquire(l, s, t, i)             do { } while (0)
+# define rwsem_acquire_nest(l, s, t, n, i)     do { } while (0)
 # define rwsem_acquire_read(l, s, t, i)                do { } while (0)
 # define rwsem_release(l, n, i)                        do { } while (0)
 #endif
index 0108a56f814ed7559b2f1fcee41a903890ed919d..28bd5fa2ff2eb1c0550891cfa5af551cc03b6c5c 100644 (file)
@@ -429,7 +429,7 @@ extern int memcg_limited_groups_array_size;
  * the slab_mutex must be held when looping through those caches
  */
 #define for_each_memcg_cache_index(_idx)       \
-       for ((_idx) = 0; i < memcg_limited_groups_array_size; (_idx)++)
+       for ((_idx) = 0; (_idx) < memcg_limited_groups_array_size; (_idx)++)
 
 static inline bool memcg_kmem_enabled(void)
 {
index 2138bd33021a629f59b868d5a3938aefa4f87449..e53dcfeaee69bb052181261f4843c9da57f04e05 100644 (file)
@@ -272,8 +272,6 @@ struct abx500_bm_data {
        const struct abx500_fg_parameters *fg_params;
 };
 
-extern struct abx500_bm_data ab8500_bm_data;
-
 enum {
        NTC_EXTERNAL = 0,
        NTC_INTERNAL,
index 44310c98ee6eb59f38ca3024622966e6b528bf1d..9bd037df97d95a673d80f771cb9b1eaf56bce1c1 100644 (file)
@@ -422,7 +422,10 @@ struct ab8500_chargalg_platform_data {
 struct ab8500_btemp;
 struct ab8500_gpadc;
 struct ab8500_fg;
+
 #ifdef CONFIG_AB8500_BM
+extern struct abx500_bm_data ab8500_bm_data;
+
 void ab8500_fg_reinit(void);
 void ab8500_charger_usb_state_changed(u8 bm_usb_state, u16 mA);
 struct ab8500_btemp *ab8500_btemp_get(void);
@@ -434,31 +437,7 @@ int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res);
 int ab8500_fg_inst_curr_done(struct ab8500_fg *di);
 
 #else
-int ab8500_fg_inst_curr_done(struct ab8500_fg *di)
-{
-}
-static void ab8500_fg_reinit(void)
-{
-}
-static void ab8500_charger_usb_state_changed(u8 bm_usb_state, u16 mA)
-{
-}
-static struct ab8500_btemp *ab8500_btemp_get(void)
-{
-       return NULL;
-}
-static int ab8500_btemp_get_batctrl_temp(struct ab8500_btemp *btemp)
-{
-       return 0;
-}
-struct ab8500_fg *ab8500_fg_get(void)
-{
-       return NULL;
-}
-static int ab8500_fg_inst_curr_blocking(struct ab8500_fg *dev)
-{
-       return -ENODEV;
-}
+static struct abx500_bm_data ab8500_bm_data;
 
 static inline int ab8500_fg_inst_curr_start(struct ab8500_fg *di)
 {
index 86dd93de6ff2b70ac4c693e8a9a05083bea72503..786d02eb79d2210ef8c3dfe145e3c8d0e0123ef1 100644 (file)
@@ -99,6 +99,9 @@ struct da9052 {
        u8 chip_id;
 
        int chip_irq;
+
+       /* SOC I/O transfer related fixes for DA9052/53 */
+       int (*fix_io) (struct da9052 *da9052, unsigned char reg);
 };
 
 /* ADC API */
@@ -113,32 +116,87 @@ static inline int da9052_reg_read(struct da9052 *da9052, unsigned char reg)
        ret = regmap_read(da9052->regmap, reg, &val);
        if (ret < 0)
                return ret;
+
+       if (da9052->fix_io) {
+               ret = da9052->fix_io(da9052, reg);
+               if (ret < 0)
+                       return ret;
+       }
+
        return val;
 }
 
 static inline int da9052_reg_write(struct da9052 *da9052, unsigned char reg,
                                    unsigned char val)
 {
-       return regmap_write(da9052->regmap, reg, val);
+       int ret;
+
+       ret = regmap_write(da9052->regmap, reg, val);
+       if (ret < 0)
+               return ret;
+
+       if (da9052->fix_io) {
+               ret = da9052->fix_io(da9052, reg);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return ret;
 }
 
 static inline int da9052_group_read(struct da9052 *da9052, unsigned char reg,
                                     unsigned reg_cnt, unsigned char *val)
 {
-       return regmap_bulk_read(da9052->regmap, reg, val, reg_cnt);
+       int ret;
+
+       ret = regmap_bulk_read(da9052->regmap, reg, val, reg_cnt);
+       if (ret < 0)
+               return ret;
+
+       if (da9052->fix_io) {
+               ret = da9052->fix_io(da9052, reg);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return ret;
 }
 
 static inline int da9052_group_write(struct da9052 *da9052, unsigned char reg,
                                      unsigned reg_cnt, unsigned char *val)
 {
-       return regmap_raw_write(da9052->regmap, reg, val, reg_cnt);
+       int ret;
+
+       ret = regmap_raw_write(da9052->regmap, reg, val, reg_cnt);
+       if (ret < 0)
+               return ret;
+
+       if (da9052->fix_io) {
+               ret = da9052->fix_io(da9052, reg);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return ret;
 }
 
 static inline int da9052_reg_update(struct da9052 *da9052, unsigned char reg,
                                     unsigned char bit_mask,
                                     unsigned char reg_val)
 {
-       return regmap_update_bits(da9052->regmap, reg, bit_mask, reg_val);
+       int ret;
+
+       ret = regmap_update_bits(da9052->regmap, reg, bit_mask, reg_val);
+       if (ret < 0)
+               return ret;
+
+       if (da9052->fix_io) {
+               ret = da9052->fix_io(da9052, reg);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return ret;
 }
 
 int da9052_device_init(struct da9052 *da9052, u8 chip_id);
index b97f7309d7f69e7189d74aead8ac4281a8ed0881..c4dd3a8add21b94cc526843c117734c506cc5233 100644 (file)
@@ -34,6 +34,9 @@
 #define DA9052_STATUS_C_REG            3
 #define DA9052_STATUS_D_REG            4
 
+/* PARK REGISTER */
+#define DA9052_PARK_REGISTER           DA9052_STATUS_D_REG
+
 /* EVENT REGISTERS */
 #define DA9052_EVENT_A_REG             5
 #define DA9052_EVENT_B_REG             6
index a8d393e3066b74465f14994740b758f1a134850b..2b13970596f53732cda07a6fd3b270cc9e933e5b 100644 (file)
@@ -38,6 +38,9 @@
 #define RTSX_SD_CARD                   0
 #define RTSX_MS_CARD                   1
 
+#define CLK_TO_DIV_N                   0
+#define DIV_N_TO_CLK                   1
+
 struct platform_device;
 
 struct rtsx_slot {
index 060b721fcbfb93b690d51191daeb95da692eef8c..4b117a3f54d493f59393226c6351e1fa8c1e16ca 100644 (file)
 #define SG_TRANS_DATA          (0x02 << 4)
 #define SG_LINK_DESC           (0x03 << 4)
 
-/* SD bank voltage */
-#define SD_IO_3V3              0
-#define SD_IO_1V8              1
-
+/* Output voltage */
+#define OUTPUT_3V3             0
+#define OUTPUT_1V8             1
 
 /* Card Clock Enable Register */
 #define SD_CLK_EN                      0x04
 #define CHANGE_CLK                     0x01
 
 /* LDO_CTL */
+#define BPP_ASIC_1V7                   0x00
+#define BPP_ASIC_1V8                   0x01
+#define BPP_ASIC_1V9                   0x02
+#define BPP_ASIC_2V0                   0x03
+#define BPP_ASIC_2V7                   0x04
+#define BPP_ASIC_2V8                   0x05
+#define BPP_ASIC_3V2                   0x06
+#define BPP_ASIC_3V3                   0x07
+#define BPP_REG_TUNED18                        0x07
+#define BPP_TUNED18_SHIFT_8402         5
+#define BPP_TUNED18_SHIFT_8411         4
+#define BPP_PAD_MASK                   0x04
+#define BPP_PAD_3V3                    0x04
+#define BPP_PAD_1V8                    0x00
 #define BPP_LDO_POWB                   0x03
 #define BPP_LDO_ON                     0x00
 #define BPP_LDO_SUSPEND                        0x02
@@ -688,7 +701,10 @@ struct pcr_ops {
        int             (*disable_auto_blink)(struct rtsx_pcr *pcr);
        int             (*card_power_on)(struct rtsx_pcr *pcr, int card);
        int             (*card_power_off)(struct rtsx_pcr *pcr, int card);
+       int             (*switch_output_voltage)(struct rtsx_pcr *pcr,
+                                               u8 voltage);
        unsigned int    (*cd_deglitch)(struct rtsx_pcr *pcr);
+       int             (*conv_clk_and_div_n)(int clk, int dir);
 };
 
 enum PDEV_STAT  {PDEV_STAT_IDLE, PDEV_STAT_RUN};
@@ -783,6 +799,7 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock,
                u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk);
 int rtsx_pci_card_power_on(struct rtsx_pcr *pcr, int card);
 int rtsx_pci_card_power_off(struct rtsx_pcr *pcr, int card);
+int rtsx_pci_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage);
 unsigned int rtsx_pci_card_exist(struct rtsx_pcr *pcr);
 void rtsx_pci_complete_unfinished_transfer(struct rtsx_pcr *pcr);
 
index 63204078f72b9798901b6ef6371847ade8031809..66e2f7c61e5c9d3a2924389e28ffa9d32c8728bf 100644 (file)
@@ -455,7 +455,6 @@ void put_pages_list(struct list_head *pages);
 
 void split_page(struct page *page, unsigned int order);
 int split_free_page(struct page *page);
-int capture_free_page(struct page *page, int alloc_order, int migratetype);
 
 /*
  * Compound pages have a destructor function.  Provide a
index bc823c4c028bd8c0404940384da729c072dc31a3..deca87452528b2888823a4daf4cb148d43a52544 100644 (file)
@@ -151,7 +151,7 @@ struct mmu_notifier_ops {
  * Therefore notifier chains can only be traversed when either
  *
  * 1. mmap_sem is held.
- * 2. One of the reverse map locks is held (i_mmap_mutex or anon_vma->mutex).
+ * 2. One of the reverse map locks is held (i_mmap_mutex or anon_vma->rwsem).
  * 3. No other concurrent thread can access the list (release)
  */
 struct mmu_notifier {
index 7760c6d344a32cd6a59cbb163a56c9bcd4888683..1375ee3f03aadd4d2504762628a3991272861fa8 100644 (file)
@@ -199,11 +199,11 @@ struct module_use {
        struct module *source, *target;
 };
 
-enum module_state
-{
-       MODULE_STATE_LIVE,
-       MODULE_STATE_COMING,
-       MODULE_STATE_GOING,
+enum module_state {
+       MODULE_STATE_LIVE,      /* Normal state. */
+       MODULE_STATE_COMING,    /* Full formed, running module_init. */
+       MODULE_STATE_GOING,     /* Going away. */
+       MODULE_STATE_UNFORMED,  /* Still setting it up. */
 };
 
 /**
index c599e4782d454cc853a6a9c1491f38bd1c1509b4..9ef07d0868b6012da1d1089daa2c9b0d7d304389 100644 (file)
@@ -60,6 +60,9 @@ struct wireless_dev;
 #define SET_ETHTOOL_OPS(netdev,ops) \
        ( (netdev)->ethtool_ops = (ops) )
 
+extern void netdev_set_default_ethtool_ops(struct net_device *dev,
+                                          const struct ethtool_ops *ops);
+
 /* hardware address assignment types */
 #define NET_ADDR_PERM          0       /* address is permanent (default) */
 #define NET_ADDR_RANDOM                1       /* address is generated randomly */
index 1693775ecfe8fd298d299ebf1ca925c76e3a2361..89573a33ab3c43ee84cf629d84e5ff3d7f091206 100644 (file)
@@ -45,7 +45,6 @@ extern long arch_ptrace(struct task_struct *child, long request,
 extern int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len);
 extern int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len);
 extern void ptrace_disable(struct task_struct *);
-extern int ptrace_check_attach(struct task_struct *task, bool ignore_state);
 extern int ptrace_request(struct task_struct *child, long request,
                          unsigned long addr, unsigned long data);
 extern void ptrace_notify(int exit_code);
index 2ac60c9cf6448fac839074e752d61f2de7b56c0a..fea49b5da12a99bfa6c87b865a461a2a28e0495b 100644 (file)
@@ -123,9 +123,9 @@ __rb_change_child(struct rb_node *old, struct rb_node *new,
 extern void __rb_erase_color(struct rb_node *parent, struct rb_root *root,
        void (*augment_rotate)(struct rb_node *old, struct rb_node *new));
 
-static __always_inline void
-rb_erase_augmented(struct rb_node *node, struct rb_root *root,
-                  const struct rb_augment_callbacks *augment)
+static __always_inline struct rb_node *
+__rb_erase_augmented(struct rb_node *node, struct rb_root *root,
+                    const struct rb_augment_callbacks *augment)
 {
        struct rb_node *child = node->rb_right, *tmp = node->rb_left;
        struct rb_node *parent, *rebalance;
@@ -217,6 +217,14 @@ rb_erase_augmented(struct rb_node *node, struct rb_root *root,
        }
 
        augment->propagate(tmp, NULL);
+       return rebalance;
+}
+
+static __always_inline void
+rb_erase_augmented(struct rb_node *node, struct rb_root *root,
+                  const struct rb_augment_callbacks *augment)
+{
+       struct rb_node *rebalance = __rb_erase_augmented(node, root, augment);
        if (rebalance)
                __rb_erase_color(rebalance, root, augment->rotate);
 }
index 54bd7cd7ecbd11449b9e15b49f9ab7e208e96413..8da67d625e13fc888413847da4e2a7ae560b3d86 100644 (file)
@@ -125,8 +125,17 @@ extern void downgrade_write(struct rw_semaphore *sem);
  */
 extern void down_read_nested(struct rw_semaphore *sem, int subclass);
 extern void down_write_nested(struct rw_semaphore *sem, int subclass);
+extern void _down_write_nest_lock(struct rw_semaphore *sem, struct lockdep_map *nest_lock);
+
+# define down_write_nest_lock(sem, nest_lock)                  \
+do {                                                           \
+       typecheck(struct lockdep_map *, &(nest_lock)->dep_map); \
+       _down_write_nest_lock(sem, &(nest_lock)->dep_map);      \
+} while (0);
+
 #else
 # define down_read_nested(sem, subclass)               down_read(sem)
+# define down_write_nest_lock(sem, nest_lock)  down_write(sem)
 # define down_write_nested(sem, subclass)      down_write(sem)
 #endif
 
index 206bb089c06b5541a889291f7938e80b3b480678..d2112477ff5e0ed9a942c9698cbf8549e6087a9c 100644 (file)
@@ -1810,6 +1810,7 @@ extern void thread_group_cputime_adjusted(struct task_struct *p, cputime_t *ut,
 #define PF_MEMALLOC    0x00000800      /* Allocating memory */
 #define PF_NPROC_EXCEEDED 0x00001000   /* set_user noticed that RLIMIT_NPROC was exceeded */
 #define PF_USED_MATH   0x00002000      /* if unset the fpu must be initialized before use */
+#define PF_USED_ASYNC  0x00004000      /* used async_schedule*(), used by module init */
 #define PF_NOFREEZE    0x00008000      /* this thread should not be frozen */
 #define PF_FROZEN      0x00010000      /* frozen for system suspend */
 #define PF_FSTRANS     0x00020000      /* inside a filesystem transaction */
@@ -2713,7 +2714,16 @@ static inline void thread_group_cputime_init(struct signal_struct *sig)
 extern void recalc_sigpending_and_wake(struct task_struct *t);
 extern void recalc_sigpending(void);
 
-extern void signal_wake_up(struct task_struct *t, int resume_stopped);
+extern void signal_wake_up_state(struct task_struct *t, unsigned int state);
+
+static inline void signal_wake_up(struct task_struct *t, bool resume)
+{
+       signal_wake_up_state(t, resume ? TASK_WAKEKILL : 0);
+}
+static inline void ptrace_signal_wake_up(struct task_struct *t, bool resume)
+{
+       signal_wake_up_state(t, resume ? __TASK_TRACED : 0);
+}
 
 /*
  * Wrappers for p->thread_info->cpu access. No-op on UP.
index 0f6afc657f778f2e682e7fb108e97b6788a53973..eee7478cda701ddeabc00150f607f368a4239350 100644 (file)
@@ -989,17 +989,29 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     tells the LSM to decrement the number of secmark labeling rules loaded
  * @req_classify_flow:
  *     Sets the flow's sid to the openreq sid.
+ * @tun_dev_alloc_security:
+ *     This hook allows a module to allocate a security structure for a TUN
+ *     device.
+ *     @security pointer to a security structure pointer.
+ *     Returns a zero on success, negative values on failure.
+ * @tun_dev_free_security:
+ *     This hook allows a module to free the security structure for a TUN
+ *     device.
+ *     @security pointer to the TUN device's security structure
  * @tun_dev_create:
  *     Check permissions prior to creating a new TUN device.
- * @tun_dev_post_create:
- *     This hook allows a module to update or allocate a per-socket security
- *     structure.
- *     @sk contains the newly created sock structure.
+ * @tun_dev_attach_queue:
+ *     Check permissions prior to attaching to a TUN device queue.
+ *     @security pointer to the TUN device's security structure.
  * @tun_dev_attach:
- *     Check permissions prior to attaching to a persistent TUN device.  This
- *     hook can also be used by the module to update any security state
+ *     This hook can be used by the module to update any security state
  *     associated with the TUN device's sock structure.
  *     @sk contains the existing sock structure.
+ *     @security pointer to the TUN device's security structure.
+ * @tun_dev_open:
+ *     This hook can be used by the module to update any security state
+ *     associated with the TUN device's security structure.
+ *     @security pointer to the TUN devices's security structure.
  *
  * Security hooks for XFRM operations.
  *
@@ -1620,9 +1632,12 @@ struct security_operations {
        void (*secmark_refcount_inc) (void);
        void (*secmark_refcount_dec) (void);
        void (*req_classify_flow) (const struct request_sock *req, struct flowi *fl);
-       int (*tun_dev_create)(void);
-       void (*tun_dev_post_create)(struct sock *sk);
-       int (*tun_dev_attach)(struct sock *sk);
+       int (*tun_dev_alloc_security) (void **security);
+       void (*tun_dev_free_security) (void *security);
+       int (*tun_dev_create) (void);
+       int (*tun_dev_attach_queue) (void *security);
+       int (*tun_dev_attach) (struct sock *sk, void *security);
+       int (*tun_dev_open) (void *security);
 #endif /* CONFIG_SECURITY_NETWORK */
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
@@ -2566,9 +2581,12 @@ void security_inet_conn_established(struct sock *sk,
 int security_secmark_relabel_packet(u32 secid);
 void security_secmark_refcount_inc(void);
 void security_secmark_refcount_dec(void);
+int security_tun_dev_alloc_security(void **security);
+void security_tun_dev_free_security(void *security);
 int security_tun_dev_create(void);
-void security_tun_dev_post_create(struct sock *sk);
-int security_tun_dev_attach(struct sock *sk);
+int security_tun_dev_attach_queue(void *security);
+int security_tun_dev_attach(struct sock *sk, void *security);
+int security_tun_dev_open(void *security);
 
 #else  /* CONFIG_SECURITY_NETWORK */
 static inline int security_unix_stream_connect(struct sock *sock,
@@ -2733,16 +2751,31 @@ static inline void security_secmark_refcount_dec(void)
 {
 }
 
+static inline int security_tun_dev_alloc_security(void **security)
+{
+       return 0;
+}
+
+static inline void security_tun_dev_free_security(void *security)
+{
+}
+
 static inline int security_tun_dev_create(void)
 {
        return 0;
 }
 
-static inline void security_tun_dev_post_create(struct sock *sk)
+static inline int security_tun_dev_attach_queue(void *security)
+{
+       return 0;
+}
+
+static inline int security_tun_dev_attach(struct sock *sk, void *security)
 {
+       return 0;
 }
 
-static inline int security_tun_dev_attach(struct sock *sk)
+static inline int security_tun_dev_open(void *security)
 {
        return 0;
 }
index 689b14b26c8d84343d8271b87d9aefca00b20904..4d22d0f6167aa49b653c8d942c2e00d61be689b8 100644 (file)
@@ -357,6 +357,8 @@ struct usb_bus {
        int bandwidth_int_reqs;         /* number of Interrupt requests */
        int bandwidth_isoc_reqs;        /* number of Isoc. requests */
 
+       unsigned resuming_ports;        /* bit array: resuming root-hub ports */
+
 #if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
        struct mon_bus *mon_bus;        /* non-null when associated */
        int monitored;                  /* non-zero when monitored */
index 608050b2545f917f790fca91bc9f89042626752c..0a78df5f6cfd23d616ecd4572b6079d1f8605dd2 100644 (file)
@@ -430,6 +430,9 @@ extern void usb_hcd_poll_rh_status(struct usb_hcd *hcd);
 extern void usb_wakeup_notification(struct usb_device *hdev,
                unsigned int portnum);
 
+extern void usb_hcd_start_port_resume(struct usb_bus *bus, int portnum);
+extern void usb_hcd_end_port_resume(struct usb_bus *bus, int portnum);
+
 /* The D0/D1 toggle bits ... USE WITH CAUTION (they're almost hcd-internal) */
 #define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> (ep)) & 1)
 #define        usb_dotoggle(dev, ep, out)  ((dev)->toggle[out] ^= (1 << (ep)))
index bd45eb7bedc8f365ab70704c552e93041af4ac94..5de7a220e98680f9c9cfc7b36c1f5c2114d7144f 100644 (file)
@@ -100,6 +100,7 @@ struct driver_info {
 #define FLAG_LINK_INTR 0x0800          /* updates link (carrier) status */
 
 #define FLAG_POINTTOPOINT 0x1000       /* possibly use "usb%d" names */
+#define FLAG_NOARP     0x2000          /* device can't do ARP */
 
 /*
  * Indicates to usbnet, that USB driver accumulates multiple IP packets.
index 50ae7d0c279ed2ddbdb626aa67dcc9abad619781..e8d65718560b3460b09123cb99d3a3f08685c217 100644 (file)
@@ -47,6 +47,7 @@ int con_set_cmap(unsigned char __user *cmap);
 int con_get_cmap(unsigned char __user *cmap);
 void scrollback(struct vc_data *vc, int lines);
 void scrollfront(struct vc_data *vc, int lines);
+void clear_buffer_attributes(struct vc_data *vc);
 void update_region(struct vc_data *vc, unsigned long start, int count);
 void redraw_screen(struct vc_data *vc, int is_switch);
 #define update_screen(x) redraw_screen(x, 0)
@@ -130,6 +131,8 @@ void vt_event_post(unsigned int event, unsigned int old, unsigned int new);
 int vt_waitactive(int n);
 void change_console(struct vc_data *new_vc);
 void reset_vc(struct vc_data *vc);
+extern int do_unbind_con_driver(const struct consw *csw, int first, int last,
+                            int deflt);
 extern int unbind_con_driver(const struct consw *csw, int first, int last,
                             int deflt);
 int vty_init(const struct file_operations *console_fops);
index 0707fb9551aa4c1011c88969a42cd4482450d035..a68f838a132c522df397b9398f8f2d64646008c0 100644 (file)
@@ -143,6 +143,8 @@ static inline struct sk_buff *ip_finish_skb(struct sock *sk, struct flowi4 *fl4)
 extern int             ip4_datagram_connect(struct sock *sk, 
                                             struct sockaddr *uaddr, int addr_len);
 
+extern void ip4_datagram_release_cb(struct sock *sk);
+
 struct ip_reply_arg {
        struct kvec iov[1];   
        int         flags;
index d8f5b9f5216939d2053c8c99ad54925fb5a0ce5c..e98aeb3da033a38e29f745d5f043a33d3888bcc3 100644 (file)
@@ -31,6 +31,8 @@ extern void nf_conntrack_cleanup(struct net *net);
 extern int nf_conntrack_proto_init(struct net *net);
 extern void nf_conntrack_proto_fini(struct net *net);
 
+extern void nf_conntrack_cleanup_end(void);
+
 extern bool
 nf_ct_get_tuple(const struct sk_buff *skb,
                unsigned int nhoff,
index 6d9e15ed1dcf0cb2875a10ca6795b14f3d4e0c51..dd8c48d14ed9637b3d1264ac5b88811e9be2dae6 100644 (file)
@@ -19,7 +19,7 @@
 
 struct cs4271_platform_data {
        int gpio_nreset;        /* GPIO driving Reset pin, if any */
-       int amutec_eq_bmutec:1; /* flag to enable AMUTEC=BMUTEC */
+       bool amutec_eq_bmutec;  /* flag to enable AMUTEC=BMUTEC */
 };
 
 #endif /* __CS4271_H */
index 769e27c774a3de382ec93650766621056dc78820..bc56738cb1091b8319b2413fe7e6b2f105ba0ac8 100644 (file)
@@ -58,8 +58,9 @@
        .info = snd_soc_info_volsw_range, .get = snd_soc_get_volsw_range, \
        .put = snd_soc_put_volsw_range, \
        .private_value = (unsigned long)&(struct soc_mixer_control) \
-               {.reg = xreg, .shift = xshift, .min = xmin,\
-                .max = xmax, .platform_max = xmax, .invert = xinvert} }
+               {.reg = xreg, .rreg = xreg, .shift = xshift, \
+                .rshift = xshift,  .min = xmin, .max = xmax, \
+                .platform_max = xmax, .invert = xinvert} }
 #define SOC_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
@@ -88,8 +89,9 @@
        .info = snd_soc_info_volsw_range, \
        .get = snd_soc_get_volsw_range, .put = snd_soc_put_volsw_range, \
        .private_value = (unsigned long)&(struct soc_mixer_control) \
-               {.reg = xreg, .shift = xshift, .min = xmin,\
-                .max = xmax, .platform_max = xmax, .invert = xinvert} }
+               {.reg = xreg, .rreg = xreg, .shift = xshift, \
+                .rshift = xshift, .min = xmin, .max = xmax, \
+                .platform_max = xmax, .invert = xinvert} }
 #define SOC_DOUBLE(xname, reg, shift_left, shift_right, max, invert) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
        .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
index 7cae2360221eb0e32f9e3a19dceaec939ade7296..663e34a5383f269d7c54a67f80fe9e73cbff1385 100644 (file)
@@ -174,6 +174,7 @@ typedef unsigned __bitwise__ sense_reason_t;
 
 enum tcm_sense_reason_table {
 #define R(x)   (__force sense_reason_t )(x)
+       TCM_NO_SENSE                            = R(0x00),
        TCM_NON_EXISTENT_LUN                    = R(0x01),
        TCM_UNSUPPORTED_SCSI_OPCODE             = R(0x02),
        TCM_INCORRECT_AMOUNT_OF_DATA            = R(0x03),
index c4d2e9c74002d00b0981157d55576609ba002899..07d59419fe6ba29a03a5cc4bdfe8ffb46acb29ab 100644 (file)
@@ -308,6 +308,8 @@ typedef struct drm_i915_irq_wait {
 #define I915_PARAM_RSVD_FOR_FUTURE_USE  22
 #define I915_PARAM_HAS_SECURE_BATCHES   23
 #define I915_PARAM_HAS_PINNED_BATCHES   24
+#define I915_PARAM_HAS_EXEC_NO_RELOC    25
+#define I915_PARAM_HAS_EXEC_HANDLE_LUT   26
 
 typedef struct drm_i915_getparam {
        int param;
@@ -628,7 +630,11 @@ struct drm_i915_gem_exec_object2 {
        __u64 offset;
 
 #define EXEC_OBJECT_NEEDS_FENCE (1<<0)
+#define EXEC_OBJECT_NEEDS_GTT  (1<<1)
+#define EXEC_OBJECT_WRITE      (1<<2)
+#define __EXEC_OBJECT_UNKNOWN_FLAGS -(EXEC_OBJECT_WRITE<<1)
        __u64 flags;
+
        __u64 rsvd1;
        __u64 rsvd2;
 };
@@ -687,6 +693,20 @@ struct drm_i915_gem_execbuffer2 {
  */
 #define I915_EXEC_IS_PINNED            (1<<10)
 
+/** Provide a hint to the kernel that the command stream and auxilliary
+ * state buffers already holds the correct presumed addresses and so the
+ * relocation process may be skipped if no buffers need to be moved in
+ * preparation for the execbuffer.
+ */
+#define I915_EXEC_NO_RELOC             (1<<11)
+
+/** Use the reloc.handle as an index into the exec object array rather
+ * than as the per-file handle.
+ */
+#define I915_EXEC_HANDLE_LUT           (1<<12)
+
+#define __I915_EXEC_UNKNOWN_FLAGS -(I915_EXEC_HANDLE_LUT<<1)
+
 #define I915_EXEC_CONTEXT_ID_MASK      (0xffffffff)
 #define i915_execbuffer2_set_context_id(eb2, context) \
        (eb2).rsvd1 = context & I915_EXEC_CONTEXT_ID_MASK
similarity index 99%
rename from drivers/staging/omapdrm/omap_drm.h
rename to include/uapi/drm/omap_drm.h
index f0ac34a8973e06f0f1f0286ffb5ae500e3acf2e9..1d0b1172664e8d8666dc49ae9f1690bf6e9feb70 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * include/drm/omap_drm.h
+ * include/uapi/drm/omap_drm.h
  *
  * Copyright (C) 2011 Texas Instruments
  * Author: Rob Clark <rob@ti.com>
index 76352ac45f24bf1290196327ad9af8fb504e4d69..9f096f1c0907b7056bcbe396783fdb2237724a65 100644 (file)
@@ -26,7 +26,6 @@
 
 #include <linux/types.h>
 #include <linux/elf-em.h>
-#include <linux/ptrace.h>
 
 /* The netlink messages for the audit system is divided into blocks:
  * 1000 - 1099 are for commanding the audit system
 #define AUDIT_MMAP             1323    /* Record showing descriptor and flags in mmap */
 #define AUDIT_NETFILTER_PKT    1324    /* Packets traversing netfilter chains */
 #define AUDIT_NETFILTER_CFG    1325    /* Netfilter chain modifications */
+#define AUDIT_SECCOMP          1326    /* Secure Computing event */
 
 #define AUDIT_AVC              1400    /* SE Linux avc denial or grant */
 #define AUDIT_SELINUX_ERR      1401    /* Internal SE Linux Errors */
index 78f99d97475b50c8427607d9bd23a3457a27319a..2c6c85f18ea027440761c780134bb757b0c53c8e 100644 (file)
@@ -50,7 +50,8 @@
 #define PORT_LPC3220   22      /* NXP LPC32xx SoC "Standard" UART */
 #define PORT_8250_CIR  23      /* CIR infrared port, has its own driver */
 #define PORT_XR17V35X  24      /* Exar XR17V35x UARTs */
-#define PORT_MAX_8250  24      /* max port ID */
+#define PORT_BRCM_TRUMANAGE    24
+#define PORT_MAX_8250  25      /* max port ID */
 
 /*
  * ARM specific type numbers.  These are not currently guaranteed
index 50598472dc411ea1b522ac5d499bd7ac8bba37ec..f738e25377ffbc3ed8e794246f03f718bdb1bab4 100644 (file)
 #define USB_INTRF_FUNC_SUSPEND_LP      (1 << (8 + 0))
 #define USB_INTRF_FUNC_SUSPEND_RW      (1 << (8 + 1))
 
+/*
+ * Interface status, Figure 9-5 USB 3.0 spec
+ */
+#define USB_INTRF_STAT_FUNC_RW_CAP     1
+#define USB_INTRF_STAT_FUNC_RW         2
+
 #define USB_ENDPOINT_HALT              0       /* IN/OUT will STALL */
 
 /* Bit array elements as returned by the USB_REQ_GET_STATUS request. */
diff --git a/include/video/display_timing.h b/include/video/display_timing.h
new file mode 100644 (file)
index 0000000..71e9a38
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>
+ *
+ * description of display timings
+ *
+ * This file is released under the GPLv2
+ */
+
+#ifndef __LINUX_DISPLAY_TIMING_H
+#define __LINUX_DISPLAY_TIMING_H
+
+#include <linux/bitops.h>
+#include <linux/types.h>
+
+/* VESA display monitor timing parameters */
+#define VESA_DMT_HSYNC_LOW             BIT(0)
+#define VESA_DMT_HSYNC_HIGH            BIT(1)
+#define VESA_DMT_VSYNC_LOW             BIT(2)
+#define VESA_DMT_VSYNC_HIGH            BIT(3)
+
+/* display specific flags */
+#define DISPLAY_FLAGS_DE_LOW           BIT(0)  /* data enable flag */
+#define DISPLAY_FLAGS_DE_HIGH          BIT(1)
+#define DISPLAY_FLAGS_PIXDATA_POSEDGE  BIT(2)  /* drive data on pos. edge */
+#define DISPLAY_FLAGS_PIXDATA_NEGEDGE  BIT(3)  /* drive data on neg. edge */
+#define DISPLAY_FLAGS_INTERLACED       BIT(4)
+#define DISPLAY_FLAGS_DOUBLESCAN       BIT(5)
+
+/*
+ * A single signal can be specified via a range of minimal and maximal values
+ * with a typical value, that lies somewhere inbetween.
+ */
+struct timing_entry {
+       u32 min;
+       u32 typ;
+       u32 max;
+};
+
+enum timing_entry_index {
+       TE_MIN = 0,
+       TE_TYP = 1,
+       TE_MAX = 2,
+};
+
+/*
+ * Single "mode" entry. This describes one set of signal timings a display can
+ * have in one setting. This struct can later be converted to struct videomode
+ * (see include/video/videomode.h). As each timing_entry can be defined as a
+ * range, one struct display_timing may become multiple struct videomodes.
+ *
+ * Example: hsync active high, vsync active low
+ *
+ *                                 Active Video
+ * Video  ______________________XXXXXXXXXXXXXXXXXXXXXX_____________________
+ *       |<- sync ->|<- back ->|<----- active ----->|<- front ->|<- sync..
+ *       |          |   porch  |                    |   porch   |
+ *
+ * HSync _|¯¯¯¯¯¯¯¯¯¯|___________________________________________|¯¯¯¯¯¯¯¯¯
+ *
+ * VSync Â¯|__________|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_________
+ */
+struct display_timing {
+       struct timing_entry pixelclock;
+
+       struct timing_entry hactive;            /* hor. active video */
+       struct timing_entry hfront_porch;       /* hor. front porch */
+       struct timing_entry hback_porch;        /* hor. back porch */
+       struct timing_entry hsync_len;          /* hor. sync len */
+
+       struct timing_entry vactive;            /* ver. active video */
+       struct timing_entry vfront_porch;       /* ver. front porch */
+       struct timing_entry vback_porch;        /* ver. back porch */
+       struct timing_entry vsync_len;          /* ver. sync len */
+
+       unsigned int dmt_flags;                 /* VESA DMT flags */
+       unsigned int data_flags;                /* video data flags */
+};
+
+/*
+ * This describes all timing settings a display provides.
+ * The native_mode is the default setting for this display.
+ * Drivers that can handle multiple videomodes should work with this struct and
+ * convert each entry to the desired end result.
+ */
+struct display_timings {
+       unsigned int num_timings;
+       unsigned int native_mode;
+
+       struct display_timing **timings;
+};
+
+/* get value specified by index from struct timing_entry */
+static inline u32 display_timing_get_value(const struct timing_entry *te,
+                                          enum timing_entry_index index)
+{
+       switch (index) {
+       case TE_MIN:
+               return te->min;
+               break;
+       case TE_TYP:
+               return te->typ;
+               break;
+       case TE_MAX:
+               return te->max;
+               break;
+       default:
+               return te->typ;
+       }
+}
+
+/* get one entry from struct display_timings */
+static inline struct display_timing *display_timings_get(const struct
+                                                        display_timings *disp,
+                                                        unsigned int index)
+{
+       if (disp->num_timings > index)
+               return disp->timings[index];
+       else
+               return NULL;
+}
+
+void display_timings_release(struct display_timings *disp);
+
+#endif
diff --git a/include/video/of_display_timing.h b/include/video/of_display_timing.h
new file mode 100644 (file)
index 0000000..8016eb7
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>
+ *
+ * display timings of helpers
+ *
+ * This file is released under the GPLv2
+ */
+
+#ifndef __LINUX_OF_DISPLAY_TIMING_H
+#define __LINUX_OF_DISPLAY_TIMING_H
+
+struct device_node;
+struct display_timings;
+
+#define OF_USE_NATIVE_MODE -1
+
+struct display_timings *of_get_display_timings(struct device_node *np);
+int of_display_timings_exist(struct device_node *np);
+
+#endif
diff --git a/include/video/of_videomode.h b/include/video/of_videomode.h
new file mode 100644 (file)
index 0000000..a07efcc
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>
+ *
+ * videomode of-helpers
+ *
+ * This file is released under the GPLv2
+ */
+
+#ifndef __LINUX_OF_VIDEOMODE_H
+#define __LINUX_OF_VIDEOMODE_H
+
+struct device_node;
+struct videomode;
+
+int of_get_videomode(struct device_node *np, struct videomode *vm,
+                    int index);
+
+#endif /* __LINUX_OF_VIDEOMODE_H */
diff --git a/include/video/videomode.h b/include/video/videomode.h
new file mode 100644 (file)
index 0000000..a421562
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>
+ *
+ * generic videomode description
+ *
+ * This file is released under the GPLv2
+ */
+
+#ifndef __LINUX_VIDEOMODE_H
+#define __LINUX_VIDEOMODE_H
+
+#include <linux/types.h>
+#include <video/display_timing.h>
+
+/*
+ * Subsystem independent description of a videomode.
+ * Can be generated from struct display_timing.
+ */
+struct videomode {
+       unsigned long pixelclock;       /* pixelclock in Hz */
+
+       u32 hactive;
+       u32 hfront_porch;
+       u32 hback_porch;
+       u32 hsync_len;
+
+       u32 vactive;
+       u32 vfront_porch;
+       u32 vback_porch;
+       u32 vsync_len;
+
+       unsigned int dmt_flags; /* VESA DMT flags */
+       unsigned int data_flags; /* video data flags */
+};
+
+/**
+ * videomode_from_timing - convert display timing to videomode
+ * @disp: structure with all possible timing entries
+ * @vm: return value
+ * @index: index into the list of display timings in devicetree
+ *
+ * DESCRIPTION:
+ * This function converts a struct display_timing to a struct videomode.
+ */
+int videomode_from_timing(const struct display_timings *disp,
+                         struct videomode *vm, unsigned int index);
+
+#endif
index 7d30240e5bfef76aedc3a8eee2a7991abe0f0a18..be8b7f55312d1f0ea3cbb64c342eb5b456ea34a1 100644 (file)
@@ -1182,7 +1182,7 @@ config CC_OPTIMIZE_FOR_SIZE
          Enabling this option will pass "-Os" instead of "-O2" to gcc
          resulting in a smaller kernel.
 
-         If unsure, say Y.
+         If unsure, say N.
 
 config SYSCTL
        bool
index 5e4ded51788eb81f277c0b5674f5602730220df4..f9acf71b98109434f09b396568caa4afc0ce3e16 100644 (file)
@@ -36,6 +36,10 @@ __setup("noinitrd", no_initrd);
 static int init_linuxrc(struct subprocess_info *info, struct cred *new)
 {
        sys_unshare(CLONE_FS | CLONE_FILES);
+       /* stdin/stdout/stderr for /linuxrc */
+       sys_open("/dev/console", O_RDWR, 0);
+       sys_dup(0);
+       sys_dup(0);
        /* move initrd over / and chdir/chroot in initrd root */
        sys_chdir("/root");
        sys_mount(".", "/", NULL, MS_MOVE, NULL);
index 85d69dffe8647bf284db0bf51ef75f5b318e9eda..cee4b5c66d81e1362fba3c7890eb1c86ab8c1abd 100644 (file)
@@ -604,7 +604,7 @@ asmlinkage void __init start_kernel(void)
        pidmap_init();
        anon_vma_init();
 #ifdef CONFIG_X86
-       if (efi_enabled)
+       if (efi_enabled(EFI_RUNTIME_SERVICES))
                efi_enter_virtual_mode();
 #endif
        thread_info_cache_init();
@@ -632,7 +632,7 @@ asmlinkage void __init start_kernel(void)
        acpi_early_init(); /* before LAPIC and SMP init */
        sfi_init_late();
 
-       if (efi_enabled) {
+       if (efi_enabled(EFI_RUNTIME_SERVICES)) {
                efi_late_init();
                efi_free_boot_services();
        }
@@ -802,7 +802,7 @@ static int run_init_process(const char *init_filename)
                (const char __user *const __user *)envp_init);
 }
 
-static void __init kernel_init_freeable(void);
+static noinline void __init kernel_init_freeable(void);
 
 static int __ref kernel_init(void *unused)
 {
@@ -845,7 +845,7 @@ static int __ref kernel_init(void *unused)
              "See Linux Documentation/init.txt for guidance.");
 }
 
-static void __init kernel_init_freeable(void)
+static noinline void __init kernel_init_freeable(void)
 {
        /*
         * Wait until kthreadd is all set-up.
index 9d31183848582ca8da812d70d2d68364ff898f1d..6f34904a0b5357ba84ae2e8eaa9188c67b1b09d4 100644 (file)
@@ -86,18 +86,27 @@ static atomic_t entry_count;
  */
 static async_cookie_t  __lowest_in_progress(struct async_domain *running)
 {
+       async_cookie_t first_running = next_cookie;     /* infinity value */
+       async_cookie_t first_pending = next_cookie;     /* ditto */
        struct async_entry *entry;
 
+       /*
+        * Both running and pending lists are sorted but not disjoint.
+        * Take the first cookies from both and return the min.
+        */
        if (!list_empty(&running->domain)) {
                entry = list_first_entry(&running->domain, typeof(*entry), list);
-               return entry->cookie;
+               first_running = entry->cookie;
        }
 
-       list_for_each_entry(entry, &async_pending, list)
-               if (entry->running == running)
-                       return entry->cookie;
+       list_for_each_entry(entry, &async_pending, list) {
+               if (entry->running == running) {
+                       first_pending = entry->cookie;
+                       break;
+               }
+       }
 
-       return next_cookie;     /* "infinity" value */
+       return min(first_running, first_pending);
 }
 
 static async_cookie_t  lowest_in_progress(struct async_domain *running)
@@ -118,13 +127,17 @@ static void async_run_entry_fn(struct work_struct *work)
 {
        struct async_entry *entry =
                container_of(work, struct async_entry, work);
+       struct async_entry *pos;
        unsigned long flags;
        ktime_t uninitialized_var(calltime), delta, rettime;
        struct async_domain *running = entry->running;
 
-       /* 1) move self to the running queue */
+       /* 1) move self to the running queue, make sure it stays sorted */
        spin_lock_irqsave(&async_lock, flags);
-       list_move_tail(&entry->list, &running->domain);
+       list_for_each_entry_reverse(pos, &running->domain, list)
+               if (entry->cookie < pos->cookie)
+                       break;
+       list_move_tail(&entry->list, &pos->list);
        spin_unlock_irqrestore(&async_lock, flags);
 
        /* 2) run (and print duration) */
@@ -196,6 +209,9 @@ static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct a
        atomic_inc(&entry_count);
        spin_unlock_irqrestore(&async_lock, flags);
 
+       /* mark that this task has queued an async job, used by module init */
+       current->flags |= PF_USED_ASYNC;
+
        /* schedule for execution */
        queue_work(system_unbound_wq, &entry->work);
 
index 40414e9143db609b194b04e95c798351118f07b7..d596e5355f153d0cac5da1bc7806239a1ec6b860 100644 (file)
@@ -272,6 +272,8 @@ static int audit_log_config_change(char *function_name, int new, int old,
        int rc = 0;
 
        ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
+       if (unlikely(!ab))
+               return rc;
        audit_log_format(ab, "%s=%d old=%d auid=%u ses=%u", function_name, new,
                         old, from_kuid(&init_user_ns, loginuid), sessionid);
        if (sid) {
@@ -619,6 +621,8 @@ static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type,
        }
 
        *ab = audit_log_start(NULL, GFP_KERNEL, msg_type);
+       if (unlikely(!*ab))
+               return rc;
        audit_log_format(*ab, "pid=%d uid=%u auid=%u ses=%u",
                         task_tgid_vnr(current),
                         from_kuid(&init_user_ns, current_uid()),
@@ -1097,6 +1101,23 @@ static inline void audit_get_stamp(struct audit_context *ctx,
        }
 }
 
+/*
+ * Wait for auditd to drain the queue a little
+ */
+static void wait_for_auditd(unsigned long sleep_time)
+{
+       DECLARE_WAITQUEUE(wait, current);
+       set_current_state(TASK_INTERRUPTIBLE);
+       add_wait_queue(&audit_backlog_wait, &wait);
+
+       if (audit_backlog_limit &&
+           skb_queue_len(&audit_skb_queue) > audit_backlog_limit)
+               schedule_timeout(sleep_time);
+
+       __set_current_state(TASK_RUNNING);
+       remove_wait_queue(&audit_backlog_wait, &wait);
+}
+
 /* Obtain an audit buffer.  This routine does locking to obtain the
  * audit buffer, but then no locking is required for calls to
  * audit_log_*format.  If the tsk is a task that is currently in a
@@ -1142,20 +1163,13 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
 
        while (audit_backlog_limit
               && skb_queue_len(&audit_skb_queue) > audit_backlog_limit + reserve) {
-               if (gfp_mask & __GFP_WAIT && audit_backlog_wait_time
-                   && time_before(jiffies, timeout_start + audit_backlog_wait_time)) {
+               if (gfp_mask & __GFP_WAIT && audit_backlog_wait_time) {
+                       unsigned long sleep_time;
 
-                       /* Wait for auditd to drain the queue a little */
-                       DECLARE_WAITQUEUE(wait, current);
-                       set_current_state(TASK_INTERRUPTIBLE);
-                       add_wait_queue(&audit_backlog_wait, &wait);
-
-                       if (audit_backlog_limit &&
-                           skb_queue_len(&audit_skb_queue) > audit_backlog_limit)
-                               schedule_timeout(timeout_start + audit_backlog_wait_time - jiffies);
-
-                       __set_current_state(TASK_RUNNING);
-                       remove_wait_queue(&audit_backlog_wait, &wait);
+                       sleep_time = timeout_start + audit_backlog_wait_time -
+                                       jiffies;
+                       if ((long)sleep_time > 0)
+                               wait_for_auditd(sleep_time);
                        continue;
                }
                if (audit_rate_check() && printk_ratelimit())
index e81175ef25f82d129dbe4c3b5df70f718d59d782..642a89c4f3d60c23cabf89b86d387f2a29b2bea9 100644 (file)
@@ -449,11 +449,26 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree)
        return 0;
 }
 
+static void audit_log_remove_rule(struct audit_krule *rule)
+{
+       struct audit_buffer *ab;
+
+       ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
+       if (unlikely(!ab))
+               return;
+       audit_log_format(ab, "op=");
+       audit_log_string(ab, "remove rule");
+       audit_log_format(ab, " dir=");
+       audit_log_untrustedstring(ab, rule->tree->pathname);
+       audit_log_key(ab, rule->filterkey);
+       audit_log_format(ab, " list=%d res=1", rule->listnr);
+       audit_log_end(ab);
+}
+
 static void kill_rules(struct audit_tree *tree)
 {
        struct audit_krule *rule, *next;
        struct audit_entry *entry;
-       struct audit_buffer *ab;
 
        list_for_each_entry_safe(rule, next, &tree->rules, rlist) {
                entry = container_of(rule, struct audit_entry, rule);
@@ -461,14 +476,7 @@ static void kill_rules(struct audit_tree *tree)
                list_del_init(&rule->rlist);
                if (rule->tree) {
                        /* not a half-baked one */
-                       ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
-                       audit_log_format(ab, "op=");
-                       audit_log_string(ab, "remove rule");
-                       audit_log_format(ab, " dir=");
-                       audit_log_untrustedstring(ab, rule->tree->pathname);
-                       audit_log_key(ab, rule->filterkey);
-                       audit_log_format(ab, " list=%d res=1", rule->listnr);
-                       audit_log_end(ab);
+                       audit_log_remove_rule(rule);
                        rule->tree = NULL;
                        list_del_rcu(&entry->list);
                        list_del(&entry->rule.list);
index 4a599f699adcfeca242e34f1a9691385a500450e..22831c4d369c67d988b1f0702f9db51bac6275bf 100644 (file)
@@ -240,6 +240,8 @@ static void audit_watch_log_rule_change(struct audit_krule *r, struct audit_watc
        if (audit_enabled) {
                struct audit_buffer *ab;
                ab = audit_log_start(NULL, GFP_NOFS, AUDIT_CONFIG_CHANGE);
+               if (unlikely(!ab))
+                       return;
                audit_log_format(ab, "auid=%u ses=%u op=",
                                 from_kuid(&init_user_ns, audit_get_loginuid(current)),
                                 audit_get_sessionid(current));
index 7f19f23d38a3347373ec00a629c6c0b39a8f770a..f9fc54bbe06faa3e845e19871d15ace3e6c5e1b3 100644 (file)
@@ -1144,7 +1144,6 @@ static void audit_log_rule_change(kuid_t loginuid, u32 sessionid, u32 sid,
  * audit_receive_filter - apply all rules to the specified message type
  * @type: audit message type
  * @pid: target pid for netlink audit messages
- * @uid: target uid for netlink audit messages
  * @seq: netlink audit message sequence (serial) number
  * @data: payload data
  * @datasz: size of payload data
index e37e6a12c5e32c204ca93b4422c52ffaee72aad3..a371f857a0a908a40a960511bf047d81ade471a9 100644 (file)
@@ -1464,14 +1464,14 @@ static void show_special(struct audit_context *context, int *call_panic)
                        audit_log_end(ab);
                        ab = audit_log_start(context, GFP_KERNEL,
                                             AUDIT_IPC_SET_PERM);
+                       if (unlikely(!ab))
+                               return;
                        audit_log_format(ab,
                                "qbytes=%lx ouid=%u ogid=%u mode=%#ho",
                                context->ipc.qbytes,
                                context->ipc.perm_uid,
                                context->ipc.perm_gid,
                                context->ipc.perm_mode);
-                       if (!ab)
-                               return;
                }
                break; }
        case AUDIT_MQ_OPEN: {
@@ -2675,7 +2675,7 @@ void __audit_mmap_fd(int fd, int flags)
        context->type = AUDIT_MMAP;
 }
 
-static void audit_log_abend(struct audit_buffer *ab, char *reason, long signr)
+static void audit_log_task(struct audit_buffer *ab)
 {
        kuid_t auid, uid;
        kgid_t gid;
@@ -2693,6 +2693,11 @@ static void audit_log_abend(struct audit_buffer *ab, char *reason, long signr)
        audit_log_task_context(ab);
        audit_log_format(ab, " pid=%d comm=", current->pid);
        audit_log_untrustedstring(ab, current->comm);
+}
+
+static void audit_log_abend(struct audit_buffer *ab, char *reason, long signr)
+{
+       audit_log_task(ab);
        audit_log_format(ab, " reason=");
        audit_log_string(ab, reason);
        audit_log_format(ab, " sig=%ld", signr);
@@ -2715,6 +2720,8 @@ void audit_core_dumps(long signr)
                return;
 
        ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_ANOM_ABEND);
+       if (unlikely(!ab))
+               return;
        audit_log_abend(ab, "memory violation", signr);
        audit_log_end(ab);
 }
@@ -2723,8 +2730,11 @@ void __audit_seccomp(unsigned long syscall, long signr, int code)
 {
        struct audit_buffer *ab;
 
-       ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_ANOM_ABEND);
-       audit_log_abend(ab, "seccomp", signr);
+       ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_SECCOMP);
+       if (unlikely(!ab))
+               return;
+       audit_log_task(ab);
+       audit_log_format(ab, " sig=%ld", signr);
        audit_log_format(ab, " syscall=%ld", syscall);
        audit_log_format(ab, " compat=%d", is_compat_task());
        audit_log_format(ab, " ip=0x%lx", KSTK_EIP(current));
index f6150e92dfc9366488ce45a2b91e98b9fbd7e150..36700e9e2be9125f976963cb1fca4316460dee00 100644 (file)
@@ -535,9 +535,11 @@ asmlinkage long compat_sys_getrusage(int who, struct compat_rusage __user *ru)
        return 0;
 }
 
-asmlinkage long
-compat_sys_wait4(compat_pid_t pid, compat_uint_t __user *stat_addr, int options,
-       struct compat_rusage __user *ru)
+COMPAT_SYSCALL_DEFINE4(wait4,
+       compat_pid_t, pid,
+       compat_uint_t __user *, stat_addr,
+       int, options,
+       struct compat_rusage __user *, ru)
 {
        if (!ru) {
                return sys_wait4(pid, stat_addr, options, NULL);
@@ -564,9 +566,10 @@ compat_sys_wait4(compat_pid_t pid, compat_uint_t __user *stat_addr, int options,
        }
 }
 
-asmlinkage long compat_sys_waitid(int which, compat_pid_t pid,
-               struct compat_siginfo __user *uinfo, int options,
-               struct compat_rusage __user *uru)
+COMPAT_SYSCALL_DEFINE5(waitid,
+               int, which, compat_pid_t, pid,
+               struct compat_siginfo __user *, uinfo, int, options,
+               struct compat_rusage __user *, uru)
 {
        siginfo_t info;
        struct rusage ru;
@@ -584,7 +587,11 @@ asmlinkage long compat_sys_waitid(int which, compat_pid_t pid,
                return ret;
 
        if (uru) {
-               ret = put_compat_rusage(&ru, uru);
+               /* sys_waitid() overwrites everything in ru */
+               if (COMPAT_USE_64BIT_TIME)
+                       ret = copy_to_user(uru, &ru, sizeof(ru));
+               else
+                       ret = put_compat_rusage(&ru, uru);
                if (ret)
                        return ret;
        }
@@ -994,7 +1001,7 @@ compat_sys_rt_sigtimedwait (compat_sigset_t __user *uthese,
        sigset_from_compat(&s, &s32);
 
        if (uts) {
-               if (get_compat_timespec(&t, uts))
+               if (compat_get_timespec(&t, uts))
                        return -EFAULT;
        }
 
index 4d5f8d5612f349389fee0c22442267b6545c3931..8875254120b66f9a0a6e784d83fc6a1e76f6f4c0 100644 (file)
@@ -1970,6 +1970,8 @@ static int kdb_lsmod(int argc, const char **argv)
 
        kdb_printf("Module                  Size  modstruct     Used by\n");
        list_for_each_entry(mod, kdb_modules, list) {
+               if (mod->state == MODULE_STATE_UNFORMED)
+                       continue;
 
                kdb_printf("%-20s%8u  0x%p ", mod->name,
                           mod->core_size, (void *)mod);
index 301079d06f24ebe44081a286766436de104a3a91..7b6646a8c067b4ec9143585746fd2b2bccb73f0c 100644 (file)
@@ -907,6 +907,15 @@ list_add_event(struct perf_event *event, struct perf_event_context *ctx)
                ctx->nr_stat++;
 }
 
+/*
+ * Initialize event state based on the perf_event_attr::disabled.
+ */
+static inline void perf_event__state_init(struct perf_event *event)
+{
+       event->state = event->attr.disabled ? PERF_EVENT_STATE_OFF :
+                                             PERF_EVENT_STATE_INACTIVE;
+}
+
 /*
  * Called at perf_event creation and when events are attached/detached from a
  * group.
@@ -6179,8 +6188,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
        event->overflow_handler = overflow_handler;
        event->overflow_handler_context = context;
 
-       if (attr->disabled)
-               event->state = PERF_EVENT_STATE_OFF;
+       perf_event__state_init(event);
 
        pmu = NULL;
 
@@ -6609,9 +6617,17 @@ SYSCALL_DEFINE5(perf_event_open,
 
                mutex_lock(&gctx->mutex);
                perf_remove_from_context(group_leader);
+
+               /*
+                * Removing from the context ends up with disabled
+                * event. What we want here is event in the initial
+                * startup state, ready to be add into new context.
+                */
+               perf_event__state_init(group_leader);
                list_for_each_entry(sibling, &group_leader->sibling_list,
                                    group_entry) {
                        perf_remove_from_context(sibling);
+                       perf_event__state_init(sibling);
                        put_ctx(gctx);
                }
                mutex_unlock(&gctx->mutex);
index 65ca6d27f24e1065013a428f12f33935b1b18490..c535f33bbb9c9ad108c394618474bbbce1f64f86 100644 (file)
@@ -1668,8 +1668,10 @@ SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
                 int, tls_val)
 #endif
 {
-       return do_fork(clone_flags, newsp, 0,
-               parent_tidptr, child_tidptr);
+       long ret = do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr);
+       asmlinkage_protect(5, ret, clone_flags, newsp,
+                       parent_tidptr, child_tidptr, tls_val);
+       return ret;
 }
 #endif
 
index 250092c1d57d6cf582ef801f70a10969e19abab2..eab08274ec9bf9a1e5b8c83de0f925da0aeaf761 100644 (file)
@@ -188,6 +188,7 @@ struct load_info {
    ongoing or failed initialization etc. */
 static inline int strong_try_module_get(struct module *mod)
 {
+       BUG_ON(mod && mod->state == MODULE_STATE_UNFORMED);
        if (mod && mod->state == MODULE_STATE_COMING)
                return -EBUSY;
        if (try_module_get(mod))
@@ -343,6 +344,9 @@ bool each_symbol_section(bool (*fn)(const struct symsearch *arr,
 #endif
                };
 
+               if (mod->state == MODULE_STATE_UNFORMED)
+                       continue;
+
                if (each_symbol_in_section(arr, ARRAY_SIZE(arr), mod, fn, data))
                        return true;
        }
@@ -450,16 +454,24 @@ const struct kernel_symbol *find_symbol(const char *name,
 EXPORT_SYMBOL_GPL(find_symbol);
 
 /* Search for module by name: must hold module_mutex. */
-struct module *find_module(const char *name)
+static struct module *find_module_all(const char *name,
+                                     bool even_unformed)
 {
        struct module *mod;
 
        list_for_each_entry(mod, &modules, list) {
+               if (!even_unformed && mod->state == MODULE_STATE_UNFORMED)
+                       continue;
                if (strcmp(mod->name, name) == 0)
                        return mod;
        }
        return NULL;
 }
+
+struct module *find_module(const char *name)
+{
+       return find_module_all(name, false);
+}
 EXPORT_SYMBOL_GPL(find_module);
 
 #ifdef CONFIG_SMP
@@ -525,6 +537,8 @@ bool is_module_percpu_address(unsigned long addr)
        preempt_disable();
 
        list_for_each_entry_rcu(mod, &modules, list) {
+               if (mod->state == MODULE_STATE_UNFORMED)
+                       continue;
                if (!mod->percpu_size)
                        continue;
                for_each_possible_cpu(cpu) {
@@ -1048,6 +1062,8 @@ static ssize_t show_initstate(struct module_attribute *mattr,
        case MODULE_STATE_GOING:
                state = "going";
                break;
+       default:
+               BUG();
        }
        return sprintf(buffer, "%s\n", state);
 }
@@ -1786,6 +1802,8 @@ void set_all_modules_text_rw(void)
 
        mutex_lock(&module_mutex);
        list_for_each_entry_rcu(mod, &modules, list) {
+               if (mod->state == MODULE_STATE_UNFORMED)
+                       continue;
                if ((mod->module_core) && (mod->core_text_size)) {
                        set_page_attributes(mod->module_core,
                                                mod->module_core + mod->core_text_size,
@@ -1807,6 +1825,8 @@ void set_all_modules_text_ro(void)
 
        mutex_lock(&module_mutex);
        list_for_each_entry_rcu(mod, &modules, list) {
+               if (mod->state == MODULE_STATE_UNFORMED)
+                       continue;
                if ((mod->module_core) && (mod->core_text_size)) {
                        set_page_attributes(mod->module_core,
                                                mod->module_core + mod->core_text_size,
@@ -2527,6 +2547,13 @@ static int copy_module_from_fd(int fd, struct load_info *info)
                err = -EFBIG;
                goto out;
        }
+
+       /* Don't hand 0 to vmalloc, it whines. */
+       if (stat.size == 0) {
+               err = -EINVAL;
+               goto out;
+       }
+
        info->hdr = vmalloc(stat.size);
        if (!info->hdr) {
                err = -ENOMEM;
@@ -2990,8 +3017,9 @@ static bool finished_loading(const char *name)
        bool ret;
 
        mutex_lock(&module_mutex);
-       mod = find_module(name);
-       ret = !mod || mod->state != MODULE_STATE_COMING;
+       mod = find_module_all(name, true);
+       ret = !mod || mod->state == MODULE_STATE_LIVE
+               || mod->state == MODULE_STATE_GOING;
        mutex_unlock(&module_mutex);
 
        return ret;
@@ -3013,6 +3041,12 @@ static int do_init_module(struct module *mod)
 {
        int ret = 0;
 
+       /*
+        * We want to find out whether @mod uses async during init.  Clear
+        * PF_USED_ASYNC.  async_schedule*() will set it.
+        */
+       current->flags &= ~PF_USED_ASYNC;
+
        blocking_notifier_call_chain(&module_notify_list,
                        MODULE_STATE_COMING, mod);
 
@@ -3058,8 +3092,25 @@ static int do_init_module(struct module *mod)
        blocking_notifier_call_chain(&module_notify_list,
                                     MODULE_STATE_LIVE, mod);
 
-       /* We need to finish all async code before the module init sequence is done */
-       async_synchronize_full();
+       /*
+        * We need to finish all async code before the module init sequence
+        * is done.  This has potential to deadlock.  For example, a newly
+        * detected block device can trigger request_module() of the
+        * default iosched from async probing task.  Once userland helper
+        * reaches here, async_synchronize_full() will wait on the async
+        * task waiting on request_module() and deadlock.
+        *
+        * This deadlock is avoided by perfomring async_synchronize_full()
+        * iff module init queued any async jobs.  This isn't a full
+        * solution as it will deadlock the same if module loading from
+        * async jobs nests more than once; however, due to the various
+        * constraints, this hack seems to be the best option for now.
+        * Please refer to the following thread for details.
+        *
+        * http://thread.gmane.org/gmane.linux.kernel/1420814
+        */
+       if (current->flags & PF_USED_ASYNC)
+               async_synchronize_full();
 
        mutex_lock(&module_mutex);
        /* Drop initial reference. */
@@ -3113,6 +3164,32 @@ static int load_module(struct load_info *info, const char __user *uargs,
                goto free_copy;
        }
 
+       /*
+        * We try to place it in the list now to make sure it's unique
+        * before we dedicate too many resources.  In particular,
+        * temporary percpu memory exhaustion.
+        */
+       mod->state = MODULE_STATE_UNFORMED;
+again:
+       mutex_lock(&module_mutex);
+       if ((old = find_module_all(mod->name, true)) != NULL) {
+               if (old->state == MODULE_STATE_COMING
+                   || old->state == MODULE_STATE_UNFORMED) {
+                       /* Wait in case it fails to load. */
+                       mutex_unlock(&module_mutex);
+                       err = wait_event_interruptible(module_wq,
+                                              finished_loading(mod->name));
+                       if (err)
+                               goto free_module;
+                       goto again;
+               }
+               err = -EEXIST;
+               mutex_unlock(&module_mutex);
+               goto free_module;
+       }
+       list_add_rcu(&mod->list, &modules);
+       mutex_unlock(&module_mutex);
+
 #ifdef CONFIG_MODULE_SIG
        mod->sig_ok = info->sig_ok;
        if (!mod->sig_ok)
@@ -3122,7 +3199,7 @@ static int load_module(struct load_info *info, const char __user *uargs,
        /* Now module is in final location, initialize linked lists, etc. */
        err = module_unload_init(mod);
        if (err)
-               goto free_module;
+               goto unlink_mod;
 
        /* Now we've got everything in the final locations, we can
         * find optional sections. */
@@ -3157,54 +3234,33 @@ static int load_module(struct load_info *info, const char __user *uargs,
                goto free_arch_cleanup;
        }
 
-       /* Mark state as coming so strong_try_module_get() ignores us. */
-       mod->state = MODULE_STATE_COMING;
-
-       /* Now sew it into the lists so we can get lockdep and oops
-        * info during argument parsing.  No one should access us, since
-        * strong_try_module_get() will fail.
-        * lockdep/oops can run asynchronous, so use the RCU list insertion
-        * function to insert in a way safe to concurrent readers.
-        * The mutex protects against concurrent writers.
-        */
-again:
-       mutex_lock(&module_mutex);
-       if ((old = find_module(mod->name)) != NULL) {
-               if (old->state == MODULE_STATE_COMING) {
-                       /* Wait in case it fails to load. */
-                       mutex_unlock(&module_mutex);
-                       err = wait_event_interruptible(module_wq,
-                                              finished_loading(mod->name));
-                       if (err)
-                               goto free_arch_cleanup;
-                       goto again;
-               }
-               err = -EEXIST;
-               goto unlock;
-       }
-
-       /* This has to be done once we're sure module name is unique. */
        dynamic_debug_setup(info->debug, info->num_debug);
 
-       /* Find duplicate symbols */
+       mutex_lock(&module_mutex);
+       /* Find duplicate symbols (must be called under lock). */
        err = verify_export_symbols(mod);
        if (err < 0)
-               goto ddebug;
+               goto ddebug_cleanup;
 
+       /* This relies on module_mutex for list integrity. */
        module_bug_finalize(info->hdr, info->sechdrs, mod);
-       list_add_rcu(&mod->list, &modules);
+
+       /* Mark state as coming so strong_try_module_get() ignores us,
+        * but kallsyms etc. can see us. */
+       mod->state = MODULE_STATE_COMING;
+
        mutex_unlock(&module_mutex);
 
        /* Module is ready to execute: parsing args may do that. */
        err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp,
                         -32768, 32767, &ddebug_dyndbg_module_param_cb);
        if (err < 0)
-               goto unlink;
+               goto bug_cleanup;
 
        /* Link in to syfs. */
        err = mod_sysfs_setup(mod, info, mod->kp, mod->num_kp);
        if (err < 0)
-               goto unlink;
+               goto bug_cleanup;
 
        /* Get rid of temporary copy. */
        free_copy(info);
@@ -3214,16 +3270,13 @@ again:
 
        return do_init_module(mod);
 
- unlink:
+ bug_cleanup:
+       /* module_bug_cleanup needs module_mutex protection */
        mutex_lock(&module_mutex);
-       /* Unlink carefully: kallsyms could be walking list. */
-       list_del_rcu(&mod->list);
        module_bug_cleanup(mod);
-       wake_up_all(&module_wq);
- ddebug:
-       dynamic_debug_remove(info->debug);
- unlock:
+ ddebug_cleanup:
        mutex_unlock(&module_mutex);
+       dynamic_debug_remove(info->debug);
        synchronize_sched();
        kfree(mod->args);
  free_arch_cleanup:
@@ -3232,6 +3285,12 @@ again:
        free_modinfo(mod);
  free_unload:
        module_unload_free(mod);
+ unlink_mod:
+       mutex_lock(&module_mutex);
+       /* Unlink carefully: kallsyms could be walking list. */
+       list_del_rcu(&mod->list);
+       wake_up_all(&module_wq);
+       mutex_unlock(&module_mutex);
  free_module:
        module_deallocate(mod, info);
  free_copy:
@@ -3354,6 +3413,8 @@ const char *module_address_lookup(unsigned long addr,
 
        preempt_disable();
        list_for_each_entry_rcu(mod, &modules, list) {
+               if (mod->state == MODULE_STATE_UNFORMED)
+                       continue;
                if (within_module_init(addr, mod) ||
                    within_module_core(addr, mod)) {
                        if (modname)
@@ -3377,6 +3438,8 @@ int lookup_module_symbol_name(unsigned long addr, char *symname)
 
        preempt_disable();
        list_for_each_entry_rcu(mod, &modules, list) {
+               if (mod->state == MODULE_STATE_UNFORMED)
+                       continue;
                if (within_module_init(addr, mod) ||
                    within_module_core(addr, mod)) {
                        const char *sym;
@@ -3401,6 +3464,8 @@ int lookup_module_symbol_attrs(unsigned long addr, unsigned long *size,
 
        preempt_disable();
        list_for_each_entry_rcu(mod, &modules, list) {
+               if (mod->state == MODULE_STATE_UNFORMED)
+                       continue;
                if (within_module_init(addr, mod) ||
                    within_module_core(addr, mod)) {
                        const char *sym;
@@ -3428,6 +3493,8 @@ int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
 
        preempt_disable();
        list_for_each_entry_rcu(mod, &modules, list) {
+               if (mod->state == MODULE_STATE_UNFORMED)
+                       continue;
                if (symnum < mod->num_symtab) {
                        *value = mod->symtab[symnum].st_value;
                        *type = mod->symtab[symnum].st_info;
@@ -3470,9 +3537,12 @@ unsigned long module_kallsyms_lookup_name(const char *name)
                        ret = mod_find_symname(mod, colon+1);
                *colon = ':';
        } else {
-               list_for_each_entry_rcu(mod, &modules, list)
+               list_for_each_entry_rcu(mod, &modules, list) {
+                       if (mod->state == MODULE_STATE_UNFORMED)
+                               continue;
                        if ((ret = mod_find_symname(mod, name)) != 0)
                                break;
+               }
        }
        preempt_enable();
        return ret;
@@ -3487,6 +3557,8 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
        int ret;
 
        list_for_each_entry(mod, &modules, list) {
+               if (mod->state == MODULE_STATE_UNFORMED)
+                       continue;
                for (i = 0; i < mod->num_symtab; i++) {
                        ret = fn(data, mod->strtab + mod->symtab[i].st_name,
                                 mod, mod->symtab[i].st_value);
@@ -3502,6 +3574,7 @@ static char *module_flags(struct module *mod, char *buf)
 {
        int bx = 0;
 
+       BUG_ON(mod->state == MODULE_STATE_UNFORMED);
        if (mod->taints ||
            mod->state == MODULE_STATE_GOING ||
            mod->state == MODULE_STATE_COMING) {
@@ -3543,6 +3616,10 @@ static int m_show(struct seq_file *m, void *p)
        struct module *mod = list_entry(p, struct module, list);
        char buf[8];
 
+       /* We always ignore unformed modules. */
+       if (mod->state == MODULE_STATE_UNFORMED)
+               return 0;
+
        seq_printf(m, "%s %u",
                   mod->name, mod->init_size + mod->core_size);
        print_unload_info(m, mod);
@@ -3603,6 +3680,8 @@ const struct exception_table_entry *search_module_extables(unsigned long addr)
 
        preempt_disable();
        list_for_each_entry_rcu(mod, &modules, list) {
+               if (mod->state == MODULE_STATE_UNFORMED)
+                       continue;
                if (mod->num_exentries == 0)
                        continue;
 
@@ -3651,10 +3730,13 @@ struct module *__module_address(unsigned long addr)
        if (addr < module_addr_min || addr > module_addr_max)
                return NULL;
 
-       list_for_each_entry_rcu(mod, &modules, list)
+       list_for_each_entry_rcu(mod, &modules, list) {
+               if (mod->state == MODULE_STATE_UNFORMED)
+                       continue;
                if (within_module_core(addr, mod)
                    || within_module_init(addr, mod))
                        return mod;
+       }
        return NULL;
 }
 EXPORT_SYMBOL_GPL(__module_address);
@@ -3707,8 +3789,11 @@ void print_modules(void)
        printk(KERN_DEFAULT "Modules linked in:");
        /* Most callers should already have preempt disabled, but make sure */
        preempt_disable();
-       list_for_each_entry_rcu(mod, &modules, list)
+       list_for_each_entry_rcu(mod, &modules, list) {
+               if (mod->state == MODULE_STATE_UNFORMED)
+                       continue;
                printk(" %s%s", mod->name, module_flags(mod, buf));
+       }
        preempt_enable();
        if (last_unloaded_module[0])
                printk(" [last unloaded: %s]", last_unloaded_module);
index 1599157336a6c2be56037a510b7c2c28648201b8..6cbeaae4406d3bff3ce60394206e041c78e1c0bc 100644 (file)
@@ -117,11 +117,45 @@ void __ptrace_unlink(struct task_struct *child)
         * TASK_KILLABLE sleeps.
         */
        if (child->jobctl & JOBCTL_STOP_PENDING || task_is_traced(child))
-               signal_wake_up(child, task_is_traced(child));
+               ptrace_signal_wake_up(child, true);
 
        spin_unlock(&child->sighand->siglock);
 }
 
+/* Ensure that nothing can wake it up, even SIGKILL */
+static bool ptrace_freeze_traced(struct task_struct *task)
+{
+       bool ret = false;
+
+       /* Lockless, nobody but us can set this flag */
+       if (task->jobctl & JOBCTL_LISTENING)
+               return ret;
+
+       spin_lock_irq(&task->sighand->siglock);
+       if (task_is_traced(task) && !__fatal_signal_pending(task)) {
+               task->state = __TASK_TRACED;
+               ret = true;
+       }
+       spin_unlock_irq(&task->sighand->siglock);
+
+       return ret;
+}
+
+static void ptrace_unfreeze_traced(struct task_struct *task)
+{
+       if (task->state != __TASK_TRACED)
+               return;
+
+       WARN_ON(!task->ptrace || task->parent != current);
+
+       spin_lock_irq(&task->sighand->siglock);
+       if (__fatal_signal_pending(task))
+               wake_up_state(task, __TASK_TRACED);
+       else
+               task->state = TASK_TRACED;
+       spin_unlock_irq(&task->sighand->siglock);
+}
+
 /**
  * ptrace_check_attach - check whether ptracee is ready for ptrace operation
  * @child: ptracee to check for
@@ -139,7 +173,7 @@ void __ptrace_unlink(struct task_struct *child)
  * RETURNS:
  * 0 on success, -ESRCH if %child is not ready.
  */
-int ptrace_check_attach(struct task_struct *child, bool ignore_state)
+static int ptrace_check_attach(struct task_struct *child, bool ignore_state)
 {
        int ret = -ESRCH;
 
@@ -151,24 +185,29 @@ int ptrace_check_attach(struct task_struct *child, bool ignore_state)
         * be changed by us so it's not changing right after this.
         */
        read_lock(&tasklist_lock);
-       if ((child->ptrace & PT_PTRACED) && child->parent == current) {
+       if (child->ptrace && child->parent == current) {
+               WARN_ON(child->state == __TASK_TRACED);
                /*
                 * child->sighand can't be NULL, release_task()
                 * does ptrace_unlink() before __exit_signal().
                 */
-               spin_lock_irq(&child->sighand->siglock);
-               WARN_ON_ONCE(task_is_stopped(child));
-               if (ignore_state || (task_is_traced(child) &&
-                                    !(child->jobctl & JOBCTL_LISTENING)))
+               if (ignore_state || ptrace_freeze_traced(child))
                        ret = 0;
-               spin_unlock_irq(&child->sighand->siglock);
        }
        read_unlock(&tasklist_lock);
 
-       if (!ret && !ignore_state)
-               ret = wait_task_inactive(child, TASK_TRACED) ? 0 : -ESRCH;
+       if (!ret && !ignore_state) {
+               if (!wait_task_inactive(child, __TASK_TRACED)) {
+                       /*
+                        * This can only happen if may_ptrace_stop() fails and
+                        * ptrace_stop() changes ->state back to TASK_RUNNING,
+                        * so we should not worry about leaking __TASK_TRACED.
+                        */
+                       WARN_ON(child->state == __TASK_TRACED);
+                       ret = -ESRCH;
+               }
+       }
 
-       /* All systems go.. */
        return ret;
 }
 
@@ -317,7 +356,7 @@ static int ptrace_attach(struct task_struct *task, long request,
         */
        if (task_is_stopped(task) &&
            task_set_jobctl_pending(task, JOBCTL_TRAP_STOP | JOBCTL_TRAPPING))
-               signal_wake_up(task, 1);
+               signal_wake_up_state(task, __TASK_STOPPED);
 
        spin_unlock(&task->sighand->siglock);
 
@@ -737,7 +776,7 @@ int ptrace_request(struct task_struct *child, long request,
                 * tracee into STOP.
                 */
                if (likely(task_set_jobctl_pending(child, JOBCTL_TRAP_STOP)))
-                       signal_wake_up(child, child->jobctl & JOBCTL_LISTENING);
+                       ptrace_signal_wake_up(child, child->jobctl & JOBCTL_LISTENING);
 
                unlock_task_sighand(child, &flags);
                ret = 0;
@@ -763,7 +802,7 @@ int ptrace_request(struct task_struct *child, long request,
                         * start of this trap and now.  Trigger re-trap.
                         */
                        if (child->jobctl & JOBCTL_TRAP_NOTIFY)
-                               signal_wake_up(child, true);
+                               ptrace_signal_wake_up(child, true);
                        ret = 0;
                }
                unlock_task_sighand(child, &flags);
@@ -900,6 +939,8 @@ SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr,
                goto out_put_task_struct;
 
        ret = arch_ptrace(child, request, addr, data);
+       if (ret || request != PTRACE_DETACH)
+               ptrace_unfreeze_traced(child);
 
  out_put_task_struct:
        put_task_struct(child);
@@ -1039,8 +1080,11 @@ asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
 
        ret = ptrace_check_attach(child, request == PTRACE_KILL ||
                                  request == PTRACE_INTERRUPT);
-       if (!ret)
+       if (!ret) {
                ret = compat_arch_ptrace(child, request, addr, data);
+               if (ret || request != PTRACE_DETACH)
+                       ptrace_unfreeze_traced(child);
+       }
 
  out_put_task_struct:
        put_task_struct(child);
index f6e5ec2932b4aed0ff40016e8fdf77e5b9c1f366..c1cc7e17ff9d9dc29db596bca0468056bd3f0665 100644 (file)
@@ -40,8 +40,7 @@
 #ifdef CONFIG_RCU_NOCB_CPU
 static cpumask_var_t rcu_nocb_mask; /* CPUs to have callbacks offloaded. */
 static bool have_rcu_nocb_mask;            /* Was rcu_nocb_mask allocated? */
-static bool rcu_nocb_poll;         /* Offload kthread are to poll. */
-module_param(rcu_nocb_poll, bool, 0444);
+static bool __read_mostly rcu_nocb_poll;    /* Offload kthread are to poll. */
 static char __initdata nocb_buf[NR_CPUS * 5];
 #endif /* #ifdef CONFIG_RCU_NOCB_CPU */
 
@@ -2159,6 +2158,13 @@ static int __init rcu_nocb_setup(char *str)
 }
 __setup("rcu_nocbs=", rcu_nocb_setup);
 
+static int __init parse_rcu_nocb_poll(char *arg)
+{
+       rcu_nocb_poll = 1;
+       return 0;
+}
+early_param("rcu_nocb_poll", parse_rcu_nocb_poll);
+
 /* Is the specified CPU a no-CPUs CPU? */
 static bool is_nocb_cpu(int cpu)
 {
@@ -2366,10 +2372,11 @@ static int rcu_nocb_kthread(void *arg)
        for (;;) {
                /* If not polling, wait for next batch of callbacks. */
                if (!rcu_nocb_poll)
-                       wait_event(rdp->nocb_wq, rdp->nocb_head);
+                       wait_event_interruptible(rdp->nocb_wq, rdp->nocb_head);
                list = ACCESS_ONCE(rdp->nocb_head);
                if (!list) {
                        schedule_timeout_interruptible(1);
+                       flush_signals(current);
                        continue;
                }
 
index 6850f53e02d82e023954df56f5230b4033e80b73..b3c6c3fcd8474237a2e41ab4db87ee04c2435b7b 100644 (file)
@@ -116,6 +116,16 @@ void down_read_nested(struct rw_semaphore *sem, int subclass)
 
 EXPORT_SYMBOL(down_read_nested);
 
+void _down_write_nest_lock(struct rw_semaphore *sem, struct lockdep_map *nest)
+{
+       might_sleep();
+       rwsem_acquire_nest(&sem->dep_map, 0, 0, nest, _RET_IP_);
+
+       LOCK_CONTENDED(sem, __down_write_trylock, __down_write);
+}
+
+EXPORT_SYMBOL(_down_write_nest_lock);
+
 void down_write_nested(struct rw_semaphore *sem, int subclass)
 {
        might_sleep();
index 257002c13bb02acad92c74347e3b38ca3bc881b1..26058d0bebba1ca927c4df3e993190d5415f006c 100644 (file)
@@ -1523,7 +1523,8 @@ out:
  */
 int wake_up_process(struct task_struct *p)
 {
-       return try_to_wake_up(p, TASK_ALL, 0);
+       WARN_ON(task_is_stopped_or_traced(p));
+       return try_to_wake_up(p, TASK_NORMAL, 0);
 }
 EXPORT_SYMBOL(wake_up_process);
 
index 2cd3c1b4e582857eefb87fa9a98fb9cad11509a6..7ae4c4c5420e65b4aea1625d37a8a28b663072fb 100644 (file)
@@ -222,8 +222,8 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq)
                        cfs_rq->runnable_load_avg);
        SEQ_printf(m, "  .%-30s: %lld\n", "blocked_load_avg",
                        cfs_rq->blocked_load_avg);
-       SEQ_printf(m, "  .%-30s: %ld\n", "tg_load_avg",
-                       atomic64_read(&cfs_rq->tg->load_avg));
+       SEQ_printf(m, "  .%-30s: %lld\n", "tg_load_avg",
+                       (unsigned long long)atomic64_read(&cfs_rq->tg->load_avg));
        SEQ_printf(m, "  .%-30s: %lld\n", "tg_load_contrib",
                        cfs_rq->tg_load_contrib);
        SEQ_printf(m, "  .%-30s: %d\n", "tg_runnable_contrib",
index 5eea8707234a15167eeaabbc5881f04cf0345ca1..81fa53643409de962e44fa99c44f5827c729cdb4 100644 (file)
@@ -2663,7 +2663,7 @@ static void destroy_cfs_bandwidth(struct cfs_bandwidth *cfs_b)
        hrtimer_cancel(&cfs_b->slack_timer);
 }
 
-static void unthrottle_offline_cfs_rqs(struct rq *rq)
+static void __maybe_unused unthrottle_offline_cfs_rqs(struct rq *rq)
 {
        struct cfs_rq *cfs_rq;
 
index 418feb01344edb7e59f11643e153e2f1866cc4ed..4f02b2847357537559cdb88c0f47f68e7837eab3 100644 (file)
@@ -566,7 +566,7 @@ static inline struct rt_bandwidth *sched_rt_bandwidth(struct rt_rq *rt_rq)
 static int do_balance_runtime(struct rt_rq *rt_rq)
 {
        struct rt_bandwidth *rt_b = sched_rt_bandwidth(rt_rq);
-       struct root_domain *rd = cpu_rq(smp_processor_id())->rd;
+       struct root_domain *rd = rq_of_rt_rq(rt_rq)->rd;
        int i, weight, more = 0;
        u64 rt_period;
 
index 372771e948c230141a767003fa0f2e18c0d44db2..3d09cf6cde756671655c96f2f81a43438eb62b7e 100644 (file)
@@ -680,23 +680,17 @@ int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
  * No need to set need_resched since signal event passing
  * goes through ->blocked
  */
-void signal_wake_up(struct task_struct *t, int resume)
+void signal_wake_up_state(struct task_struct *t, unsigned int state)
 {
-       unsigned int mask;
-
        set_tsk_thread_flag(t, TIF_SIGPENDING);
-
        /*
-        * For SIGKILL, we want to wake it up in the stopped/traced/killable
+        * TASK_WAKEKILL also means wake it up in the stopped/traced/killable
         * case. We don't check t->state here because there is a race with it
         * executing another processor and just now entering stopped state.
         * By using wake_up_state, we ensure the process will wake up and
         * handle its death signal.
         */
-       mask = TASK_INTERRUPTIBLE;
-       if (resume)
-               mask |= TASK_WAKEKILL;
-       if (!wake_up_state(t, mask))
+       if (!wake_up_state(t, state | TASK_INTERRUPTIBLE))
                kick_process(t);
 }
 
@@ -844,7 +838,7 @@ static void ptrace_trap_notify(struct task_struct *t)
        assert_spin_locked(&t->sighand->siglock);
 
        task_set_jobctl_pending(t, JOBCTL_TRAP_NOTIFY);
-       signal_wake_up(t, t->jobctl & JOBCTL_LISTENING);
+       ptrace_signal_wake_up(t, t->jobctl & JOBCTL_LISTENING);
 }
 
 /*
@@ -1800,6 +1794,10 @@ static inline int may_ptrace_stop(void)
         * If SIGKILL was already sent before the caller unlocked
         * ->siglock we must see ->core_state != NULL. Otherwise it
         * is safe to enter schedule().
+        *
+        * This is almost outdated, a task with the pending SIGKILL can't
+        * block in TASK_TRACED. But PTRACE_EVENT_EXIT can be reported
+        * after SIGKILL was already dequeued.
         */
        if (unlikely(current->mm->core_state) &&
            unlikely(current->mm == current->parent->mm))
@@ -1925,6 +1923,7 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info)
                if (gstop_done)
                        do_notify_parent_cldstop(current, false, why);
 
+               /* tasklist protects us from ptrace_freeze_traced() */
                __set_current_state(TASK_RUNNING);
                if (clear_code)
                        current->exit_code = 0;
@@ -3116,8 +3115,9 @@ int __save_altstack(stack_t __user *uss, unsigned long sp)
 
 #ifdef CONFIG_COMPAT
 #ifdef CONFIG_GENERIC_SIGALTSTACK
-asmlinkage long compat_sys_sigaltstack(const compat_stack_t __user *uss_ptr,
-                                      compat_stack_t __user *uoss_ptr)
+COMPAT_SYSCALL_DEFINE2(sigaltstack,
+                       const compat_stack_t __user *, uss_ptr,
+                       compat_stack_t __user *, uoss_ptr)
 {
        stack_t uss, uoss;
        int ret;
index 29dd40a9f2f403ab86f39c96ddb505851b1d36c2..69f38bd98b423a53ada73c7a6b7a2217e91a77b4 100644 (file)
@@ -33,6 +33,7 @@ struct call_function_data {
        struct call_single_data csd;
        atomic_t                refs;
        cpumask_var_t           cpumask;
+       cpumask_var_t           cpumask_ipi;
 };
 
 static DEFINE_PER_CPU_SHARED_ALIGNED(struct call_function_data, cfd_data);
@@ -56,6 +57,9 @@ hotplug_cfd(struct notifier_block *nfb, unsigned long action, void *hcpu)
                if (!zalloc_cpumask_var_node(&cfd->cpumask, GFP_KERNEL,
                                cpu_to_node(cpu)))
                        return notifier_from_errno(-ENOMEM);
+               if (!zalloc_cpumask_var_node(&cfd->cpumask_ipi, GFP_KERNEL,
+                               cpu_to_node(cpu)))
+                       return notifier_from_errno(-ENOMEM);
                break;
 
 #ifdef CONFIG_HOTPLUG_CPU
@@ -65,6 +69,7 @@ hotplug_cfd(struct notifier_block *nfb, unsigned long action, void *hcpu)
        case CPU_DEAD:
        case CPU_DEAD_FROZEN:
                free_cpumask_var(cfd->cpumask);
+               free_cpumask_var(cfd->cpumask_ipi);
                break;
 #endif
        };
@@ -526,6 +531,12 @@ void smp_call_function_many(const struct cpumask *mask,
                return;
        }
 
+       /*
+        * After we put an entry into the list, data->cpumask
+        * may be cleared again when another CPU sends another IPI for
+        * a SMP function call, so data->cpumask will be zero.
+        */
+       cpumask_copy(data->cpumask_ipi, data->cpumask);
        raw_spin_lock_irqsave(&call_function.lock, flags);
        /*
         * Place entry at the _HEAD_ of the list, so that any cpu still
@@ -549,7 +560,7 @@ void smp_call_function_many(const struct cpumask *mask,
        smp_mb();
 
        /* Send a message to all CPUs in the map */
-       arch_send_call_function_ipi_mask(data->cpumask);
+       arch_send_call_function_ipi_mask(data->cpumask_ipi);
 
        /* Optionally wait for the CPUs to complete */
        if (wait)
index 3ffe4c5ad3f37353d45d828407fb484ab811e441..41473b4ad7a47c4ef41672da3ecf797bb722a89b 100644 (file)
@@ -3998,7 +3998,7 @@ static int ftrace_module_notify(struct notifier_block *self,
 
 struct notifier_block ftrace_module_nb = {
        .notifier_call = ftrace_module_notify,
-       .priority = 0,
+       .priority = INT_MAX,    /* Run before anything that can use kprobes */
 };
 
 extern unsigned long __start_mcount_loc[];
index 1bbfa0446507405d2fce92036322224170d7411e..3c13e46d7d24e1ecbdeb532f6538354f6eca7ddf 100644 (file)
@@ -3454,7 +3454,7 @@ static int tracing_wait_pipe(struct file *filp)
                        return -EINTR;
 
                /*
-                * We block until we read something and tracing is enabled.
+                * We block until we read something and tracing is disabled.
                 * We still block if tracing is disabled, but we have never
                 * read anything. This allows a user to cat this file, and
                 * then enable tracing. But after we have read something,
@@ -3462,7 +3462,7 @@ static int tracing_wait_pipe(struct file *filp)
                 *
                 * iter->pos will be 0 if we haven't read anything.
                 */
-               if (tracing_is_enabled() && iter->pos)
+               if (!tracing_is_enabled() && iter->pos)
                        break;
        }
 
@@ -4817,10 +4817,17 @@ rb_simple_write(struct file *filp, const char __user *ubuf,
                return ret;
 
        if (buffer) {
-               if (val)
+               mutex_lock(&trace_types_lock);
+               if (val) {
                        ring_buffer_record_on(buffer);
-               else
+                       if (current_trace->start)
+                               current_trace->start(tr);
+               } else {
                        ring_buffer_record_off(buffer);
+                       if (current_trace->stop)
+                               current_trace->stop(tr);
+               }
+               mutex_unlock(&trace_types_lock);
        }
 
        (*ppos)++;
index a28c1415357cac9d30fc09195652fbfd099bc421..d0cdf14c651ae629cba6731debb3b6a5edee39bf 100644 (file)
--- a/lib/bug.c
+++ b/lib/bug.c
@@ -55,6 +55,7 @@ static inline unsigned long bug_addr(const struct bug_entry *bug)
 }
 
 #ifdef CONFIG_MODULES
+/* Updates are protected by module mutex */
 static LIST_HEAD(module_bug_list);
 
 static const struct bug_entry *module_find_bug(unsigned long bugaddr)
index 145dec5267c91a6989402acc51ec50895e488988..5fbed5caba6e833222a62952cb4ba6e3430c053e 100644 (file)
@@ -45,6 +45,7 @@ struct cpu_rmap *alloc_cpu_rmap(unsigned int size, gfp_t flags)
        if (!rmap)
                return NULL;
 
+       kref_init(&rmap->refcount);
        rmap->obj = (void **)((char *)rmap + obj_offset);
 
        /* Initially assign CPUs to objects on a rota, since we have
@@ -63,6 +64,35 @@ struct cpu_rmap *alloc_cpu_rmap(unsigned int size, gfp_t flags)
 }
 EXPORT_SYMBOL(alloc_cpu_rmap);
 
+/**
+ * cpu_rmap_release - internal reclaiming helper called from kref_put
+ * @ref: kref to struct cpu_rmap
+ */
+static void cpu_rmap_release(struct kref *ref)
+{
+       struct cpu_rmap *rmap = container_of(ref, struct cpu_rmap, refcount);
+       kfree(rmap);
+}
+
+/**
+ * cpu_rmap_get - internal helper to get new ref on a cpu_rmap
+ * @rmap: reverse-map allocated with alloc_cpu_rmap()
+ */
+static inline void cpu_rmap_get(struct cpu_rmap *rmap)
+{
+       kref_get(&rmap->refcount);
+}
+
+/**
+ * cpu_rmap_put - release ref on a cpu_rmap
+ * @rmap: reverse-map allocated with alloc_cpu_rmap()
+ */
+int cpu_rmap_put(struct cpu_rmap *rmap)
+{
+       return kref_put(&rmap->refcount, cpu_rmap_release);
+}
+EXPORT_SYMBOL(cpu_rmap_put);
+
 /* Reevaluate nearest object for given CPU, comparing with the given
  * neighbours at the given distance.
  */
@@ -197,8 +227,7 @@ struct irq_glue {
  * free_irq_cpu_rmap - free a CPU affinity reverse-map used for IRQs
  * @rmap: Reverse-map allocated with alloc_irq_cpu_map(), or %NULL
  *
- * Must be called in process context, before freeing the IRQs, and
- * without holding any locks required by global workqueue items.
+ * Must be called in process context, before freeing the IRQs.
  */
 void free_irq_cpu_rmap(struct cpu_rmap *rmap)
 {
@@ -212,12 +241,18 @@ void free_irq_cpu_rmap(struct cpu_rmap *rmap)
                glue = rmap->obj[index];
                irq_set_affinity_notifier(glue->notify.irq, NULL);
        }
-       irq_run_affinity_notifiers();
 
-       kfree(rmap);
+       cpu_rmap_put(rmap);
 }
 EXPORT_SYMBOL(free_irq_cpu_rmap);
 
+/**
+ * irq_cpu_rmap_notify - callback for IRQ subsystem when IRQ affinity updated
+ * @notify: struct irq_affinity_notify passed by irq/manage.c
+ * @mask: cpu mask for new SMP affinity
+ *
+ * This is executed in workqueue context.
+ */
 static void
 irq_cpu_rmap_notify(struct irq_affinity_notify *notify, const cpumask_t *mask)
 {
@@ -230,10 +265,16 @@ irq_cpu_rmap_notify(struct irq_affinity_notify *notify, const cpumask_t *mask)
                pr_warning("irq_cpu_rmap_notify: update failed: %d\n", rc);
 }
 
+/**
+ * irq_cpu_rmap_release - reclaiming callback for IRQ subsystem
+ * @ref: kref to struct irq_affinity_notify passed by irq/manage.c
+ */
 static void irq_cpu_rmap_release(struct kref *ref)
 {
        struct irq_glue *glue =
                container_of(ref, struct irq_glue, notify.kref);
+
+       cpu_rmap_put(glue->rmap);
        kfree(glue);
 }
 
@@ -258,10 +299,13 @@ int irq_cpu_rmap_add(struct cpu_rmap *rmap, int irq)
        glue->notify.notify = irq_cpu_rmap_notify;
        glue->notify.release = irq_cpu_rmap_release;
        glue->rmap = rmap;
+       cpu_rmap_get(rmap);
        glue->index = cpu_rmap_add(rmap, glue);
        rc = irq_set_affinity_notifier(irq, &glue->notify);
-       if (rc)
+       if (rc) {
+               cpu_rmap_put(glue->rmap);
                kfree(glue);
+       }
        return rc;
 }
 EXPORT_SYMBOL(irq_cpu_rmap_add);
index 8c0e62975c88d49a09c9c29ab9e7a2b1334a6587..dc2be7ed1765b0dc3675c97b2cf862dc969dcd90 100644 (file)
@@ -162,6 +162,8 @@ static int digsig_verify_rsa(struct key *key,
        memset(out1, 0, head);
        memcpy(out1 + head, p, l);
 
+       kfree(p);
+
        err = pkcs_1_v1_5_decode_emsa(out1, len, mblen, out2, &len);
        if (err)
                goto err;
index 4f56a11d67fa9da105b34337280277d6fe437b2e..c0e31fe2fabf5b99c160fb01daf618cb7c0488b3 100644 (file)
@@ -194,8 +194,12 @@ __rb_insert(struct rb_node *node, struct rb_root *root,
        }
 }
 
-__always_inline void
-__rb_erase_color(struct rb_node *parent, struct rb_root *root,
+/*
+ * Inline version for rb_erase() use - we want to be able to inline
+ * and eliminate the dummy_rotate callback there
+ */
+static __always_inline void
+____rb_erase_color(struct rb_node *parent, struct rb_root *root,
        void (*augment_rotate)(struct rb_node *old, struct rb_node *new))
 {
        struct rb_node *node = NULL, *sibling, *tmp1, *tmp2;
@@ -355,6 +359,13 @@ __rb_erase_color(struct rb_node *parent, struct rb_root *root,
                }
        }
 }
+
+/* Non-inline version for rb_erase_augmented() use */
+void __rb_erase_color(struct rb_node *parent, struct rb_root *root,
+       void (*augment_rotate)(struct rb_node *old, struct rb_node *new))
+{
+       ____rb_erase_color(parent, root, augment_rotate);
+}
 EXPORT_SYMBOL(__rb_erase_color);
 
 /*
@@ -380,7 +391,10 @@ EXPORT_SYMBOL(rb_insert_color);
 
 void rb_erase(struct rb_node *node, struct rb_root *root)
 {
-       rb_erase_augmented(node, root, &dummy_callbacks);
+       struct rb_node *rebalance;
+       rebalance = __rb_erase_augmented(node, root, &dummy_callbacks);
+       if (rebalance)
+               ____rb_erase_color(rebalance, root, dummy_rotate);
 }
 EXPORT_SYMBOL(rb_erase);
 
index 1324cd74faec45dc2b0ba4f0b3fb0659640e9f25..b93376c39b61308fe2ef7de2466e83306c865cf9 100644 (file)
@@ -185,10 +185,23 @@ static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata)
 
        while (start < end) {
                unsigned long *map, idx, vec;
+               unsigned shift;
 
                map = bdata->node_bootmem_map;
                idx = start - bdata->node_min_pfn;
+               shift = idx & (BITS_PER_LONG - 1);
+               /*
+                * vec holds at most BITS_PER_LONG map bits,
+                * bit 0 corresponds to start.
+                */
                vec = ~map[idx / BITS_PER_LONG];
+
+               if (shift) {
+                       vec >>= shift;
+                       if (end - start >= BITS_PER_LONG)
+                               vec |= ~map[idx / BITS_PER_LONG + 1] <<
+                                       (BITS_PER_LONG - shift);
+               }
                /*
                 * If we have a properly aligned and fully unreserved
                 * BITS_PER_LONG block of pages in front of us, free
@@ -201,19 +214,18 @@ static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata)
                        count += BITS_PER_LONG;
                        start += BITS_PER_LONG;
                } else {
-                       unsigned long off = 0;
+                       unsigned long cur = start;
 
-                       vec >>= start & (BITS_PER_LONG - 1);
-                       while (vec) {
+                       start = ALIGN(start + 1, BITS_PER_LONG);
+                       while (vec && cur != start) {
                                if (vec & 1) {
-                                       page = pfn_to_page(start + off);
+                                       page = pfn_to_page(cur);
                                        __free_pages_bootmem(page, 0);
                                        count++;
                                }
                                vec >>= 1;
-                               off++;
+                               ++cur;
                        }
-                       start = ALIGN(start + 1, BITS_PER_LONG);
                }
        }
 
index 6b807e46649703909d0662a44f88d7c4b2057635..c62bd063d766c7333ca0370444636a52ddc70802 100644 (file)
@@ -816,6 +816,7 @@ static isolate_migrate_t isolate_migratepages(struct zone *zone,
 static int compact_finished(struct zone *zone,
                            struct compact_control *cc)
 {
+       unsigned int order;
        unsigned long watermark;
 
        if (fatal_signal_pending(current))
@@ -850,22 +851,16 @@ static int compact_finished(struct zone *zone,
                return COMPACT_CONTINUE;
 
        /* Direct compactor: Is a suitable page free? */
-       if (cc->page) {
-               /* Was a suitable page captured? */
-               if (*cc->page)
+       for (order = cc->order; order < MAX_ORDER; order++) {
+               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]))
+                       return COMPACT_PARTIAL;
+
+               /* Job done if allocation would set block type */
+               if (cc->order >= pageblock_order && area->nr_free)
                        return COMPACT_PARTIAL;
-       } else {
-               unsigned int order;
-               for (order = cc->order; order < MAX_ORDER; order++) {
-                       struct free_area *area = &zone->free_area[cc->order];
-                       /* Job done if page is free of the right migratetype */
-                       if (!list_empty(&area->free_list[cc->migratetype]))
-                               return COMPACT_PARTIAL;
-
-                       /* Job done if allocation would set block type */
-                       if (cc->order >= pageblock_order && area->nr_free)
-                               return COMPACT_PARTIAL;
-               }
        }
 
        return COMPACT_CONTINUE;
@@ -921,60 +916,6 @@ unsigned long compaction_suitable(struct zone *zone, int order)
        return COMPACT_CONTINUE;
 }
 
-static void compact_capture_page(struct compact_control *cc)
-{
-       unsigned long flags;
-       int mtype, mtype_low, mtype_high;
-
-       if (!cc->page || *cc->page)
-               return;
-
-       /*
-        * For MIGRATE_MOVABLE allocations we capture a suitable page ASAP
-        * regardless of the migratetype of the freelist is is captured from.
-        * This is fine because the order for a high-order MIGRATE_MOVABLE
-        * allocation is typically at least a pageblock size and overall
-        * fragmentation is not impaired. Other allocation types must
-        * capture pages from their own migratelist because otherwise they
-        * could pollute other pageblocks like MIGRATE_MOVABLE with
-        * difficult to move pages and making fragmentation worse overall.
-        */
-       if (cc->migratetype == MIGRATE_MOVABLE) {
-               mtype_low = 0;
-               mtype_high = MIGRATE_PCPTYPES;
-       } else {
-               mtype_low = cc->migratetype;
-               mtype_high = cc->migratetype + 1;
-       }
-
-       /* Speculatively examine the free lists without zone lock */
-       for (mtype = mtype_low; mtype < mtype_high; mtype++) {
-               int order;
-               for (order = cc->order; order < MAX_ORDER; order++) {
-                       struct page *page;
-                       struct free_area *area;
-                       area = &(cc->zone->free_area[order]);
-                       if (list_empty(&area->free_list[mtype]))
-                               continue;
-
-                       /* Take the lock and attempt capture of the page */
-                       if (!compact_trylock_irqsave(&cc->zone->lock, &flags, cc))
-                               return;
-                       if (!list_empty(&area->free_list[mtype])) {
-                               page = list_entry(area->free_list[mtype].next,
-                                                       struct page, lru);
-                               if (capture_free_page(page, cc->order, mtype)) {
-                                       spin_unlock_irqrestore(&cc->zone->lock,
-                                                                       flags);
-                                       *cc->page = page;
-                                       return;
-                               }
-                       }
-                       spin_unlock_irqrestore(&cc->zone->lock, flags);
-               }
-       }
-}
-
 static int compact_zone(struct zone *zone, struct compact_control *cc)
 {
        int ret;
@@ -1054,9 +995,6 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
                                goto out;
                        }
                }
-
-               /* Capture a page now if it is a suitable size */
-               compact_capture_page(cc);
        }
 
 out:
@@ -1069,8 +1007,7 @@ out:
 
 static unsigned long compact_zone_order(struct zone *zone,
                                 int order, gfp_t gfp_mask,
-                                bool sync, bool *contended,
-                                struct page **page)
+                                bool sync, bool *contended)
 {
        unsigned long ret;
        struct compact_control cc = {
@@ -1080,7 +1017,6 @@ static unsigned long compact_zone_order(struct zone *zone,
                .migratetype = allocflags_to_migratetype(gfp_mask),
                .zone = zone,
                .sync = sync,
-               .page = page,
        };
        INIT_LIST_HEAD(&cc.freepages);
        INIT_LIST_HEAD(&cc.migratepages);
@@ -1110,7 +1046,7 @@ int sysctl_extfrag_threshold = 500;
  */
 unsigned long try_to_compact_pages(struct zonelist *zonelist,
                        int order, gfp_t gfp_mask, nodemask_t *nodemask,
-                       bool sync, bool *contended, struct page **page)
+                       bool sync, bool *contended)
 {
        enum zone_type high_zoneidx = gfp_zone(gfp_mask);
        int may_enter_fs = gfp_mask & __GFP_FS;
@@ -1136,7 +1072,7 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist,
                int status;
 
                status = compact_zone_order(zone, order, gfp_mask, sync,
-                                               contended, page);
+                                               contended);
                rc = max(status, rc);
 
                /* If a normal allocation would succeed, stop compacting */
@@ -1192,7 +1128,6 @@ int compact_pgdat(pg_data_t *pgdat, int order)
        struct compact_control cc = {
                .order = order,
                .sync = false,
-               .page = NULL,
        };
 
        return __compact_pgdat(pgdat, &cc);
@@ -1203,14 +1138,13 @@ static int compact_node(int nid)
        struct compact_control cc = {
                .order = -1,
                .sync = true,
-               .page = NULL,
        };
 
        return __compact_pgdat(NODE_DATA(nid), &cc);
 }
 
 /* Compact all nodes in the system */
-static int compact_nodes(void)
+static void compact_nodes(void)
 {
        int nid;
 
@@ -1219,8 +1153,6 @@ static int compact_nodes(void)
 
        for_each_online_node(nid)
                compact_node(nid);
-
-       return COMPACT_COMPLETE;
 }
 
 /* The written value is actually unused, all memory is compacted */
@@ -1231,7 +1163,7 @@ int sysctl_compaction_handler(struct ctl_table *table, int write,
                        void __user *buffer, size_t *length, loff_t *ppos)
 {
        if (write)
-               return compact_nodes();
+               compact_nodes();
 
        return 0;
 }
index 9e894edc7811ca66bda078e9606011ca25035238..b5783d81eda90fc9a808478ac6dcea4f74b4424e 100644 (file)
@@ -1257,6 +1257,10 @@ struct page *follow_trans_huge_pmd(struct vm_area_struct *vma,
        if (flags & FOLL_WRITE && !pmd_write(*pmd))
                goto out;
 
+       /* Avoid dumping huge zero page */
+       if ((flags & FOLL_DUMP) && is_huge_zero_pmd(*pmd))
+               return ERR_PTR(-EFAULT);
+
        page = pmd_page(*pmd);
        VM_BUG_ON(!PageHead(page));
        if (flags & FOLL_TOUCH) {
@@ -1819,9 +1823,19 @@ int split_huge_page(struct page *page)
 
        BUG_ON(is_huge_zero_pfn(page_to_pfn(page)));
        BUG_ON(!PageAnon(page));
-       anon_vma = page_lock_anon_vma_read(page);
+
+       /*
+        * The caller does not necessarily hold an mmap_sem that would prevent
+        * the anon_vma disappearing so we first we take a reference to it
+        * and then lock the anon_vma for write. This is similar to
+        * page_lock_anon_vma_read except the write lock is taken to serialise
+        * against parallel split or collapse operations.
+        */
+       anon_vma = page_get_anon_vma(page);
        if (!anon_vma)
                goto out;
+       anon_vma_lock_write(anon_vma);
+
        ret = 0;
        if (!PageCompound(page))
                goto out_unlock;
@@ -1832,7 +1846,8 @@ int split_huge_page(struct page *page)
 
        BUG_ON(PageCompound(page));
 out_unlock:
-       page_unlock_anon_vma_read(anon_vma);
+       anon_vma_unlock(anon_vma);
+       put_anon_vma(anon_vma);
 out:
        return ret;
 }
index 4f3ea0b1e57ce33bbf71bd1d3f41e04e11ae05dc..546db81820e45e8279f5a6425794da2980031dc8 100644 (file)
@@ -3033,6 +3033,7 @@ unsigned long hugetlb_change_protection(struct vm_area_struct *vma,
                if (!huge_pte_none(huge_ptep_get(ptep))) {
                        pte = huge_ptep_get_and_clear(mm, address, ptep);
                        pte = pte_mkhuge(pte_modify(pte, newprot));
+                       pte = arch_make_huge_pte(pte, vma, NULL, 0);
                        set_huge_pte_at(mm, address, ptep, pte);
                        pages++;
                }
index d597f94cc2059ade4d9fd808457d240538faac55..9ba21100ebf3b01bb0f2296baaf55ec41e303450 100644 (file)
@@ -135,7 +135,6 @@ struct compact_control {
        int migratetype;                /* MOVABLE, RECLAIMABLE etc */
        struct zone *zone;
        bool contended;                 /* True if a lock was contended */
-       struct page **page;             /* Page captured of requested size */
 };
 
 unsigned long
index 625905523c2a1592f539c46f6721723d62376648..88adc8afb6103d6c1ba2eb7e20c1ed226cf2b1d3 100644 (file)
@@ -314,7 +314,8 @@ static void __init_memblock memblock_merge_regions(struct memblock_type *type)
                }
 
                this->size += next->size;
-               memmove(next, next + 1, (type->cnt - (i + 1)) * sizeof(*next));
+               /* move forward from next + 1, index of which is i + 2 */
+               memmove(next, next + 1, (type->cnt - (i + 2)) * sizeof(*next));
                type->cnt--;
        }
 }
index 3b676b0c5c3ecca91d5e91d8ef2adc2982353f57..2fd8b4af47440a39a31d48a1096e24b571028455 100644 (file)
@@ -160,8 +160,10 @@ static int remove_migration_pte(struct page *new, struct vm_area_struct *vma,
        if (is_write_migration_entry(entry))
                pte = pte_mkwrite(pte);
 #ifdef CONFIG_HUGETLB_PAGE
-       if (PageHuge(new))
+       if (PageHuge(new)) {
                pte = pte_mkhuge(pte);
+               pte = arch_make_huge_pte(pte, vma, new, 0);
+       }
 #endif
        flush_cache_page(vma, addr, pte_pfn(pte));
        set_pte_at(mm, addr, ptep, pte);
@@ -1679,9 +1681,21 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
        page_xchg_last_nid(new_page, page_last_nid(page));
 
        isolated = numamigrate_isolate_page(pgdat, page);
-       if (!isolated) {
+
+       /*
+        * Failing to isolate or a GUP pin prevents migration. The expected
+        * page count is 2. 1 for anonymous pages without a mapping and 1
+        * for the callers pin. If the page was isolated, the page will
+        * need to be put back on the LRU.
+        */
+       if (!isolated || page_count(page) != 2) {
                count_vm_events(PGMIGRATE_FAIL, HPAGE_PMD_NR);
                put_page(new_page);
+               if (isolated) {
+                       putback_lru_page(page);
+                       isolated = 0;
+                       goto out;
+               }
                goto out_keep_locked;
        }
 
index f54b235f29a98c3b3a91c2c1e6b4b50ef5e8f287..d1e4124f3d0ec0be1edaf2605848269d6a0cfedf 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -2886,7 +2886,7 @@ static void vm_lock_anon_vma(struct mm_struct *mm, struct anon_vma *anon_vma)
                 * The LSB of head.next can't change from under us
                 * because we hold the mm_all_locks_mutex.
                 */
-               down_write(&anon_vma->root->rwsem);
+               down_write_nest_lock(&anon_vma->root->rwsem, &mm->mmap_sem);
                /*
                 * We can safely modify head.next after taking the
                 * anon_vma->root->rwsem. If some other vma in this mm shares
@@ -2943,7 +2943,7 @@ static void vm_lock_mapping(struct mm_struct *mm, struct address_space *mapping)
  * vma in this mm is backed by the same anon_vma or address_space.
  *
  * We can take all the locks in random order because the VM code
- * taking i_mmap_mutex or anon_vma->mutex outside the mmap_sem never
+ * taking i_mmap_mutex or anon_vma->rwsem outside the mmap_sem never
  * takes more than one of them in a row. Secondly we're protected
  * against a concurrent mm_take_all_locks() by the mm_all_locks_mutex.
  *
index bc6cc0e913bd7d18214218b5fe3d7226e42e24d6..df2022ff0c8a1d9fb7ff13ed4cf88058999485e6 100644 (file)
@@ -1384,14 +1384,8 @@ void split_page(struct page *page, unsigned int order)
                set_page_refcounted(page + i);
 }
 
-/*
- * Similar to the split_page family of functions except that the page
- * required at the given order and being isolated now to prevent races
- * with parallel allocators
- */
-int capture_free_page(struct page *page, int alloc_order, int migratetype)
+static int __isolate_free_page(struct page *page, unsigned int order)
 {
-       unsigned int order;
        unsigned long watermark;
        struct zone *zone;
        int mt;
@@ -1399,7 +1393,6 @@ int capture_free_page(struct page *page, int alloc_order, int migratetype)
        BUG_ON(!PageBuddy(page));
 
        zone = page_zone(page);
-       order = page_order(page);
        mt = get_pageblock_migratetype(page);
 
        if (mt != MIGRATE_ISOLATE) {
@@ -1408,7 +1401,7 @@ int capture_free_page(struct page *page, int alloc_order, int migratetype)
                if (!zone_watermark_ok(zone, 0, watermark, 0, 0))
                        return 0;
 
-               __mod_zone_freepage_state(zone, -(1UL << alloc_order), mt);
+               __mod_zone_freepage_state(zone, -(1UL << order), mt);
        }
 
        /* Remove page from free list */
@@ -1416,11 +1409,7 @@ int capture_free_page(struct page *page, int alloc_order, int migratetype)
        zone->free_area[order].nr_free--;
        rmv_page_order(page);
 
-       if (alloc_order != order)
-               expand(zone, page, alloc_order, order,
-                       &zone->free_area[order], migratetype);
-
-       /* Set the pageblock if the captured page is at least a pageblock */
+       /* Set the pageblock if the isolated page is at least a pageblock */
        if (order >= pageblock_order - 1) {
                struct page *endpage = page + (1 << order) - 1;
                for (; page < endpage; page += pageblock_nr_pages) {
@@ -1431,7 +1420,7 @@ int capture_free_page(struct page *page, int alloc_order, int migratetype)
                }
        }
 
-       return 1UL << alloc_order;
+       return 1UL << order;
 }
 
 /*
@@ -1449,10 +1438,9 @@ int split_free_page(struct page *page)
        unsigned int order;
        int nr_pages;
 
-       BUG_ON(!PageBuddy(page));
        order = page_order(page);
 
-       nr_pages = capture_free_page(page, order, 0);
+       nr_pages = __isolate_free_page(page, order);
        if (!nr_pages)
                return 0;
 
@@ -2136,8 +2124,6 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
        bool *contended_compaction, bool *deferred_compaction,
        unsigned long *did_some_progress)
 {
-       struct page *page = NULL;
-
        if (!order)
                return NULL;
 
@@ -2149,16 +2135,12 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
        current->flags |= PF_MEMALLOC;
        *did_some_progress = try_to_compact_pages(zonelist, order, gfp_mask,
                                                nodemask, sync_migration,
-                                               contended_compaction, &page);
+                                               contended_compaction);
        current->flags &= ~PF_MEMALLOC;
 
-       /* If compaction captured a page, prep and use it */
-       if (page) {
-               prep_new_page(page, order, gfp_mask);
-               goto got_page;
-       }
-
        if (*did_some_progress != COMPACT_SKIPPED) {
+               struct page *page;
+
                /* Page migration frees to the PCP lists but we want merging */
                drain_pages(get_cpu());
                put_cpu();
@@ -2168,7 +2150,6 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
                                alloc_flags & ~ALLOC_NO_WATERMARKS,
                                preferred_zone, migratetype);
                if (page) {
-got_page:
                        preferred_zone->compact_blockskip_flush = false;
                        preferred_zone->compact_considered = 0;
                        preferred_zone->compact_defer_shift = 0;
@@ -5604,7 +5585,7 @@ static inline int pfn_to_bitidx(struct zone *zone, unsigned long pfn)
        pfn &= (PAGES_PER_SECTION-1);
        return (pfn >> pageblock_order) * NR_PAGEBLOCK_BITS;
 #else
-       pfn = pfn - zone->zone_start_pfn;
+       pfn = pfn - round_down(zone->zone_start_pfn, pageblock_nr_pages);
        return (pfn >> pageblock_order) * NR_PAGEBLOCK_BITS;
 #endif /* CONFIG_SPARSEMEM */
 }
index 8e1d89d2b1c1cada56f190bcca967525a2b7a2e7..183f97a86bb24c8099f4d3b7b7d4a5919d3432c4 100644 (file)
@@ -738,6 +738,7 @@ static uint16_t batadv_arp_get_type(struct batadv_priv *bat_priv,
        struct arphdr *arphdr;
        struct ethhdr *ethhdr;
        __be32 ip_src, ip_dst;
+       uint8_t *hw_src, *hw_dst;
        uint16_t type = 0;
 
        /* pull the ethernet header */
@@ -777,9 +778,23 @@ static uint16_t batadv_arp_get_type(struct batadv_priv *bat_priv,
        ip_src = batadv_arp_ip_src(skb, hdr_size);
        ip_dst = batadv_arp_ip_dst(skb, hdr_size);
        if (ipv4_is_loopback(ip_src) || ipv4_is_multicast(ip_src) ||
-           ipv4_is_loopback(ip_dst) || ipv4_is_multicast(ip_dst))
+           ipv4_is_loopback(ip_dst) || ipv4_is_multicast(ip_dst) ||
+           ipv4_is_zeronet(ip_src) || ipv4_is_lbcast(ip_src) ||
+           ipv4_is_zeronet(ip_dst) || ipv4_is_lbcast(ip_dst))
                goto out;
 
+       hw_src = batadv_arp_hw_src(skb, hdr_size);
+       if (is_zero_ether_addr(hw_src) || is_multicast_ether_addr(hw_src))
+               goto out;
+
+       /* we don't care about the destination MAC address in ARP requests */
+       if (arphdr->ar_op != htons(ARPOP_REQUEST)) {
+               hw_dst = batadv_arp_hw_dst(skb, hdr_size);
+               if (is_zero_ether_addr(hw_dst) ||
+                   is_multicast_ether_addr(hw_dst))
+                       goto out;
+       }
+
        type = ntohs(arphdr->ar_op);
 out:
        return type;
@@ -1012,6 +1027,8 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
         */
        ret = !batadv_is_my_client(bat_priv, hw_dst);
 out:
+       if (ret)
+               kfree_skb(skb);
        /* if ret == false -> packet has to be delivered to the interface */
        return ret;
 }
index 596660d37c5e56d6a0178b16b288a2d3c868f965..0f78e34220c9025aae08f3b38b49924b6c397ff6 100644 (file)
@@ -2810,14 +2810,6 @@ static void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb)
        if (conn) {
                hci_conn_enter_active_mode(conn, BT_POWER_FORCE_ACTIVE_OFF);
 
-               hci_dev_lock(hdev);
-               if (test_bit(HCI_MGMT, &hdev->dev_flags) &&
-                   !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
-                       mgmt_device_connected(hdev, &conn->dst, conn->type,
-                                             conn->dst_type, 0, NULL, 0,
-                                             conn->dev_class);
-               hci_dev_unlock(hdev);
-
                /* Send to upper protocol */
                l2cap_recv_acldata(conn, skb, flags);
                return;
index 705078a0cc393c023054ad1d55aea1a548e3da1d..81b44481d0d93a8dd8513dd15f97f9a21b8a32d6 100644 (file)
@@ -2688,7 +2688,7 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
        if (ev->opcode != HCI_OP_NOP)
                del_timer(&hdev->cmd_timer);
 
-       if (ev->ncmd) {
+       if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) {
                atomic_set(&hdev->cmd_cnt, 1);
                if (!skb_queue_empty(&hdev->cmd_q))
                        queue_work(hdev->workqueue, &hdev->cmd_work);
index b2bcbe2dc328ba8473227035c39a7ff467ee5659..a7352ff3fd1e7884c2fc43142fba9bc0033063b8 100644 (file)
@@ -931,7 +931,7 @@ static int hidp_setup_hid(struct hidp_session *session,
        hid->version = req->version;
        hid->country = req->country;
 
-       strncpy(hid->name, req->name, 128);
+       strncpy(hid->name, req->name, sizeof(req->name) - 1);
 
        snprintf(hid->phys, sizeof(hid->phys), "%pMR",
                 &bt_sk(session->ctrl_sock->sk)->src);
index 2c78208d793eb8db838cf85a480e029a4dc3567b..22e658322845b9f16bcf8de52c1ba7c401bfef87 100644 (file)
@@ -3727,6 +3727,17 @@ sendresp:
 static int l2cap_connect_req(struct l2cap_conn *conn,
                             struct l2cap_cmd_hdr *cmd, u8 *data)
 {
+       struct hci_dev *hdev = conn->hcon->hdev;
+       struct hci_conn *hcon = conn->hcon;
+
+       hci_dev_lock(hdev);
+       if (test_bit(HCI_MGMT, &hdev->dev_flags) &&
+           !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &hcon->flags))
+               mgmt_device_connected(hdev, &hcon->dst, hcon->type,
+                                     hcon->dst_type, 0, NULL, 0,
+                                     hcon->dev_class);
+       hci_dev_unlock(hdev);
+
        l2cap_connect(conn, cmd, data, L2CAP_CONN_RSP, 0);
        return 0;
 }
index 531a93d613d4f3f86eff5174cdbb1236b75cfb53..57f250c20e399851ca6998d9813dc60e3ffa3455 100644 (file)
@@ -352,7 +352,7 @@ static void __sco_sock_close(struct sock *sk)
 
        case BT_CONNECTED:
        case BT_CONFIG:
-               if (sco_pi(sk)->conn) {
+               if (sco_pi(sk)->conn->hcon) {
                        sk->sk_state = BT_DISCONN;
                        sco_sock_set_timer(sk, SCO_DISCONN_TIMEOUT);
                        hci_conn_put(sco_pi(sk)->conn->hcon);
index 515473ee52cbb6327a9310922d0fb1c3d763df2f..f64e439b4a00fe206fb634745a470475eb31cbb9 100644 (file)
@@ -6121,6 +6121,14 @@ struct netdev_queue *dev_ingress_queue_create(struct net_device *dev)
 
 static const struct ethtool_ops default_ethtool_ops;
 
+void netdev_set_default_ethtool_ops(struct net_device *dev,
+                                   const struct ethtool_ops *ops)
+{
+       if (dev->ethtool_ops == &default_ethtool_ops)
+               dev->ethtool_ops = ops;
+}
+EXPORT_SYMBOL_GPL(netdev_set_default_ethtool_ops);
+
 /**
  *     alloc_netdev_mqs - allocate network device
  *     @sizeof_priv:   size of private data to allocate space for
index c31d9e8668c30346894adbf3be55eed4beeb1258..4425148d2b51592626b92a1451d9bc1208213fb8 100644 (file)
@@ -186,8 +186,6 @@ void reqsk_fastopen_remove(struct sock *sk, struct request_sock *req,
        struct fastopen_queue *fastopenq =
            inet_csk(lsk)->icsk_accept_queue.fastopenq;
 
-       BUG_ON(!spin_is_locked(&sk->sk_lock.slock) && !sock_owned_by_user(sk));
-
        tcp_sk(sk)->fastopen_rsk = NULL;
        spin_lock_bh(&fastopenq->lock);
        fastopenq->qlen--;
index 57fb1ee6649f65a0994d86c6f8763fe6e14ba081..905dcc6ad1e3b480c01f87df5157f4e37de112a1 100644 (file)
@@ -35,6 +35,7 @@
 #include <net/sock.h>
 #include <net/compat.h>
 #include <net/scm.h>
+#include <net/cls_cgroup.h>
 
 
 /*
@@ -302,8 +303,10 @@ void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)
                }
                /* Bump the usage count and install the file. */
                sock = sock_from_file(fp[i], &err);
-               if (sock)
+               if (sock) {
                        sock_update_netprioidx(sock->sk, current);
+                       sock_update_classid(sock->sk, current);
+               }
                fd_install(new_fd, get_file(fp[i]));
        }
 
index 3ab989b0de42a0bfe7905ee99d98901ee9e9a075..a9a2ae3e2213a3769bd79df65229822768c09264 100644 (file)
@@ -1649,7 +1649,7 @@ static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i)
 
 static struct page *linear_to_page(struct page *page, unsigned int *len,
                                   unsigned int *offset,
-                                  struct sk_buff *skb, struct sock *sk)
+                                  struct sock *sk)
 {
        struct page_frag *pfrag = sk_page_frag(sk);
 
@@ -1682,14 +1682,14 @@ static bool spd_can_coalesce(const struct splice_pipe_desc *spd,
 static bool spd_fill_page(struct splice_pipe_desc *spd,
                          struct pipe_inode_info *pipe, struct page *page,
                          unsigned int *len, unsigned int offset,
-                         struct sk_buff *skb, bool linear,
+                         bool linear,
                          struct sock *sk)
 {
        if (unlikely(spd->nr_pages == MAX_SKB_FRAGS))
                return true;
 
        if (linear) {
-               page = linear_to_page(page, len, &offset, skb, sk);
+               page = linear_to_page(page, len, &offset, sk);
                if (!page)
                        return true;
        }
@@ -1706,23 +1706,9 @@ static bool spd_fill_page(struct splice_pipe_desc *spd,
        return false;
 }
 
-static inline void __segment_seek(struct page **page, unsigned int *poff,
-                                 unsigned int *plen, unsigned int off)
-{
-       unsigned long n;
-
-       *poff += off;
-       n = *poff / PAGE_SIZE;
-       if (n)
-               *page = nth_page(*page, n);
-
-       *poff = *poff % PAGE_SIZE;
-       *plen -= off;
-}
-
 static bool __splice_segment(struct page *page, unsigned int poff,
                             unsigned int plen, unsigned int *off,
-                            unsigned int *len, struct sk_buff *skb,
+                            unsigned int *len,
                             struct splice_pipe_desc *spd, bool linear,
                             struct sock *sk,
                             struct pipe_inode_info *pipe)
@@ -1737,23 +1723,19 @@ static bool __splice_segment(struct page *page, unsigned int poff,
        }
 
        /* ignore any bits we already processed */
-       if (*off) {
-               __segment_seek(&page, &poff, &plen, *off);
-               *off = 0;
-       }
+       poff += *off;
+       plen -= *off;
+       *off = 0;
 
        do {
                unsigned int flen = min(*len, plen);
 
-               /* the linear region may spread across several pages  */
-               flen = min_t(unsigned int, flen, PAGE_SIZE - poff);
-
-               if (spd_fill_page(spd, pipe, page, &flen, poff, skb, linear, sk))
+               if (spd_fill_page(spd, pipe, page, &flen, poff,
+                                 linear, sk))
                        return true;
-
-               __segment_seek(&page, &poff, &plen, flen);
+               poff += flen;
+               plen -= flen;
                *len -= flen;
-
        } while (*len && plen);
 
        return false;
@@ -1777,7 +1759,7 @@ static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe,
        if (__splice_segment(virt_to_page(skb->data),
                             (unsigned long) skb->data & (PAGE_SIZE - 1),
                             skb_headlen(skb),
-                            offset, len, skb, spd,
+                            offset, len, spd,
                             skb_head_is_locked(skb),
                             sk, pipe))
                return true;
@@ -1790,7 +1772,7 @@ static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe,
 
                if (__splice_segment(skb_frag_page(f),
                                     f->page_offset, skb_frag_size(f),
-                                    offset, len, skb, spd, false, sk, pipe))
+                                    offset, len, spd, false, sk, pipe))
                        return true;
        }
 
index a0d8392491c3c3e947876c943457227e4ed4ad92..a69b4e4a02b5099043d98e5278355274e18ea72b 100644 (file)
@@ -269,7 +269,11 @@ static void ah_input_done(struct crypto_async_request *base, int err)
        skb->network_header += ah_hlen;
        memcpy(skb_network_header(skb), work_iph, ihl);
        __skb_pull(skb, ah_hlen + ihl);
-       skb_set_transport_header(skb, -ihl);
+
+       if (x->props.mode == XFRM_MODE_TUNNEL)
+               skb_reset_transport_header(skb);
+       else
+               skb_set_transport_header(skb, -ihl);
 out:
        kfree(AH_SKB_CB(skb)->tmp);
        xfrm_input_resume(skb, err);
@@ -381,7 +385,10 @@ static int ah_input(struct xfrm_state *x, struct sk_buff *skb)
        skb->network_header += ah_hlen;
        memcpy(skb_network_header(skb), work_iph, ihl);
        __skb_pull(skb, ah_hlen + ihl);
-       skb_set_transport_header(skb, -ihl);
+       if (x->props.mode == XFRM_MODE_TUNNEL)
+               skb_reset_transport_header(skb);
+       else
+               skb_set_transport_header(skb, -ihl);
 
        err = nexthdr;
 
@@ -413,9 +420,12 @@ static void ah4_err(struct sk_buff *skb, u32 info)
        if (!x)
                return;
 
-       if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
+       if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH) {
+               atomic_inc(&flow_cache_genid);
+               rt_genid_bump(net);
+
                ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_AH, 0);
-       else
+       else
                ipv4_redirect(skb, net, 0, 0, IPPROTO_AH, 0);
        xfrm_state_put(x);
 }
index 424fafbc8cb02c49fb2dd3551f3583290f2fc330..b28e863fe0a7143b199bee51cfdf242c125f31cf 100644 (file)
@@ -85,3 +85,28 @@ out:
        return err;
 }
 EXPORT_SYMBOL(ip4_datagram_connect);
+
+void ip4_datagram_release_cb(struct sock *sk)
+{
+       const struct inet_sock *inet = inet_sk(sk);
+       const struct ip_options_rcu *inet_opt;
+       __be32 daddr = inet->inet_daddr;
+       struct flowi4 fl4;
+       struct rtable *rt;
+
+       if (! __sk_dst_get(sk) || __sk_dst_check(sk, 0))
+               return;
+
+       rcu_read_lock();
+       inet_opt = rcu_dereference(inet->inet_opt);
+       if (inet_opt && inet_opt->opt.srr)
+               daddr = inet_opt->opt.faddr;
+       rt = ip_route_output_ports(sock_net(sk), &fl4, sk, daddr,
+                                  inet->inet_saddr, inet->inet_dport,
+                                  inet->inet_sport, sk->sk_protocol,
+                                  RT_CONN_FLAGS(sk), sk->sk_bound_dev_if);
+       if (!IS_ERR(rt))
+               __sk_dst_set(sk, &rt->dst);
+       rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(ip4_datagram_release_cb);
index b61e9deb7c7ecc9cae92ab0365b871a773627cc4..3b4f0cd2e63edbd136683577873b288712a4b92a 100644 (file)
@@ -346,7 +346,10 @@ static int esp_input_done2(struct sk_buff *skb, int err)
 
        pskb_trim(skb, skb->len - alen - padlen - 2);
        __skb_pull(skb, hlen);
-       skb_set_transport_header(skb, -ihl);
+       if (x->props.mode == XFRM_MODE_TUNNEL)
+               skb_reset_transport_header(skb);
+       else
+               skb_set_transport_header(skb, -ihl);
 
        err = nexthdr[1];
 
@@ -499,9 +502,12 @@ static void esp4_err(struct sk_buff *skb, u32 info)
        if (!x)
                return;
 
-       if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
+       if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH) {
+               atomic_inc(&flow_cache_genid);
+               rt_genid_bump(net);
+
                ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_ESP, 0);
-       else
+       else
                ipv4_redirect(skb, net, 0, 0, IPPROTO_ESP, 0);
        xfrm_state_put(x);
 }
index 303012adf9e6e0b442268b6f53b50ac8aea745fe..e81b1caf2ea2a0ceaa20ef8e1847ebd4f71cd98e 100644 (file)
@@ -963,8 +963,12 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
                        ptr--;
                }
                if (tunnel->parms.o_flags&GRE_CSUM) {
+                       int offset = skb_transport_offset(skb);
+
                        *ptr = 0;
-                       *(__sum16 *)ptr = ip_compute_csum((void *)(iph+1), skb->len - sizeof(struct iphdr));
+                       *(__sum16 *)ptr = csum_fold(skb_checksum(skb, offset,
+                                                                skb->len - offset,
+                                                                0));
                }
        }
 
index 3c9d20880283de0f9b5244eae8184e76d9a20dcd..d9c4f113d7093bba7eba2beefc31cd0af4b9bb95 100644 (file)
@@ -590,7 +590,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
        case IP_TTL:
                if (optlen < 1)
                        goto e_inval;
-               if (val != -1 && (val < 0 || val > 255))
+               if (val != -1 && (val < 1 || val > 255))
                        goto e_inval;
                inet->uc_ttl = val;
                break;
index d3ab47e19a896277161c2bf1b87706c20ebd73be..9a46daed2f3c05be9ed82138edac731fcc571145 100644 (file)
@@ -47,9 +47,12 @@ static void ipcomp4_err(struct sk_buff *skb, u32 info)
        if (!x)
                return;
 
-       if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
+       if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH) {
+               atomic_inc(&flow_cache_genid);
+               rt_genid_bump(net);
+
                ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_COMP, 0);
-       else
+       else
                ipv4_redirect(skb, net, 0, 0, IPPROTO_COMP, 0);
        xfrm_state_put(x);
 }
index 8f3d05424a3e8f2f318c36bf007fd17724533997..6f9c07268cf6d433379898421e978d74fa49952f 100644 (file)
@@ -738,6 +738,7 @@ struct proto ping_prot = {
        .recvmsg =      ping_recvmsg,
        .bind =         ping_bind,
        .backlog_rcv =  ping_queue_rcv_skb,
+       .release_cb =   ip4_datagram_release_cb,
        .hash =         ping_v4_hash,
        .unhash =       ping_v4_unhash,
        .get_port =     ping_v4_get_port,
index 73d1e4df4bf630f176f385b96639b4e803469458..6f08991409c3ad9d3620357d04d1f59005f7e2fa 100644 (file)
@@ -894,6 +894,7 @@ struct proto raw_prot = {
        .recvmsg           = raw_recvmsg,
        .bind              = raw_bind,
        .backlog_rcv       = raw_rcv_skb,
+       .release_cb        = ip4_datagram_release_cb,
        .hash              = raw_hash_sk,
        .unhash            = raw_unhash_sk,
        .obj_size          = sizeof(struct raw_sock),
index 844a9ef60dbd89f459515101ebb0db6e7cfaa8a3..a0fcc47fee732744baf42bf1e61772faad87982d 100644 (file)
@@ -912,6 +912,9 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
        struct dst_entry *dst = &rt->dst;
        struct fib_result res;
 
+       if (dst_metric_locked(dst, RTAX_MTU))
+               return;
+
        if (dst->dev->mtu < mtu)
                return;
 
@@ -962,7 +965,7 @@ void ipv4_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu,
 }
 EXPORT_SYMBOL_GPL(ipv4_update_pmtu);
 
-void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu)
+static void __ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu)
 {
        const struct iphdr *iph = (const struct iphdr *) skb->data;
        struct flowi4 fl4;
@@ -975,6 +978,53 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu)
                ip_rt_put(rt);
        }
 }
+
+void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu)
+{
+       const struct iphdr *iph = (const struct iphdr *) skb->data;
+       struct flowi4 fl4;
+       struct rtable *rt;
+       struct dst_entry *dst;
+       bool new = false;
+
+       bh_lock_sock(sk);
+       rt = (struct rtable *) __sk_dst_get(sk);
+
+       if (sock_owned_by_user(sk) || !rt) {
+               __ipv4_sk_update_pmtu(skb, sk, mtu);
+               goto out;
+       }
+
+       __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0);
+
+       if (!__sk_dst_check(sk, 0)) {
+               rt = ip_route_output_flow(sock_net(sk), &fl4, sk);
+               if (IS_ERR(rt))
+                       goto out;
+
+               new = true;
+       }
+
+       __ip_rt_update_pmtu((struct rtable *) rt->dst.path, &fl4, mtu);
+
+       dst = dst_check(&rt->dst, 0);
+       if (!dst) {
+               if (new)
+                       dst_release(&rt->dst);
+
+               rt = ip_route_output_flow(sock_net(sk), &fl4, sk);
+               if (IS_ERR(rt))
+                       goto out;
+
+               new = true;
+       }
+
+       if (new)
+               __sk_dst_set(sk, &rt->dst);
+
+out:
+       bh_unlock_sock(sk);
+}
 EXPORT_SYMBOL_GPL(ipv4_sk_update_pmtu);
 
 void ipv4_redirect(struct sk_buff *skb, struct net *net,
@@ -1120,7 +1170,7 @@ static unsigned int ipv4_mtu(const struct dst_entry *dst)
        if (!mtu || time_after_eq(jiffies, rt->dst.expires))
                mtu = dst_metric_raw(dst, RTAX_MTU);
 
-       if (mtu && rt_is_output_route(rt))
+       if (mtu)
                return mtu;
 
        mtu = dst->dev->mtu;
index 1ca253635f7acdda23624c28ae1ead4645bd0dd5..2aa69c8ae60c1990c76e69fe5aef46b990b6da6e 100644 (file)
@@ -1428,12 +1428,12 @@ static void tcp_service_net_dma(struct sock *sk, bool wait)
 }
 #endif
 
-static inline struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off)
+static struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off)
 {
        struct sk_buff *skb;
        u32 offset;
 
-       skb_queue_walk(&sk->sk_receive_queue, skb) {
+       while ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) {
                offset = seq - TCP_SKB_CB(skb)->seq;
                if (tcp_hdr(skb)->syn)
                        offset--;
@@ -1441,6 +1441,11 @@ static inline struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off)
                        *off = offset;
                        return skb;
                }
+               /* This looks weird, but this can happen if TCP collapsing
+                * splitted a fat GRO packet, while we released socket lock
+                * in skb_splice_bits()
+                */
+               sk_eat_skb(sk, skb, false);
        }
        return NULL;
 }
@@ -1482,7 +1487,7 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc,
                                        break;
                        }
                        used = recv_actor(desc, skb, offset, len);
-                       if (used < 0) {
+                       if (used <= 0) {
                                if (!copied)
                                        copied = used;
                                break;
@@ -1520,8 +1525,10 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc,
        tcp_rcv_space_adjust(sk);
 
        /* Clean up data we have read: This will do ACK frames. */
-       if (copied > 0)
+       if (copied > 0) {
+               tcp_recv_skb(sk, seq, &offset);
                tcp_cleanup_rbuf(sk, copied);
+       }
        return copied;
 }
 EXPORT_SYMBOL(tcp_read_sock);
index a28e4db8a952b15cc2f06578f12acbe6353c162a..18f97ca76b00223b25d0f6faed8a829c45fb95c8 100644 (file)
@@ -5543,7 +5543,7 @@ slow_path:
        if (len < (th->doff << 2) || tcp_checksum_complete_user(sk, skb))
                goto csum_error;
 
-       if (!th->ack)
+       if (!th->ack && !th->rst)
                goto discard;
 
        /*
@@ -5988,7 +5988,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
                        goto discard;
        }
 
-       if (!th->ack)
+       if (!th->ack && !th->rst)
                goto discard;
 
        if (!tcp_validate_incoming(sk, skb, th, 0))
index 54139fa514e6ee2d82c4cf9ca8528e012b546a90..70b09ef2463b3678c5da4a1d5fc0edb4656e4940 100644 (file)
@@ -369,11 +369,10 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
         * We do take care of PMTU discovery (RFC1191) special case :
         * we can receive locally generated ICMP messages while socket is held.
         */
-       if (sock_owned_by_user(sk) &&
-           type != ICMP_DEST_UNREACH &&
-           code != ICMP_FRAG_NEEDED)
-               NET_INC_STATS_BH(net, LINUX_MIB_LOCKDROPPEDICMPS);
-
+       if (sock_owned_by_user(sk)) {
+               if (!(type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED))
+                       NET_INC_STATS_BH(net, LINUX_MIB_LOCKDROPPEDICMPS);
+       }
        if (sk->sk_state == TCP_CLOSE)
                goto out;
 
index 79c8dbe59b5474bdc3e23ba8adc227e1bee016a4..1f4d405eafba746d2b8085132a740b17a6658c80 100644 (file)
@@ -1952,6 +1952,7 @@ struct proto udp_prot = {
        .recvmsg           = udp_recvmsg,
        .sendpage          = udp_sendpage,
        .backlog_rcv       = __udp_queue_rcv_skb,
+       .release_cb        = ip4_datagram_release_cb,
        .hash              = udp_lib_hash,
        .unhash            = udp_lib_unhash,
        .rehash            = udp_v4_rehash,
index 408cac4ae00a41129e4bf89506e96f646c607380..420e563263840442a150c13098148921559c3f2d 100644 (file)
@@ -154,6 +154,11 @@ static void addrconf_type_change(struct net_device *dev,
                                 unsigned long event);
 static int addrconf_ifdown(struct net_device *dev, int how);
 
+static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
+                                                 int plen,
+                                                 const struct net_device *dev,
+                                                 u32 flags, u32 noflags);
+
 static void addrconf_dad_start(struct inet6_ifaddr *ifp);
 static void addrconf_dad_timer(unsigned long data);
 static void addrconf_dad_completed(struct inet6_ifaddr *ifp);
@@ -250,12 +255,6 @@ static inline bool addrconf_qdisc_ok(const struct net_device *dev)
        return !qdisc_tx_is_noop(dev);
 }
 
-/* Check if a route is valid prefix route */
-static inline int addrconf_is_prefix_route(const struct rt6_info *rt)
-{
-       return (rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0;
-}
-
 static void addrconf_del_timer(struct inet6_ifaddr *ifp)
 {
        if (del_timer(&ifp->timer))
@@ -941,17 +940,15 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
        if ((ifp->flags & IFA_F_PERMANENT) && onlink < 1) {
                struct in6_addr prefix;
                struct rt6_info *rt;
-               struct net *net = dev_net(ifp->idev->dev);
-               struct flowi6 fl6 = {};
 
                ipv6_addr_prefix(&prefix, &ifp->addr, ifp->prefix_len);
-               fl6.flowi6_oif = ifp->idev->dev->ifindex;
-               fl6.daddr = prefix;
-               rt = (struct rt6_info *)ip6_route_lookup(net, &fl6,
-                                                        RT6_LOOKUP_F_IFACE);
 
-               if (rt != net->ipv6.ip6_null_entry &&
-                   addrconf_is_prefix_route(rt)) {
+               rt = addrconf_get_prefix_route(&prefix,
+                                              ifp->prefix_len,
+                                              ifp->idev->dev,
+                                              0, RTF_GATEWAY | RTF_DEFAULT);
+
+               if (rt) {
                        if (onlink == 0) {
                                ip6_del_rt(rt);
                                rt = NULL;
@@ -1877,7 +1874,7 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
                        continue;
                if ((rt->rt6i_flags & flags) != flags)
                        continue;
-               if ((noflags != 0) && ((rt->rt6i_flags & flags) != 0))
+               if ((rt->rt6i_flags & noflags) != 0)
                        continue;
                dst_hold(&rt->dst);
                break;
index ecc35b93314bb1b73c70df219c9cf8f125c77908..384233188ac1e38468b5edf71c105c21e6bf38bc 100644 (file)
@@ -472,7 +472,10 @@ static void ah6_input_done(struct crypto_async_request *base, int err)
        skb->network_header += ah_hlen;
        memcpy(skb_network_header(skb), work_iph, hdr_len);
        __skb_pull(skb, ah_hlen + hdr_len);
-       skb_set_transport_header(skb, -hdr_len);
+       if (x->props.mode == XFRM_MODE_TUNNEL)
+               skb_reset_transport_header(skb);
+       else
+               skb_set_transport_header(skb, -hdr_len);
 out:
        kfree(AH_SKB_CB(skb)->tmp);
        xfrm_input_resume(skb, err);
@@ -593,9 +596,13 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)
 
        skb->network_header += ah_hlen;
        memcpy(skb_network_header(skb), work_iph, hdr_len);
-       skb->transport_header = skb->network_header;
        __skb_pull(skb, ah_hlen + hdr_len);
 
+       if (x->props.mode == XFRM_MODE_TUNNEL)
+               skb_reset_transport_header(skb);
+       else
+               skb_set_transport_header(skb, -hdr_len);
+
        err = nexthdr;
 
 out_free:
index 282f3723ee194704ab7fa0757e2c032254903300..40ffd72243a4f2d1ec9e5da494b3f2650dd027a6 100644 (file)
@@ -300,7 +300,10 @@ static int esp_input_done2(struct sk_buff *skb, int err)
 
        pskb_trim(skb, skb->len - alen - padlen - 2);
        __skb_pull(skb, hlen);
-       skb_set_transport_header(skb, -hdr_len);
+       if (x->props.mode == XFRM_MODE_TUNNEL)
+               skb_reset_transport_header(skb);
+       else
+               skb_set_transport_header(skb, -hdr_len);
 
        err = nexthdr[1];
 
index b4a9fd51dae74bd8143b2d32e791e089f1d8562f..fff5bdd8b6800d56679957a62e201e84fd846d09 100644 (file)
@@ -81,10 +81,22 @@ static inline struct sock *icmpv6_sk(struct net *net)
        return net->ipv6.icmp_sk[smp_processor_id()];
 }
 
+static void icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+                      u8 type, u8 code, int offset, __be32 info)
+{
+       struct net *net = dev_net(skb->dev);
+
+       if (type == ICMPV6_PKT_TOOBIG)
+               ip6_update_pmtu(skb, net, info, 0, 0);
+       else if (type == NDISC_REDIRECT)
+               ip6_redirect(skb, net, 0, 0);
+}
+
 static int icmpv6_rcv(struct sk_buff *skb);
 
 static const struct inet6_protocol icmpv6_protocol = {
        .handler        =       icmpv6_rcv,
+       .err_handler    =       icmpv6_err,
        .flags          =       INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
 };
 
index 5552d13ae92f8554c04ec912b82317dfeef065a5..0c7c03d50dc0342c7caad25c9d24c3931c50438b 100644 (file)
@@ -1213,10 +1213,10 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
                if (dst_allfrag(rt->dst.path))
                        cork->flags |= IPCORK_ALLFRAG;
                cork->length = 0;
-               exthdrlen = (opt ? opt->opt_flen : 0) - rt->rt6i_nfheader_len;
+               exthdrlen = (opt ? opt->opt_flen : 0);
                length += exthdrlen;
                transhdrlen += exthdrlen;
-               dst_exthdrlen = rt->dst.header_len;
+               dst_exthdrlen = rt->dst.header_len - rt->rt6i_nfheader_len;
        } else {
                rt = (struct rt6_info *)cork->dst;
                fl6 = &inet->cork.fl.u.ip6;
index 26dcdec9e3a5f6cbbe799402c39bb5d09c9d561d..8fd154e5f07966dd049bcdb8e93d9f7b8ab5452e 100644 (file)
@@ -1710,6 +1710,9 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns
                        return -EINVAL;
                if (get_user(v, (u32 __user *)optval))
                        return -EFAULT;
+               /* "pim6reg%u" should not exceed 16 bytes (IFNAMSIZ) */
+               if (v != RT_TABLE_DEFAULT && v >= 100000000)
+                       return -EINVAL;
                if (sk == mrt->mroute6_sk)
                        return -EBUSY;
 
index 5c61677487cf831324994e24e22528d15f35aa3a..516fbc96feff11831a9ad308541e6bdc16a7089d 100644 (file)
@@ -164,7 +164,17 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
                        sta = sta_info_get(sdata, mac_addr);
                else
                        sta = sta_info_get_bss(sdata, mac_addr);
-               if (!sta) {
+               /*
+                * The ASSOC test makes sure the driver is ready to
+                * receive the key. When wpa_supplicant has roamed
+                * using FT, it attempts to set the key before
+                * association has completed, this rejects that attempt
+                * so it will set the key again after assocation.
+                *
+                * TODO: accept the key if we have a station entry and
+                *       add it to the device after the station.
+                */
+               if (!sta || !test_sta_flag(sta, WLAN_STA_ASSOC)) {
                        ieee80211_key_free(sdata->local, key);
                        err = -ENOENT;
                        goto out_unlock;
@@ -1009,6 +1019,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
        if (old_probe_resp)
                kfree_rcu(old_probe_resp, rcu_head);
 
+       list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+               sta_info_flush(local, vlan);
        sta_info_flush(local, sdata);
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
 
index 53f03120db55dc3d2c1e8c775a054182d21dac69..80e55527504b91cd71b6d33a946507ad10b59c7e 100644 (file)
@@ -4,6 +4,7 @@
 
 #include <linux/nl80211.h>
 #include <linux/export.h>
+#include <linux/rtnetlink.h>
 #include <net/cfg80211.h>
 #include "ieee80211_i.h"
 #include "driver-ops.h"
@@ -197,6 +198,15 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
 
        ctx = container_of(conf, struct ieee80211_chanctx, conf);
 
+       if (sdata->vif.type == NL80211_IFTYPE_AP) {
+               struct ieee80211_sub_if_data *vlan;
+
+               /* for the VLAN list */
+               ASSERT_RTNL();
+               list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+                       rcu_assign_pointer(vlan->vif.chanctx_conf, NULL);
+       }
+
        ieee80211_unassign_vif_chanctx(sdata, ctx);
        if (ctx->refcount == 0)
                ieee80211_free_chanctx(local, ctx);
@@ -316,6 +326,15 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
                goto out;
        }
 
+       if (sdata->vif.type == NL80211_IFTYPE_AP) {
+               struct ieee80211_sub_if_data *vlan;
+
+               /* for the VLAN list */
+               ASSERT_RTNL();
+               list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+                       rcu_assign_pointer(vlan->vif.chanctx_conf, &ctx->conf);
+       }
+
        ieee80211_recalc_smps_chanctx(local, ctx);
  out:
        mutex_unlock(&local->chanctx_mtx);
@@ -331,6 +350,25 @@ void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
        mutex_unlock(&sdata->local->chanctx_mtx);
 }
 
+void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_sub_if_data *ap;
+       struct ieee80211_chanctx_conf *conf;
+
+       if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss))
+               return;
+
+       ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
+
+       mutex_lock(&local->chanctx_mtx);
+
+       conf = rcu_dereference_protected(ap->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
+       rcu_assign_pointer(sdata->vif.chanctx_conf, conf);
+       mutex_unlock(&local->chanctx_mtx);
+}
+
 void ieee80211_iter_chan_contexts_atomic(
        struct ieee80211_hw *hw,
        void (*iter)(struct ieee80211_hw *hw,
index 8881fc77fb1324c50d79ca6dcc2dd5423d7a18c9..6b7644e818d8f30629513318d4f9a17e4e622606 100644 (file)
@@ -703,8 +703,8 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
        sdata_info(sdata,
                   "No active IBSS STAs - trying to scan for other IBSS networks with same SSID (merge)\n");
 
-       ieee80211_request_internal_scan(sdata,
-                       ifibss->ssid, ifibss->ssid_len, NULL);
+       ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len,
+                                   NULL);
 }
 
 static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
@@ -802,9 +802,8 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
                                        IEEE80211_SCAN_INTERVAL)) {
                sdata_info(sdata, "Trigger new scan to find an IBSS to join\n");
 
-               ieee80211_request_internal_scan(sdata,
-                               ifibss->ssid, ifibss->ssid_len,
-                               ifibss->fixed_channel ? ifibss->channel : NULL);
+               ieee80211_request_ibss_scan(sdata, ifibss->ssid,
+                                           ifibss->ssid_len, chan);
        } else {
                int interval = IEEE80211_SCAN_INTERVAL;
 
index 42d0d026773090633aec6cecf21d5c6065a4b69d..2ed065c095629403c42574872fec6a8b85819871 100644 (file)
@@ -92,8 +92,6 @@ struct ieee80211_bss {
 
        u32 device_ts;
 
-       u8 dtim_period;
-
        bool wmm_used;
        bool uapsd_supported;
 
@@ -140,7 +138,6 @@ enum ieee80211_bss_corrupt_data_flags {
 
 /**
  * enum ieee80211_valid_data_flags - BSS valid data flags
- * @IEEE80211_BSS_VALID_DTIM: DTIM data was gathered from non-corrupt IE
  * @IEEE80211_BSS_VALID_WMM: WMM/UAPSD data was gathered from non-corrupt IE
  * @IEEE80211_BSS_VALID_RATES: Supported rates were gathered from non-corrupt IE
  * @IEEE80211_BSS_VALID_ERP: ERP flag was gathered from non-corrupt IE
@@ -151,7 +148,6 @@ enum ieee80211_bss_corrupt_data_flags {
  * beacon/probe response.
  */
 enum ieee80211_bss_valid_data_flags {
-       IEEE80211_BSS_VALID_DTIM                = BIT(0),
        IEEE80211_BSS_VALID_WMM                 = BIT(1),
        IEEE80211_BSS_VALID_RATES               = BIT(2),
        IEEE80211_BSS_VALID_ERP                 = BIT(3)
@@ -440,6 +436,7 @@ struct ieee80211_if_managed {
        unsigned long timers_running; /* used for quiesce/restart */
        bool powersave; /* powersave requested for this iface */
        bool broken_ap; /* AP is broken -- turn off powersave */
+       u8 dtim_period;
        enum ieee80211_smps_mode req_smps, /* requested smps mode */
                                 driver_smps_mode; /* smps mode request */
 
@@ -773,6 +770,10 @@ struct ieee80211_sub_if_data {
                u32 mntr_flags;
        } u;
 
+       spinlock_t cleanup_stations_lock;
+       struct list_head cleanup_stations;
+       struct work_struct cleanup_stations_wk;
+
 #ifdef CONFIG_MAC80211_DEBUGFS
        struct {
                struct dentry *dir;
@@ -1329,9 +1330,9 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
 
 /* scan/BSS handling */
 void ieee80211_scan_work(struct work_struct *work);
-int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
-                                   const u8 *ssid, u8 ssid_len,
-                                   struct ieee80211_channel *chan);
+int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
+                               const u8 *ssid, u8 ssid_len,
+                               struct ieee80211_channel *chan);
 int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
                           struct cfg80211_scan_request *req);
 void ieee80211_scan_cancel(struct ieee80211_local *local);
@@ -1357,10 +1358,8 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata);
 void ieee80211_sched_scan_stopped_work(struct work_struct *work);
 
 /* off-channel helpers */
-void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local,
-                                   bool offchannel_ps_enable);
-void ieee80211_offchannel_return(struct ieee80211_local *local,
-                                bool offchannel_ps_disable);
+void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local);
+void ieee80211_offchannel_return(struct ieee80211_local *local);
 void ieee80211_roc_setup(struct ieee80211_local *local);
 void ieee80211_start_next_roc(struct ieee80211_local *local);
 void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata);
@@ -1628,6 +1627,7 @@ ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
                          const struct cfg80211_chan_def *chandef,
                          enum ieee80211_chanctx_mode mode);
 void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);
+void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata);
 
 void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
                                   struct ieee80211_chanctx *chanctx);
index 09a80b55cf5a1ffaf06fdbe892ac469f6790b2bf..8be854e86cd987d61e01cc7eabc8f7ddb068eff3 100644 (file)
@@ -207,17 +207,8 @@ void ieee80211_recalc_idle(struct ieee80211_local *local)
 
 static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
 {
-       int meshhdrlen;
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
-       meshhdrlen = (sdata->vif.type == NL80211_IFTYPE_MESH_POINT) ? 5 : 0;
-
-       /* FIX: what would be proper limits for MTU?
-        * This interface uses 802.3 frames. */
-       if (new_mtu < 256 ||
-           new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6 - meshhdrlen) {
+       if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN)
                return -EINVAL;
-       }
 
        dev->mtu = new_mtu;
        return 0;
@@ -586,11 +577,13 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP_VLAN:
-               /* no need to tell driver, but set carrier */
-               if (rtnl_dereference(sdata->bss->beacon))
+               /* no need to tell driver, but set carrier and chanctx */
+               if (rtnl_dereference(sdata->bss->beacon)) {
+                       ieee80211_vif_vlan_copy_chanctx(sdata);
                        netif_carrier_on(dev);
-               else
+               } else {
                        netif_carrier_off(dev);
+               }
                break;
        case NL80211_IFTYPE_MONITOR:
                if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) {
@@ -839,6 +832,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP_VLAN:
                list_del(&sdata->u.vlan.list);
+               rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
                /* no need to tell driver */
                break;
        case NL80211_IFTYPE_MONITOR:
@@ -865,20 +859,11 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
                cancel_work_sync(&sdata->work);
                /*
                 * When we get here, the interface is marked down.
-                * Call rcu_barrier() to wait both for the RX path
+                * Call synchronize_rcu() to wait for the RX path
                 * should it be using the interface and enqueuing
-                * frames at this very time on another CPU, and
-                * for the sta free call_rcu callbacks.
-                */
-               rcu_barrier();
-
-               /*
-                * free_sta_rcu() enqueues a work for the actual
-                * sta cleanup, so we need to flush it while
-                * sdata is still valid.
+                * frames at this very time on another CPU.
                 */
-               flush_workqueue(local->workqueue);
-
+               synchronize_rcu();
                skb_queue_purge(&sdata->skb_queue);
 
                /*
@@ -1498,6 +1483,15 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,
        mutex_unlock(&local->iflist_mtx);
 }
 
+static void ieee80211_cleanup_sdata_stas_wk(struct work_struct *wk)
+{
+       struct ieee80211_sub_if_data *sdata;
+
+       sdata = container_of(wk, struct ieee80211_sub_if_data, cleanup_stations_wk);
+
+       ieee80211_cleanup_sdata_stas(sdata);
+}
+
 int ieee80211_if_add(struct ieee80211_local *local, const char *name,
                     struct wireless_dev **new_wdev, enum nl80211_iftype type,
                     struct vif_params *params)
@@ -1573,6 +1567,10 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
 
        INIT_LIST_HEAD(&sdata->key_list);
 
+       spin_lock_init(&sdata->cleanup_stations_lock);
+       INIT_LIST_HEAD(&sdata->cleanup_stations);
+       INIT_WORK(&sdata->cleanup_stations_wk, ieee80211_cleanup_sdata_stas_wk);
+
        for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
                struct ieee80211_supported_band *sband;
                sband = local->hw.wiphy->bands[i];
index 1bf03f9ff3ba74394fb26201dfba3b3fd2e86a01..649ad513547f99728d43dbeeabcd2b58d7862c30 100644 (file)
@@ -163,7 +163,7 @@ int mesh_rmc_init(struct ieee80211_sub_if_data *sdata)
                return -ENOMEM;
        sdata->u.mesh.rmc->idx_mask = RMC_BUCKETS - 1;
        for (i = 0; i < RMC_BUCKETS; i++)
-               INIT_LIST_HEAD(&sdata->u.mesh.rmc->bucket[i].list);
+               INIT_LIST_HEAD(&sdata->u.mesh.rmc->bucket[i]);
        return 0;
 }
 
@@ -177,7 +177,7 @@ void mesh_rmc_free(struct ieee80211_sub_if_data *sdata)
                return;
 
        for (i = 0; i < RMC_BUCKETS; i++)
-               list_for_each_entry_safe(p, n, &rmc->bucket[i].list, list) {
+               list_for_each_entry_safe(p, n, &rmc->bucket[i], list) {
                        list_del(&p->list);
                        kmem_cache_free(rm_cache, p);
                }
@@ -210,7 +210,7 @@ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr,
        /* Don't care about endianness since only match matters */
        memcpy(&seqnum, &mesh_hdr->seqnum, sizeof(mesh_hdr->seqnum));
        idx = le32_to_cpu(mesh_hdr->seqnum) & rmc->idx_mask;
-       list_for_each_entry_safe(p, n, &rmc->bucket[idx].list, list) {
+       list_for_each_entry_safe(p, n, &rmc->bucket[idx], list) {
                ++entries;
                if (time_after(jiffies, p->exp_time) ||
                                (entries == RMC_QUEUE_MAX_LEN)) {
@@ -229,7 +229,7 @@ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr,
        p->seqnum = seqnum;
        p->exp_time = jiffies + RMC_TIMEOUT;
        memcpy(p->sa, sa, ETH_ALEN);
-       list_add(&p->list, &rmc->bucket[idx].list);
+       list_add(&p->list, &rmc->bucket[idx]);
        return 0;
 }
 
index 7c9215fb2ac84ec5c17d3ee3d372a7a6bb4feedc..84c28c6101cdd2ef832920677d05b7f02b27ada2 100644 (file)
@@ -184,7 +184,7 @@ struct rmc_entry {
 };
 
 struct mesh_rmc {
-       struct rmc_entry bucket[RMC_BUCKETS];
+       struct list_head bucket[RMC_BUCKETS];
        u32 idx_mask;
 };
 
index 47aeee2d8db160f6fa9eb62c32131bbd686acc29..2659e428b80c86cf367a75b034c8d89a856f28a1 100644 (file)
@@ -215,6 +215,7 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata,
        skb->priority = 7;
 
        info->control.vif = &sdata->vif;
+       info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
        ieee80211_set_qos_hdr(sdata, skb);
 }
 
@@ -246,11 +247,13 @@ int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn,
                return -EAGAIN;
 
        skb = dev_alloc_skb(local->tx_headroom +
+                           IEEE80211_ENCRYPT_HEADROOM +
+                           IEEE80211_ENCRYPT_TAILROOM +
                            hdr_len +
                            2 + 15 /* PERR IE */);
        if (!skb)
                return -1;
-       skb_reserve(skb, local->tx_headroom);
+       skb_reserve(skb, local->tx_headroom + IEEE80211_ENCRYPT_HEADROOM);
        mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len);
        memset(mgmt, 0, hdr_len);
        mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
index 7753a9ca98a6c67870f68bd40b22f3af453a3945..a3552929a21d68f469696c4131ffb6296becd5df 100644 (file)
@@ -1074,12 +1074,8 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
                if (beaconint_us > latency) {
                        local->ps_sdata = NULL;
                } else {
-                       struct ieee80211_bss *bss;
                        int maxslp = 1;
-                       u8 dtimper;
-
-                       bss = (void *)found->u.mgd.associated->priv;
-                       dtimper = bss->dtim_period;
+                       u8 dtimper = found->u.mgd.dtim_period;
 
                        /* If the TIM IE is invalid, pretend the value is 1 */
                        if (!dtimper)
@@ -1410,10 +1406,17 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_led_assoc(local, 1);
 
-       if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD)
-               bss_conf->dtim_period = bss->dtim_period;
-       else
+       if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) {
+               /*
+                * If the AP is buggy we may get here with no DTIM period
+                * known, so assume it's 1 which is the only safe assumption
+                * in that case, although if the TIM IE is broken powersave
+                * probably just won't work at all.
+                */
+               bss_conf->dtim_period = sdata->u.mgd.dtim_period ?: 1;
+       } else {
                bss_conf->dtim_period = 0;
+       }
 
        bss_conf->assoc = 1;
 
@@ -1562,6 +1565,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
 
        sdata->u.mgd.timers_running = 0;
 
+       sdata->vif.bss_conf.dtim_period = 0;
+
        ifmgd->flags = 0;
        ieee80211_vif_release_channel(sdata);
 }
@@ -2373,11 +2378,18 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_channel *channel;
        bool need_ps = false;
 
-       if (sdata->u.mgd.associated &&
-           ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) {
-               bss = (void *)sdata->u.mgd.associated->priv;
+       if ((sdata->u.mgd.associated &&
+            ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) ||
+           (sdata->u.mgd.assoc_data &&
+            ether_addr_equal(mgmt->bssid,
+                             sdata->u.mgd.assoc_data->bss->bssid))) {
                /* not previously set so we may need to recalc */
-               need_ps = !bss->dtim_period;
+               need_ps = sdata->u.mgd.associated && !sdata->u.mgd.dtim_period;
+
+               if (elems->tim && !elems->parse_error) {
+                       struct ieee80211_tim_ie *tim_ie = elems->tim;
+                       sdata->u.mgd.dtim_period = tim_ie->dtim_period;
+               }
        }
 
        if (elems->ds_params && elems->ds_params_len == 1)
@@ -3896,20 +3908,41 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        /* kick off associate process */
 
        ifmgd->assoc_data = assoc_data;
+       ifmgd->dtim_period = 0;
 
        err = ieee80211_prep_connection(sdata, req->bss, true);
        if (err)
                goto err_clear;
 
-       if (!bss->dtim_period &&
-           sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) {
-               /*
-                * Wait up to one beacon interval ...
-                * should this be more if we miss one?
-                */
-               sdata_info(sdata, "waiting for beacon from %pM\n",
-                          ifmgd->bssid);
-               assoc_data->timeout = TU_TO_EXP_TIME(req->bss->beacon_interval);
+       if (sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) {
+               const struct cfg80211_bss_ies *beacon_ies;
+
+               rcu_read_lock();
+               beacon_ies = rcu_dereference(req->bss->beacon_ies);
+               if (!beacon_ies) {
+                       /*
+                        * Wait up to one beacon interval ...
+                        * should this be more if we miss one?
+                        */
+                       sdata_info(sdata, "waiting for beacon from %pM\n",
+                                  ifmgd->bssid);
+                       assoc_data->timeout =
+                               TU_TO_EXP_TIME(req->bss->beacon_interval);
+               } else {
+                       const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM,
+                                                           beacon_ies->data,
+                                                           beacon_ies->len);
+                       if (tim_ie && tim_ie[1] >=
+                                       sizeof(struct ieee80211_tim_ie)) {
+                               const struct ieee80211_tim_ie *tim;
+                               tim = (void *)(tim_ie + 2);
+                               ifmgd->dtim_period = tim->dtim_period;
+                       }
+                       assoc_data->have_beacon = true;
+                       assoc_data->sent_assoc = false;
+                       assoc_data->timeout = jiffies;
+               }
+               rcu_read_unlock();
        } else {
                assoc_data->have_beacon = true;
                assoc_data->sent_assoc = false;
index a5379aea7d09d2410c70a3b467a1f336aa32dc43..a3ad4c3c80a34229801bdbdd1124201485a2f671 100644 (file)
@@ -102,8 +102,7 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata)
        ieee80211_sta_reset_conn_monitor(sdata);
 }
 
-void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local,
-                                   bool offchannel_ps_enable)
+void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local)
 {
        struct ieee80211_sub_if_data *sdata;
 
@@ -134,8 +133,7 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local,
 
                if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
                        netif_tx_stop_all_queues(sdata->dev);
-                       if (offchannel_ps_enable &&
-                           (sdata->vif.type == NL80211_IFTYPE_STATION) &&
+                       if (sdata->vif.type == NL80211_IFTYPE_STATION &&
                            sdata->u.mgd.associated)
                                ieee80211_offchannel_ps_enable(sdata);
                }
@@ -143,8 +141,7 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local,
        mutex_unlock(&local->iflist_mtx);
 }
 
-void ieee80211_offchannel_return(struct ieee80211_local *local,
-                                bool offchannel_ps_disable)
+void ieee80211_offchannel_return(struct ieee80211_local *local)
 {
        struct ieee80211_sub_if_data *sdata;
 
@@ -163,11 +160,9 @@ void ieee80211_offchannel_return(struct ieee80211_local *local,
                        continue;
 
                /* Tell AP we're back */
-               if (offchannel_ps_disable &&
-                   sdata->vif.type == NL80211_IFTYPE_STATION) {
-                       if (sdata->u.mgd.associated)
-                               ieee80211_offchannel_ps_disable(sdata);
-               }
+               if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+                   sdata->u.mgd.associated)
+                       ieee80211_offchannel_ps_disable(sdata);
 
                if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
                        /*
@@ -385,7 +380,7 @@ void ieee80211_sw_roc_work(struct work_struct *work)
                        local->tmp_channel = NULL;
                        ieee80211_hw_config(local, 0);
 
-                       ieee80211_offchannel_return(local, true);
+                       ieee80211_offchannel_return(local);
                }
 
                ieee80211_recalc_idle(local);
index 8ed83dcc149fc7f38ca9beaf83a2c67a82c336ec..bf82e69d0601bf9210e817837b2af7725a9f7d87 100644 (file)
@@ -113,18 +113,6 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
                        bss->valid_data |= IEEE80211_BSS_VALID_ERP;
        }
 
-       if (elems->tim && (!elems->parse_error ||
-                          !(bss->valid_data & IEEE80211_BSS_VALID_DTIM))) {
-               struct ieee80211_tim_ie *tim_ie = elems->tim;
-               bss->dtim_period = tim_ie->dtim_period;
-               if (!elems->parse_error)
-                       bss->valid_data |= IEEE80211_BSS_VALID_DTIM;
-       }
-
-       /* If the beacon had no TIM IE, or it was invalid, use 1 */
-       if (beacon && !bss->dtim_period)
-               bss->dtim_period = 1;
-
        /* replace old supported rates if we get new values */
        if (!elems->parse_error ||
            !(bss->valid_data & IEEE80211_BSS_VALID_RATES)) {
@@ -304,7 +292,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
        if (!was_hw_scan) {
                ieee80211_configure_filter(local);
                drv_sw_scan_complete(local);
-               ieee80211_offchannel_return(local, true);
+               ieee80211_offchannel_return(local);
        }
 
        ieee80211_recalc_idle(local);
@@ -353,7 +341,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
        local->next_scan_state = SCAN_DECISION;
        local->scan_channel_idx = 0;
 
-       ieee80211_offchannel_stop_vifs(local, true);
+       ieee80211_offchannel_stop_vifs(local);
 
        ieee80211_configure_filter(local);
 
@@ -690,12 +678,8 @@ static void ieee80211_scan_state_suspend(struct ieee80211_local *local,
        local->scan_channel = NULL;
        ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
 
-       /*
-        * Re-enable vifs and beaconing.  Leave PS
-        * in off-channel state..will put that back
-        * on-channel at the end of scanning.
-        */
-       ieee80211_offchannel_return(local, false);
+       /* disable PS */
+       ieee80211_offchannel_return(local);
 
        *next_delay = HZ / 5;
        /* afterwards, resume scan & go to next channel */
@@ -705,8 +689,7 @@ static void ieee80211_scan_state_suspend(struct ieee80211_local *local,
 static void ieee80211_scan_state_resume(struct ieee80211_local *local,
                                        unsigned long *next_delay)
 {
-       /* PS already is in off-channel mode */
-       ieee80211_offchannel_stop_vifs(local, false);
+       ieee80211_offchannel_stop_vifs(local);
 
        if (local->ops->flush) {
                drv_flush(local, false);
@@ -832,9 +815,9 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
        return res;
 }
 
-int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
-                                   const u8 *ssid, u8 ssid_len,
-                                   struct ieee80211_channel *chan)
+int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
+                               const u8 *ssid, u8 ssid_len,
+                               struct ieee80211_channel *chan)
 {
        struct ieee80211_local *local = sdata->local;
        int ret = -EBUSY;
@@ -848,22 +831,36 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
 
        /* fill internal scan request */
        if (!chan) {
-               int i, nchan = 0;
+               int i, max_n;
+               int n_ch = 0;
 
                for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
                        if (!local->hw.wiphy->bands[band])
                                continue;
-                       for (i = 0;
-                            i < local->hw.wiphy->bands[band]->n_channels;
-                            i++) {
-                               local->int_scan_req->channels[nchan] =
+
+                       max_n = local->hw.wiphy->bands[band]->n_channels;
+                       for (i = 0; i < max_n; i++) {
+                               struct ieee80211_channel *tmp_ch =
                                    &local->hw.wiphy->bands[band]->channels[i];
-                               nchan++;
+
+                               if (tmp_ch->flags & (IEEE80211_CHAN_NO_IBSS |
+                                                    IEEE80211_CHAN_DISABLED))
+                                       continue;
+
+                               local->int_scan_req->channels[n_ch] = tmp_ch;
+                               n_ch++;
                        }
                }
 
-               local->int_scan_req->n_channels = nchan;
+               if (WARN_ON_ONCE(n_ch == 0))
+                       goto unlock;
+
+               local->int_scan_req->n_channels = n_ch;
        } else {
+               if (WARN_ON_ONCE(chan->flags & (IEEE80211_CHAN_NO_IBSS |
+                                               IEEE80211_CHAN_DISABLED)))
+                       goto unlock;
+
                local->int_scan_req->channels[0] = chan;
                local->int_scan_req->n_channels = 1;
        }
index f3e502502fee27374dc4c183733cbcc95217aed5..ca9fde1981889a559efa6b49385f071f1475a06f 100644 (file)
@@ -91,9 +91,8 @@ static int sta_info_hash_del(struct ieee80211_local *local,
        return -ENOENT;
 }
 
-static void free_sta_work(struct work_struct *wk)
+static void cleanup_single_sta(struct sta_info *sta)
 {
-       struct sta_info *sta = container_of(wk, struct sta_info, free_sta_wk);
        int ac, i;
        struct tid_ampdu_tx *tid_tx;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
@@ -153,11 +152,35 @@ static void free_sta_work(struct work_struct *wk)
        sta_info_free(local, sta);
 }
 
+void ieee80211_cleanup_sdata_stas(struct ieee80211_sub_if_data *sdata)
+{
+       struct sta_info *sta;
+
+       spin_lock_bh(&sdata->cleanup_stations_lock);
+       while (!list_empty(&sdata->cleanup_stations)) {
+               sta = list_first_entry(&sdata->cleanup_stations,
+                                      struct sta_info, list);
+               list_del(&sta->list);
+               spin_unlock_bh(&sdata->cleanup_stations_lock);
+
+               cleanup_single_sta(sta);
+
+               spin_lock_bh(&sdata->cleanup_stations_lock);
+       }
+
+       spin_unlock_bh(&sdata->cleanup_stations_lock);
+}
+
 static void free_sta_rcu(struct rcu_head *h)
 {
        struct sta_info *sta = container_of(h, struct sta_info, rcu_head);
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
 
-       ieee80211_queue_work(&sta->local->hw, &sta->free_sta_wk);
+       spin_lock(&sdata->cleanup_stations_lock);
+       list_add_tail(&sta->list, &sdata->cleanup_stations);
+       spin_unlock(&sdata->cleanup_stations_lock);
+
+       ieee80211_queue_work(&sdata->local->hw, &sdata->cleanup_stations_wk);
 }
 
 /* protected by RCU */
@@ -310,7 +333,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 
        spin_lock_init(&sta->lock);
        INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
-       INIT_WORK(&sta->free_sta_wk, free_sta_work);
        INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
        mutex_init(&sta->ampdu_mlme.mtx);
 
@@ -862,7 +884,7 @@ void sta_info_init(struct ieee80211_local *local)
 
 void sta_info_stop(struct ieee80211_local *local)
 {
-       del_timer(&local->sta_cleanup);
+       del_timer_sync(&local->sta_cleanup);
        sta_info_flush(local, NULL);
 }
 
@@ -891,6 +913,20 @@ int sta_info_flush(struct ieee80211_local *local,
        }
        mutex_unlock(&local->sta_mtx);
 
+       rcu_barrier();
+
+       if (sdata) {
+               ieee80211_cleanup_sdata_stas(sdata);
+               cancel_work_sync(&sdata->cleanup_stations_wk);
+       } else {
+               mutex_lock(&local->iflist_mtx);
+               list_for_each_entry(sdata, &local->interfaces, list) {
+                       ieee80211_cleanup_sdata_stas(sdata);
+                       cancel_work_sync(&sdata->cleanup_stations_wk);
+               }
+               mutex_unlock(&local->iflist_mtx);
+       }
+
        return ret;
 }
 
index 1489bca9ea97c19ee525ea44c0d1af794ec94cca..37c1889afd3ae02f9d9d5f2573738a28d26c266e 100644 (file)
@@ -299,7 +299,6 @@ struct sta_info {
        spinlock_t lock;
 
        struct work_struct drv_unblock_wk;
-       struct work_struct free_sta_wk;
 
        u16 listen_interval;
 
@@ -563,4 +562,6 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta);
 void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta);
 void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta);
 
+void ieee80211_cleanup_sdata_stas(struct ieee80211_sub_if_data *sdata);
+
 #endif /* STA_INFO_H */
index e9eadc40c09cc20bce9ad37531ef45ae767f62e8..467c1d1b66f2f5615182abd689349027b88abe64 100644 (file)
@@ -1673,10 +1673,13 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
                        chanctx_conf =
                                rcu_dereference(tmp_sdata->vif.chanctx_conf);
        }
-       if (!chanctx_conf)
-               goto fail_rcu;
 
-       chan = chanctx_conf->def.chan;
+       if (chanctx_conf)
+               chan = chanctx_conf->def.chan;
+       else if (!local->use_chanctx)
+               chan = local->_oper_channel;
+       else
+               goto fail_rcu;
 
        /*
         * Frame injection is not allowed if beaconing is not allowed
index 016d95ead930cba8e6d2e9335cd2b64f473c09d1..e4a0c4fb3a7cef64d1f15c9173d5a3e62ad616b4 100644 (file)
@@ -1376,11 +1376,12 @@ void nf_conntrack_cleanup(struct net *net)
        synchronize_net();
        nf_conntrack_proto_fini(net);
        nf_conntrack_cleanup_net(net);
+}
 
-       if (net_eq(net, &init_net)) {
-               RCU_INIT_POINTER(nf_ct_destroy, NULL);
-               nf_conntrack_cleanup_init_net();
-       }
+void nf_conntrack_cleanup_end(void)
+{
+       RCU_INIT_POINTER(nf_ct_destroy, NULL);
+       nf_conntrack_cleanup_init_net();
 }
 
 void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls)
index 363285d544a1c7402152e6a7da3a7129d94b83ae..e7185c68481659445b571ab5e7866728dd3947ec 100644 (file)
@@ -575,6 +575,7 @@ static int __init nf_conntrack_standalone_init(void)
 static void __exit nf_conntrack_standalone_fini(void)
 {
        unregister_pernet_subsys(&nf_conntrack_net_ops);
+       nf_conntrack_cleanup_end();
 }
 
 module_init(nf_conntrack_standalone_init);
index 8d987c3573fd4ca9e1708e188d487e53ebd7dfd7..7b3a9e5999c0664cebe524fefba7130a6df003ce 100644 (file)
@@ -345,19 +345,27 @@ int xt_find_revision(u8 af, const char *name, u8 revision, int target,
 }
 EXPORT_SYMBOL_GPL(xt_find_revision);
 
-static char *textify_hooks(char *buf, size_t size, unsigned int mask)
+static char *
+textify_hooks(char *buf, size_t size, unsigned int mask, uint8_t nfproto)
 {
-       static const char *const names[] = {
+       static const char *const inetbr_names[] = {
                "PREROUTING", "INPUT", "FORWARD",
                "OUTPUT", "POSTROUTING", "BROUTING",
        };
-       unsigned int i;
+       static const char *const arp_names[] = {
+               "INPUT", "FORWARD", "OUTPUT",
+       };
+       const char *const *names;
+       unsigned int i, max;
        char *p = buf;
        bool np = false;
        int res;
 
+       names = (nfproto == NFPROTO_ARP) ? arp_names : inetbr_names;
+       max   = (nfproto == NFPROTO_ARP) ? ARRAY_SIZE(arp_names) :
+                                          ARRAY_SIZE(inetbr_names);
        *p = '\0';
-       for (i = 0; i < ARRAY_SIZE(names); ++i) {
+       for (i = 0; i < max; ++i) {
                if (!(mask & (1 << i)))
                        continue;
                res = snprintf(p, size, "%s%s", np ? "/" : "", names[i]);
@@ -402,8 +410,10 @@ int xt_check_match(struct xt_mtchk_param *par,
                pr_err("%s_tables: %s match: used from hooks %s, but only "
                       "valid from %s\n",
                       xt_prefix[par->family], par->match->name,
-                      textify_hooks(used, sizeof(used), par->hook_mask),
-                      textify_hooks(allow, sizeof(allow), par->match->hooks));
+                      textify_hooks(used, sizeof(used), par->hook_mask,
+                                    par->family),
+                      textify_hooks(allow, sizeof(allow), par->match->hooks,
+                                    par->family));
                return -EINVAL;
        }
        if (par->match->proto && (par->match->proto != proto || inv_proto)) {
@@ -575,8 +585,10 @@ int xt_check_target(struct xt_tgchk_param *par,
                pr_err("%s_tables: %s target: used from hooks %s, but only "
                       "usable from %s\n",
                       xt_prefix[par->family], par->target->name,
-                      textify_hooks(used, sizeof(used), par->hook_mask),
-                      textify_hooks(allow, sizeof(allow), par->target->hooks));
+                      textify_hooks(used, sizeof(used), par->hook_mask,
+                                    par->family),
+                      textify_hooks(allow, sizeof(allow), par->target->hooks,
+                                    par->family));
                return -EINVAL;
        }
        if (par->target->proto && (par->target->proto != proto || inv_proto)) {
index 2a0843081840c0cd937a3fb0e749965bf9e28d0c..bde009ed8d3bf36d6defdbdbad8aca991c539281 100644 (file)
@@ -109,7 +109,7 @@ static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par)
        struct xt_ct_target_info *info = par->targinfo;
        struct nf_conntrack_tuple t;
        struct nf_conn *ct;
-       int ret;
+       int ret = -EOPNOTSUPP;
 
        if (info->flags & ~XT_CT_NOTRACK)
                return -EINVAL;
@@ -247,7 +247,7 @@ static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
        struct xt_ct_target_info_v1 *info = par->targinfo;
        struct nf_conntrack_tuple t;
        struct nf_conn *ct;
-       int ret;
+       int ret = -EOPNOTSUPP;
 
        if (info->flags & ~XT_CT_NOTRACK)
                return -EINVAL;
index 379c81dee9d1256b3a1af84b84bc23b28092f12b..9bcdbd02d77713a5973dd9afd71a9c4783919648 100644 (file)
@@ -224,7 +224,7 @@ void sctp_outq_init(struct sctp_association *asoc, struct sctp_outq *q)
 
 /* Free the outqueue structure and any related pending chunks.
  */
-void sctp_outq_teardown(struct sctp_outq *q)
+static void __sctp_outq_teardown(struct sctp_outq *q)
 {
        struct sctp_transport *transport;
        struct list_head *lchunk, *temp;
@@ -277,8 +277,6 @@ void sctp_outq_teardown(struct sctp_outq *q)
                sctp_chunk_free(chunk);
        }
 
-       q->error = 0;
-
        /* Throw away any leftover control chunks. */
        list_for_each_entry_safe(chunk, tmp, &q->control_chunk_list, list) {
                list_del_init(&chunk->list);
@@ -286,11 +284,17 @@ void sctp_outq_teardown(struct sctp_outq *q)
        }
 }
 
+void sctp_outq_teardown(struct sctp_outq *q)
+{
+       __sctp_outq_teardown(q);
+       sctp_outq_init(q->asoc, q);
+}
+
 /* Free the outqueue structure and any related pending chunks.  */
 void sctp_outq_free(struct sctp_outq *q)
 {
        /* Throw away leftover chunks. */
-       sctp_outq_teardown(q);
+       __sctp_outq_teardown(q);
 
        /* If we were kmalloc()'d, free the memory.  */
        if (q->malloced)
index 618ec7e216cae9bb17038c07ff5d87a75c8f6208..5131fcfedb03c0b09e8c7e451341cebd82e49692 100644 (file)
@@ -1779,8 +1779,10 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(struct net *net,
 
        /* Update the content of current association. */
        sctp_add_cmd_sf(commands, SCTP_CMD_UPDATE_ASSOC, SCTP_ASOC(new_asoc));
-       sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
        sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev));
+       sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+                       SCTP_STATE(SCTP_STATE_ESTABLISHED));
+       sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
        return SCTP_DISPOSITION_CONSUME;
 
 nomem_ev:
index 043889ac86c0419b3d368481b202864077034efa..bf3c6e8fc4017a64f93ff91c0ce96c8debbf44f4 100644 (file)
@@ -366,7 +366,11 @@ int sctp_sysctl_net_register(struct net *net)
 
 void sctp_sysctl_net_unregister(struct net *net)
 {
+       struct ctl_table *table;
+
+       table = net->sctp.sysctl_header->ctl_table_arg;
        unregister_net_sysctl_table(net->sctp.sysctl_header);
+       kfree(table);
 }
 
 static struct ctl_table_header * sctp_sysctl_header;
index 1915ffe598e3faf6e0aff577f530fab85106c7c4..507b5e84fbdb26f88fab789e37f65a976ef6f198 100644 (file)
@@ -555,7 +555,7 @@ EXPORT_SYMBOL_GPL(rpc_clone_client);
  * rpc_clone_client_set_auth - Clone an RPC client structure and set its auth
  *
  * @clnt: RPC client whose parameters are copied
- * @auth: security flavor for new client
+ * @flavor: security flavor for new client
  *
  * Returns a fresh RPC client or an ERR_PTR.
  */
index b4133bd13915317a96d94de9dcf2e5d48551870e..fb20f25ddec9b70c805ad65d675441d02b62055b 100644 (file)
@@ -98,9 +98,25 @@ __rpc_add_timer(struct rpc_wait_queue *queue, struct rpc_task *task)
        list_add(&task->u.tk_wait.timer_list, &queue->timer_list.list);
 }
 
+static void rpc_rotate_queue_owner(struct rpc_wait_queue *queue)
+{
+       struct list_head *q = &queue->tasks[queue->priority];
+       struct rpc_task *task;
+
+       if (!list_empty(q)) {
+               task = list_first_entry(q, struct rpc_task, u.tk_wait.list);
+               if (task->tk_owner == queue->owner)
+                       list_move_tail(&task->u.tk_wait.list, q);
+       }
+}
+
 static void rpc_set_waitqueue_priority(struct rpc_wait_queue *queue, int priority)
 {
-       queue->priority = priority;
+       if (queue->priority != priority) {
+               /* Fairness: rotate the list when changing priority */
+               rpc_rotate_queue_owner(queue);
+               queue->priority = priority;
+       }
 }
 
 static void rpc_set_waitqueue_owner(struct rpc_wait_queue *queue, pid_t pid)
@@ -972,8 +988,7 @@ static void rpc_async_release(struct work_struct *work)
 
 static void rpc_release_resources_task(struct rpc_task *task)
 {
-       if (task->tk_rqstp)
-               xprt_release(task);
+       xprt_release(task);
        if (task->tk_msg.rpc_cred) {
                put_rpccred(task->tk_msg.rpc_cred);
                task->tk_msg.rpc_cred = NULL;
index bd462a532acfa695906887db429896ae569eac26..33811db8788a2e74e5781a0c20bdbceb829b5018 100644 (file)
@@ -1136,10 +1136,18 @@ static void xprt_request_init(struct rpc_task *task, struct rpc_xprt *xprt)
 void xprt_release(struct rpc_task *task)
 {
        struct rpc_xprt *xprt;
-       struct rpc_rqst *req;
+       struct rpc_rqst *req = task->tk_rqstp;
 
-       if (!(req = task->tk_rqstp))
+       if (req == NULL) {
+               if (task->tk_client) {
+                       rcu_read_lock();
+                       xprt = rcu_dereference(task->tk_client->cl_xprt);
+                       if (xprt->snd_task == task)
+                               xprt_release_write(xprt, task);
+                       rcu_read_unlock();
+               }
                return;
+       }
 
        xprt = req->rq_xprt;
        if (task->tk_ops->rpc_count_stats != NULL)
index 14d990400354200056e99d14132d6e7ab2504ccb..b677eab55b68465494bffae68a2c38c2321f18ca 100644 (file)
@@ -866,8 +866,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                /* allow mac80211 to determine the timeout */
                wdev->ps_timeout = -1;
 
-               if (!dev->ethtool_ops)
-                       dev->ethtool_ops = &cfg80211_ethtool_ops;
+               netdev_set_default_ethtool_ops(dev, &cfg80211_ethtool_ops);
 
                if ((wdev->iftype == NL80211_IFTYPE_STATION ||
                     wdev->iftype == NL80211_IFTYPE_P2P_CLIENT ||
index 41eabc46f110d9cb607cb24f8af68054b91bcccd..07c585756d2a52530dc3fd1f5b14192b1a3c4cd6 100644 (file)
@@ -2656,7 +2656,7 @@ static void xfrm_policy_fini(struct net *net)
                WARN_ON(!hlist_empty(&net->xfrm.policy_inexact[dir]));
 
                htab = &net->xfrm.policy_bydst[dir];
-               sz = (htab->hmask + 1);
+               sz = (htab->hmask + 1) * sizeof(struct hlist_head);
                WARN_ON(!hlist_empty(htab->table));
                xfrm_hash_free(htab->table, sz);
        }
index 765f6fe951ebc7ed8fabaed3a4dc6ea4299aebfa..35754cc8a9e5b9c51cdaf52693128ee2098718f7 100644 (file)
@@ -242,11 +242,13 @@ static void xfrm_replay_advance_bmp(struct xfrm_state *x, __be32 net_seq)
        u32 diff;
        struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
        u32 seq = ntohl(net_seq);
-       u32 pos = (replay_esn->seq - 1) % replay_esn->replay_window;
+       u32 pos;
 
        if (!replay_esn->replay_window)
                return;
 
+       pos = (replay_esn->seq - 1) % replay_esn->replay_window;
+
        if (seq > replay_esn->seq) {
                diff = seq - replay_esn->seq;
 
index bbbd276659ba5edad0e01b915d03639c7980803f..7203e66dcd6f9e035a0710147ec179bab9acabb9 100644 (file)
@@ -19,6 +19,7 @@ bpf-direct-objs := bpf-direct.o
 
 # Try to match the kernel target.
 ifndef CONFIG_64BIT
+ifndef CROSS_COMPILE
 
 # s390 has -m31 flag to build 31 bit binaries
 ifndef CONFIG_S390
@@ -35,6 +36,7 @@ HOSTLOADLIBES_bpf-direct += $(MFLAG)
 HOSTLOADLIBES_bpf-fancy += $(MFLAG)
 HOSTLOADLIBES_dropper += $(MFLAG)
 endif
+endif
 
 # Tell kbuild to always build the programs
 always := $(hostprogs-y)
index 4d2c7dfdaabd44abd21e53072eee8f8f884890e8..2bb08a962ce3168a7fe1c01d3f397349e0143bc5 100755 (executable)
@@ -230,12 +230,12 @@ our $Inline       = qr{inline|__always_inline|noinline};
 our $Member    = qr{->$Ident|\.$Ident|\[[^]]*\]};
 our $Lval      = qr{$Ident(?:$Member)*};
 
-our $Float_hex = qr{(?i:0x[0-9a-f]+p-?[0-9]+[fl]?)};
-our $Float_dec = qr{(?i:((?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?))};
-our $Float_int = qr{(?i:[0-9]+e-?[0-9]+[fl]?)};
+our $Float_hex = qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?};
+our $Float_dec = qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?};
+our $Float_int = qr{(?i)[0-9]+e-?[0-9]+[fl]?};
 our $Float     = qr{$Float_hex|$Float_dec|$Float_int};
-our $Constant  = qr{(?:$Float|(?i:(?:0x[0-9a-f]+|[0-9]+)[ul]*))};
-our $Assignment        = qr{(?:\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=)};
+our $Constant  = qr{$Float|(?i)(?:0x[0-9a-f]+|[0-9]+)[ul]*};
+our $Assignment        = qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=};
 our $Compare    = qr{<=|>=|==|!=|<|>};
 our $Operators = qr{
                        <=|>=|==|!=|
index 0fe5a026aef8d22ac83619dd0bbc2ba50965b63e..579775088967fdb11a044beb75c70de48d572ad6 100644 (file)
@@ -709,16 +709,31 @@ static void cap_req_classify_flow(const struct request_sock *req,
 {
 }
 
+static int cap_tun_dev_alloc_security(void **security)
+{
+       return 0;
+}
+
+static void cap_tun_dev_free_security(void *security)
+{
+}
+
 static int cap_tun_dev_create(void)
 {
        return 0;
 }
 
-static void cap_tun_dev_post_create(struct sock *sk)
+static int cap_tun_dev_attach_queue(void *security)
+{
+       return 0;
+}
+
+static int cap_tun_dev_attach(struct sock *sk, void *security)
 {
+       return 0;
 }
 
-static int cap_tun_dev_attach(struct sock *sk)
+static int cap_tun_dev_open(void *security)
 {
        return 0;
 }
@@ -1050,8 +1065,11 @@ void __init security_fixup_ops(struct security_operations *ops)
        set_to_cap_if_null(ops, secmark_refcount_inc);
        set_to_cap_if_null(ops, secmark_refcount_dec);
        set_to_cap_if_null(ops, req_classify_flow);
+       set_to_cap_if_null(ops, tun_dev_alloc_security);
+       set_to_cap_if_null(ops, tun_dev_free_security);
        set_to_cap_if_null(ops, tun_dev_create);
-       set_to_cap_if_null(ops, tun_dev_post_create);
+       set_to_cap_if_null(ops, tun_dev_open);
+       set_to_cap_if_null(ops, tun_dev_attach_queue);
        set_to_cap_if_null(ops, tun_dev_attach);
 #endif /* CONFIG_SECURITY_NETWORK */
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
index 19ecc8de9e6bfd837e2589546fdfe49b04b23ed0..d794abcc4b3b7e5e87173ba8ca45deca5b6d09d8 100644 (file)
@@ -215,7 +215,9 @@ static void devcgroup_css_free(struct cgroup *cgroup)
        struct dev_cgroup *dev_cgroup;
 
        dev_cgroup = cgroup_to_devcgroup(cgroup);
+       mutex_lock(&devcgroup_mutex);
        dev_exception_clean(dev_cgroup);
+       mutex_unlock(&devcgroup_mutex);
        kfree(dev_cgroup);
 }
 
index dfb26918699c5be5cffcfe4f37295914561096d4..7dd538ef5b8319e645465eb62294158f3dbe271a 100644 (file)
@@ -205,9 +205,9 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
                rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_EVM,
                                           &xattr_data,
                                           sizeof(xattr_data), 0);
-       }
-       else if (rc == -ENODATA)
+       } else if (rc == -ENODATA && inode->i_op->removexattr) {
                rc = inode->i_op->removexattr(dentry, XATTR_NAME_EVM);
+       }
        return rc;
 }
 
index daa97f4ac9d13515909028bf1ffa6903d8aed0c7..7b88c6aeaed43e5f37b449fd8ec87610b3b69a93 100644 (file)
@@ -1254,24 +1254,42 @@ void security_secmark_refcount_dec(void)
 }
 EXPORT_SYMBOL(security_secmark_refcount_dec);
 
+int security_tun_dev_alloc_security(void **security)
+{
+       return security_ops->tun_dev_alloc_security(security);
+}
+EXPORT_SYMBOL(security_tun_dev_alloc_security);
+
+void security_tun_dev_free_security(void *security)
+{
+       security_ops->tun_dev_free_security(security);
+}
+EXPORT_SYMBOL(security_tun_dev_free_security);
+
 int security_tun_dev_create(void)
 {
        return security_ops->tun_dev_create();
 }
 EXPORT_SYMBOL(security_tun_dev_create);
 
-void security_tun_dev_post_create(struct sock *sk)
+int security_tun_dev_attach_queue(void *security)
 {
-       return security_ops->tun_dev_post_create(sk);
+       return security_ops->tun_dev_attach_queue(security);
 }
-EXPORT_SYMBOL(security_tun_dev_post_create);
+EXPORT_SYMBOL(security_tun_dev_attach_queue);
 
-int security_tun_dev_attach(struct sock *sk)
+int security_tun_dev_attach(struct sock *sk, void *security)
 {
-       return security_ops->tun_dev_attach(sk);
+       return security_ops->tun_dev_attach(sk, security);
 }
 EXPORT_SYMBOL(security_tun_dev_attach);
 
+int security_tun_dev_open(void *security)
+{
+       return security_ops->tun_dev_open(security);
+}
+EXPORT_SYMBOL(security_tun_dev_open);
+
 #endif /* CONFIG_SECURITY_NETWORK */
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
index 61a53367d0292600fbb2d11da95c21118164fca4..ef26e9611ffbab91ad50faefa81a56a425cefb93 100644 (file)
@@ -4399,6 +4399,24 @@ static void selinux_req_classify_flow(const struct request_sock *req,
        fl->flowi_secid = req->secid;
 }
 
+static int selinux_tun_dev_alloc_security(void **security)
+{
+       struct tun_security_struct *tunsec;
+
+       tunsec = kzalloc(sizeof(*tunsec), GFP_KERNEL);
+       if (!tunsec)
+               return -ENOMEM;
+       tunsec->sid = current_sid();
+
+       *security = tunsec;
+       return 0;
+}
+
+static void selinux_tun_dev_free_security(void *security)
+{
+       kfree(security);
+}
+
 static int selinux_tun_dev_create(void)
 {
        u32 sid = current_sid();
@@ -4414,8 +4432,17 @@ static int selinux_tun_dev_create(void)
                            NULL);
 }
 
-static void selinux_tun_dev_post_create(struct sock *sk)
+static int selinux_tun_dev_attach_queue(void *security)
 {
+       struct tun_security_struct *tunsec = security;
+
+       return avc_has_perm(current_sid(), tunsec->sid, SECCLASS_TUN_SOCKET,
+                           TUN_SOCKET__ATTACH_QUEUE, NULL);
+}
+
+static int selinux_tun_dev_attach(struct sock *sk, void *security)
+{
+       struct tun_security_struct *tunsec = security;
        struct sk_security_struct *sksec = sk->sk_security;
 
        /* we don't currently perform any NetLabel based labeling here and it
@@ -4425,20 +4452,19 @@ static void selinux_tun_dev_post_create(struct sock *sk)
         * cause confusion to the TUN user that had no idea network labeling
         * protocols were being used */
 
-       /* see the comments in selinux_tun_dev_create() about why we don't use
-        * the sockcreate SID here */
-
-       sksec->sid = current_sid();
+       sksec->sid = tunsec->sid;
        sksec->sclass = SECCLASS_TUN_SOCKET;
+
+       return 0;
 }
 
-static int selinux_tun_dev_attach(struct sock *sk)
+static int selinux_tun_dev_open(void *security)
 {
-       struct sk_security_struct *sksec = sk->sk_security;
+       struct tun_security_struct *tunsec = security;
        u32 sid = current_sid();
        int err;
 
-       err = avc_has_perm(sid, sksec->sid, SECCLASS_TUN_SOCKET,
+       err = avc_has_perm(sid, tunsec->sid, SECCLASS_TUN_SOCKET,
                           TUN_SOCKET__RELABELFROM, NULL);
        if (err)
                return err;
@@ -4446,8 +4472,7 @@ static int selinux_tun_dev_attach(struct sock *sk)
                           TUN_SOCKET__RELABELTO, NULL);
        if (err)
                return err;
-
-       sksec->sid = sid;
+       tunsec->sid = sid;
 
        return 0;
 }
@@ -5642,9 +5667,12 @@ static struct security_operations selinux_ops = {
        .secmark_refcount_inc =         selinux_secmark_refcount_inc,
        .secmark_refcount_dec =         selinux_secmark_refcount_dec,
        .req_classify_flow =            selinux_req_classify_flow,
+       .tun_dev_alloc_security =       selinux_tun_dev_alloc_security,
+       .tun_dev_free_security =        selinux_tun_dev_free_security,
        .tun_dev_create =               selinux_tun_dev_create,
-       .tun_dev_post_create =          selinux_tun_dev_post_create,
+       .tun_dev_attach_queue =         selinux_tun_dev_attach_queue,
        .tun_dev_attach =               selinux_tun_dev_attach,
+       .tun_dev_open =                 selinux_tun_dev_open,
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
        .xfrm_policy_alloc_security =   selinux_xfrm_policy_alloc,
index df2de54a958debfab56caf3483cd5d93856e5be4..14d04e63b1f0e09ef15b4cf64057bd7cc861ed1d 100644 (file)
@@ -150,6 +150,6 @@ struct security_class_mapping secclass_map[] = {
            NULL } },
        { "kernel_service", { "use_as_override", "create_files_as", NULL } },
        { "tun_socket",
-         { COMMON_SOCK_PERMS, NULL } },
+         { COMMON_SOCK_PERMS, "attach_queue", NULL } },
        { NULL }
   };
index 26c7eee1c309b0a2f45a23c8b70d2157841c1670..aa47bcabb5f65e728aadbaa39cdecfa55d20aa16 100644 (file)
@@ -110,6 +110,10 @@ struct sk_security_struct {
        u16 sclass;                     /* sock security class */
 };
 
+struct tun_security_struct {
+       u32 sid;                        /* SID for the tun device sockets */
+};
+
 struct key_security_struct {
        u32 sid;        /* SID of key */
 };
index 6fc0ae90e5b1de99992b288d35035e2a0d061df2..fff7753e35c15ba6f82612594fbda19e7203b66e 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/delay.h>
 #include <linux/module.h>
 #include <linux/io.h>
+#include <linux/gpio.h>
 
 #include <sound/ac97_codec.h>
 #include <sound/pxa2xx-lib.h>
@@ -148,6 +149,8 @@ static inline void pxa_ac97_warm_pxa27x(void)
 
 static inline void pxa_ac97_cold_pxa27x(void)
 {
+       unsigned int timeout;
+
        GCR &=  GCR_COLD_RST;  /* clear everything but nCRST */
        GCR &= ~GCR_COLD_RST;  /* then assert nCRST */
 
@@ -157,8 +160,10 @@ static inline void pxa_ac97_cold_pxa27x(void)
        clk_enable(ac97conf_clk);
        udelay(5);
        clk_disable(ac97conf_clk);
-       GCR = GCR_COLD_RST;
-       udelay(50);
+       GCR = GCR_COLD_RST | GCR_WARM_RST;
+       timeout = 100;     /* wait for the codec-ready bit to be set */
+       while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
+               mdelay(1);
 }
 #endif
 
@@ -340,8 +345,21 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev)
        }
 
        if (cpu_is_pxa27x()) {
-               /* Use GPIO 113 as AC97 Reset on Bulverde */
+               /*
+                * This gpio is needed for a work-around to a bug in the ac97
+                * controller during warm reset.  The direction and level is set
+                * here so that it is an output driven high when switching from
+                * AC97_nRESET alt function to generic gpio.
+                */
+               ret = gpio_request_one(reset_gpio, GPIOF_OUT_INIT_HIGH,
+                                      "pxa27x ac97 reset");
+               if (ret < 0) {
+                       pr_err("%s: gpio_request_one() failed: %d\n",
+                              __func__, ret);
+                       goto err_conf;
+               }
                pxa27x_assert_ac97reset(reset_gpio, 0);
+
                ac97conf_clk = clk_get(&dev->dev, "AC97CONFCLK");
                if (IS_ERR(ac97conf_clk)) {
                        ret = PTR_ERR(ac97conf_clk);
@@ -384,6 +402,8 @@ EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_probe);
 
 void pxa2xx_ac97_hw_remove(struct platform_device *dev)
 {
+       if (cpu_is_pxa27x())
+               gpio_free(reset_gpio);
        GCR |= GCR_ACLINK_OFF;
        free_irq(IRQ_AC97, NULL);
        if (ac97conf_clk) {
index b8fb0a5adb9b54a67c687d5da6b142a917bcb261..822df971972c1ff54ea7886a3524356ff92fca50 100644 (file)
@@ -3654,6 +3654,7 @@ static void hda_call_codec_resume(struct hda_codec *codec)
        hda_set_power_state(codec, AC_PWRST_D0);
        restore_shutup_pins(codec);
        hda_exec_init_verbs(codec);
+       snd_hda_jack_set_dirty_all(codec);
        if (codec->patch_ops.resume)
                codec->patch_ops.resume(codec);
        else {
@@ -3665,10 +3666,8 @@ static void hda_call_codec_resume(struct hda_codec *codec)
 
        if (codec->jackpoll_interval)
                hda_jackpoll_work(&codec->jackpoll_work.work);
-       else {
-               snd_hda_jack_set_dirty_all(codec);
+       else
                snd_hda_jack_report_sync(codec);
-       }
 
        codec->in_pm = 0;
        snd_hda_power_down(codec); /* flag down before returning */
index cca87277baf088b273279c6b1621aa52a981a47e..c78286f6e5d8133e070d34e3927a12c70be626f1 100644 (file)
@@ -573,9 +573,12 @@ enum {
 #define AZX_DCAPS_PM_RUNTIME   (1 << 26)       /* runtime PM support */
 
 /* quirks for Intel PCH */
-#define AZX_DCAPS_INTEL_PCH \
+#define AZX_DCAPS_INTEL_PCH_NOPM \
        (AZX_DCAPS_SCH_SNOOP | AZX_DCAPS_BUFSIZE | \
-        AZX_DCAPS_COUNT_LPIB_DELAY | AZX_DCAPS_PM_RUNTIME)
+        AZX_DCAPS_COUNT_LPIB_DELAY)
+
+#define AZX_DCAPS_INTEL_PCH \
+       (AZX_DCAPS_INTEL_PCH_NOPM | AZX_DCAPS_PM_RUNTIME)
 
 /* quirks for ATI SB / AMD Hudson */
 #define AZX_DCAPS_PRESET_ATI_SB \
@@ -653,29 +656,43 @@ static char *driver_short_names[] = {
 #define get_azx_dev(substream) (substream->runtime->private_data)
 
 #ifdef CONFIG_X86
-static void __mark_pages_wc(struct azx *chip, void *addr, size_t size, bool on)
+static void __mark_pages_wc(struct azx *chip, struct snd_dma_buffer *dmab, bool on)
 {
+       int pages;
+
        if (azx_snoop(chip))
                return;
-       if (addr && size) {
-               int pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+       if (!dmab || !dmab->area || !dmab->bytes)
+               return;
+
+#ifdef CONFIG_SND_DMA_SGBUF
+       if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_SG) {
+               struct snd_sg_buf *sgbuf = dmab->private_data;
                if (on)
-                       set_memory_wc((unsigned long)addr, pages);
+                       set_pages_array_wc(sgbuf->page_table, sgbuf->pages);
                else
-                       set_memory_wb((unsigned long)addr, pages);
+                       set_pages_array_wb(sgbuf->page_table, sgbuf->pages);
+               return;
        }
+#endif
+
+       pages = (dmab->bytes + PAGE_SIZE - 1) >> PAGE_SHIFT;
+       if (on)
+               set_memory_wc((unsigned long)dmab->area, pages);
+       else
+               set_memory_wb((unsigned long)dmab->area, pages);
 }
 
 static inline void mark_pages_wc(struct azx *chip, struct snd_dma_buffer *buf,
                                 bool on)
 {
-       __mark_pages_wc(chip, buf->area, buf->bytes, on);
+       __mark_pages_wc(chip, buf, on);
 }
 static inline void mark_runtime_wc(struct azx *chip, struct azx_dev *azx_dev,
-                                  struct snd_pcm_runtime *runtime, bool on)
+                                  struct snd_pcm_substream *substream, bool on)
 {
        if (azx_dev->wc_marked != on) {
-               __mark_pages_wc(chip, runtime->dma_area, runtime->dma_bytes, on);
+               __mark_pages_wc(chip, snd_pcm_get_dma_buf(substream), on);
                azx_dev->wc_marked = on;
        }
 }
@@ -686,7 +703,7 @@ static inline void mark_pages_wc(struct azx *chip, struct snd_dma_buffer *buf,
 {
 }
 static inline void mark_runtime_wc(struct azx *chip, struct azx_dev *azx_dev,
-                                  struct snd_pcm_runtime *runtime, bool on)
+                                  struct snd_pcm_substream *substream, bool on)
 {
 }
 #endif
@@ -1965,11 +1982,10 @@ static int azx_pcm_hw_params(struct snd_pcm_substream *substream,
 {
        struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
        struct azx *chip = apcm->chip;
-       struct snd_pcm_runtime *runtime = substream->runtime;
        struct azx_dev *azx_dev = get_azx_dev(substream);
        int ret;
 
-       mark_runtime_wc(chip, azx_dev, runtime, false);
+       mark_runtime_wc(chip, azx_dev, substream, false);
        azx_dev->bufsize = 0;
        azx_dev->period_bytes = 0;
        azx_dev->format_val = 0;
@@ -1977,7 +1993,7 @@ static int azx_pcm_hw_params(struct snd_pcm_substream *substream,
                                        params_buffer_bytes(hw_params));
        if (ret < 0)
                return ret;
-       mark_runtime_wc(chip, azx_dev, runtime, true);
+       mark_runtime_wc(chip, azx_dev, substream, true);
        return ret;
 }
 
@@ -1986,7 +2002,6 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
        struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
        struct azx_dev *azx_dev = get_azx_dev(substream);
        struct azx *chip = apcm->chip;
-       struct snd_pcm_runtime *runtime = substream->runtime;
        struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
 
        /* reset BDL address */
@@ -1999,7 +2014,7 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
 
        snd_hda_codec_cleanup(apcm->codec, hinfo, substream);
 
-       mark_runtime_wc(chip, azx_dev, runtime, false);
+       mark_runtime_wc(chip, azx_dev, substream, false);
        return snd_pcm_lib_free_pages(substream);
 }
 
@@ -3586,13 +3601,13 @@ static void azx_remove(struct pci_dev *pci)
 static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
        /* CPT */
        { PCI_DEVICE(0x8086, 0x1c20),
-         .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+         .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM },
        /* PBG */
        { PCI_DEVICE(0x8086, 0x1d20),
-         .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+         .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM },
        /* Panther Point */
        { PCI_DEVICE(0x8086, 0x1e20),
-         .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+         .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM },
        /* Lynx Point */
        { PCI_DEVICE(0x8086, 0x8c20),
          .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
@@ -3610,13 +3625,12 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
        /* 5 Series/3400 */
        { PCI_DEVICE(0x8086, 0x3b56),
          .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH },
-       /* SCH */
+       /* Poulsbo */
        { PCI_DEVICE(0x8086, 0x811b),
-         .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP |
-         AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_LPIB }, /* Poulsbo */
+         .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM },
+       /* Oaktrail */
        { PCI_DEVICE(0x8086, 0x080a),
-         .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP |
-         AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_LPIB }, /* Oaktrail */
+         .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM },
        /* ICH */
        { PCI_DEVICE(0x8086, 0x2668),
          .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC |
index 60890bfecc196600ffa15ab5979c06aef8891f9b..009b77a693cf3326a3cdec7b6fa335a4c51c80be 100644 (file)
@@ -558,24 +558,12 @@ static int conexant_build_controls(struct hda_codec *codec)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int conexant_suspend(struct hda_codec *codec)
-{
-       snd_hda_shutup_pins(codec);
-       return 0;
-}
-#endif
-
 static const struct hda_codec_ops conexant_patch_ops = {
        .build_controls = conexant_build_controls,
        .build_pcms = conexant_build_pcms,
        .init = conexant_init,
        .free = conexant_free,
        .set_power_state = conexant_set_power,
-#ifdef CONFIG_PM
-       .suspend = conexant_suspend,
-#endif
-       .reboot_notify = snd_hda_shutup_pins,
 };
 
 #ifdef CONFIG_SND_HDA_INPUT_BEEP
@@ -4405,10 +4393,6 @@ static const struct hda_codec_ops cx_auto_patch_ops = {
        .init = cx_auto_init,
        .free = conexant_free,
        .unsol_event = snd_hda_jack_unsol_event,
-#ifdef CONFIG_PM
-       .suspend = conexant_suspend,
-#endif
-       .reboot_notify = snd_hda_shutup_pins,
 };
 
 /*
@@ -4652,6 +4636,12 @@ static const struct hda_codec_preset snd_hda_preset_conexant[] = {
          .patch = patch_conexant_auto },
        { .id = 0x14f15111, .name = "CX20753/4",
          .patch = patch_conexant_auto },
+       { .id = 0x14f15113, .name = "CX20755",
+         .patch = patch_conexant_auto },
+       { .id = 0x14f15114, .name = "CX20756",
+         .patch = patch_conexant_auto },
+       { .id = 0x14f15115, .name = "CX20757",
+         .patch = patch_conexant_auto },
        {} /* terminator */
 };
 
@@ -4675,6 +4665,9 @@ MODULE_ALIAS("snd-hda-codec-id:14f150b9");
 MODULE_ALIAS("snd-hda-codec-id:14f1510f");
 MODULE_ALIAS("snd-hda-codec-id:14f15110");
 MODULE_ALIAS("snd-hda-codec-id:14f15111");
+MODULE_ALIAS("snd-hda-codec-id:14f15113");
+MODULE_ALIAS("snd-hda-codec-id:14f15114");
+MODULE_ALIAS("snd-hda-codec-id:14f15115");
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Conexant HD-audio codec");
index b6c21ea187ca00df7233416f516111567b6b9ca7..807a2aa1ff384e5a54f64a6f951f8d8e44e3061b 100644 (file)
@@ -1502,7 +1502,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
        ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
        substream = snd_pcm_chmap_substream(info, ctl_idx);
        if (!substream || !substream->runtime)
-               return -EBADFD;
+               return 0; /* just for avoiding error from alsactl restore */
        switch (substream->runtime->status->state) {
        case SNDRV_PCM_STATE_OPEN:
        case SNDRV_PCM_STATE_SETUP:
index 71ae23dd71035edd0214fb8a4f209959cde932d1..5faaad219a7f37e588e8b5a27b298cbb7adfa69d 100644 (file)
@@ -4694,6 +4694,7 @@ static const struct snd_pci_quirk alc880_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_FIXUP_VOL_KNOB),
        SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810),
        SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM),
+       SND_PCI_QUIRK(0x1631, 0xe011, "PB 13201056", ALC880_FIXUP_6ST),
        SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_FIXUP_F1734),
        SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FIXUP_FUJITSU),
        SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_FIXUP_F1734),
@@ -5708,6 +5709,7 @@ static const struct alc_model_fixup alc268_fixup_models[] = {
 };
 
 static const struct snd_pci_quirk alc268_fixup_tbl[] = {
+       SND_PCI_QUIRK(0x1025, 0x015b, "Acer AOA 150 (ZG5)", ALC268_FIXUP_INV_DMIC),
        /* below is codec SSID since multiple Toshiba laptops have the
         * same PCI SSID 1179:ff00
         */
@@ -5817,6 +5819,9 @@ enum {
        ALC269_TYPE_ALC269VB,
        ALC269_TYPE_ALC269VC,
        ALC269_TYPE_ALC269VD,
+       ALC269_TYPE_ALC280,
+       ALC269_TYPE_ALC282,
+       ALC269_TYPE_ALC284,
 };
 
 /*
@@ -5833,10 +5838,13 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
        switch (spec->codec_variant) {
        case ALC269_TYPE_ALC269VA:
        case ALC269_TYPE_ALC269VC:
+       case ALC269_TYPE_ALC280:
+       case ALC269_TYPE_ALC284:
                ssids = alc269va_ssids;
                break;
        case ALC269_TYPE_ALC269VB:
        case ALC269_TYPE_ALC269VD:
+       case ALC269_TYPE_ALC282:
                ssids = alc269_ssids;
                break;
        default:
@@ -6245,6 +6253,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC),
        SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_MIC2_MUTE_LED),
        SND_PCI_QUIRK(0x103c, 0x1972, "HP Pavilion 17", ALC269_FIXUP_MIC1_MUTE_LED),
+       SND_PCI_QUIRK(0x103c, 0x1977, "HP Pavilion 14", ALC269_FIXUP_MIC1_MUTE_LED),
        SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_DMIC),
        SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_DMIC),
        SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),
@@ -6259,6 +6268,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
        SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
        SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
+       SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK),
        SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK),
        SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC),
        SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK),
@@ -6400,7 +6410,8 @@ static int patch_alc269(struct hda_codec *codec)
 
        alc_auto_parse_customize_define(codec);
 
-       if (codec->vendor_id == 0x10ec0269) {
+       switch (codec->vendor_id) {
+       case 0x10ec0269:
                spec->codec_variant = ALC269_TYPE_ALC269VA;
                switch (alc_get_coef0(codec) & 0x00f0) {
                case 0x0010:
@@ -6425,6 +6436,20 @@ static int patch_alc269(struct hda_codec *codec)
                        goto error;
                spec->init_hook = alc269_fill_coef;
                alc269_fill_coef(codec);
+               break;
+
+       case 0x10ec0280:
+       case 0x10ec0290:
+               spec->codec_variant = ALC269_TYPE_ALC280;
+               break;
+       case 0x10ec0282:
+       case 0x10ec0283:
+               spec->codec_variant = ALC269_TYPE_ALC282;
+               break;
+       case 0x10ec0284:
+       case 0x10ec0292:
+               spec->codec_variant = ALC269_TYPE_ALC284;
+               break;
        }
 
        /* automatic parse from the BIOS config */
@@ -7129,6 +7154,7 @@ static const struct hda_codec_preset snd_hda_preset_realtek[] = {
        { .id = 0x10ec0280, .name = "ALC280", .patch = patch_alc269 },
        { .id = 0x10ec0282, .name = "ALC282", .patch = patch_alc269 },
        { .id = 0x10ec0283, .name = "ALC283", .patch = patch_alc269 },
+       { .id = 0x10ec0284, .name = "ALC284", .patch = patch_alc269 },
        { .id = 0x10ec0290, .name = "ALC290", .patch = patch_alc269 },
        { .id = 0x10ec0292, .name = "ALC292", .patch = patch_alc269 },
        { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
index 6e02e064d7b43df1159554899f1007a9f223bc7b..223c3d9cc69efa1af38e6dee6c45ffc0f3733c55 100644 (file)
@@ -441,6 +441,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
 */
 /* status */
 #define HDSPM_AES32_wcLock     0x0200000
+#define HDSPM_AES32_wcSync     0x0100000
 #define HDSPM_AES32_wcFreq_bit  22
 /* (status >> HDSPM_AES32_wcFreq_bit) & 0xF gives WC frequency (cf function
   HDSPM_bit2freq */
@@ -3467,10 +3468,12 @@ static int hdspm_wc_sync_check(struct hdspm *hdspm)
        switch (hdspm->io_type) {
        case AES32:
                status = hdspm_read(hdspm, HDSPM_statusRegister);
-               if (status & HDSPM_wcSync)
-                       return 2;
-               else if (status & HDSPM_wcLock)
-                       return 1;
+               if (status & HDSPM_AES32_wcLock) {
+                       if (status & HDSPM_AES32_wcSync)
+                               return 2;
+                       else
+                               return 1;
+               }
                return 0;
                break;
 
@@ -4658,6 +4661,7 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry,
        unsigned int status;
        unsigned int status2;
        unsigned int timecode;
+       unsigned int wcLock, wcSync;
        int pref_syncref;
        char *autosync_ref;
        int x;
@@ -4751,8 +4755,11 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry,
 
        snd_iprintf(buffer, "--- Status:\n");
 
+       wcLock = status & HDSPM_AES32_wcLock;
+       wcSync = wcLock && (status & HDSPM_AES32_wcSync);
+
        snd_iprintf(buffer, "Word: %s  Frequency: %d\n",
-                   (status & HDSPM_AES32_wcLock) ? "Sync   " : "No Lock",
+                   (wcLock) ? (wcSync ? "Sync   " : "Lock   ") : "No Lock",
                    HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF));
 
        for (x = 0; x < 8; x++) {
index adf397b9d0e650eb19768e7bc6c592352014a595..ef62c435848eb776421776d7e80e90527e3f2b72 100644 (file)
@@ -446,15 +446,9 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        case SND_SOC_DAIFMT_DSP_A:
                mode = 0;
                break;
-       case SND_SOC_DAIFMT_DSP_B:
-               mode = 1;
-               break;
        case SND_SOC_DAIFMT_I2S:
                mode = 2;
                break;
-       case SND_SOC_DAIFMT_LEFT_J:
-               mode = 3;
-               break;
        default:
                arizona_aif_err(dai, "Unsupported DAI format %d\n",
                                fmt & SND_SOC_DAIFMT_FORMAT_MASK);
@@ -691,7 +685,7 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
        }
        sr_val = i;
 
-       lrclk = snd_soc_params_to_bclk(params) / params_rate(params);
+       lrclk = rates[bclk] / params_rate(params);
 
        arizona_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n",
                        rates[bclk], rates[bclk] / lrclk);
@@ -714,7 +708,8 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
                snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1,
                                    ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val);
                snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
-                                   ARIZONA_AIF1_RATE_MASK, 8);
+                                   ARIZONA_AIF1_RATE_MASK,
+                                   8 << ARIZONA_AIF1_RATE_SHIFT);
                break;
        default:
                arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk);
@@ -1087,6 +1082,9 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq,
                        id, ret);
        }
 
+       regmap_update_bits(arizona->regmap, fll->base + 1,
+                          ARIZONA_FLL1_FREERUN, 0);
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(arizona_init_fll);
index 41dae1ed3b714a4734448b1d3c99938af1fe9d9a..4deebeb0717754085edceb38e7c00b141f4502fb 100644 (file)
 
 #define ARIZONA_FLL_SRC_MCLK1      0
 #define ARIZONA_FLL_SRC_MCLK2      1
-#define ARIZONA_FLL_SRC_SLIMCLK    2
-#define ARIZONA_FLL_SRC_FLL1       3
-#define ARIZONA_FLL_SRC_FLL2       4
-#define ARIZONA_FLL_SRC_AIF1BCLK   5
-#define ARIZONA_FLL_SRC_AIF2BCLK   6
-#define ARIZONA_FLL_SRC_AIF3BCLK   7
-#define ARIZONA_FLL_SRC_AIF1LRCLK  8
-#define ARIZONA_FLL_SRC_AIF2LRCLK  9
-#define ARIZONA_FLL_SRC_AIF3LRCLK 10
+#define ARIZONA_FLL_SRC_SLIMCLK    3
+#define ARIZONA_FLL_SRC_FLL1       4
+#define ARIZONA_FLL_SRC_FLL2       5
+#define ARIZONA_FLL_SRC_AIF1BCLK   8
+#define ARIZONA_FLL_SRC_AIF2BCLK   9
+#define ARIZONA_FLL_SRC_AIF3BCLK  10
+#define ARIZONA_FLL_SRC_AIF1LRCLK 12
+#define ARIZONA_FLL_SRC_AIF2LRCLK 13
+#define ARIZONA_FLL_SRC_AIF3LRCLK 14
 
 #define ARIZONA_MIXER_VOL_MASK             0x00FE
 #define ARIZONA_MIXER_VOL_SHIFT                 1
index 4f1127935fdf5f9578970b13fcf73b8815d469e5..ac8742a1f25ab7c69fcca2b22458ae02acf3d67e 100644 (file)
@@ -474,16 +474,16 @@ static int cs4271_probe(struct snd_soc_codec *codec)
        struct cs4271_platform_data *cs4271plat = codec->dev->platform_data;
        int ret;
        int gpio_nreset = -EINVAL;
-       int amutec_eq_bmutec = 0;
+       bool amutec_eq_bmutec = false;
 
 #ifdef CONFIG_OF
        if (of_match_device(cs4271_dt_ids, codec->dev)) {
                gpio_nreset = of_get_named_gpio(codec->dev->of_node,
                                                "reset-gpio", 0);
 
-               if (!of_get_property(codec->dev->of_node,
+               if (of_get_property(codec->dev->of_node,
                                     "cirrus,amutec-eq-bmutec", NULL))
-                       amutec_eq_bmutec = 1;
+                       amutec_eq_bmutec = true;
        }
 #endif
 
index 99bb1c69499e42a6791ae12c55dde2d53efb7f49..9811a5478c87649da6c42806a3a43bfcc5a5767e 100644 (file)
@@ -737,7 +737,7 @@ static const struct cs42l52_clk_para clk_map_table[] = {
 
 static int cs42l52_get_clk(int mclk, int rate)
 {
-       int i, ret = 0;
+       int i, ret = -EINVAL;
        u_int mclk1, mclk2 = 0;
 
        for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) {
@@ -749,8 +749,6 @@ static int cs42l52_get_clk(int mclk, int rate)
                        }
                }
        }
-       if (ret > ARRAY_SIZE(clk_map_table))
-               return -EINVAL;
        return ret;
 }
 
index d75257d40a496d26e9bb513f6fd000abe6f13a76..e19490cfb3a8bde2036e61d877b3a8c407cc510c 100644 (file)
@@ -111,9 +111,9 @@ static struct reg_default lm49453_reg_defs[] = {
        { 101, 0x00 },
        { 102, 0x00 },
        { 103, 0x01 },
-       { 105, 0x01 },
-       { 106, 0x00 },
-       { 107, 0x01 },
+       { 104, 0x01 },
+       { 105, 0x00 },
+       { 106, 0x01 },
        { 107, 0x00 },
        { 108, 0x00 },
        { 109, 0x00 },
@@ -163,56 +163,25 @@ static struct reg_default lm49453_reg_defs[] = {
        { 184, 0x00 },
        { 185, 0x00 },
        { 186, 0x00 },
-       { 189, 0x00 },
+       { 187, 0x00 },
        { 188, 0x00 },
-       { 194, 0x00 },
-       { 195, 0x00 },
-       { 196, 0x00 },
-       { 197, 0x00 },
-       { 200, 0x00 },
-       { 201, 0x00 },
-       { 202, 0x00 },
-       { 203, 0x00 },
-       { 204, 0x00 },
-       { 205, 0x00 },
-       { 208, 0x00 },
+       { 189, 0x00 },
+       { 208, 0x06 },
        { 209, 0x00 },
-       { 210, 0x00 },
-       { 211, 0x00 },
-       { 213, 0x00 },
-       { 214, 0x00 },
-       { 215, 0x00 },
-       { 216, 0x00 },
-       { 217, 0x00 },
-       { 218, 0x00 },
-       { 219, 0x00 },
+       { 210, 0x08 },
+       { 211, 0x54 },
+       { 212, 0x14 },
+       { 213, 0x0d },
+       { 214, 0x0d },
+       { 215, 0x14 },
+       { 216, 0x60 },
        { 221, 0x00 },
        { 222, 0x00 },
+       { 223, 0x00 },
        { 224, 0x00 },
-       { 225, 0x00 },
-       { 226, 0x00 },
-       { 227, 0x00 },
-       { 228, 0x00 },
-       { 229, 0x00 },
-       { 230, 0x13 },
-       { 231, 0x00 },
-       { 232, 0x80 },
-       { 233, 0x0C },
-       { 234, 0xDD },
-       { 235, 0x00 },
-       { 236, 0x04 },
-       { 237, 0x00 },
-       { 238, 0x00 },
-       { 239, 0x00 },
-       { 240, 0x00 },
-       { 241, 0x00 },
-       { 242, 0x00 },
-       { 243, 0x00 },
-       { 244, 0x00 },
-       { 245, 0x00 },
        { 248, 0x00 },
        { 249, 0x00 },
-       { 254, 0x00 },
+       { 250, 0x00 },
        { 255, 0x00 },
 };
 
@@ -525,36 +494,41 @@ SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_PORT2_TX2_REG, 7, 1, 0),
 };
 
 /* TLV Declarations */
-static const DECLARE_TLV_DB_SCALE(digital_tlv, -7650, 150, 1);
-static const DECLARE_TLV_DB_SCALE(port_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(adc_dac_tlv, -7650, 150, 1);
+static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 200, 1);
+static const DECLARE_TLV_DB_SCALE(port_tlv, -1800, 600, 0);
+static const DECLARE_TLV_DB_SCALE(stn_tlv, -7200, 150, 0);
 
 static const struct snd_kcontrol_new lm49453_sidetone_mixer_controls[] = {
 /* Sidetone supports mono only */
 SOC_DAPM_SINGLE_TLV("Sidetone ADCL Volume", LM49453_P0_STN_VOL_ADCL_REG,
-                    0, 0x3F, 0, digital_tlv),
+                    0, 0x3F, 0, stn_tlv),
 SOC_DAPM_SINGLE_TLV("Sidetone ADCR Volume", LM49453_P0_STN_VOL_ADCR_REG,
-                    0, 0x3F, 0, digital_tlv),
+                    0, 0x3F, 0, stn_tlv),
 SOC_DAPM_SINGLE_TLV("Sidetone DMIC1L Volume", LM49453_P0_STN_VOL_DMIC1L_REG,
-                    0, 0x3F, 0, digital_tlv),
+                    0, 0x3F, 0, stn_tlv),
 SOC_DAPM_SINGLE_TLV("Sidetone DMIC1R Volume", LM49453_P0_STN_VOL_DMIC1R_REG,
-                    0, 0x3F, 0, digital_tlv),
+                    0, 0x3F, 0, stn_tlv),
 SOC_DAPM_SINGLE_TLV("Sidetone DMIC2L Volume", LM49453_P0_STN_VOL_DMIC2L_REG,
-                    0, 0x3F, 0, digital_tlv),
+                    0, 0x3F, 0, stn_tlv),
 SOC_DAPM_SINGLE_TLV("Sidetone DMIC2R Volume", LM49453_P0_STN_VOL_DMIC2R_REG,
-                    0, 0x3F, 0, digital_tlv),
+                    0, 0x3F, 0, stn_tlv),
 };
 
 static const struct snd_kcontrol_new lm49453_snd_controls[] = {
        /* mic1 and mic2 supports mono only */
-       SOC_SINGLE_TLV("Mic1 Volume", LM49453_P0_ADC_LEVELL_REG, 0, 6,
-                       0, digital_tlv),
-       SOC_SINGLE_TLV("Mic2 Volume", LM49453_P0_ADC_LEVELR_REG, 0, 6,
-                       0, digital_tlv),
+       SOC_SINGLE_TLV("Mic1 Volume", LM49453_P0_MICL_REG, 0, 15, 0, mic_tlv),
+       SOC_SINGLE_TLV("Mic2 Volume", LM49453_P0_MICR_REG, 0, 15, 0, mic_tlv),
+
+       SOC_SINGLE_TLV("ADCL Volume", LM49453_P0_ADC_LEVELL_REG, 0, 63,
+                       0, adc_dac_tlv),
+       SOC_SINGLE_TLV("ADCR Volume", LM49453_P0_ADC_LEVELR_REG, 0, 63,
+                       0, adc_dac_tlv),
 
        SOC_DOUBLE_R_TLV("DMIC1 Volume", LM49453_P0_DMIC1_LEVELL_REG,
-                         LM49453_P0_DMIC1_LEVELR_REG, 0, 6, 0, digital_tlv),
+                         LM49453_P0_DMIC1_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
        SOC_DOUBLE_R_TLV("DMIC2 Volume", LM49453_P0_DMIC2_LEVELL_REG,
-                         LM49453_P0_DMIC2_LEVELR_REG, 0, 6, 0, digital_tlv),
+                         LM49453_P0_DMIC2_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
 
        SOC_DAPM_ENUM("Mic2Mode", lm49453_mic2mode_enum),
        SOC_DAPM_ENUM("DMIC12 SRC", lm49453_dmic12_cfg_enum),
@@ -569,16 +543,16 @@ static const struct snd_kcontrol_new lm49453_snd_controls[] = {
                                          2, 1, 0),
 
        SOC_DOUBLE_R_TLV("DAC HP Volume", LM49453_P0_DAC_HP_LEVELL_REG,
-                         LM49453_P0_DAC_HP_LEVELR_REG, 0, 6, 0, digital_tlv),
+                         LM49453_P0_DAC_HP_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
        SOC_DOUBLE_R_TLV("DAC LO Volume", LM49453_P0_DAC_LO_LEVELL_REG,
-                         LM49453_P0_DAC_LO_LEVELR_REG, 0, 6, 0, digital_tlv),
+                         LM49453_P0_DAC_LO_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
        SOC_DOUBLE_R_TLV("DAC LS Volume", LM49453_P0_DAC_LS_LEVELL_REG,
-                         LM49453_P0_DAC_LS_LEVELR_REG, 0, 6, 0, digital_tlv),
+                         LM49453_P0_DAC_LS_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
        SOC_DOUBLE_R_TLV("DAC HA Volume", LM49453_P0_DAC_HA_LEVELL_REG,
-                         LM49453_P0_DAC_HA_LEVELR_REG, 0, 6, 0, digital_tlv),
+                         LM49453_P0_DAC_HA_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
 
        SOC_SINGLE_TLV("EP Volume", LM49453_P0_DAC_LS_LEVELL_REG,
-                       0, 6, 0, digital_tlv),
+                       0, 63, 0, adc_dac_tlv),
 
        SOC_SINGLE_TLV("PORT1_1_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG,
                        0, 3, 0, port_tlv),
@@ -1218,7 +1192,7 @@ static int lm49453_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
        }
 
        snd_soc_update_bits(codec, LM49453_P0_AUDIO_PORT1_BASIC_REG,
-                           LM49453_AUDIO_PORT1_BASIC_FMT_MASK|BIT(1)|BIT(5),
+                           LM49453_AUDIO_PORT1_BASIC_FMT_MASK|BIT(0)|BIT(5),
                            (aif_val | mode | clk_phase));
 
        snd_soc_write(codec, LM49453_P0_AUDIO_PORT1_RX_MSB_REG, clk_shift);
index cb1675cd8e1c5ffd9b404b9d12ee9c404f6f2efd..92bbfec9b107a0ec5ee55205b9272ce9e67caa74 100644 (file)
@@ -401,7 +401,7 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
                        5, 1, 0),
 
        SOC_SINGLE_TLV("Mic Volume", SGTL5000_CHIP_MIC_CTRL,
-                       0, 4, 0, mic_gain_tlv),
+                       0, 3, 0, mic_gain_tlv),
 };
 
 /* mute the codec used by alsa core */
@@ -1344,7 +1344,7 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
                        SGTL5000_HP_ZCD_EN |
                        SGTL5000_ADC_ZCD_EN);
 
-       snd_soc_write(codec, SGTL5000_CHIP_MIC_CTRL, 0);
+       snd_soc_write(codec, SGTL5000_CHIP_MIC_CTRL, 2);
 
        /*
         * disable DAP
index ab355c4f0b2de0aa786e754360a6a877a0855756..40c07be9b5814f170f73fa1b3670d3813101bb67 100644 (file)
                                SNDRV_PCM_FMTBIT_S32_LE)
 #define        S2PC_VALUE              0x98
 #define CLOCK_OUT              0x60
-#define LEFT_J_DATA_FORMAT     0x10
-#define I2S_DATA_FORMAT                0x12
-#define RIGHT_J_DATA_FORMAT    0x14
+#define DATA_FORMAT_MSK                0x0E
+#define LEFT_J_DATA_FORMAT     0x00
+#define I2S_DATA_FORMAT                0x02
+#define RIGHT_J_DATA_FORMAT    0x04
 #define CODEC_MUTE_VAL         0x80
 
 #define POWER_CNTLMSAK         0x40
@@ -289,7 +290,7 @@ static int sta529_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
                return -EINVAL;
        }
 
-       snd_soc_update_bits(codec, STA529_S2PCFG0, 0x0D, mode);
+       snd_soc_update_bits(codec, STA529_S2PCFG0, DATA_FORMAT_MSK, mode);
 
        return 0;
 }
index 1cbe88f01d634137e0d7e85218556e1c81710d59..12bcae63a7f020f2dab718b535ad8b3d41d49ccd 100644 (file)
@@ -209,9 +209,9 @@ static int wm2000_power_up(struct i2c_client *i2c, int analogue)
 
        ret = wm2000_read(i2c, WM2000_REG_SPEECH_CLARITY);
        if (wm2000->speech_clarity)
-               ret &= ~WM2000_SPEECH_CLARITY;
-       else
                ret |= WM2000_SPEECH_CLARITY;
+       else
+               ret &= ~WM2000_SPEECH_CLARITY;
        wm2000_write(i2c, WM2000_REG_SPEECH_CLARITY, ret);
 
        wm2000_write(i2c, WM2000_REG_SYS_START0, 0x33);
index afcf31df77e06efe19c709630f53990741348227..d8c65f5746583474b4cdfb286d339c512f3562d6 100644 (file)
@@ -1019,8 +1019,6 @@ static const char *wm2200_mixer_texts[] = {
        "EQR",
        "LHPF1",
        "LHPF2",
-       "LHPF3",
-       "LHPF4",
        "DSP1.1",
        "DSP1.2",
        "DSP1.3",
@@ -1053,7 +1051,6 @@ static int wm2200_mixer_values[] = {
        0x25,
        0x50,   /* EQ */
        0x51,
-       0x52,
        0x60,   /* LHPF1 */
        0x61,   /* LHPF2 */
        0x68,   /* DSP1 */
@@ -1566,15 +1563,9 @@ static int wm2200_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        case SND_SOC_DAIFMT_DSP_A:
                fmt_val = 0;
                break;
-       case SND_SOC_DAIFMT_DSP_B:
-               fmt_val = 1;
-               break;
        case SND_SOC_DAIFMT_I2S:
                fmt_val = 2;
                break;
-       case SND_SOC_DAIFMT_LEFT_J:
-               fmt_val = 3;
-               break;
        default:
                dev_err(codec->dev, "Unsupported DAI format %d\n",
                        fmt & SND_SOC_DAIFMT_FORMAT_MASK);
@@ -1626,7 +1617,7 @@ static int wm2200_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
                            WM2200_AIF1TX_LRCLK_MSTR | WM2200_AIF1TX_LRCLK_INV,
                            lrclk);
        snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_5,
-                           WM2200_AIF1_FMT_MASK << 1, fmt_val << 1);
+                           WM2200_AIF1_FMT_MASK, fmt_val);
 
        return 0;
 }
index 5a5f3693623568d2d6268e9bc10e7055664c5d3d..54397a50807379bf2e625b4f980773ad72515164 100644 (file)
@@ -1279,15 +1279,9 @@ static int wm5100_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        case SND_SOC_DAIFMT_DSP_A:
                mask = 0;
                break;
-       case SND_SOC_DAIFMT_DSP_B:
-               mask = 1;
-               break;
        case SND_SOC_DAIFMT_I2S:
                mask = 2;
                break;
-       case SND_SOC_DAIFMT_LEFT_J:
-               mask = 3;
-               break;
        default:
                dev_err(codec->dev, "Unsupported DAI format %d\n",
                        fmt & SND_SOC_DAIFMT_FORMAT_MASK);
index 688ade0805897557baac586aeb6387a70361fd2c..1440b3f9b7bbe1e04afdfd3d6857808f52972fa9 100644 (file)
@@ -36,6 +36,9 @@
 struct wm5102_priv {
        struct arizona_priv core;
        struct arizona_fll fll[2];
+
+       unsigned int spk_ena:2;
+       unsigned int spk_ena_pending:1;
 };
 
 static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
@@ -787,6 +790,47 @@ ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE),
 };
 
+static int wm5102_spk_ev(struct snd_soc_dapm_widget *w,
+                        struct snd_kcontrol *kcontrol,
+                        int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+       struct wm5102_priv *wm5102 = snd_soc_codec_get_drvdata(codec);
+
+       if (arizona->rev < 1)
+               return 0;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               if (!wm5102->spk_ena) {
+                       snd_soc_write(codec, 0x4f5, 0x25a);
+                       wm5102->spk_ena_pending = true;
+               }
+               break;
+       case SND_SOC_DAPM_POST_PMU:
+               if (wm5102->spk_ena_pending) {
+                       msleep(75);
+                       snd_soc_write(codec, 0x4f5, 0xda);
+                       wm5102->spk_ena_pending = false;
+                       wm5102->spk_ena++;
+               }
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               wm5102->spk_ena--;
+               if (!wm5102->spk_ena)
+                       snd_soc_write(codec, 0x4f5, 0x25a);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               if (!wm5102->spk_ena)
+                       snd_soc_write(codec, 0x4f5, 0x0da);
+               break;
+       }
+
+       return 0;
+}
+
+
 ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
 ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE);
 ARIZONA_MIXER_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE);
@@ -852,8 +896,7 @@ static const unsigned int wm5102_aec_loopback_values[] = {
 
 static const struct soc_enum wm5102_aec_loopback =
        SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1,
-                             ARIZONA_AEC_LOOPBACK_SRC_SHIFT,
-                             ARIZONA_AEC_LOOPBACK_SRC_MASK,
+                             ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
                              ARRAY_SIZE(wm5102_aec_loopback_texts),
                              wm5102_aec_loopback_texts,
                              wm5102_aec_loopback_values);
@@ -1034,10 +1077,10 @@ SND_SOC_DAPM_PGA_E("OUT3L", ARIZONA_OUTPUT_ENABLES_1,
                   ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
 SND_SOC_DAPM_PGA_E("OUT4L", ARIZONA_OUTPUT_ENABLES_1,
-                  ARIZONA_OUT4L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+                  ARIZONA_OUT4L_ENA_SHIFT, 0, NULL, 0, wm5102_spk_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
 SND_SOC_DAPM_PGA_E("OUT4R", ARIZONA_OUTPUT_ENABLES_1,
-                  ARIZONA_OUT4R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+                  ARIZONA_OUT4R_ENA_SHIFT, 0, NULL, 0, wm5102_spk_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
 SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1,
                   ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
index ae80c8c285360cffd1ef82af06626ceccf2769d2..7a090968c4f737bd360f05c2c7a66dc362350d21 100644 (file)
@@ -344,8 +344,7 @@ static const unsigned int wm5110_aec_loopback_values[] = {
 
 static const struct soc_enum wm5110_aec_loopback =
        SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1,
-                             ARIZONA_AEC_LOOPBACK_SRC_SHIFT,
-                             ARIZONA_AEC_LOOPBACK_SRC_MASK,
+                             ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
                              ARRAY_SIZE(wm5110_aec_loopback_texts),
                              wm5110_aec_loopback_texts,
                              wm5110_aec_loopback_values);
index ffc89fab96fbf4b6ccc81973f69df863132da1ec..b6b65483758518f8b8cab7672af882ef923d2768 100644 (file)
@@ -169,6 +169,7 @@ static int wm_adsp_load(struct wm_adsp *dsp)
        const struct wm_adsp_region *mem;
        const char *region_name;
        char *file, *text;
+       void *buf;
        unsigned int reg;
        int regions = 0;
        int ret, offset, type, sizes;
@@ -322,8 +323,18 @@ static int wm_adsp_load(struct wm_adsp *dsp)
                }
 
                if (reg) {
-                       ret = regmap_raw_write(regmap, reg, region->data,
+                       buf = kmemdup(region->data, le32_to_cpu(region->len),
+                                     GFP_KERNEL | GFP_DMA);
+                       if (!buf) {
+                               adsp_err(dsp, "Out of memory\n");
+                               return -ENOMEM;
+                       }
+
+                       ret = regmap_raw_write(regmap, reg, buf,
                                               le32_to_cpu(region->len));
+
+                       kfree(buf);
+
                        if (ret != 0) {
                                adsp_err(dsp,
                                        "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
@@ -359,6 +370,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
        const char *region_name;
        int ret, pos, blocks, type, offset, reg;
        char *file;
+       void *buf;
 
        file = kzalloc(PAGE_SIZE, GFP_KERNEL);
        if (file == NULL)
@@ -384,7 +396,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
        hdr = (void*)&firmware->data[0];
        if (memcmp(hdr->magic, "WMDR", 4) != 0) {
                adsp_err(dsp, "%s: invalid magic\n", file);
-               return -EINVAL;
+               goto out_fw;
        }
 
        adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
@@ -426,6 +438,13 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
                }
 
                if (reg) {
+                       buf = kmemdup(blk->data, le32_to_cpu(blk->len),
+                                     GFP_KERNEL | GFP_DMA);
+                       if (!buf) {
+                               adsp_err(dsp, "Out of memory\n");
+                               return -ENOMEM;
+                       }
+
                        ret = regmap_raw_write(regmap, reg, blk->data,
                                               le32_to_cpu(blk->len));
                        if (ret != 0) {
@@ -433,6 +452,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
                                        "%s.%d: Failed to write to %x in %s\n",
                                        file, blocks, reg, region_name);
                        }
+
+                       kfree(buf);
                }
 
                pos += le32_to_cpu(blk->len) + sizeof(*blk);
index 3b98159d9645a8ecf917fb8c86c41de6a401c929..a210c8d7b4bce6d85afdb371b32b8b866d21da50 100644 (file)
@@ -108,18 +108,13 @@ if SND_IMX_SOC
 config SND_SOC_IMX_SSI
        tristate
 
-config SND_SOC_IMX_PCM
-       tristate
-
 config SND_SOC_IMX_PCM_FIQ
-       bool
+       tristate
        select FIQ
-       select SND_SOC_IMX_PCM
 
 config SND_SOC_IMX_PCM_DMA
-       bool
+       tristate
        select SND_SOC_DMAENGINE_PCM
-       select SND_SOC_IMX_PCM
 
 config SND_SOC_IMX_AUDMUX
        tristate
index afd34794db539a0f5de4154a5a635a69f40913e1..ec1457915d7c13ae5e97fd9482b34b46fc7f77b6 100644 (file)
@@ -41,7 +41,10 @@ endif
 obj-$(CONFIG_SND_SOC_IMX_SSI) += snd-soc-imx-ssi.o
 obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o
 
-obj-$(CONFIG_SND_SOC_IMX_PCM) += snd-soc-imx-pcm.o
+obj-$(CONFIG_SND_SOC_IMX_PCM_FIQ) += snd-soc-imx-pcm-fiq.o
+snd-soc-imx-pcm-fiq-y := imx-pcm-fiq.o imx-pcm.o
+obj-$(CONFIG_SND_SOC_IMX_PCM_DMA) += snd-soc-imx-pcm-dma.o
+snd-soc-imx-pcm-dma-y := imx-pcm-dma.o imx-pcm.o
 
 # i.MX Machine Support
 snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
index d5cd9eff3b48e92ca77349b930fe84056fcd7017..0c9f188ddc683e77110d788f5150fc8d6098a703 100644 (file)
@@ -31,7 +31,6 @@ int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
                        runtime->dma_bytes);
        return ret;
 }
-EXPORT_SYMBOL_GPL(snd_imx_pcm_mmap);
 
 static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
 {
@@ -80,7 +79,6 @@ int imx_pcm_new(struct snd_soc_pcm_runtime *rtd)
 out:
        return ret;
 }
-EXPORT_SYMBOL_GPL(imx_pcm_new);
 
 void imx_pcm_free(struct snd_pcm *pcm)
 {
@@ -102,7 +100,6 @@ void imx_pcm_free(struct snd_pcm *pcm)
                buf->area = NULL;
        }
 }
-EXPORT_SYMBOL_GPL(imx_pcm_free);
 
 MODULE_DESCRIPTION("Freescale i.MX PCM driver");
 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
index 91d592ff67b7914cdbeec98c24eb3b4c1cb6a364..2370063b58245946f71adfa05f361097d3e53374 100644 (file)
@@ -1255,6 +1255,8 @@ static int soc_post_component_init(struct snd_soc_card *card,
        INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].fe_clients);
        ret = device_add(rtd->dev);
        if (ret < 0) {
+               /* calling put_device() here to free the rtd->dev */
+               put_device(rtd->dev);
                dev_err(card->dev,
                        "ASoC: failed to register runtime device: %d\n", ret);
                return ret;
@@ -1554,7 +1556,7 @@ static void soc_remove_aux_dev(struct snd_soc_card *card, int num)
        /* unregister the rtd device */
        if (rtd->dev_registered) {
                device_remove_file(rtd->dev, &dev_attr_codec_reg);
-               device_del(rtd->dev);
+               device_unregister(rtd->dev);
                rtd->dev_registered = 0;
        }
 
@@ -2917,7 +2919,7 @@ int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol,
        platform_max = mc->platform_max;
 
        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-       uinfo->count = 1;
+       uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
        uinfo->value.integer.min = 0;
        uinfo->value.integer.max = platform_max - min;
 
@@ -2941,12 +2943,14 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
                (struct soc_mixer_control *)kcontrol->private_value;
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        unsigned int reg = mc->reg;
+       unsigned int rreg = mc->rreg;
        unsigned int shift = mc->shift;
        int min = mc->min;
        int max = mc->max;
        unsigned int mask = (1 << fls(max)) - 1;
        unsigned int invert = mc->invert;
        unsigned int val, val_mask;
+       int ret;
 
        val = ((ucontrol->value.integer.value[0] + min) & mask);
        if (invert)
@@ -2954,7 +2958,21 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
        val_mask = mask << shift;
        val = val << shift;
 
-       return snd_soc_update_bits_locked(codec, reg, val_mask, val);
+       ret = snd_soc_update_bits_locked(codec, reg, val_mask, val);
+       if (ret != 0)
+               return ret;
+
+       if (snd_soc_volsw_is_stereo(mc)) {
+               val = ((ucontrol->value.integer.value[1] + min) & mask);
+               if (invert)
+                       val = max - val;
+               val_mask = mask << shift;
+               val = val << shift;
+
+               ret = snd_soc_update_bits_locked(codec, rreg, val_mask, val);
+       }
+
+       return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range);
 
@@ -2974,6 +2992,7 @@ int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
                (struct soc_mixer_control *)kcontrol->private_value;
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        unsigned int reg = mc->reg;
+       unsigned int rreg = mc->rreg;
        unsigned int shift = mc->shift;
        int min = mc->min;
        int max = mc->max;
@@ -2988,6 +3007,16 @@ int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
        ucontrol->value.integer.value[0] =
                ucontrol->value.integer.value[0] - min;
 
+       if (snd_soc_volsw_is_stereo(mc)) {
+               ucontrol->value.integer.value[1] =
+                       (snd_soc_read(codec, rreg) >> shift) & mask;
+               if (invert)
+                       ucontrol->value.integer.value[1] =
+                               max - ucontrol->value.integer.value[1];
+               ucontrol->value.integer.value[1] =
+                       ucontrol->value.integer.value[1] - min;
+       }
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range);
index 1e36bc81e5af1fb015ffef62f628781910984bbb..258acadb9e7d238a97b7af6508ffc21bd1a4488e 100644 (file)
@@ -1023,7 +1023,7 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w,
 
        if (SND_SOC_DAPM_EVENT_ON(event)) {
                if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) {
-                       ret = regulator_allow_bypass(w->regulator, true);
+                       ret = regulator_allow_bypass(w->regulator, false);
                        if (ret != 0)
                                dev_warn(w->dapm->dev,
                                         "ASoC: Failed to bypass %s: %d\n",
@@ -1033,7 +1033,7 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w,
                return regulator_enable(w->regulator);
        } else {
                if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) {
-                       ret = regulator_allow_bypass(w->regulator, false);
+                       ret = regulator_allow_bypass(w->regulator, true);
                        if (ret != 0)
                                dev_warn(w->dapm->dev,
                                         "ASoC: Failed to unbypass %s: %d\n",
@@ -3039,6 +3039,14 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
                                w->name, ret);
                        return NULL;
                }
+
+               if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) {
+                       ret = regulator_allow_bypass(w->regulator, true);
+                       if (ret != 0)
+                               dev_warn(w->dapm->dev,
+                                        "ASoC: Failed to unbypass %s: %d\n",
+                                        w->name, ret);
+               }
                break;
        case snd_soc_dapm_clock_supply:
 #ifdef CONFIG_CLKDEV_LOOKUP
index d7711fce119b7b949bc521759b2f13e177dd657d..cf191e6aebbe1d1bfeb9dd511d0fe9b5e5bd3bbd 100644 (file)
@@ -1243,6 +1243,7 @@ static int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream)
                if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
                    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
                    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
+                   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED) &&
                    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
                        continue;
 
index ed4d89c8b52a52e6425ce5c992cc5d324b339d35..e90daf8cdaa8726f8311092636461a9fee10b187 100644 (file)
@@ -1331,16 +1331,23 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
                }
                channels = (hdr->bLength - 7) / csize - 1;
                bmaControls = hdr->bmaControls;
+               if (hdr->bLength < 7 + csize) {
+                       snd_printk(KERN_ERR "usbaudio: unit %u: "
+                                  "invalid UAC_FEATURE_UNIT descriptor\n",
+                                  unitid);
+                       return -EINVAL;
+               }
        } else {
                struct uac2_feature_unit_descriptor *ftr = _ftr;
                csize = 4;
                channels = (hdr->bLength - 6) / 4 - 1;
                bmaControls = ftr->bmaControls;
-       }
-
-       if (hdr->bLength < 7 || !csize || hdr->bLength < 7 + csize) {
-               snd_printk(KERN_ERR "usbaudio: unit %u: invalid UAC_FEATURE_UNIT descriptor\n", unitid);
-               return -EINVAL;
+               if (hdr->bLength < 6 + csize) {
+                       snd_printk(KERN_ERR "usbaudio: unit %u: "
+                                  "invalid UAC_FEATURE_UNIT descriptor\n",
+                                  unitid);
+                       return -EINVAL;
+               }
        }
 
        /* parse the source unit */
index e71fe55cebefa8285963643e7a592a0b71fbfc73..0e2ed3d05c451c32a53032b735bc95c7868df143 100644 (file)
@@ -179,6 +179,15 @@ static struct usbmix_name_map audigy2nx_map[] = {
        { 0 } /* terminator */
 };
 
+static struct usbmix_selector_map c400_selectors[] = {
+       {
+               .id = 0x80,
+               .count = 2,
+               .names = (const char*[]) {"Internal", "SPDIF"}
+       },
+       { 0 } /* terminator */
+};
+
 static struct usbmix_selector_map audigy2nx_selectors[] = {
        {
                .id = 14, /* Capture Source */
@@ -366,6 +375,10 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
                .id = USB_ID(0x06f8, 0xc000),
                .map = hercules_usb51_map,
        },
+       {
+               .id = USB_ID(0x0763, 0x2030),
+               .selector_map = c400_selectors,
+       },
        {
                .id = USB_ID(0x08bb, 0x2702),
                .map = linex_map,
index 0422b1360af3a896da708a79365e3370be771fd8..15520de1df5647c95a4ffe1a13d8ede4485dee09 100644 (file)
@@ -1206,7 +1206,7 @@ static int snd_c400_create_mixer(struct usb_mixer_interface *mixer)
  * are valid they presents mono controls as L and R channels of
  * stereo. So we provide a good mixer here.
  */
-struct std_mono_table ebox44_table[] = {
+static struct std_mono_table ebox44_table[] = {
        {
                .unitid = 4,
                .control = 1,
index c6593101c049b88ca6e5661f8765b3f3df0b4d33..d82e378d37cbb7202aaa487b6003b6b171b79eab 100644 (file)
@@ -511,6 +511,16 @@ static int configure_sync_endpoint(struct snd_usb_substream *subs)
        struct snd_usb_substream *sync_subs =
                &subs->stream->substream[subs->direction ^ 1];
 
+       if (subs->sync_endpoint->type != SND_USB_ENDPOINT_TYPE_DATA ||
+           !subs->stream)
+               return snd_usb_endpoint_set_params(subs->sync_endpoint,
+                                                  subs->pcm_format,
+                                                  subs->channels,
+                                                  subs->period_bytes,
+                                                  subs->cur_rate,
+                                                  subs->cur_audiofmt,
+                                                  NULL);
+
        /* Try to find the best matching audioformat. */
        list_for_each_entry(fp, &sync_subs->fmt_list, list) {
                int score = match_endpoint_audioformats(fp, subs->cur_audiofmt,
index 78e845ec65dafabc06332de0a3e7ea3d31e8b667..64d25a7a4d599538398e5a14d37d11a154ec65fd 100644 (file)
@@ -2289,7 +2289,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                                        .rate_table = (unsigned int[]) {
                                                        44100, 48000, 88200, 96000
                                        },
-                                       .clock = 0x81,
+                                       .clock = 0x80,
                                }
                        },
                        /* Capture */
@@ -2315,7 +2315,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                                        .rate_table = (unsigned int[]) {
                                                44100, 48000, 88200, 96000
                                        },
-                                       .clock = 0x81,
+                                       .clock = 0x80,
                                }
                        },
                        /* MIDI */
index acc12f004c23f05f12b21aa311f2f49dc77652d5..2c971858d6b70a146e57266bfa0afe7a1bfe80cb 100644 (file)
@@ -387,11 +387,13 @@ static int snd_usb_fasttrackpro_boot_quirk(struct usb_device *dev)
                 * rules
                 */
                err = usb_driver_set_configuration(dev, 2);
-               if (err < 0) {
+               if (err < 0)
                        snd_printdd("error usb_driver_set_configuration: %d\n",
                                    err);
-                       return -ENODEV;
-               }
+               /* Always return an error, so that we stop creating a device
+                  that will just be destroyed and recreated with a new
+                  configuration */
+               return -ENODEV;
        } else
                snd_printk(KERN_INFO "usb-audio: Fast Track Pro config OK\n");
 
@@ -859,6 +861,17 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep)
        if ((le16_to_cpu(ep->chip->dev->descriptor.idVendor) == 0x23ba) &&
            ep->type == SND_USB_ENDPOINT_TYPE_SYNC)
                ep->skip_packets = 4;
+
+       /*
+        * M-Audio Fast Track C400 - when packets are not skipped, real world
+        * latency varies by approx. +/- 50 frames (at 96KHz) each time the
+        * stream is (re)started. When skipping packets 16 at endpoint start
+        * up, the real world latency is stable within +/- 1 frame (also
+        * across power cycles).
+        */
+       if (ep->chip->usb_id == USB_ID(0x0763, 0x2030) &&
+           ep->type == SND_USB_ENDPOINT_TYPE_DATA)
+               ep->skip_packets = 16;
 }
 
 void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
index 80db3f4bcf7a7d07a8233b535f43a4862fd40b21..39d41068484f963efc242cb3fc75649875f874f3 100644 (file)
@@ -11,11 +11,21 @@ lib/rbtree.c
 include/linux/swab.h
 arch/*/include/asm/unistd*.h
 arch/*/include/asm/perf_regs.h
+arch/*/include/uapi/asm/unistd*.h
+arch/*/include/uapi/asm/perf_regs.h
 arch/*/lib/memcpy*.S
 arch/*/lib/memset*.S
 include/linux/poison.h
 include/linux/magic.h
 include/linux/hw_breakpoint.h
+include/linux/rbtree_augmented.h
+include/uapi/linux/perf_event.h
+include/uapi/linux/const.h
+include/uapi/linux/swab.h
+include/uapi/linux/hw_breakpoint.h
 arch/x86/include/asm/svm.h
 arch/x86/include/asm/vmx.h
 arch/x86/include/asm/kvm_host.h
+arch/x86/include/uapi/asm/svm.h
+arch/x86/include/uapi/asm/vmx.h
+arch/x86/include/uapi/asm/kvm.h
index 891bc77bdb2cbba9910813c6df8866b1351a7734..8ab05e543ef406a143e64276d7337d0e348289f4 100644 (file)
@@ -58,7 +58,7 @@ ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
                                  -e s/arm.*/arm/ -e s/sa110/arm/ \
                                  -e s/s390x/s390/ -e s/parisc64/parisc/ \
                                  -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
-                                 -e s/sh[234].*/sh/ )
+                                 -e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ )
 NO_PERF_REGS := 1
 
 CC = $(CROSS_COMPILE)gcc
diff --git a/tools/vm/.gitignore b/tools/vm/.gitignore
new file mode 100644 (file)
index 0000000..44f095f
--- /dev/null
@@ -0,0 +1,2 @@
+slabinfo
+page-types