Merge branch 'drm-next-3.7' of git://people.freedesktop.org/~agd5f/linux into drm...
authorDave Airlie <airlied@redhat.com>
Wed, 3 Oct 2012 00:32:58 +0000 (10:32 +1000)
committerDave Airlie <airlied@redhat.com>
Wed, 3 Oct 2012 00:32:58 +0000 (10:32 +1000)
Alex writes:
"The big changes for 3.7 include:
- Asynchronous VM page table updates for Cayman/SI
- 2 level VM page table support.  Saves memory compared to 1 level
  page tables.
- Reworked PLL handing in the display code allows lots more
  combinations of monitors to work, including more than two
  DP displays assuming compatible clocks across shared PLLs.
  This also allows us to power down extra PLLs when we can
  share a single one across multiple displays which saves power.
- Native backlight control on ATOMBIOS systems.
- Improved ACPI support for interacting with the GPU.  Fixes
  backlight control on some laptops.
- Document AMD ACPI interfaces
- Lots of code cleanup
- Bug fixes"

* 'drm-next-3.7' of git://people.freedesktop.org/~agd5f/linux: (79 commits)
  drm/radeon: add vm set_page() callback for SI
  drm/radeon: rework the vm_flush interface
  drm/radeon: use WRITE_DATA packets for vm flush on SI
  drm/radeon/pm: fix multi-head profile handling on BTC+ (v2)
  drm/radeon: fix radeon power state debug output
  drm/radeon: force MSIs on RS690 asics
  drm/radeon: Add MSI quirk for gateway RS690
  drm/radeon: allow MIP_ADDRESS=0 for MSAA textures on Evergreen
  drm/radeon/kms: allow STRMOUT_BASE_UPDATE on RS780 and RS880
  drm/radeon: add 2-level VM pagetables support v9
  drm/radeon: refactor set_page chipset interface v5
  drm/radeon: Fix scratch register leak in IB test.
  drm/radeon: restore backlight level on resume
  drm/radeon: add get_backlight_level callback
  drm/radeon: only adjust default clocks on NI GPUs
  drm/radeon: validate PPLL in crtc fixup
  drm/radeon: work around KMS modeset limitations in PLL allocation (v2)
  drm/radeon: make non-DP PPLL sharing more robust
  drm/radeon: store the encoder in the radeon_crtc
  drm/radeon: rework crtc pll setup to better support PPLL sharing
  ...

592 files changed:
Documentation/DocBook/drm.tmpl
MAINTAINERS
Makefile
arch/arm/Kconfig.debug
arch/arm/Makefile
arch/arm/boot/compressed/head.S
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/include/asm/assembler.h
arch/arm/include/asm/memory.h
arch/arm/include/asm/tlb.h
arch/arm/include/asm/uaccess.h
arch/arm/include/asm/unistd.h
arch/arm/kernel/calls.S
arch/arm/kernel/hw_breakpoint.c
arch/arm/kernel/smp_twd.c
arch/arm/kernel/traps.c
arch/arm/lib/delay.c
arch/arm/lib/getuser.S
arch/arm/lib/putuser.S
arch/arm/mach-imx/clk-imx25.c
arch/arm/mach-imx/clk-imx35.c
arch/arm/mach-imx/mach-armadillo5x0.c
arch/arm/mach-omap2/Kconfig
arch/arm/mach-omap2/Makefile
arch/arm/mach-omap2/clock33xx_data.c
arch/arm/mach-omap2/clockdomain2xxx_3xxx.c
arch/arm/mach-omap2/cm-regbits-34xx.h
arch/arm/mach-omap2/omap-wakeupgen.c
arch/arm/mach-omap2/omap_hwmod.c
arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
arch/arm/mach-omap2/omap_hwmod_44xx_data.c
arch/arm/mach-omap2/timer.c
arch/arm/mach-shmobile/board-kzm9g.c
arch/arm/mm/context.c
arch/arm/mm/dma-mapping.c
arch/arm/mm/mm.h
arch/arm/mm/mmu.c
arch/arm/plat-mxc/include/mach/mx25.h
arch/arm/plat-omap/sram.c
arch/arm/plat-samsung/clock.c
arch/m68k/platform/coldfire/clk.c
arch/mips/kernel/smp-cmp.c
arch/mips/mm/gup.c
arch/mips/mti-malta/malta-int.c
arch/mips/mti-malta/malta-platform.c
arch/s390/include/asm/hugetlb.h
arch/s390/include/asm/tlbflush.h
arch/s390/kernel/setup.c
arch/s390/lib/uaccess_pt.c
arch/s390/oprofile/init.c
arch/sh/kernel/cpu/sh5/entry.S
arch/sh/kernel/entry-common.S
arch/sparc/kernel/module.c
arch/x86/Kconfig
arch/x86/Makefile
arch/x86/include/asm/xen/page.h
arch/x86/kernel/cpu/perf_event.h
arch/x86/kernel/cpu/perf_event_amd_ibs.c
arch/x86/kernel/cpu/perf_event_intel.c
arch/x86/kernel/cpu/perf_event_intel_ds.c
arch/x86/kernel/cpu/perf_event_intel_lbr.c
arch/x86/kernel/cpu/perf_event_intel_uncore.c
arch/x86/kernel/microcode_core.c
arch/x86/mm/init.c
arch/x86/xen/enlighten.c
arch/x86/xen/p2m.c
block/blk-core.c
block/ioctl.c
drivers/acpi/bus.c
drivers/acpi/power.c
drivers/ata/ahci.c
drivers/block/aoe/aoecmd.c
drivers/block/cciss_scsi.c
drivers/block/mtip32xx/mtip32xx.c
drivers/block/mtip32xx/mtip32xx.h
drivers/block/nbd.c
drivers/block/xen-blkback/blkback.c
drivers/bluetooth/ath3k.c
drivers/bluetooth/btusb.c
drivers/char/agp/intel-gtt.c
drivers/clk/Makefile
drivers/clk/clk-devres.c [new file with mode: 0644]
drivers/clk/clkdev.c
drivers/cpufreq/powernow-k8.c
drivers/dma/at_hdmac.c
drivers/dma/pl330.c
drivers/edac/edac_mc.c
drivers/extcon/extcon-max77693.c
drivers/gpu/drm/Kconfig
drivers/gpu/drm/Makefile
drivers/gpu/drm/ast/ast_drv.h
drivers/gpu/drm/ast/ast_mode.c
drivers/gpu/drm/cirrus/cirrus_drv.h
drivers/gpu/drm/drm_cache.c
drivers/gpu/drm/drm_crtc.c
drivers/gpu/drm/drm_drv.c
drivers/gpu/drm/drm_edid.c
drivers/gpu/drm/drm_edid_load.c
drivers/gpu/drm/drm_edid_modes.h
drivers/gpu/drm/drm_fb_cma_helper.c [new file with mode: 0644]
drivers/gpu/drm/drm_fb_helper.c
drivers/gpu/drm/drm_gem_cma_helper.c [new file with mode: 0644]
drivers/gpu/drm/drm_irq.c
drivers/gpu/drm/drm_vm.c
drivers/gpu/drm/exynos/exynos_drm_connector.c
drivers/gpu/drm/exynos/exynos_drm_fbdev.c
drivers/gpu/drm/exynos/exynos_drm_vidi.c
drivers/gpu/drm/gma500/Makefile
drivers/gpu/drm/gma500/backlight.c
drivers/gpu/drm/gma500/cdv_device.c
drivers/gpu/drm/gma500/cdv_intel_display.c
drivers/gpu/drm/gma500/cdv_intel_dp.c [new file with mode: 0644]
drivers/gpu/drm/gma500/cdv_intel_hdmi.c
drivers/gpu/drm/gma500/cdv_intel_lvds.c
drivers/gpu/drm/gma500/framebuffer.c
drivers/gpu/drm/gma500/gem.c
drivers/gpu/drm/gma500/gem_glue.c [deleted file]
drivers/gpu/drm/gma500/gem_glue.h [deleted file]
drivers/gpu/drm/gma500/intel_bios.c
drivers/gpu/drm/gma500/intel_bios.h
drivers/gpu/drm/gma500/mdfld_dsi_output.c
drivers/gpu/drm/gma500/mid_bios.c
drivers/gpu/drm/gma500/oaktrail_hdmi.c
drivers/gpu/drm/gma500/opregion.c
drivers/gpu/drm/gma500/psb_device.c
drivers/gpu/drm/gma500/psb_drv.h
drivers/gpu/drm/gma500/psb_intel_drv.h
drivers/gpu/drm/gma500/psb_intel_lvds.c
drivers/gpu/drm/gma500/psb_intel_reg.h
drivers/gpu/drm/gma500/psb_intel_sdvo.c
drivers/gpu/drm/i2c/ch7006_drv.c
drivers/gpu/drm/i915/Makefile
drivers/gpu/drm/i915/dvo.h
drivers/gpu/drm/i915/dvo_ch7017.c
drivers/gpu/drm/i915/dvo_ch7xxx.c
drivers/gpu/drm/i915/dvo_ivch.c
drivers/gpu/drm/i915/dvo_ns2501.c [new file with mode: 0644]
drivers/gpu/drm/i915/dvo_sil164.c
drivers/gpu/drm/i915/dvo_tfp410.c
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_tiling.c
drivers/gpu/drm/i915/i915_irq.c
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/i915_sysfs.c
drivers/gpu/drm/i915/i915_trace.h
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_hdmi.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_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_tv.c
drivers/gpu/drm/mgag200/mgag200_drv.h
drivers/gpu/drm/mgag200/mgag200_mode.c
drivers/gpu/drm/nouveau/nouveau_dp.c
drivers/gpu/drm/nouveau/nv50_gpio.c
drivers/gpu/drm/radeon/atombios_dp.c
drivers/gpu/drm/shmobile/Kconfig [new file with mode: 0644]
drivers/gpu/drm/shmobile/Makefile [new file with mode: 0644]
drivers/gpu/drm/shmobile/shmob_drm_backlight.c [new file with mode: 0644]
drivers/gpu/drm/shmobile/shmob_drm_backlight.h [new file with mode: 0644]
drivers/gpu/drm/shmobile/shmob_drm_crtc.c [new file with mode: 0644]
drivers/gpu/drm/shmobile/shmob_drm_crtc.h [new file with mode: 0644]
drivers/gpu/drm/shmobile/shmob_drm_drv.c [new file with mode: 0644]
drivers/gpu/drm/shmobile/shmob_drm_drv.h [new file with mode: 0644]
drivers/gpu/drm/shmobile/shmob_drm_kms.c [new file with mode: 0644]
drivers/gpu/drm/shmobile/shmob_drm_kms.h [new file with mode: 0644]
drivers/gpu/drm/shmobile/shmob_drm_plane.c [new file with mode: 0644]
drivers/gpu/drm/shmobile/shmob_drm_plane.h [new file with mode: 0644]
drivers/gpu/drm/shmobile/shmob_drm_regs.h [new file with mode: 0644]
drivers/gpu/drm/ttm/ttm_bo_util.c
drivers/gpu/drm/ttm/ttm_page_alloc_dma.c
drivers/gpu/drm/ttm/ttm_tt.c
drivers/gpu/drm/udl/udl_connector.c
drivers/gpu/drm/udl/udl_encoder.c
drivers/gpu/drm/udl/udl_fb.c
drivers/gpu/drm/udl/udl_gem.c
drivers/gpu/drm/udl/udl_main.c
drivers/gpu/drm/udl/udl_modeset.c
drivers/gpu/drm/udl/udl_transfer.c
drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
drivers/hid/hid-lenovo-tpkbd.c
drivers/hid/hid-logitech-dj.c
drivers/hid/hid-logitech-dj.h
drivers/hwmon/ad7314.c
drivers/hwmon/ads7871.c
drivers/hwmon/applesmc.c
drivers/hwmon/coretemp.c
drivers/hwmon/fam15h_power.c
drivers/hwmon/ina2xx.c
drivers/hwmon/twl4030-madc-hwmon.c
drivers/hwmon/via-cputemp.c
drivers/hwspinlock/hwspinlock_core.c
drivers/i2c/algos/i2c-algo-pca.c
drivers/i2c/busses/i2c-mxs.c
drivers/i2c/busses/i2c-pnx.c
drivers/iio/adc/at91_adc.c
drivers/infiniband/hw/cxgb4/cm.c
drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
drivers/infiniband/hw/qib/qib_mad.c
drivers/infiniband/ulp/ipoib/ipoib.h
drivers/infiniband/ulp/ipoib/ipoib_main.c
drivers/infiniband/ulp/ipoib/ipoib_multicast.c
drivers/input/keyboard/imx_keypad.c
drivers/input/misc/ab8500-ponkey.c
drivers/input/mouse/sentelic.c
drivers/input/serio/ambakmi.c
drivers/input/serio/i8042-x86ia64io.h
drivers/input/touchscreen/edt-ft5x06.c
drivers/input/touchscreen/usbtouchscreen.c
drivers/isdn/hardware/mISDN/avmfritz.c
drivers/isdn/hardware/mISDN/hfcmulti.c
drivers/isdn/hardware/mISDN/mISDNipac.c
drivers/isdn/hardware/mISDN/mISDNisar.c
drivers/isdn/hardware/mISDN/netjet.c
drivers/isdn/hardware/mISDN/w6692.c
drivers/isdn/mISDN/hwchannel.c
drivers/md/md.c
drivers/md/raid5.c
drivers/mfd/88pm800.c
drivers/mfd/88pm805.c
drivers/mfd/88pm860x-core.c
drivers/mfd/aat2870-core.c
drivers/mfd/ab3100-core.c
drivers/mfd/ab8500-core.c
drivers/mfd/arizona-core.c
drivers/mfd/asic3.c
drivers/mfd/cs5535-mfd.c
drivers/mfd/da9052-core.c
drivers/mfd/davinci_voicecodec.c
drivers/mfd/db8500-prcmu.c
drivers/mfd/htc-pasic3.c
drivers/mfd/intel_msic.c
drivers/mfd/janz-cmodio.c
drivers/mfd/jz4740-adc.c
drivers/mfd/lm3533-core.c
drivers/mfd/lpc_ich.c
drivers/mfd/lpc_sch.c
drivers/mfd/max77686.c
drivers/mfd/max77693-irq.c
drivers/mfd/max77693.c
drivers/mfd/max8925-core.c
drivers/mfd/max8997.c
drivers/mfd/max8998.c
drivers/mfd/mc13xxx-core.c
drivers/mfd/mfd-core.c
drivers/mfd/palmas.c
drivers/mfd/rc5t583.c
drivers/mfd/rdc321x-southbridge.c
drivers/mfd/sec-core.c
drivers/mfd/sta2x11-mfd.c
drivers/mfd/stmpe.c
drivers/mfd/t7l66xb.c
drivers/mfd/tc3589x.c
drivers/mfd/tc6387xb.c
drivers/mfd/tc6393xb.c
drivers/mfd/ti-ssp.c
drivers/mfd/timberdale.c
drivers/mfd/tps6105x.c
drivers/mfd/tps6507x.c
drivers/mfd/tps65090.c
drivers/mfd/tps65217.c
drivers/mfd/tps6586x.c
drivers/mfd/tps65910.c
drivers/mfd/tps65912-core.c
drivers/mfd/twl4030-audio.c
drivers/mfd/twl6040-core.c
drivers/mfd/vx855.c
drivers/mfd/wl1273-core.c
drivers/mfd/wm831x-core.c
drivers/mfd/wm8400-core.c
drivers/mfd/wm8994-core.c
drivers/net/can/janz-ican3.c
drivers/net/can/mcp251x.c
drivers/net/can/ti_hecc.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_dump.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c
drivers/net/ethernet/cadence/at91_ether.c
drivers/net/ethernet/freescale/gianfar_ethtool.c
drivers/net/ethernet/freescale/gianfar_ptp.c
drivers/net/ethernet/i825xx/znet.c
drivers/net/ethernet/ibm/ibmveth.c
drivers/net/ethernet/intel/e1000/e1000_main.c
drivers/net/ethernet/mellanox/mlx4/icm.c
drivers/net/ethernet/mellanox/mlx4/icm.h
drivers/net/ethernet/mellanox/mlx4/main.c
drivers/net/ethernet/mellanox/mlx4/mcg.c
drivers/net/ethernet/mellanox/mlx4/mlx4.h
drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
drivers/net/ethernet/seeq/sgiseeq.c
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/stmicro/stmmac/stmmac_timer.c
drivers/net/irda/sh_sir.c
drivers/net/usb/asix_devices.c
drivers/net/usb/qmi_wwan.c
drivers/net/usb/sierra_net.c
drivers/net/usb/usbnet.c
drivers/net/wan/ixp4xx_hss.c
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
drivers/net/wireless/ath/ath9k/ar9003_paprd.c
drivers/net/wireless/ath/ath9k/ar9003_phy.h
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/gpio.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/link.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
drivers/net/wireless/brcm80211/brcmfmac/usb.c
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
drivers/net/wireless/brcm80211/brcmsmac/channel.c
drivers/net/wireless/libertas/if_sdio.c
drivers/net/wireless/mwifiex/cmdevt.c
drivers/net/wireless/rt2x00/rt2400pci.c
drivers/net/wireless/rt2x00/rt2400pci.h
drivers/net/wireless/rt2x00/rt2500pci.c
drivers/net/wireless/rt2x00/rt2500usb.c
drivers/net/wireless/rt2x00/rt2500usb.h
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2800pci.c
drivers/net/wireless/rt2x00/rt2800usb.c
drivers/net/wireless/rt2x00/rt2x00dev.c
drivers/net/wireless/rt2x00/rt61pci.c
drivers/net/wireless/rt2x00/rt61pci.h
drivers/net/wireless/rt2x00/rt73usb.c
drivers/net/wireless/rt2x00/rt73usb.h
drivers/net/wireless/rtlwifi/rtl8192ce/def.h
drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
drivers/platform/x86/acer-wmi.c
drivers/platform/x86/apple-gmux.c
drivers/platform/x86/asus-laptop.c
drivers/platform/x86/asus-wmi.c
drivers/platform/x86/eeepc-laptop.c
drivers/platform/x86/samsung-laptop.c
drivers/platform/x86/thinkpad_acpi.c
drivers/pwm/pwm-tiecap.c
drivers/pwm/pwm-tiehrpwm.c
drivers/regulator/tps65217-regulator.c
drivers/rpmsg/virtio_rpmsg_bus.c
drivers/rtc/rtc-twl.c
drivers/s390/block/dasd.c
drivers/s390/block/dasd_alias.c
drivers/s390/block/dasd_eckd.c
drivers/s390/cio/device.c
drivers/scsi/bnx2i/bnx2i_hwi.c
drivers/scsi/hpsa.c
drivers/scsi/mpt2sas/mpt2sas_base.c
drivers/scsi/virtio_scsi.c
drivers/sh/intc/core.c
drivers/sh/pfc/pinctrl.c
drivers/staging/android/android_alarm.h
drivers/staging/comedi/drivers/amplc_dio200.c
drivers/staging/comedi/drivers/amplc_pc236.c
drivers/staging/comedi/drivers/amplc_pc263.c
drivers/staging/comedi/drivers/amplc_pci224.c
drivers/staging/comedi/drivers/amplc_pci230.c
drivers/staging/comedi/drivers/das08.c
drivers/staging/iio/accel/lis3l02dq_ring.c
drivers/staging/iio/adc/ad7192.c
drivers/staging/iio/gyro/adis16260_core.c
drivers/staging/iio/imu/adis16400_core.c
drivers/staging/iio/meter/ade7753.c
drivers/staging/iio/meter/ade7754.c
drivers/staging/iio/meter/ade7759.c
drivers/staging/nvec/nvec.c
drivers/staging/omapdrm/omap_connector.c
drivers/staging/omapdrm/omap_fbdev.c
drivers/staging/ozwpan/ozcdev.c
drivers/staging/rtl8712/recv_linux.c
drivers/staging/vt6656/dpc.c
drivers/staging/vt6656/rxtx.c
drivers/staging/wlan-ng/cfg80211.c
drivers/staging/zcache/zcache-main.c
drivers/target/iscsi/iscsi_target_login.c
drivers/target/target_core_alua.c
drivers/target/target_core_device.c
drivers/target/target_core_iblock.c
drivers/target/target_core_pr.c
drivers/target/target_core_pscsi.c
drivers/target/target_core_spc.c
drivers/target/target_core_transport.c
drivers/tty/serial/imx.c
drivers/usb/chipidea/udc.c
drivers/usb/class/cdc-wdm.c
drivers/usb/core/quirks.c
drivers/usb/dwc3/core.c
drivers/usb/dwc3/ep0.c
drivers/usb/dwc3/gadget.c
drivers/usb/gadget/at91_udc.c
drivers/usb/gadget/dummy_hcd.c
drivers/usb/gadget/f_fs.c
drivers/usb/gadget/s3c-hsotg.c
drivers/usb/gadget/u_serial.c
drivers/usb/host/ehci-q.c
drivers/usb/host/ohci-at91.c
drivers/usb/host/pci-quirks.c
drivers/usb/host/pci-quirks.h
drivers/usb/host/xhci-hub.c
drivers/usb/host/xhci-plat.c
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h
drivers/usb/musb/musb_host.c
drivers/usb/musb/musbhsdma.c
drivers/usb/musb/tusb6010.c
drivers/usb/renesas_usbhs/fifo.c
drivers/usb/serial/ftdi_sio.c
drivers/usb/serial/ftdi_sio_ids.h
drivers/usb/serial/option.c
drivers/watchdog/hpwdt.c
drivers/watchdog/watchdog_core.c
drivers/xen/gntdev.c
drivers/xen/grant-table.c
fs/btrfs/qgroup.c
fs/cifs/cifs_unicode.c
fs/dcache.c
fs/debugfs/file.c
fs/ecryptfs/file.c
fs/ecryptfs/inode.c
fs/ecryptfs/main.c
fs/gfs2/file.c
fs/gfs2/inode.c
fs/gfs2/rgrp.c
fs/nfs/file.c
fs/nfs/inode.c
fs/nfs/nfs3proc.c
fs/nfs/nfs4file.c
fs/nfs/nfs4proc.c
fs/nfs/nfs4xdr.c
fs/nfs/super.c
fs/proc/proc_sysctl.c
fs/stat.c
fs/xfs/xfs_buf.c
fs/xfs/xfs_buf.h
fs/xfs/xfs_super.c
include/drm/drmP.h
include/drm/drm_crtc.h
include/drm/drm_fb_cma_helper.h [new file with mode: 0644]
include/drm/drm_fourcc.h
include/drm/drm_gem_cma_helper.h [new file with mode: 0644]
include/drm/drm_sarea.h
include/drm/i915_drm.h
include/drm/intel-gtt.h
include/linux/atmel-ssc.h
include/linux/compiler-gcc4.h
include/linux/compiler.h
include/linux/dcache.h
include/linux/i2c-pnx.h
include/linux/kernel.h
include/linux/kobject.h
include/linux/mISDNhw.h
include/linux/memory.h
include/linux/mfd/core.h
include/linux/mfd/tps65217.h
include/linux/mlx4/device.h
include/linux/nfs_fs.h
include/linux/nfs_xdr.h
include/linux/perf_event.h
include/linux/platform_data/shmob_drm.h [new file with mode: 0644]
include/linux/sched.h
include/linux/sunrpc/xprt.h
include/linux/xfrm.h
include/net/bluetooth/smp.h
include/net/ip6_fib.h
include/net/net_namespace.h
include/net/netns/ipv4.h
include/net/route.h
include/net/sock.h
include/net/xfrm.h
include/target/target_core_backend.h
include/target/target_core_base.h
include/trace/events/kmem.h
include/xen/grant_table.h
kernel/events/core.c
kernel/events/hw_breakpoint.c
kernel/pid_namespace.c
kernel/sched/core.c
kernel/sched/fair.c
kernel/sched/rt.c
kernel/sched/sched.h
kernel/time/tick-sched.c
kernel/time/timekeeping.c
kernel/workqueue.c
lib/digsig.c
mm/memblock.c
mm/memory_hotplug.c
mm/page_alloc.c
mm/slab.c
mm/slub.c
mm/vmscan.c
net/batman-adv/bitarray.h
net/bluetooth/bnep/sock.c
net/bluetooth/cmtp/sock.c
net/bluetooth/hci_conn.c
net/bluetooth/hci_sock.c
net/bluetooth/hidp/sock.c
net/bluetooth/l2cap_core.c
net/bluetooth/l2cap_sock.c
net/bluetooth/smp.c
net/bridge/netfilter/ebt_log.c
net/caif/cfsrvl.c
net/core/dev.c
net/core/pktgen.c
net/core/skbuff.c
net/core/sock.c
net/ipv4/arp.c
net/ipv4/devinet.c
net/ipv4/fib_frontend.c
net/ipv4/fib_rules.c
net/ipv4/fib_trie.c
net/ipv4/route.c
net/ipv4/tcp.c
net/ipv4/tcp_input.c
net/ipv4/udp.c
net/ipv6/inet6_connection_sock.c
net/ipv6/ip6_fib.c
net/ipv6/route.c
net/ipv6/tcp_ipv6.c
net/ipv6/udp.c
net/l2tp/l2tp_core.c
net/l2tp/l2tp_eth.c
net/mac80211/cfg.c
net/mac80211/mlme.c
net/netfilter/nf_conntrack_proto_tcp.c
net/netfilter/nfnetlink_log.c
net/netfilter/xt_LOG.c
net/netrom/af_netrom.c
net/openvswitch/actions.c
net/openvswitch/datapath.c
net/openvswitch/flow.h
net/sched/sch_cbq.c
net/sched/sch_fq_codel.c
net/sched/sch_gred.c
net/sched/sch_qfq.c
net/sctp/output.c
net/sunrpc/xprt.c
net/sunrpc/xprtrdma/transport.c
net/sunrpc/xprtsock.c
net/wireless/nl80211.c
net/xfrm/xfrm_input.c
net/xfrm/xfrm_policy.c
net/xfrm/xfrm_replay.c
net/xfrm/xfrm_user.c
scripts/Makefile.fwinst
security/selinux/include/xfrm.h
sound/core/compress_offload.c
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_intel.c
sound/pci/hda/patch_sigmatel.c
sound/pci/ice1712/prodigy_hifi.c
sound/soc/codecs/arizona.c
sound/soc/codecs/mc13783.c
sound/soc/codecs/wm8904.c
sound/soc/fsl/imx-sgtl5000.c
sound/soc/omap/am3517evm.c
sound/soc/samsung/dma.c
sound/soc/soc-dapm.c
sound/soc/spear/spear_pcm.c
sound/soc/tegra/tegra_alc5632.c
sound/soc/tegra/tegra_pcm.c
sound/soc/ux500/ux500_msp_i2s.c
sound/usb/pcm.c

index 196b8b9dba1112b245e331a76b62e804604b191a..b0300529ab13ede1178cf51097a02da67cfcc315 100644 (file)
@@ -6,11 +6,36 @@
   <bookinfo>
     <title>Linux DRM Developer's Guide</title>
 
+    <authorgroup>
+      <author>
+       <firstname>Jesse</firstname>
+       <surname>Barnes</surname>
+       <contrib>Initial version</contrib>
+       <affiliation>
+         <orgname>Intel Corporation</orgname>
+         <address>
+           <email>jesse.barnes@intel.com</email>
+         </address>
+       </affiliation>
+      </author>
+      <author>
+       <firstname>Laurent</firstname>
+       <surname>Pinchart</surname>
+       <contrib>Driver internals</contrib>
+       <affiliation>
+         <orgname>Ideas on board SPRL</orgname>
+         <address>
+           <email>laurent.pinchart@ideasonboard.com</email>
+         </address>
+       </affiliation>
+      </author>
+    </authorgroup>
+
     <copyright>
       <year>2008-2009</year>
-      <holder>
-       Intel Corporation (Jesse Barnes &lt;jesse.barnes@intel.com&gt;)
-      </holder>
+      <year>2012</year>
+      <holder>Intel Corporation</holder>
+      <holder>Laurent Pinchart</holder>
     </copyright>
 
     <legalnotice>
        the kernel source COPYING file.
       </para>
     </legalnotice>
+
+    <revhistory>
+      <!-- Put document revisions here, newest first. -->
+      <revision>
+       <revnumber>1.0</revnumber>
+       <date>2012-07-13</date>
+       <authorinitials>LP</authorinitials>
+       <revremark>Added extensive documentation about driver internals.
+       </revremark>
+      </revision>
+    </revhistory>
   </bookinfo>
 
 <toc></toc>
       submission &amp; fencing, suspend/resume support, and DMA
       services.
     </para>
-    <para>
-      The core of every DRM driver is struct drm_driver.  Drivers
-      typically statically initialize a drm_driver structure,
-      then pass it to drm_init() at load time.
-    </para>
 
   <!-- Internals: driver init -->
 
   <sect1>
-    <title>Driver initialization</title>
-    <para>
-      Before calling the DRM initialization routines, the driver must
-      first create and fill out a struct drm_driver structure.
-    </para>
-    <programlisting>
-      static struct drm_driver driver = {
-       /* Don't use MTRRs here; the Xserver or userspace app should
-        * deal with them for Intel hardware.
-        */
-       .driver_features =
-           DRIVER_USE_AGP | DRIVER_REQUIRE_AGP |
-           DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_MODESET,
-       .load = i915_driver_load,
-       .unload = i915_driver_unload,
-       .firstopen = i915_driver_firstopen,
-       .lastclose = i915_driver_lastclose,
-       .preclose = i915_driver_preclose,
-       .save = i915_save,
-       .restore = i915_restore,
-       .device_is_agp = i915_driver_device_is_agp,
-       .get_vblank_counter = i915_get_vblank_counter,
-       .enable_vblank = i915_enable_vblank,
-       .disable_vblank = i915_disable_vblank,
-       .irq_preinstall = i915_driver_irq_preinstall,
-       .irq_postinstall = i915_driver_irq_postinstall,
-       .irq_uninstall = i915_driver_irq_uninstall,
-       .irq_handler = i915_driver_irq_handler,
-       .reclaim_buffers = drm_core_reclaim_buffers,
-       .get_map_ofs = drm_core_get_map_ofs,
-       .get_reg_ofs = drm_core_get_reg_ofs,
-       .fb_probe = intelfb_probe,
-       .fb_remove = intelfb_remove,
-       .fb_resize = intelfb_resize,
-       .master_create = i915_master_create,
-       .master_destroy = i915_master_destroy,
-#if defined(CONFIG_DEBUG_FS)
-       .debugfs_init = i915_debugfs_init,
-       .debugfs_cleanup = i915_debugfs_cleanup,
-#endif
-       .gem_init_object = i915_gem_init_object,
-       .gem_free_object = i915_gem_free_object,
-       .gem_vm_ops = &amp;i915_gem_vm_ops,
-       .ioctls = i915_ioctls,
-       .fops = {
-               .owner = THIS_MODULE,
-               .open = drm_open,
-               .release = drm_release,
-               .ioctl = drm_ioctl,
-               .mmap = drm_mmap,
-               .poll = drm_poll,
-               .fasync = drm_fasync,
-#ifdef CONFIG_COMPAT
-               .compat_ioctl = i915_compat_ioctl,
-#endif
-               .llseek = noop_llseek,
-               },
-       .pci_driver = {
-               .name = DRIVER_NAME,
-               .id_table = pciidlist,
-               .probe = probe,
-               .remove = __devexit_p(drm_cleanup_pci),
-               },
-       .name = DRIVER_NAME,
-       .desc = DRIVER_DESC,
-       .date = DRIVER_DATE,
-       .major = DRIVER_MAJOR,
-       .minor = DRIVER_MINOR,
-       .patchlevel = DRIVER_PATCHLEVEL,
-      };
-    </programlisting>
-    <para>
-      In the example above, taken from the i915 DRM driver, the driver
-      sets several flags indicating what core features it supports;
-      we go over the individual callbacks in later sections.  Since
-      flags indicate which features your driver supports to the DRM
-      core, you need to set most of them prior to calling drm_init().  Some,
-      like DRIVER_MODESET can be set later based on user supplied parameters,
-      but that's the exception rather than the rule.
-    </para>
-    <variablelist>
-      <title>Driver flags</title>
-      <varlistentry>
-       <term>DRIVER_USE_AGP</term>
-       <listitem><para>
-           Driver uses AGP interface
-       </para></listitem>
-      </varlistentry>
-      <varlistentry>
-       <term>DRIVER_REQUIRE_AGP</term>
-       <listitem><para>
-           Driver needs AGP interface to function.
-       </para></listitem>
-      </varlistentry>
-      <varlistentry>
-       <term>DRIVER_USE_MTRR</term>
-       <listitem>
-         <para>
-           Driver uses MTRR interface for mapping memory.  Deprecated.
-         </para>
-       </listitem>
-      </varlistentry>
-      <varlistentry>
-       <term>DRIVER_PCI_DMA</term>
-       <listitem><para>
-           Driver is capable of PCI DMA.  Deprecated.
-       </para></listitem>
-      </varlistentry>
-      <varlistentry>
-       <term>DRIVER_SG</term>
-       <listitem><para>
-           Driver can perform scatter/gather DMA.  Deprecated.
-       </para></listitem>
-      </varlistentry>
-      <varlistentry>
-       <term>DRIVER_HAVE_DMA</term>
-       <listitem><para>Driver supports DMA.  Deprecated.</para></listitem>
-      </varlistentry>
-      <varlistentry>
-       <term>DRIVER_HAVE_IRQ</term><term>DRIVER_IRQ_SHARED</term>
-       <listitem>
-         <para>
-           DRIVER_HAVE_IRQ indicates whether the driver has an IRQ
-           handler.  DRIVER_IRQ_SHARED indicates whether the device &amp;
-           handler support shared IRQs (note that this is required of
-           PCI drivers).
-         </para>
-       </listitem>
-      </varlistentry>
-      <varlistentry>
-       <term>DRIVER_DMA_QUEUE</term>
-       <listitem>
-         <para>
-           Should be set if the driver queues DMA requests and completes them
-           asynchronously.  Deprecated.
-         </para>
-       </listitem>
-      </varlistentry>
-      <varlistentry>
-       <term>DRIVER_FB_DMA</term>
-       <listitem>
-         <para>
-           Driver supports DMA to/from the framebuffer.  Deprecated.
-         </para>
-       </listitem>
-      </varlistentry>
-      <varlistentry>
-       <term>DRIVER_MODESET</term>
-       <listitem>
-         <para>
-           Driver supports mode setting interfaces.
-         </para>
-       </listitem>
-      </varlistentry>
-    </variablelist>
-    <para>
-      In this specific case, the driver requires AGP and supports
-      IRQs.  DMA, as discussed later, is handled by device-specific ioctls
-      in this case.  It also supports the kernel mode setting APIs, though
-      unlike in the actual i915 driver source, this example unconditionally
-      exports KMS capability.
+    <title>Driver Initialization</title>
+    <para>
+      At the core of every DRM driver is a <structname>drm_driver</structname>
+      structure. Drivers typically statically initialize a drm_driver structure,
+      and then pass it to one of the <function>drm_*_init()</function> functions
+      to register it with the DRM subsystem.
     </para>
-  </sect1>
-
-  <!-- Internals: driver load -->
-
-  <sect1>
-    <title>Driver load</title>
-    <para>
-      In the previous section, we saw what a typical drm_driver
-      structure might look like.  One of the more important fields in
-      the structure is the hook for the load function.
-    </para>
-    <programlisting>
-      static struct drm_driver driver = {
-       ...
-       .load = i915_driver_load,
-        ...
-      };
-    </programlisting>
-    <para>
-      The load function has many responsibilities: allocating a driver
-      private structure, specifying supported performance counters,
-      configuring the device (e.g. mapping registers &amp; command
-      buffers), initializing the memory manager, and setting up the
-      initial output configuration.
-    </para>
-    <para>
-      If compatibility is a concern (e.g. with drivers converted over
-      to the new interfaces from the old ones), care must be taken to
-      prevent device initialization and control that is incompatible with
-      currently active userspace drivers.  For instance, if user
-      level mode setting drivers are in use, it would be problematic
-      to perform output discovery &amp; configuration at load time.
-      Likewise, if user-level drivers unaware of memory management are
-      in use, memory management and command buffer setup may need to
-      be omitted.  These requirements are driver-specific, and care
-      needs to be taken to keep both old and new applications and
-      libraries working.  The i915 driver supports the "modeset"
-      module parameter to control whether advanced features are
-      enabled at load time or in legacy fashion.
+    <para>
+      The <structname>drm_driver</structname> structure contains static
+      information that describes the driver and features it supports, and
+      pointers to methods that the DRM core will call to implement the DRM API.
+      We will first go through the <structname>drm_driver</structname> static
+      information fields, and will then describe individual operations in
+      details as they get used in later sections.
     </para>
-
     <sect2>
-      <title>Driver private &amp; performance counters</title>
-      <para>
-       The driver private hangs off the main drm_device structure and
-       can be used for tracking various device-specific bits of
-       information, like register offsets, command buffer status,
-       register state for suspend/resume, etc.  At load time, a
-       driver may simply allocate one and set drm_device.dev_priv
-       appropriately; it should be freed and drm_device.dev_priv set
-       to NULL when the driver is unloaded.
-      </para>
+      <title>Driver Information</title>
+      <sect3>
+        <title>Driver Features</title>
+        <para>
+          Drivers inform the DRM core about their requirements and supported
+          features by setting appropriate flags in the
+          <structfield>driver_features</structfield> field. Since those flags
+          influence the DRM core behaviour since registration time, most of them
+          must be set to registering the <structname>drm_driver</structname>
+          instance.
+        </para>
+        <synopsis>u32 driver_features;</synopsis>
+        <variablelist>
+          <title>Driver Feature Flags</title>
+          <varlistentry>
+            <term>DRIVER_USE_AGP</term>
+            <listitem><para>
+              Driver uses AGP interface, the DRM core will manage AGP resources.
+            </para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term>DRIVER_REQUIRE_AGP</term>
+            <listitem><para>
+              Driver needs AGP interface to function. AGP initialization failure
+              will become a fatal error.
+            </para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term>DRIVER_USE_MTRR</term>
+            <listitem><para>
+              Driver uses MTRR interface for mapping memory, the DRM core will
+              manage MTRR resources. Deprecated.
+            </para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term>DRIVER_PCI_DMA</term>
+            <listitem><para>
+              Driver is capable of PCI DMA, mapping of PCI DMA buffers to
+              userspace will be enabled. Deprecated.
+            </para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term>DRIVER_SG</term>
+            <listitem><para>
+              Driver can perform scatter/gather DMA, allocation and mapping of
+              scatter/gather buffers will be enabled. Deprecated.
+            </para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term>DRIVER_HAVE_DMA</term>
+            <listitem><para>
+              Driver supports DMA, the userspace DMA API will be supported.
+              Deprecated.
+            </para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term>DRIVER_HAVE_IRQ</term><term>DRIVER_IRQ_SHARED</term>
+            <listitem><para>
+              DRIVER_HAVE_IRQ indicates whether the driver has an IRQ handler. The
+              DRM core will automatically register an interrupt handler when the
+              flag is set. DRIVER_IRQ_SHARED indicates whether the device &amp;
+              handler support shared IRQs (note that this is required of PCI
+              drivers).
+            </para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term>DRIVER_IRQ_VBL</term>
+            <listitem><para>Unused. Deprecated.</para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term>DRIVER_DMA_QUEUE</term>
+            <listitem><para>
+              Should be set if the driver queues DMA requests and completes them
+              asynchronously.  Deprecated.
+            </para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term>DRIVER_FB_DMA</term>
+            <listitem><para>
+              Driver supports DMA to/from the framebuffer, mapping of frambuffer
+              DMA buffers to userspace will be supported. Deprecated.
+            </para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term>DRIVER_IRQ_VBL2</term>
+            <listitem><para>Unused. Deprecated.</para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term>DRIVER_GEM</term>
+            <listitem><para>
+              Driver use the GEM memory manager.
+            </para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term>DRIVER_MODESET</term>
+            <listitem><para>
+              Driver supports mode setting interfaces (KMS).
+            </para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term>DRIVER_PRIME</term>
+            <listitem><para>
+              Driver implements DRM PRIME buffer sharing.
+            </para></listitem>
+          </varlistentry>
+        </variablelist>
+      </sect3>
+      <sect3>
+        <title>Major, Minor and Patchlevel</title>
+        <synopsis>int major;
+int minor;
+int patchlevel;</synopsis>
+        <para>
+          The DRM core identifies driver versions by a major, minor and patch
+          level triplet. The information is printed to the kernel log at
+          initialization time and passed to userspace through the
+          DRM_IOCTL_VERSION ioctl.
+        </para>
+        <para>
+          The major and minor numbers are also used to verify the requested driver
+          API version passed to DRM_IOCTL_SET_VERSION. When the driver API changes
+          between minor versions, applications can call DRM_IOCTL_SET_VERSION to
+          select a specific version of the API. If the requested major isn't equal
+          to the driver major, or the requested minor is larger than the driver
+          minor, the DRM_IOCTL_SET_VERSION call will return an error. Otherwise
+          the driver's set_version() method will be called with the requested
+          version.
+        </para>
+      </sect3>
+      <sect3>
+        <title>Name, Description and Date</title>
+        <synopsis>char *name;
+char *desc;
+char *date;</synopsis>
+        <para>
+          The driver name is printed to the kernel log at initialization time,
+          used for IRQ registration and passed to userspace through
+          DRM_IOCTL_VERSION.
+        </para>
+        <para>
+          The driver description is a purely informative string passed to
+          userspace through the DRM_IOCTL_VERSION ioctl and otherwise unused by
+          the kernel.
+        </para>
+        <para>
+          The driver date, formatted as YYYYMMDD, is meant to identify the date of
+          the latest modification to the driver. However, as most drivers fail to
+          update it, its value is mostly useless. The DRM core prints it to the
+          kernel log at initialization time and passes it to userspace through the
+          DRM_IOCTL_VERSION ioctl.
+        </para>
+      </sect3>
+    </sect2>
+    <sect2>
+      <title>Driver Load</title>
       <para>
-       The DRM supports several counters which may be used for rough
-       performance characterization.  Note that the DRM stat counter
-       system is not often used by applications, and supporting
-       additional counters is completely optional.
+        The <methodname>load</methodname> method is the driver and device
+        initialization entry point. The method is responsible for allocating and
+        initializing driver private data, specifying supported performance
+        counters, performing resource allocation and mapping (e.g. acquiring
+        clocks, mapping registers or allocating command buffers), initializing
+        the memory manager (<xref linkend="drm-memory-management"/>), installing
+        the IRQ handler (<xref linkend="drm-irq-registration"/>), setting up
+        vertical blanking handling (<xref linkend="drm-vertical-blank"/>), mode
+       setting (<xref linkend="drm-mode-setting"/>) and initial output
+       configuration (<xref linkend="drm-kms-init"/>).
       </para>
+      <note><para>
+        If compatibility is a concern (e.g. with drivers converted over from
+        User Mode Setting to Kernel Mode Setting), care must be taken to prevent
+        device initialization and control that is incompatible with currently
+        active userspace drivers. For instance, if user level mode setting
+        drivers are in use, it would be problematic to perform output discovery
+        &amp; configuration at load time. Likewise, if user-level drivers
+        unaware of memory management are in use, memory management and command
+        buffer setup may need to be omitted. These requirements are
+        driver-specific, and care needs to be taken to keep both old and new
+        applications and libraries working.
+      </para></note>
+      <synopsis>int (*load) (struct drm_device *, unsigned long flags);</synopsis>
       <para>
-       These interfaces are deprecated and should not be used.  If performance
-       monitoring is desired, the developer should investigate and
-       potentially enhance the kernel perf and tracing infrastructure to export
-       GPU related performance information for consumption by performance
-       monitoring tools and applications.
+        The method takes two arguments, a pointer to the newly created
+       <structname>drm_device</structname> and flags. The flags are used to
+       pass the <structfield>driver_data</structfield> field of the device id
+       corresponding to the device passed to <function>drm_*_init()</function>.
+       Only PCI devices currently use this, USB and platform DRM drivers have
+       their <methodname>load</methodname> method called with flags to 0.
       </para>
+      <sect3>
+        <title>Driver Private &amp; Performance Counters</title>
+        <para>
+          The driver private hangs off the main
+          <structname>drm_device</structname> structure and can be used for
+          tracking various device-specific bits of information, like register
+          offsets, command buffer status, register state for suspend/resume, etc.
+          At load time, a driver may simply allocate one and set
+          <structname>drm_device</structname>.<structfield>dev_priv</structfield>
+          appropriately; it should be freed and
+          <structname>drm_device</structname>.<structfield>dev_priv</structfield>
+          set to NULL when the driver is unloaded.
+        </para>
+        <para>
+          DRM supports several counters which were used for rough performance
+          characterization. This stat counter system is deprecated and should not
+          be used. If performance monitoring is desired, the developer should
+          investigate and potentially enhance the kernel perf and tracing
+          infrastructure to export GPU related performance information for
+          consumption by performance monitoring tools and applications.
+        </para>
+      </sect3>
+      <sect3 id="drm-irq-registration">
+        <title>IRQ Registration</title>
+        <para>
+          The DRM core tries to facilitate IRQ handler registration and
+          unregistration by providing <function>drm_irq_install</function> and
+          <function>drm_irq_uninstall</function> functions. Those functions only
+          support a single interrupt per device.
+        </para>
+  <!--!Fdrivers/char/drm/drm_irq.c drm_irq_install-->
+        <para>
+          Both functions get the device IRQ by calling
+          <function>drm_dev_to_irq</function>. This inline function will call a
+          bus-specific operation to retrieve the IRQ number. For platform devices,
+          <function>platform_get_irq</function>(..., 0) is used to retrieve the
+          IRQ number.
+        </para>
+        <para>
+          <function>drm_irq_install</function> starts by calling the
+          <methodname>irq_preinstall</methodname> driver operation. The operation
+          is optional and must make sure that the interrupt will not get fired by
+          clearing all pending interrupt flags or disabling the interrupt.
+        </para>
+        <para>
+          The IRQ will then be requested by a call to
+          <function>request_irq</function>. If the DRIVER_IRQ_SHARED driver
+          feature flag is set, a shared (IRQF_SHARED) IRQ handler will be
+          requested.
+        </para>
+        <para>
+          The IRQ handler function must be provided as the mandatory irq_handler
+          driver operation. It will get passed directly to
+          <function>request_irq</function> and thus has the same prototype as all
+          IRQ handlers. It will get called with a pointer to the DRM device as the
+          second argument.
+        </para>
+        <para>
+          Finally the function calls the optional
+          <methodname>irq_postinstall</methodname> driver operation. The operation
+          usually enables interrupts (excluding the vblank interrupt, which is
+          enabled separately), but drivers may choose to enable/disable interrupts
+          at a different time.
+        </para>
+        <para>
+          <function>drm_irq_uninstall</function> is similarly used to uninstall an
+          IRQ handler. It starts by waking up all processes waiting on a vblank
+          interrupt to make sure they don't hang, and then calls the optional
+          <methodname>irq_uninstall</methodname> driver operation. The operation
+          must disable all hardware interrupts. Finally the function frees the IRQ
+          by calling <function>free_irq</function>.
+        </para>
+      </sect3>
+      <sect3>
+        <title>Memory Manager Initialization</title>
+        <para>
+          Every DRM driver requires a memory manager which must be initialized at
+          load time. DRM currently contains two memory managers, the Translation
+          Table Manager (TTM) and the Graphics Execution Manager (GEM).
+          This document describes the use of the GEM memory manager only. See
+          <xref linkend="drm-memory-management"/> for details.
+        </para>
+      </sect3>
+      <sect3>
+        <title>Miscellaneous Device Configuration</title>
+        <para>
+          Another task that may be necessary for PCI devices during configuration
+          is mapping the video BIOS. On many devices, the VBIOS describes device
+          configuration, LCD panel timings (if any), and contains flags indicating
+          device state. Mapping the BIOS can be done using the pci_map_rom() call,
+          a convenience function that takes care of mapping the actual ROM,
+          whether it has been shadowed into memory (typically at address 0xc0000)
+          or exists on the PCI device in the ROM BAR. Note that after the ROM has
+          been mapped and any necessary information has been extracted, it should
+          be unmapped; on many devices, the ROM address decoder is shared with
+          other BARs, so leaving it mapped could cause undesired behaviour like
+          hangs or memory corruption.
+  <!--!Fdrivers/pci/rom.c pci_map_rom-->
+        </para>
+      </sect3>
     </sect2>
+  </sect1>
 
-    <sect2>
-      <title>Configuring the device</title>
-      <para>
-       Obviously, device configuration is device-specific.
-       However, there are several common operations: finding a
-       device's PCI resources, mapping them, and potentially setting
-       up an IRQ handler.
-      </para>
-      <para>
-       Finding &amp; mapping resources is fairly straightforward.  The
-       DRM wrapper functions, drm_get_resource_start() and
-       drm_get_resource_len(), may be used to find BARs on the given
-       drm_device struct.  Once those values have been retrieved, the
-       driver load function can call drm_addmap() to create a new
-       mapping for the BAR in question.  Note that you probably want a
-       drm_local_map_t in your driver private structure to track any
-       mappings you create.
-<!-- !Fdrivers/gpu/drm/drm_bufs.c drm_get_resource_* -->
-<!-- !Finclude/drm/drmP.h drm_local_map_t -->
-      </para>
-      <para>
-       if compatibility with other operating systems isn't a concern
-       (DRM drivers can run under various BSD variants and OpenSolaris),
-       native Linux calls may be used for the above, e.g. pci_resource_*
-       and iomap*/iounmap.  See the Linux device driver book for more
-       info.
-      </para>
-      <para>
-       Once you have a register map, you may use the DRM_READn() and
-       DRM_WRITEn() macros to access the registers on your device, or
-       use driver-specific versions to offset into your MMIO space
-       relative to a driver-specific base pointer (see I915_READ for
-       an example).
-      </para>
-      <para>
-       If your device supports interrupt generation, you may want to
-       set up an interrupt handler when the driver is loaded.  This
-       is done using the drm_irq_install() function.  If your device
-       supports vertical blank interrupts, it should call
-       drm_vblank_init() to initialize the core vblank handling code before
-       enabling interrupts on your device.  This ensures the vblank related
-       structures are allocated and allows the core to handle vblank events.
-      </para>
-<!--!Fdrivers/char/drm/drm_irq.c drm_irq_install-->
-      <para>
-       Once your interrupt handler is registered (it uses your
-       drm_driver.irq_handler as the actual interrupt handling
-       function), you can safely enable interrupts on your device,
-       assuming any other state your interrupt handler uses is also
-       initialized.
-      </para>
-      <para>
-       Another task that may be necessary during configuration is
-       mapping the video BIOS.  On many devices, the VBIOS describes
-       device configuration, LCD panel timings (if any), and contains
-       flags indicating device state.  Mapping the BIOS can be done
-       using the pci_map_rom() call, a convenience function that
-       takes care of mapping the actual ROM, whether it has been
-       shadowed into memory (typically at address 0xc0000) or exists
-       on the PCI device in the ROM BAR.  Note that after the ROM
-       has been mapped and any necessary information has been extracted,
-       it should be unmapped; on many devices, the ROM address decoder is
-       shared with other BARs, so leaving it mapped could cause
-       undesired behavior like hangs or memory corruption.
-<!--!Fdrivers/pci/rom.c pci_map_rom-->
-      </para>
-    </sect2>
+  <!-- Internals: memory management -->
 
+  <sect1 id="drm-memory-management">
+    <title>Memory management</title>
+    <para>
+      Modern Linux systems require large amount of graphics memory to store
+      frame buffers, textures, vertices and other graphics-related data. Given
+      the very dynamic nature of many of that data, managing graphics memory
+      efficiently is thus crucial for the graphics stack and plays a central
+      role in the DRM infrastructure.
+    </para>
+    <para>
+      The DRM core includes two memory managers, namely Translation Table Maps
+      (TTM) and Graphics Execution Manager (GEM). TTM was the first DRM memory
+      manager to be developed and tried to be a one-size-fits-them all
+      solution. It provides a single userspace API to accomodate the need of
+      all hardware, supporting both Unified Memory Architecture (UMA) devices
+      and devices with dedicated video RAM (i.e. most discrete video cards).
+      This resulted in a large, complex piece of code that turned out to be
+      hard to use for driver development.
+    </para>
+    <para>
+      GEM started as an Intel-sponsored project in reaction to TTM's
+      complexity. Its design philosophy is completely different: instead of
+      providing a solution to every graphics memory-related problems, GEM
+      identified common code between drivers and created a support library to
+      share it. GEM has simpler initialization and execution requirements than
+      TTM, but has no video RAM management capabitilies and is thus limited to
+      UMA devices.
+    </para>
     <sect2>
-      <title>Memory manager initialization</title>
-      <para>
-       In order to allocate command buffers, cursor memory, scanout
-       buffers, etc., as well as support the latest features provided
-       by packages like Mesa and the X.Org X server, your driver
-       should support a memory manager.
-      </para>
+      <title>The Translation Table Manager (TTM)</title>
       <para>
-       If your driver supports memory management (it should!), you
-       need to set that up at load time as well.  How you initialize
-       it depends on which memory manager you're using: TTM or GEM.
+       TTM design background and information belongs here.
       </para>
       <sect3>
        <title>TTM initialization</title>
-       <para>
-         TTM (for Translation Table Manager) manages video memory and
-         aperture space for graphics devices. TTM supports both UMA devices
-         and devices with dedicated video RAM (VRAM), i.e. most discrete
-         graphics devices.  If your device has dedicated RAM, supporting
-         TTM is desirable.  TTM also integrates tightly with your
-         driver-specific buffer execution function.  See the radeon
-         driver for examples.
-       </para>
-       <para>
-         The core TTM structure is the ttm_bo_driver struct.  It contains
-         several fields with function pointers for initializing the TTM,
-         allocating and freeing memory, waiting for command completion
-         and fence synchronization, and memory migration.  See the
-         radeon_ttm.c file for an example of usage.
+        <warning><para>This section is outdated.</para></warning>
+        <para>
+          Drivers wishing to support TTM must fill out a drm_bo_driver
+          structure. The structure contains several fields with function
+          pointers for initializing the TTM, allocating and freeing memory,
+          waiting for command completion and fence synchronization, and memory
+          migration. See the radeon_ttm.c file for an example of usage.
        </para>
        <para>
          The ttm_global_reference structure is made up of several fields:
          count for the TTM, which will call your initialization function.
        </para>
       </sect3>
+    </sect2>
+    <sect2 id="drm-gem">
+      <title>The Graphics Execution Manager (GEM)</title>
+      <para>
+        The GEM design approach has resulted in a memory manager that doesn't
+        provide full coverage of all (or even all common) use cases in its
+        userspace or kernel API. GEM exposes a set of standard memory-related
+        operations to userspace and a set of helper functions to drivers, and let
+        drivers implement hardware-specific operations with their own private API.
+      </para>
+      <para>
+        The GEM userspace API is described in the
+        <ulink url="http://lwn.net/Articles/283798/"><citetitle>GEM - the Graphics
+        Execution Manager</citetitle></ulink> article on LWN. While slightly
+        outdated, the document provides a good overview of the GEM API principles.
+        Buffer allocation and read and write operations, described as part of the
+        common GEM API, are currently implemented using driver-specific ioctls.
+      </para>
+      <para>
+        GEM is data-agnostic. It manages abstract buffer objects without knowing
+        what individual buffers contain. APIs that require knowledge of buffer
+        contents or purpose, such as buffer allocation or synchronization
+        primitives, are thus outside of the scope of GEM and must be implemented
+        using driver-specific ioctls.
+      </para>
+      <para>
+       On a fundamental level, GEM involves several operations:
+       <itemizedlist>
+         <listitem>Memory allocation and freeing</listitem>
+         <listitem>Command execution</listitem>
+         <listitem>Aperture management at command execution time</listitem>
+       </itemizedlist>
+       Buffer object allocation is relatively straightforward and largely
+        provided by Linux's shmem layer, which provides memory to back each
+        object.
+      </para>
+      <para>
+        Device-specific operations, such as command execution, pinning, buffer
+       read &amp; write, mapping, and domain ownership transfers are left to
+        driver-specific ioctls.
+      </para>
+      <sect3>
+        <title>GEM Initialization</title>
+        <para>
+          Drivers that use GEM must set the DRIVER_GEM bit in the struct
+          <structname>drm_driver</structname>
+          <structfield>driver_features</structfield> field. The DRM core will
+          then automatically initialize the GEM core before calling the
+          <methodname>load</methodname> operation. Behind the scene, this will
+          create a DRM Memory Manager object which provides an address space
+          pool for object allocation.
+        </para>
+        <para>
+          In a KMS configuration, drivers need to allocate and initialize a
+          command ring buffer following core GEM initialization if required by
+          the hardware. UMA devices usually have what is called a "stolen"
+          memory region, which provides space for the initial framebuffer and
+          large, contiguous memory regions required by the device. This space is
+          typically not managed by GEM, and must be initialized separately into
+          its own DRM MM object.
+        </para>
+      </sect3>
       <sect3>
-       <title>GEM initialization</title>
-       <para>
-         GEM is an alternative to TTM, designed specifically for UMA
-         devices.  It has simpler initialization and execution requirements
-         than TTM, but has no VRAM management capability.  Core GEM
-         is initialized by calling drm_mm_init() to create
-         a GTT DRM MM object, which provides an address space pool for
-         object allocation.  In a KMS configuration, the driver
-         needs to allocate and initialize a command ring buffer following
-         core GEM initialization.  A UMA device usually has what is called a
-         "stolen" memory region, which provides space for the initial
-         framebuffer and large, contiguous memory regions required by the
-         device.  This space is not typically managed by GEM, and it must
-         be initialized separately into its own DRM MM object.
-       </para>
-       <para>
-         Initialization is driver-specific. In the case of Intel
-         integrated graphics chips like 965GM, GEM initialization can
-         be done by calling the internal GEM init function,
-         i915_gem_do_init().  Since the 965GM is a UMA device
-         (i.e. it doesn't have dedicated VRAM), GEM manages
-         making regular RAM available for GPU operations.  Memory set
-         aside by the BIOS (called "stolen" memory by the i915
-         driver) is managed by the DRM memrange allocator; the
-         rest of the aperture is managed by GEM.
-         <programlisting>
-           /* Basic memrange allocator for stolen space (aka vram) */
-           drm_memrange_init(&amp;dev_priv->vram, 0, prealloc_size);
-           /* Let GEM Manage from end of prealloc space to end of aperture */
-           i915_gem_do_init(dev, prealloc_size, agp_size);
-         </programlisting>
-<!--!Edrivers/char/drm/drm_memrange.c-->
-       </para>
-       <para>
-         Once the memory manager has been set up, we may allocate the
-         command buffer.  In the i915 case, this is also done with a
-         GEM function, i915_gem_init_ringbuffer().
-       </para>
+        <title>GEM Objects Creation</title>
+        <para>
+          GEM splits creation of GEM objects and allocation of the memory that
+          backs them in two distinct operations.
+        </para>
+        <para>
+          GEM objects are represented by an instance of struct
+          <structname>drm_gem_object</structname>. Drivers usually need to extend
+          GEM objects with private information and thus create a driver-specific
+          GEM object structure type that embeds an instance of struct
+          <structname>drm_gem_object</structname>.
+        </para>
+        <para>
+          To create a GEM object, a driver allocates memory for an instance of its
+          specific GEM object type and initializes the embedded struct
+          <structname>drm_gem_object</structname> with a call to
+          <function>drm_gem_object_init</function>. The function takes a pointer to
+          the DRM device, a pointer to the GEM object and the buffer object size
+          in bytes.
+        </para>
+        <para>
+          GEM uses shmem to allocate anonymous pageable memory.
+          <function>drm_gem_object_init</function> will create an shmfs file of
+          the requested size and store it into the struct
+          <structname>drm_gem_object</structname> <structfield>filp</structfield>
+          field. The memory is used as either main storage for the object when the
+          graphics hardware uses system memory directly or as a backing store
+          otherwise.
+        </para>
+        <para>
+          Drivers are responsible for the actual physical pages allocation by
+          calling <function>shmem_read_mapping_page_gfp</function> for each page.
+          Note that they can decide to allocate pages when initializing the GEM
+          object, or to delay allocation until the memory is needed (for instance
+          when a page fault occurs as a result of a userspace memory access or
+          when the driver needs to start a DMA transfer involving the memory).
+        </para>
+        <para>
+          Anonymous pageable memory allocation is not always desired, for instance
+          when the hardware requires physically contiguous system memory as is
+          often the case in embedded devices. Drivers can create GEM objects with
+          no shmfs backing (called private GEM objects) by initializing them with
+          a call to <function>drm_gem_private_object_init</function> instead of
+          <function>drm_gem_object_init</function>. Storage for private GEM
+          objects must be managed by drivers.
+        </para>
+        <para>
+          Drivers that do not need to extend GEM objects with private information
+          can call the <function>drm_gem_object_alloc</function> function to
+          allocate and initialize a struct <structname>drm_gem_object</structname>
+          instance. The GEM core will call the optional driver
+          <methodname>gem_init_object</methodname> operation after initializing
+          the GEM object with <function>drm_gem_object_init</function>.
+          <synopsis>int (*gem_init_object) (struct drm_gem_object *obj);</synopsis>
+        </para>
+        <para>
+          No alloc-and-init function exists for private GEM objects.
+        </para>
+      </sect3>
+      <sect3>
+        <title>GEM Objects Lifetime</title>
+        <para>
+          All GEM objects are reference-counted by the GEM core. References can be
+          acquired and release by <function>calling drm_gem_object_reference</function>
+          and <function>drm_gem_object_unreference</function> respectively. The
+          caller must hold the <structname>drm_device</structname>
+          <structfield>struct_mutex</structfield> lock. As a convenience, GEM
+          provides the <function>drm_gem_object_reference_unlocked</function> and
+          <function>drm_gem_object_unreference_unlocked</function> functions that
+          can be called without holding the lock.
+        </para>
+        <para>
+          When the last reference to a GEM object is released the GEM core calls
+          the <structname>drm_driver</structname>
+          <methodname>gem_free_object</methodname> operation. That operation is
+          mandatory for GEM-enabled drivers and must free the GEM object and all
+          associated resources.
+        </para>
+        <para>
+          <synopsis>void (*gem_free_object) (struct drm_gem_object *obj);</synopsis>
+          Drivers are responsible for freeing all GEM object resources, including
+          the resources created by the GEM core. If an mmap offset has been
+          created for the object (in which case
+          <structname>drm_gem_object</structname>::<structfield>map_list</structfield>::<structfield>map</structfield>
+          is not NULL) it must be freed by a call to
+          <function>drm_gem_free_mmap_offset</function>. The shmfs backing store
+          must be released by calling <function>drm_gem_object_release</function>
+          (that function can safely be called if no shmfs backing store has been
+          created).
+        </para>
+      </sect3>
+      <sect3>
+        <title>GEM Objects Naming</title>
+        <para>
+          Communication between userspace and the kernel refers to GEM objects
+          using local handles, global names or, more recently, file descriptors.
+          All of those are 32-bit integer values; the usual Linux kernel limits
+          apply to the file descriptors.
+        </para>
+        <para>
+          GEM handles are local to a DRM file. Applications get a handle to a GEM
+          object through a driver-specific ioctl, and can use that handle to refer
+          to the GEM object in other standard or driver-specific ioctls. Closing a
+          DRM file handle frees all its GEM handles and dereferences the
+          associated GEM objects.
+        </para>
+        <para>
+          To create a handle for a GEM object drivers call
+          <function>drm_gem_handle_create</function>. The function takes a pointer
+          to the DRM file and the GEM object and returns a locally unique handle.
+          When the handle is no longer needed drivers delete it with a call to
+          <function>drm_gem_handle_delete</function>. Finally the GEM object
+          associated with a handle can be retrieved by a call to
+          <function>drm_gem_object_lookup</function>.
+        </para>
+        <para>
+          Handles don't take ownership of GEM objects, they only take a reference
+          to the object that will be dropped when the handle is destroyed. To
+          avoid leaking GEM objects, drivers must make sure they drop the
+          reference(s) they own (such as the initial reference taken at object
+          creation time) as appropriate, without any special consideration for the
+          handle. For example, in the particular case of combined GEM object and
+          handle creation in the implementation of the
+          <methodname>dumb_create</methodname> operation, drivers must drop the
+          initial reference to the GEM object before returning the handle.
+        </para>
+        <para>
+          GEM names are similar in purpose to handles but are not local to DRM
+          files. They can be passed between processes to reference a GEM object
+          globally. Names can't be used directly to refer to objects in the DRM
+          API, applications must convert handles to names and names to handles
+          using the DRM_IOCTL_GEM_FLINK and DRM_IOCTL_GEM_OPEN ioctls
+          respectively. The conversion is handled by the DRM core without any
+          driver-specific support.
+        </para>
+        <para>
+          Similar to global names, GEM file descriptors are also used to share GEM
+          objects across processes. They offer additional security: as file
+          descriptors must be explictly sent over UNIX domain sockets to be shared
+          between applications, they can't be guessed like the globally unique GEM
+          names.
+        </para>
+        <para>
+          Drivers that support GEM file descriptors, also known as the DRM PRIME
+          API, must set the DRIVER_PRIME bit in the struct
+          <structname>drm_driver</structname>
+          <structfield>driver_features</structfield> field, and implement the
+          <methodname>prime_handle_to_fd</methodname> and
+          <methodname>prime_fd_to_handle</methodname> operations.
+        </para>
+        <para>
+          <synopsis>int (*prime_handle_to_fd)(struct drm_device *dev,
+                            struct drm_file *file_priv, uint32_t handle,
+                            uint32_t flags, int *prime_fd);
+  int (*prime_fd_to_handle)(struct drm_device *dev,
+                            struct drm_file *file_priv, int prime_fd,
+                            uint32_t *handle);</synopsis>
+          Those two operations convert a handle to a PRIME file descriptor and
+          vice versa. Drivers must use the kernel dma-buf buffer sharing framework
+          to manage the PRIME file descriptors.
+        </para>
+        <para>
+          While non-GEM drivers must implement the operations themselves, GEM
+          drivers must use the <function>drm_gem_prime_handle_to_fd</function>
+          and <function>drm_gem_prime_fd_to_handle</function> helper functions.
+          Those helpers rely on the driver
+          <methodname>gem_prime_export</methodname> and
+          <methodname>gem_prime_import</methodname> operations to create a dma-buf
+          instance from a GEM object (dma-buf exporter role) and to create a GEM
+          object from a dma-buf instance (dma-buf importer role).
+        </para>
+        <para>
+          <synopsis>struct dma_buf * (*gem_prime_export)(struct drm_device *dev,
+                                       struct drm_gem_object *obj,
+                                       int flags);
+  struct drm_gem_object * (*gem_prime_import)(struct drm_device *dev,
+                                              struct dma_buf *dma_buf);</synopsis>
+          These two operations are mandatory for GEM drivers that support DRM
+          PRIME.
+        </para>
+      </sect3>
+      <sect3 id="drm-gem-objects-mapping">
+        <title>GEM Objects Mapping</title>
+        <para>
+          Because mapping operations are fairly heavyweight GEM favours
+          read/write-like access to buffers, implemented through driver-specific
+          ioctls, over mapping buffers to userspace. However, when random access
+          to the buffer is needed (to perform software rendering for instance),
+          direct access to the object can be more efficient.
+        </para>
+        <para>
+          The mmap system call can't be used directly to map GEM objects, as they
+          don't have their own file handle. Two alternative methods currently
+          co-exist to map GEM objects to userspace. The first method uses a
+          driver-specific ioctl to perform the mapping operation, calling
+          <function>do_mmap</function> under the hood. This is often considered
+          dubious, seems to be discouraged for new GEM-enabled drivers, and will
+          thus not be described here.
+        </para>
+        <para>
+          The second method uses the mmap system call on the DRM file handle.
+          <synopsis>void *mmap(void *addr, size_t length, int prot, int flags, int fd,
+             off_t offset);</synopsis>
+          DRM identifies the GEM object to be mapped by a fake offset passed
+          through the mmap offset argument. Prior to being mapped, a GEM object
+          must thus be associated with a fake offset. To do so, drivers must call
+          <function>drm_gem_create_mmap_offset</function> on the object. The
+          function allocates a fake offset range from a pool and stores the
+          offset divided by PAGE_SIZE in
+          <literal>obj-&gt;map_list.hash.key</literal>. Care must be taken not to
+          call <function>drm_gem_create_mmap_offset</function> if a fake offset
+          has already been allocated for the object. This can be tested by
+          <literal>obj-&gt;map_list.map</literal> being non-NULL.
+        </para>
+        <para>
+          Once allocated, the fake offset value
+          (<literal>obj-&gt;map_list.hash.key &lt;&lt; PAGE_SHIFT</literal>)
+          must be passed to the application in a driver-specific way and can then
+          be used as the mmap offset argument.
+        </para>
+        <para>
+          The GEM core provides a helper method <function>drm_gem_mmap</function>
+          to handle object mapping. The method can be set directly as the mmap
+          file operation handler. It will look up the GEM object based on the
+          offset value and set the VMA operations to the
+          <structname>drm_driver</structname> <structfield>gem_vm_ops</structfield>
+          field. Note that <function>drm_gem_mmap</function> doesn't map memory to
+          userspace, but relies on the driver-provided fault handler to map pages
+          individually.
+        </para>
+        <para>
+          To use <function>drm_gem_mmap</function>, drivers must fill the struct
+          <structname>drm_driver</structname> <structfield>gem_vm_ops</structfield>
+          field with a pointer to VM operations.
+        </para>
+        <para>
+          <synopsis>struct vm_operations_struct *gem_vm_ops
+
+  struct vm_operations_struct {
+          void (*open)(struct vm_area_struct * area);
+          void (*close)(struct vm_area_struct * area);
+          int (*fault)(struct vm_area_struct *vma, struct vm_fault *vmf);
+  };</synopsis>
+        </para>
+        <para>
+          The <methodname>open</methodname> and <methodname>close</methodname>
+          operations must update the GEM object reference count. Drivers can use
+          the <function>drm_gem_vm_open</function> and
+          <function>drm_gem_vm_close</function> helper functions directly as open
+          and close handlers.
+        </para>
+        <para>
+          The fault operation handler is responsible for mapping individual pages
+          to userspace when a page fault occurs. Depending on the memory
+          allocation scheme, drivers can allocate pages at fault time, or can
+          decide to allocate memory for the GEM object at the time the object is
+          created.
+        </para>
+        <para>
+          Drivers that want to map the GEM object upfront instead of handling page
+          faults can implement their own mmap file operation handler.
+        </para>
+      </sect3>
+      <sect3>
+        <title>Dumb GEM Objects</title>
+        <para>
+          The GEM API doesn't standardize GEM objects creation and leaves it to
+          driver-specific ioctls. While not an issue for full-fledged graphics
+          stacks that include device-specific userspace components (in libdrm for
+          instance), this limit makes DRM-based early boot graphics unnecessarily
+          complex.
+        </para>
+        <para>
+          Dumb GEM objects partly alleviate the problem by providing a standard
+          API to create dumb buffers suitable for scanout, which can then be used
+          to create KMS frame buffers.
+        </para>
+        <para>
+          To support dumb GEM objects drivers must implement the
+          <methodname>dumb_create</methodname>,
+          <methodname>dumb_destroy</methodname> and
+          <methodname>dumb_map_offset</methodname> operations.
+        </para>
+        <itemizedlist>
+          <listitem>
+            <synopsis>int (*dumb_create)(struct drm_file *file_priv, struct drm_device *dev,
+                     struct drm_mode_create_dumb *args);</synopsis>
+            <para>
+              The <methodname>dumb_create</methodname> operation creates a GEM
+              object suitable for scanout based on the width, height and depth
+              from the struct <structname>drm_mode_create_dumb</structname>
+              argument. It fills the argument's <structfield>handle</structfield>,
+              <structfield>pitch</structfield> and <structfield>size</structfield>
+              fields with a handle for the newly created GEM object and its line
+              pitch and size in bytes.
+            </para>
+          </listitem>
+          <listitem>
+            <synopsis>int (*dumb_destroy)(struct drm_file *file_priv, struct drm_device *dev,
+                      uint32_t handle);</synopsis>
+            <para>
+              The <methodname>dumb_destroy</methodname> operation destroys a dumb
+              GEM object created by <methodname>dumb_create</methodname>.
+            </para>
+          </listitem>
+          <listitem>
+            <synopsis>int (*dumb_map_offset)(struct drm_file *file_priv, struct drm_device *dev,
+                         uint32_t handle, uint64_t *offset);</synopsis>
+            <para>
+              The <methodname>dumb_map_offset</methodname> operation associates an
+              mmap fake offset with the GEM object given by the handle and returns
+              it. Drivers must use the
+              <function>drm_gem_create_mmap_offset</function> function to
+              associate the fake offset as described in
+              <xref linkend="drm-gem-objects-mapping"/>.
+            </para>
+          </listitem>
+        </itemizedlist>
+      </sect3>
+      <sect3>
+        <title>Memory Coherency</title>
+        <para>
+          When mapped to the device or used in a command buffer, backing pages
+          for an object are flushed to memory and marked write combined so as to
+          be coherent with the GPU. Likewise, if the CPU accesses an object
+          after the GPU has finished rendering to the object, then the object
+          must be made coherent with the CPU's view of memory, usually involving
+          GPU cache flushing of various kinds. This core CPU&lt;-&gt;GPU
+          coherency management is provided by a device-specific ioctl, which
+          evaluates an object's current domain and performs any necessary
+          flushing or synchronization to put the object into the desired
+          coherency domain (note that the object may be busy, i.e. an active
+          render target; in that case, setting the domain blocks the client and
+          waits for rendering to complete before performing any necessary
+          flushing operations).
+        </para>
+      </sect3>
+      <sect3>
+        <title>Command Execution</title>
+        <para>
+         Perhaps the most important GEM function for GPU devices is providing a
+          command execution interface to clients. Client programs construct
+          command buffers containing references to previously allocated memory
+          objects, and then submit them to GEM. At that point, GEM takes care to
+          bind all the objects into the GTT, execute the buffer, and provide
+          necessary synchronization between clients accessing the same buffers.
+          This often involves evicting some objects from the GTT and re-binding
+          others (a fairly expensive operation), and providing relocation
+          support which hides fixed GTT offsets from clients. Clients must take
+          care not to submit command buffers that reference more objects than
+          can fit in the GTT; otherwise, GEM will reject them and no rendering
+          will occur. Similarly, if several objects in the buffer require fence
+          registers to be allocated for correct rendering (e.g. 2D blits on
+          pre-965 chips), care must be taken not to require more fence registers
+          than are available to the client. Such resource management should be
+          abstracted from the client in libdrm.
+        </para>
       </sect3>
     </sect2>
+  </sect1>
+
+  <!-- Internals: mode setting -->
 
+  <sect1 id="drm-mode-setting">
+    <title>Mode Setting</title>
+    <para>
+      Drivers must initialize the mode setting core by calling
+      <function>drm_mode_config_init</function> on the DRM device. The function
+      initializes the <structname>drm_device</structname>
+      <structfield>mode_config</structfield> field and never fails. Once done,
+      mode configuration must be setup by initializing the following fields.
+    </para>
+    <itemizedlist>
+      <listitem>
+        <synopsis>int min_width, min_height;
+int max_width, max_height;</synopsis>
+        <para>
+         Minimum and maximum width and height of the frame buffers in pixel
+         units.
+       </para>
+      </listitem>
+      <listitem>
+        <synopsis>struct drm_mode_config_funcs *funcs;</synopsis>
+       <para>Mode setting functions.</para>
+      </listitem>
+    </itemizedlist>
     <sect2>
-      <title>Output configuration</title>
+      <title>Frame Buffer Creation</title>
+      <synopsis>struct drm_framebuffer *(*fb_create)(struct drm_device *dev,
+                                    struct drm_file *file_priv,
+                                    struct drm_mode_fb_cmd2 *mode_cmd);</synopsis>
       <para>
-       The final initialization task is output configuration.  This involves:
-       <itemizedlist>
-         <listitem>
-           Finding and initializing the CRTCs, encoders, and connectors
-           for the device.
-         </listitem>
-         <listitem>
-           Creating an initial configuration.
-         </listitem>
-         <listitem>
-           Registering a framebuffer console driver.
-         </listitem>
-       </itemizedlist>
+        Frame buffers are abstract memory objects that provide a source of
+        pixels to scanout to a CRTC. Applications explicitly request the
+        creation of frame buffers through the DRM_IOCTL_MODE_ADDFB(2) ioctls and
+        receive an opaque handle that can be passed to the KMS CRTC control,
+        plane configuration and page flip functions.
+      </para>
+      <para>
+        Frame buffers rely on the underneath memory manager for low-level memory
+        operations. When creating a frame buffer applications pass a memory
+        handle (or a list of memory handles for multi-planar formats) through
+        the <parameter>drm_mode_fb_cmd2</parameter> argument. This document
+        assumes that the driver uses GEM, those handles thus reference GEM
+        objects.
+      </para>
+      <para>
+        Drivers must first validate the requested frame buffer parameters passed
+        through the mode_cmd argument. In particular this is where invalid
+        sizes, pixel formats or pitches can be caught.
+      </para>
+      <para>
+        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
+        <itemizedlist>
+          <listitem>
+            <synopsis>int (*create_handle)(struct drm_framebuffer *fb,
+                    struct drm_file *file_priv, unsigned int *handle);</synopsis>
+            <para>
+              Create a handle to the frame buffer underlying memory object. If
+              the frame buffer uses a multi-plane format, the handle will
+              reference the memory object associated with the first plane.
+            </para>
+            <para>
+              Drivers call <function>drm_gem_handle_create</function> to create
+              the handle.
+            </para>
+          </listitem>
+          <listitem>
+            <synopsis>void (*destroy)(struct drm_framebuffer *framebuffer);</synopsis>
+            <para>
+              Destroy the frame buffer object and frees all associated
+              resources. Drivers must call
+              <function>drm_framebuffer_cleanup</function> to free resources
+              allocated by the DRM core for the frame buffer object, and must
+              make sure to unreference all memory objects associated with the
+              frame buffer. Handles created by the
+              <methodname>create_handle</methodname> operation are released by
+              the DRM core.
+            </para>
+          </listitem>
+          <listitem>
+            <synopsis>int (*dirty)(struct drm_framebuffer *framebuffer,
+            struct drm_file *file_priv, unsigned flags, unsigned color,
+            struct drm_clip_rect *clips, unsigned num_clips);</synopsis>
+            <para>
+              This optional operation notifies the driver that a region of the
+              frame buffer has changed in response to a DRM_IOCTL_MODE_DIRTYFB
+              ioctl call.
+            </para>
+          </listitem>
+        </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>
+    </sect2>
+    <sect2>
+      <title>Output Polling</title>
+      <synopsis>void (*output_poll_changed)(struct drm_device *dev);</synopsis>
+      <para>
+        This operation notifies the driver that the status of one or more
+        connectors has changed. Drivers that use the fb helper can just call the
+        <function>drm_fb_helper_hotplug_event</function> function to handle this
+        operation.
+      </para>
+    </sect2>
+  </sect1>
+
+  <!-- Internals: kms initialization and cleanup -->
+
+  <sect1 id="drm-kms-init">
+    <title>KMS Initialization and Cleanup</title>
+    <para>
+      A KMS device is abstracted and exposed as a set of planes, CRTCs, encoders
+      and connectors. KMS drivers must thus create and initialize all those
+      objects at load time after initializing mode setting.
+    </para>
+    <sect2>
+      <title>CRTCs (struct <structname>drm_crtc</structname>)</title>
+      <para>
+        A CRTC is an abstraction representing a part of the chip that contains a
+       pointer to a scanout buffer. Therefore, the number of CRTCs available
+       determines how many independent scanout buffers can be active at any
+       given time. The CRTC structure contains several fields to support this:
+       a pointer to some video memory (abstracted as a frame buffer object), a
+       display mode, and an (x, y) offset into the video memory to support
+       panning or configurations where one piece of video memory spans multiple
+       CRTCs.
       </para>
       <sect3>
-       <title>Output discovery and initialization</title>
-       <para>
-         Several core functions exist to create CRTCs, encoders, and
-         connectors, namely: drm_crtc_init(), drm_connector_init(), and
-         drm_encoder_init(), along with several "helper" functions to
-         perform common tasks.
-       </para>
-       <para>
-         Connectors should be registered with sysfs once they've been
-         detected and initialized, using the
-         drm_sysfs_connector_add() function.  Likewise, when they're
-         removed from the system, they should be destroyed with
-         drm_sysfs_connector_remove().
-       </para>
-       <programlisting>
-<![CDATA[
+        <title>CRTC Initialization</title>
+        <para>
+          A KMS device must create and register at least one struct
+          <structname>drm_crtc</structname> instance. The instance is allocated
+          and zeroed by the driver, possibly as part of a larger structure, and
+          registered with a call to <function>drm_crtc_init</function> with a
+          pointer to CRTC functions.
+        </para>
+      </sect3>
+      <sect3>
+        <title>CRTC Operations</title>
+        <sect4>
+          <title>Set Configuration</title>
+          <synopsis>int (*set_config)(struct drm_mode_set *set);</synopsis>
+          <para>
+            Apply a new CRTC configuration to the device. The configuration
+            specifies a CRTC, a frame buffer to scan out from, a (x,y) position in
+            the frame buffer, a display mode and an array of connectors to drive
+            with the CRTC if possible.
+          </para>
+          <para>
+            If the frame buffer specified in the configuration is NULL, the driver
+            must detach all encoders connected to the CRTC and all connectors
+            attached to those encoders and disable them.
+          </para>
+          <para>
+            This operation is called with the mode config lock held.
+          </para>
+          <note><para>
+            FIXME: How should set_config interact with DPMS? If the CRTC is
+            suspended, should it be resumed?
+          </para></note>
+        </sect4>
+        <sect4>
+          <title>Page Flipping</title>
+          <synopsis>int (*page_flip)(struct drm_crtc *crtc, struct drm_framebuffer *fb,
+                   struct drm_pending_vblank_event *event);</synopsis>
+          <para>
+            Schedule a page flip to the given frame buffer for the CRTC. This
+            operation is called with the mode config mutex held.
+          </para>
+          <para>
+            Page flipping is a synchronization mechanism that replaces the frame
+            buffer being scanned out by the CRTC with a new frame buffer during
+            vertical blanking, avoiding tearing. When an application requests a page
+            flip the DRM core verifies that the new frame buffer is large enough to
+            be scanned out by  the CRTC in the currently configured mode and then
+            calls the CRTC <methodname>page_flip</methodname> operation with a
+            pointer to the new frame buffer.
+          </para>
+          <para>
+            The <methodname>page_flip</methodname> operation schedules a page flip.
+            Once any pending rendering targetting the new frame buffer has
+            completed, the CRTC will be reprogrammed to display that frame buffer
+            after the next vertical refresh. The operation must return immediately
+            without waiting for rendering or page flip to complete and must block
+            any new rendering to the frame buffer until the page flip completes.
+          </para>
+          <para>
+            If a page flip is already pending, the
+            <methodname>page_flip</methodname> operation must return
+            -<errorname>EBUSY</errorname>.
+          </para>
+          <para>
+            To synchronize page flip to vertical blanking the driver will likely
+            need to enable vertical blanking interrupts. It should call
+            <function>drm_vblank_get</function> for that purpose, and call
+            <function>drm_vblank_put</function> after the page flip completes.
+          </para>
+          <para>
+            If the application has requested to be notified when page flip completes
+            the <methodname>page_flip</methodname> operation will be called with a
+            non-NULL <parameter>event</parameter> argument pointing to a
+            <structname>drm_pending_vblank_event</structname> instance. Upon page
+            flip completion the driver must fill the
+            <parameter>event</parameter>::<structfield>event</structfield>
+            <structfield>sequence</structfield>, <structfield>tv_sec</structfield>
+            and <structfield>tv_usec</structfield> fields with the associated
+            vertical blanking count and timestamp, add the event to the
+            <parameter>drm_file</parameter> list of events to be signaled, and wake
+            up any waiting process. This can be performed with
+            <programlisting><![CDATA[
+            struct timeval now;
+
+            event->event.sequence = drm_vblank_count_and_time(..., &now);
+            event->event.tv_sec = now.tv_sec;
+            event->event.tv_usec = now.tv_usec;
+
+            spin_lock_irqsave(&dev->event_lock, flags);
+            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);
+            ]]></programlisting>
+          </para>
+          <note><para>
+            FIXME: Could drivers that don't need to wait for rendering to complete
+            just add the event to <literal>dev-&gt;vblank_event_list</literal> and
+            let the DRM core handle everything, as for "normal" vertical blanking
+            events?
+          </para></note>
+          <para>
+            While waiting for the page flip to complete, the
+            <literal>event-&gt;base.link</literal> list head can be used freely by
+            the driver to store the pending event in a driver-specific list.
+          </para>
+          <para>
+            If the file handle is closed before the event is signaled, drivers must
+            take care to destroy the event in their
+            <methodname>preclose</methodname> operation (and, if needed, call
+            <function>drm_vblank_put</function>).
+          </para>
+        </sect4>
+        <sect4>
+          <title>Miscellaneous</title>
+          <itemizedlist>
+            <listitem>
+              <synopsis>void (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
+                        uint32_t start, uint32_t size);</synopsis>
+              <para>
+                Apply a gamma table to the device. The operation is optional.
+              </para>
+            </listitem>
+            <listitem>
+              <synopsis>void (*destroy)(struct drm_crtc *crtc);</synopsis>
+              <para>
+                Destroy the CRTC when not needed anymore. See
+                <xref linkend="drm-kms-init"/>.
+              </para>
+            </listitem>
+          </itemizedlist>
+        </sect4>
+      </sect3>
+    </sect2>
+    <sect2>
+      <title>Planes (struct <structname>drm_plane</structname>)</title>
+      <para>
+        A plane represents an image source that can be blended with or overlayed
+       on top of a CRTC during the scanout process. Planes are associated with
+       a frame buffer to crop a portion of the image memory (source) and
+       optionally scale it to a destination size. The result is then blended
+       with or overlayed on top of a CRTC.
+      </para>
+      <sect3>
+        <title>Plane Initialization</title>
+        <para>
+          Planes are optional. To create a plane, a KMS drivers allocates and
+          zeroes an instances of struct <structname>drm_plane</structname>
+          (possibly as part of a larger structure) and registers it with a call
+          to <function>drm_plane_init</function>. The function takes a bitmask
+          of the CRTCs that can be associated with the plane, a pointer to the
+          plane functions and a list of format supported formats.
+        </para>
+      </sect3>
+      <sect3>
+        <title>Plane Operations</title>
+        <itemizedlist>
+          <listitem>
+            <synopsis>int (*update_plane)(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);</synopsis>
+            <para>
+              Enable and configure the plane to use the given CRTC and frame buffer.
+            </para>
+            <para>
+              The source rectangle in frame buffer memory coordinates is given by
+              the <parameter>src_x</parameter>, <parameter>src_y</parameter>,
+              <parameter>src_w</parameter> and <parameter>src_h</parameter>
+              parameters (as 16.16 fixed point values). Devices that don't support
+              subpixel plane coordinates can ignore the fractional part.
+            </para>
+            <para>
+              The destination rectangle in CRTC coordinates is given by the
+              <parameter>crtc_x</parameter>, <parameter>crtc_y</parameter>,
+              <parameter>crtc_w</parameter> and <parameter>crtc_h</parameter>
+              parameters (as integer values). Devices scale the source rectangle to
+              the destination rectangle. If scaling is not supported, and the source
+              rectangle size doesn't match the destination rectangle size, the
+              driver must return a -<errorname>EINVAL</errorname> error.
+            </para>
+          </listitem>
+          <listitem>
+            <synopsis>int (*disable_plane)(struct drm_plane *plane);</synopsis>
+            <para>
+              Disable the plane. The DRM core calls this method in response to a
+              DRM_IOCTL_MODE_SETPLANE ioctl call with the frame buffer ID set to 0.
+              Disabled planes must not be processed by the CRTC.
+            </para>
+          </listitem>
+          <listitem>
+            <synopsis>void (*destroy)(struct drm_plane *plane);</synopsis>
+            <para>
+              Destroy the plane when not needed anymore. See
+              <xref linkend="drm-kms-init"/>.
+            </para>
+          </listitem>
+        </itemizedlist>
+      </sect3>
+    </sect2>
+    <sect2>
+      <title>Encoders (struct <structname>drm_encoder</structname>)</title>
+      <para>
+        An encoder takes pixel data from a CRTC and converts it to a format
+       suitable for any attached connectors. On some devices, it may be
+       possible to have a CRTC send data to more than one encoder. In that
+       case, both encoders would receive data from the same scanout buffer,
+       resulting in a "cloned" display configuration across the connectors
+       attached to each encoder.
+      </para>
+      <sect3>
+        <title>Encoder Initialization</title>
+        <para>
+          As for CRTCs, a KMS driver must create, initialize and register at
+          least one struct <structname>drm_encoder</structname> instance. The
+          instance is allocated and zeroed by the driver, possibly as part of a
+          larger structure.
+        </para>
+        <para>
+          Drivers must initialize the struct <structname>drm_encoder</structname>
+          <structfield>possible_crtcs</structfield> and
+          <structfield>possible_clones</structfield> fields before registering the
+          encoder. Both fields are bitmasks of respectively the CRTCs that the
+          encoder can be connected to, and sibling encoders candidate for cloning.
+        </para>
+        <para>
+          After being initialized, the encoder must be registered with a call to
+          <function>drm_encoder_init</function>. The function takes a pointer to
+          the encoder functions and an encoder type. Supported types are
+          <itemizedlist>
+            <listitem>
+              DRM_MODE_ENCODER_DAC for VGA and analog on DVI-I/DVI-A
+              </listitem>
+            <listitem>
+              DRM_MODE_ENCODER_TMDS for DVI, HDMI and (embedded) DisplayPort
+            </listitem>
+            <listitem>
+              DRM_MODE_ENCODER_LVDS for display panels
+            </listitem>
+            <listitem>
+              DRM_MODE_ENCODER_TVDAC for TV output (Composite, S-Video, Component,
+              SCART)
+            </listitem>
+            <listitem>
+              DRM_MODE_ENCODER_VIRTUAL for virtual machine displays
+            </listitem>
+          </itemizedlist>
+        </para>
+        <para>
+          Encoders must be attached to a CRTC to be used. DRM drivers leave
+          encoders unattached at initialization time. Applications (or the fbdev
+          compatibility layer when implemented) are responsible for attaching the
+          encoders they want to use to a CRTC.
+        </para>
+      </sect3>
+      <sect3>
+        <title>Encoder Operations</title>
+        <itemizedlist>
+          <listitem>
+            <synopsis>void (*destroy)(struct drm_encoder *encoder);</synopsis>
+            <para>
+              Called to destroy the encoder when not needed anymore. See
+              <xref linkend="drm-kms-init"/>.
+            </para>
+          </listitem>
+        </itemizedlist>
+      </sect3>
+    </sect2>
+    <sect2>
+      <title>Connectors (struct <structname>drm_connector</structname>)</title>
+      <para>
+        A connector is the final destination for pixel data on a device, and
+       usually connects directly to an external display device like a monitor
+       or laptop panel. A connector can only be attached to one encoder at a
+       time. The connector is also the structure where information about the
+       attached display is kept, so it contains fields for display data, EDID
+       data, DPMS &amp; connection status, and information about modes
+       supported on the attached displays.
+      </para>
+      <sect3>
+        <title>Connector Initialization</title>
+        <para>
+          Finally a KMS driver must create, initialize, register and attach at
+          least one struct <structname>drm_connector</structname> instance. The
+          instance is created as other KMS objects and initialized by setting the
+          following fields.
+        </para>
+        <variablelist>
+          <varlistentry>
+            <term><structfield>interlace_allowed</structfield></term>
+            <listitem><para>
+              Whether the connector can handle interlaced modes.
+            </para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term><structfield>doublescan_allowed</structfield></term>
+            <listitem><para>
+              Whether the connector can handle doublescan.
+            </para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term><structfield>display_info
+            </structfield></term>
+            <listitem><para>
+              Display information is filled from EDID information when a display
+              is detected. For non hot-pluggable displays such as flat panels in
+              embedded systems, the driver should initialize the
+              <structfield>display_info</structfield>.<structfield>width_mm</structfield>
+              and
+              <structfield>display_info</structfield>.<structfield>height_mm</structfield>
+              fields with the physical size of the display.
+            </para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term id="drm-kms-connector-polled"><structfield>polled</structfield></term>
+            <listitem><para>
+              Connector polling mode, a combination of
+              <variablelist>
+                <varlistentry>
+                  <term>DRM_CONNECTOR_POLL_HPD</term>
+                  <listitem><para>
+                    The connector generates hotplug events and doesn't need to be
+                    periodically polled. The CONNECT and DISCONNECT flags must not
+                    be set together with the HPD flag.
+                  </para></listitem>
+                </varlistentry>
+                <varlistentry>
+                  <term>DRM_CONNECTOR_POLL_CONNECT</term>
+                  <listitem><para>
+                    Periodically poll the connector for connection.
+                  </para></listitem>
+                </varlistentry>
+                <varlistentry>
+                  <term>DRM_CONNECTOR_POLL_DISCONNECT</term>
+                  <listitem><para>
+                    Periodically poll the connector for disconnection.
+                  </para></listitem>
+                </varlistentry>
+              </variablelist>
+              Set to 0 for connectors that don't support connection status
+              discovery.
+            </para></listitem>
+          </varlistentry>
+        </variablelist>
+        <para>
+          The connector is then registered with a call to
+          <function>drm_connector_init</function> with a pointer to the connector
+          functions and a connector type, and exposed through sysfs with a call to
+          <function>drm_sysfs_connector_add</function>.
+        </para>
+        <para>
+          Supported connector types are
+          <itemizedlist>
+            <listitem>DRM_MODE_CONNECTOR_VGA</listitem>
+            <listitem>DRM_MODE_CONNECTOR_DVII</listitem>
+            <listitem>DRM_MODE_CONNECTOR_DVID</listitem>
+            <listitem>DRM_MODE_CONNECTOR_DVIA</listitem>
+            <listitem>DRM_MODE_CONNECTOR_Composite</listitem>
+            <listitem>DRM_MODE_CONNECTOR_SVIDEO</listitem>
+            <listitem>DRM_MODE_CONNECTOR_LVDS</listitem>
+            <listitem>DRM_MODE_CONNECTOR_Component</listitem>
+            <listitem>DRM_MODE_CONNECTOR_9PinDIN</listitem>
+            <listitem>DRM_MODE_CONNECTOR_DisplayPort</listitem>
+            <listitem>DRM_MODE_CONNECTOR_HDMIA</listitem>
+            <listitem>DRM_MODE_CONNECTOR_HDMIB</listitem>
+            <listitem>DRM_MODE_CONNECTOR_TV</listitem>
+            <listitem>DRM_MODE_CONNECTOR_eDP</listitem>
+            <listitem>DRM_MODE_CONNECTOR_VIRTUAL</listitem>
+          </itemizedlist>
+        </para>
+        <para>
+          Connectors must be attached to an encoder to be used. For devices that
+          map connectors to encoders 1:1, the connector should be attached at
+          initialization time with a call to
+          <function>drm_mode_connector_attach_encoder</function>. The driver must
+          also set the <structname>drm_connector</structname>
+          <structfield>encoder</structfield> field to point to the attached
+          encoder.
+        </para>
+        <para>
+          Finally, drivers must initialize the connectors state change detection
+          with a call to <function>drm_kms_helper_poll_init</function>. If at
+          least one connector is pollable but can't generate hotplug interrupts
+          (indicated by the DRM_CONNECTOR_POLL_CONNECT and
+          DRM_CONNECTOR_POLL_DISCONNECT connector flags), a delayed work will
+          automatically be queued to periodically poll for changes. Connectors
+          that can generate hotplug interrupts must be marked with the
+          DRM_CONNECTOR_POLL_HPD flag instead, and their interrupt handler must
+          call <function>drm_helper_hpd_irq_event</function>. The function will
+          queue a delayed work to check the state of all connectors, but no
+          periodic polling will be done.
+        </para>
+      </sect3>
+      <sect3>
+        <title>Connector Operations</title>
+        <note><para>
+          Unless otherwise state, all operations are mandatory.
+        </para></note>
+        <sect4>
+          <title>DPMS</title>
+          <synopsis>void (*dpms)(struct drm_connector *connector, int mode);</synopsis>
+          <para>
+            The DPMS operation sets the power state of a connector. The mode
+            argument is one of
+            <itemizedlist>
+              <listitem><para>DRM_MODE_DPMS_ON</para></listitem>
+              <listitem><para>DRM_MODE_DPMS_STANDBY</para></listitem>
+              <listitem><para>DRM_MODE_DPMS_SUSPEND</para></listitem>
+              <listitem><para>DRM_MODE_DPMS_OFF</para></listitem>
+            </itemizedlist>
+          </para>
+          <para>
+            In all but DPMS_ON mode the encoder to which the connector is attached
+            should put the display in low-power mode by driving its signals
+            appropriately. If more than one connector is attached to the encoder
+            care should be taken not to change the power state of other displays as
+            a side effect. Low-power mode should be propagated to the encoders and
+            CRTCs when all related connectors are put in low-power mode.
+          </para>
+        </sect4>
+        <sect4>
+          <title>Modes</title>
+          <synopsis>int (*fill_modes)(struct drm_connector *connector, uint32_t max_width,
+                      uint32_t max_height);</synopsis>
+          <para>
+            Fill the mode list with all supported modes for the connector. If the
+            <parameter>max_width</parameter> and <parameter>max_height</parameter>
+            arguments are non-zero, the implementation must ignore all modes wider
+            than <parameter>max_width</parameter> or higher than
+            <parameter>max_height</parameter>.
+          </para>
+          <para>
+            The connector must also fill in this operation its
+            <structfield>display_info</structfield>
+            <structfield>width_mm</structfield> and
+            <structfield>height_mm</structfield> fields with the connected display
+            physical size in millimeters. The fields should be set to 0 if the value
+            isn't known or is not applicable (for instance for projector devices).
+          </para>
+        </sect4>
+        <sect4>
+          <title>Connection Status</title>
+          <para>
+            The connection status is updated through polling or hotplug events when
+            supported (see <xref linkend="drm-kms-connector-polled"/>). The status
+            value is reported to userspace through ioctls and must not be used
+            inside the driver, as it only gets initialized by a call to
+            <function>drm_mode_getconnector</function> from userspace.
+          </para>
+          <synopsis>enum drm_connector_status (*detect)(struct drm_connector *connector,
+                                        bool force);</synopsis>
+          <para>
+            Check to see if anything is attached to the connector. The
+            <parameter>force</parameter> parameter is set to false whilst polling or
+            to true when checking the connector due to user request.
+            <parameter>force</parameter> can be used by the driver to avoid
+            expensive, destructive operations during automated probing.
+          </para>
+          <para>
+            Return connector_status_connected if something is connected to the
+            connector, connector_status_disconnected if nothing is connected and
+            connector_status_unknown if the connection state isn't known.
+          </para>
+          <para>
+            Drivers should only return connector_status_connected if the connection
+            status has really been probed as connected. Connectors that can't detect
+            the connection status, or failed connection status probes, should return
+            connector_status_unknown.
+          </para>
+        </sect4>
+        <sect4>
+          <title>Miscellaneous</title>
+          <itemizedlist>
+            <listitem>
+              <synopsis>void (*destroy)(struct drm_connector *connector);</synopsis>
+              <para>
+                Destroy the connector when not needed anymore. See
+                <xref linkend="drm-kms-init"/>.
+              </para>
+            </listitem>
+          </itemizedlist>
+        </sect4>
+      </sect3>
+    </sect2>
+    <sect2>
+      <title>Cleanup</title>
+      <para>
+        The DRM core manages its objects' lifetime. When an object is not needed
+       anymore the core calls its destroy function, which must clean up and
+       free every resource allocated for the object. Every
+       <function>drm_*_init</function> call must be matched with a
+       corresponding <function>drm_*_cleanup</function> call to cleanup CRTCs
+       (<function>drm_crtc_cleanup</function>), planes
+       (<function>drm_plane_cleanup</function>), encoders
+       (<function>drm_encoder_cleanup</function>) and connectors
+       (<function>drm_connector_cleanup</function>). Furthermore, connectors
+       that have been added to sysfs must be removed by a call to
+       <function>drm_sysfs_connector_remove</function> before calling
+       <function>drm_connector_cleanup</function>.
+      </para>
+      <para>
+        Connectors state change detection must be cleanup up with a call to
+       <function>drm_kms_helper_poll_fini</function>.
+      </para>
+    </sect2>
+    <sect2>
+      <title>Output discovery and initialization example</title>
+      <programlisting><![CDATA[
 void intel_crt_init(struct drm_device *dev)
 {
        struct drm_connector *connector;
@@ -556,252 +1610,741 @@ void intel_crt_init(struct drm_device *dev)
        drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
 
        drm_sysfs_connector_add(connector);
-}
-]]>
-       </programlisting>
-       <para>
-         In the example above (again, taken from the i915 driver), a
-         CRT connector and encoder combination is created.  A device-specific
-         i2c bus is also created for fetching EDID data and
-         performing monitor detection.  Once the process is complete,
-         the new connector is registered with sysfs to make its
-         properties available to applications.
-       </para>
-       <sect4>
-         <title>Helper functions and core functions</title>
-         <para>
-           Since many PC-class graphics devices have similar display output
-           designs, the DRM provides a set of helper functions to make
-           output management easier.  The core helper routines handle
-           encoder re-routing and the disabling of unused functions following
-           mode setting.  Using the helpers is optional, but recommended for
-           devices with PC-style architectures (i.e. a set of display planes
-           for feeding pixels to encoders which are in turn routed to
-           connectors).  Devices with more complex requirements needing
-           finer grained management may opt to use the core callbacks
-           directly.
-         </para>
-         <para>
-           [Insert typical diagram here.]  [Insert OMAP style config here.]
-         </para>
-       </sect4>
-       <para>
-         Each encoder object needs to provide:
-         <itemizedlist>
-           <listitem>
-             A DPMS (basically on/off) function.
-           </listitem>
-           <listitem>
-             A mode-fixup function (for converting requested modes into
-             native hardware timings).
-           </listitem>
-           <listitem>
-             Functions (prepare, set, and commit) for use by the core DRM
-             helper functions.
-           </listitem>
-         </itemizedlist>
-         Connector helpers need to provide functions (mode-fetch, validity,
-         and encoder-matching) for returning an ideal encoder for a given
-         connector.  The core connector functions include a DPMS callback,
-         save/restore routines (deprecated), detection, mode probing,
-         property handling, and cleanup functions.
-       </para>
-<!--!Edrivers/char/drm/drm_crtc.h-->
-<!--!Edrivers/char/drm/drm_crtc.c-->
-<!--!Edrivers/char/drm/drm_crtc_helper.c-->
-      </sect3>
+}]]></programlisting>
+      <para>
+        In the example above (taken from the i915 driver), a CRTC, connector and
+        encoder combination is created. A device-specific i2c bus is also
+        created for fetching EDID data and performing monitor detection. Once
+        the process is complete, the new connector is registered with sysfs to
+        make its properties available to applications.
+      </para>
     </sect2>
   </sect1>
 
-  <!-- Internals: vblank handling -->
+  <!-- Internals: mid-layer helper functions -->
 
   <sect1>
-    <title>VBlank event handling</title>
+    <title>Mid-layer Helper Functions</title>
     <para>
-      The DRM core exposes two vertical blank related ioctls:
-      <variablelist>
-        <varlistentry>
-          <term>DRM_IOCTL_WAIT_VBLANK</term>
-          <listitem>
-            <para>
-              This takes a struct drm_wait_vblank structure as its argument,
-              and it is used to block or request a signal when a specified
-              vblank event occurs.
-            </para>
-          </listitem>
-        </varlistentry>
-        <varlistentry>
-          <term>DRM_IOCTL_MODESET_CTL</term>
-          <listitem>
-            <para>
-              This should be called by application level drivers before and
-              after mode setting, since on many devices the vertical blank
-              counter is reset at that time.  Internally, the DRM snapshots
-              the last vblank count when the ioctl is called with the
-              _DRM_PRE_MODESET command, so that the counter won't go backwards
-              (which is dealt with when _DRM_POST_MODESET is used).
-            </para>
-          </listitem>
-        </varlistentry>
-      </variablelist>
-<!--!Edrivers/char/drm/drm_irq.c-->
+      The CRTC, encoder and connector functions provided by the drivers
+      implement the DRM API. They're called by the DRM core and ioctl handlers
+      to handle device state changes and configuration request. As implementing
+      those functions often requires logic not specific to drivers, mid-layer
+      helper functions are available to avoid duplicating boilerplate code.
+    </para>
+    <para>
+      The DRM core contains one mid-layer implementation. The mid-layer provides
+      implementations of several CRTC, encoder and connector functions (called
+      from the top of the mid-layer) that pre-process requests and call
+      lower-level functions provided by the driver (at the bottom of the
+      mid-layer). For instance, the
+      <function>drm_crtc_helper_set_config</function> function can be used to
+      fill the struct <structname>drm_crtc_funcs</structname>
+      <structfield>set_config</structfield> field. When called, it will split
+      the <methodname>set_config</methodname> operation in smaller, simpler
+      operations and call the driver to handle them.
     </para>
     <para>
-      To support the functions above, the DRM core provides several
-      helper functions for tracking vertical blank counters, and
-      requires drivers to provide several callbacks:
-      get_vblank_counter(), enable_vblank() and disable_vblank().  The
-      core uses get_vblank_counter() to keep the counter accurate
-      across interrupt disable periods.  It should return the current
-      vertical blank event count, which is often tracked in a device
-      register.  The enable and disable vblank callbacks should enable
-      and disable vertical blank interrupts, respectively.  In the
-      absence of DRM clients waiting on vblank events, the core DRM
-      code uses the disable_vblank() function to disable
-      interrupts, which saves power.  They are re-enabled again when
-      a client calls the vblank wait ioctl above.
+      To use the mid-layer, drivers call <function>drm_crtc_helper_add</function>,
+      <function>drm_encoder_helper_add</function> and
+      <function>drm_connector_helper_add</function> functions to install their
+      mid-layer bottom operations handlers, and fill the
+      <structname>drm_crtc_funcs</structname>,
+      <structname>drm_encoder_funcs</structname> and
+      <structname>drm_connector_funcs</structname> structures with pointers to
+      the mid-layer top API functions. Installing the mid-layer bottom operation
+      handlers is best done right after registering the corresponding KMS object.
     </para>
     <para>
-      A device that doesn't provide a count register may simply use an
-      internal atomic counter incremented on every vertical blank
-      interrupt (and then treat the enable_vblank() and disable_vblank()
-      callbacks as no-ops).
+      The mid-layer is not split between CRTC, encoder and connector operations.
+      To use it, a driver must provide bottom functions for all of the three KMS
+      entities.
     </para>
+    <sect2>
+      <title>Helper Functions</title>
+      <itemizedlist>
+        <listitem>
+          <synopsis>int drm_crtc_helper_set_config(struct drm_mode_set *set);</synopsis>
+          <para>
+            The <function>drm_crtc_helper_set_config</function> helper function
+            is a CRTC <methodname>set_config</methodname> implementation. It
+            first tries to locate the best encoder for each connector by calling
+            the connector <methodname>best_encoder</methodname> helper
+            operation.
+          </para>
+          <para>
+            After locating the appropriate encoders, the helper function will
+            call the <methodname>mode_fixup</methodname> encoder and CRTC helper
+            operations to adjust the requested mode, or reject it completely in
+            which case an error will be returned to the application. If the new
+            configuration after mode adjustment is identical to the current
+            configuration the helper function will return without performing any
+            other operation.
+          </para>
+          <para>
+            If the adjusted mode is identical to the current mode but changes to
+            the frame buffer need to be applied, the
+            <function>drm_crtc_helper_set_config</function> function will call
+            the CRTC <methodname>mode_set_base</methodname> helper operation. If
+            the adjusted mode differs from the current mode, or if the
+            <methodname>mode_set_base</methodname> helper operation is not
+            provided, the helper function performs a full mode set sequence by
+            calling the <methodname>prepare</methodname>,
+            <methodname>mode_set</methodname> and
+            <methodname>commit</methodname> CRTC and encoder helper operations,
+            in that order.
+          </para>
+        </listitem>
+        <listitem>
+          <synopsis>void drm_helper_connector_dpms(struct drm_connector *connector, int mode);</synopsis>
+          <para>
+            The <function>drm_helper_connector_dpms</function> helper function
+            is a connector <methodname>dpms</methodname> implementation that
+            tracks power state of connectors. To use the function, drivers must
+            provide <methodname>dpms</methodname> helper operations for CRTCs
+            and encoders to apply the DPMS state to the device.
+          </para>
+          <para>
+            The mid-layer doesn't track the power state of CRTCs and encoders.
+            The <methodname>dpms</methodname> helper operations can thus be
+            called with a mode identical to the currently active mode.
+          </para>
+        </listitem>
+        <listitem>
+          <synopsis>int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
+                                            uint32_t maxX, uint32_t maxY);</synopsis>
+          <para>
+            The <function>drm_helper_probe_single_connector_modes</function> helper
+            function is a connector <methodname>fill_modes</methodname>
+            implementation that updates the connection status for the connector
+            and then retrieves a list of modes by calling the connector
+            <methodname>get_modes</methodname> helper operation.
+          </para>
+          <para>
+            The function filters out modes larger than
+            <parameter>max_width</parameter> and <parameter>max_height</parameter>
+            if specified. It then calls the connector
+            <methodname>mode_valid</methodname> helper operation for  each mode in
+            the probed list to check whether the mode is valid for the connector.
+          </para>
+        </listitem>
+      </itemizedlist>
+    </sect2>
+    <sect2>
+      <title>CRTC Helper Operations</title>
+      <itemizedlist>
+        <listitem id="drm-helper-crtc-mode-fixup">
+          <synopsis>bool (*mode_fixup)(struct drm_crtc *crtc,
+                       const struct drm_display_mode *mode,
+                       struct drm_display_mode *adjusted_mode);</synopsis>
+          <para>
+            Let CRTCs adjust the requested mode or reject it completely. This
+            operation returns true if the mode is accepted (possibly after being
+            adjusted) or false if it is rejected.
+          </para>
+          <para>
+            The <methodname>mode_fixup</methodname> operation should reject the
+            mode if it can't reasonably use it. The definition of "reasonable"
+            is currently fuzzy in this context. One possible behaviour would be
+            to set the adjusted mode to the panel timings when a fixed-mode
+            panel is used with hardware capable of scaling. Another behaviour
+            would be to accept any input mode and adjust it to the closest mode
+            supported by the hardware (FIXME: This needs to be clarified).
+          </para>
+        </listitem>
+        <listitem>
+          <synopsis>int (*mode_set_base)(struct drm_crtc *crtc, int x, int y,
+                     struct drm_framebuffer *old_fb)</synopsis>
+          <para>
+            Move the CRTC on the current frame buffer (stored in
+            <literal>crtc-&gt;fb</literal>) to position (x,y). Any of the frame
+            buffer, x position or y position may have been modified.
+          </para>
+          <para>
+            This helper operation is optional. If not provided, the
+            <function>drm_crtc_helper_set_config</function> function will fall
+            back to the <methodname>mode_set</methodname> helper operation.
+          </para>
+          <note><para>
+            FIXME: Why are x and y passed as arguments, as they can be accessed
+            through <literal>crtc-&gt;x</literal> and
+            <literal>crtc-&gt;y</literal>?
+          </para></note>
+        </listitem>
+        <listitem>
+          <synopsis>void (*prepare)(struct drm_crtc *crtc);</synopsis>
+          <para>
+            Prepare the CRTC for mode setting. This operation is called after
+            validating the requested mode. Drivers use it to perform
+            device-specific operations required before setting the new mode.
+          </para>
+        </listitem>
+        <listitem>
+          <synopsis>int (*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);</synopsis>
+          <para>
+            Set a new mode, position and frame buffer. Depending on the device
+            requirements, the mode can be stored internally by the driver and
+            applied in the <methodname>commit</methodname> operation, or
+            programmed to the hardware immediately.
+          </para>
+          <para>
+            The <methodname>mode_set</methodname> operation returns 0 on success
+           or a negative error code if an error occurs.
+          </para>
+        </listitem>
+        <listitem>
+          <synopsis>void (*commit)(struct drm_crtc *crtc);</synopsis>
+          <para>
+            Commit a mode. This operation is called after setting the new mode.
+            Upon return the device must use the new mode and be fully
+            operational.
+          </para>
+        </listitem>
+      </itemizedlist>
+    </sect2>
+    <sect2>
+      <title>Encoder Helper Operations</title>
+      <itemizedlist>
+        <listitem>
+          <synopsis>bool (*mode_fixup)(struct drm_encoder *encoder,
+                       const struct drm_display_mode *mode,
+                       struct drm_display_mode *adjusted_mode);</synopsis>
+          <note><para>
+            FIXME: The mode argument be const, but the i915 driver modifies
+            mode-&gt;clock in <function>intel_dp_mode_fixup</function>.
+          </para></note>
+          <para>
+            Let encoders adjust the requested mode or reject it completely. This
+            operation returns true if the mode is accepted (possibly after being
+            adjusted) or false if it is rejected. See the
+            <link linkend="drm-helper-crtc-mode-fixup">mode_fixup CRTC helper
+            operation</link> for an explanation of the allowed adjustments.
+          </para>
+        </listitem>
+        <listitem>
+          <synopsis>void (*prepare)(struct drm_encoder *encoder);</synopsis>
+          <para>
+            Prepare the encoder for mode setting. This operation is called after
+            validating the requested mode. Drivers use it to perform
+            device-specific operations required before setting the new mode.
+          </para>
+        </listitem>
+        <listitem>
+          <synopsis>void (*mode_set)(struct drm_encoder *encoder,
+                 struct drm_display_mode *mode,
+                 struct drm_display_mode *adjusted_mode);</synopsis>
+          <para>
+            Set a new mode. Depending on the device requirements, the mode can
+            be stored internally by the driver and applied in the
+            <methodname>commit</methodname> operation, or programmed to the
+            hardware immediately.
+          </para>
+        </listitem>
+        <listitem>
+          <synopsis>void (*commit)(struct drm_encoder *encoder);</synopsis>
+          <para>
+            Commit a mode. This operation is called after setting the new mode.
+            Upon return the device must use the new mode and be fully
+            operational.
+          </para>
+        </listitem>
+      </itemizedlist>
+    </sect2>
+    <sect2>
+      <title>Connector Helper Operations</title>
+      <itemizedlist>
+        <listitem>
+          <synopsis>struct drm_encoder *(*best_encoder)(struct drm_connector *connector);</synopsis>
+          <para>
+            Return a pointer to the best encoder for the connecter. Device that
+            map connectors to encoders 1:1 simply return the pointer to the
+            associated encoder. This operation is mandatory.
+          </para>
+        </listitem>
+        <listitem>
+          <synopsis>int (*get_modes)(struct drm_connector *connector);</synopsis>
+          <para>
+            Fill the connector's <structfield>probed_modes</structfield> list
+            by parsing EDID data with <function>drm_add_edid_modes</function> or
+            calling <function>drm_mode_probed_add</function> directly for every
+            supported mode and return the number of modes it has detected. This
+            operation is mandatory.
+          </para>
+          <para>
+            When adding modes manually the driver creates each mode with a call to
+            <function>drm_mode_create</function> and must fill the following fields.
+            <itemizedlist>
+              <listitem>
+                <synopsis>__u32 type;</synopsis>
+                <para>
+                  Mode type bitmask, a combination of
+                  <variablelist>
+                    <varlistentry>
+                      <term>DRM_MODE_TYPE_BUILTIN</term>
+                      <listitem><para>not used?</para></listitem>
+                    </varlistentry>
+                    <varlistentry>
+                      <term>DRM_MODE_TYPE_CLOCK_C</term>
+                      <listitem><para>not used?</para></listitem>
+                    </varlistentry>
+                    <varlistentry>
+                      <term>DRM_MODE_TYPE_CRTC_C</term>
+                      <listitem><para>not used?</para></listitem>
+                    </varlistentry>
+                    <varlistentry>
+                      <term>
+        DRM_MODE_TYPE_PREFERRED - The preferred mode for the connector
+                      </term>
+                      <listitem>
+                        <para>not used?</para>
+                      </listitem>
+                    </varlistentry>
+                    <varlistentry>
+                      <term>DRM_MODE_TYPE_DEFAULT</term>
+                      <listitem><para>not used?</para></listitem>
+                    </varlistentry>
+                    <varlistentry>
+                      <term>DRM_MODE_TYPE_USERDEF</term>
+                      <listitem><para>not used?</para></listitem>
+                    </varlistentry>
+                    <varlistentry>
+                      <term>DRM_MODE_TYPE_DRIVER</term>
+                      <listitem>
+                        <para>
+                          The mode has been created by the driver (as opposed to
+                          to user-created modes).
+                        </para>
+                      </listitem>
+                    </varlistentry>
+                  </variablelist>
+                  Drivers must set the DRM_MODE_TYPE_DRIVER bit for all modes they
+                  create, and set the DRM_MODE_TYPE_PREFERRED bit for the preferred
+                  mode.
+                </para>
+              </listitem>
+              <listitem>
+                <synopsis>__u32 clock;</synopsis>
+                <para>Pixel clock frequency in kHz unit</para>
+              </listitem>
+              <listitem>
+                <synopsis>__u16 hdisplay, hsync_start, hsync_end, htotal;
+    __u16 vdisplay, vsync_start, vsync_end, vtotal;</synopsis>
+                <para>Horizontal and vertical timing information</para>
+                <screen><![CDATA[
+             Active                 Front           Sync           Back
+             Region                 Porch                          Porch
+    <-----------------------><----------------><-------------><-------------->
+
+      //////////////////////|
+     ////////////////////// |
+    //////////////////////  |..................               ................
+                                               _______________
+
+    <----- [hv]display ----->
+    <------------- [hv]sync_start ------------>
+    <--------------------- [hv]sync_end --------------------->
+    <-------------------------------- [hv]total ----------------------------->
+]]></screen>
+              </listitem>
+              <listitem>
+                <synopsis>__u16 hskew;
+    __u16 vscan;</synopsis>
+                <para>Unknown</para>
+              </listitem>
+              <listitem>
+                <synopsis>__u32 flags;</synopsis>
+                <para>
+                  Mode flags, a combination of
+                  <variablelist>
+                    <varlistentry>
+                      <term>DRM_MODE_FLAG_PHSYNC</term>
+                      <listitem><para>
+                        Horizontal sync is active high
+                      </para></listitem>
+                    </varlistentry>
+                    <varlistentry>
+                      <term>DRM_MODE_FLAG_NHSYNC</term>
+                      <listitem><para>
+                        Horizontal sync is active low
+                      </para></listitem>
+                    </varlistentry>
+                    <varlistentry>
+                      <term>DRM_MODE_FLAG_PVSYNC</term>
+                      <listitem><para>
+                        Vertical sync is active high
+                      </para></listitem>
+                    </varlistentry>
+                    <varlistentry>
+                      <term>DRM_MODE_FLAG_NVSYNC</term>
+                      <listitem><para>
+                        Vertical sync is active low
+                      </para></listitem>
+                    </varlistentry>
+                    <varlistentry>
+                      <term>DRM_MODE_FLAG_INTERLACE</term>
+                      <listitem><para>
+                        Mode is interlaced
+                      </para></listitem>
+                    </varlistentry>
+                    <varlistentry>
+                      <term>DRM_MODE_FLAG_DBLSCAN</term>
+                      <listitem><para>
+                        Mode uses doublescan
+                      </para></listitem>
+                    </varlistentry>
+                    <varlistentry>
+                      <term>DRM_MODE_FLAG_CSYNC</term>
+                      <listitem><para>
+                        Mode uses composite sync
+                      </para></listitem>
+                    </varlistentry>
+                    <varlistentry>
+                      <term>DRM_MODE_FLAG_PCSYNC</term>
+                      <listitem><para>
+                        Composite sync is active high
+                      </para></listitem>
+                    </varlistentry>
+                    <varlistentry>
+                      <term>DRM_MODE_FLAG_NCSYNC</term>
+                      <listitem><para>
+                        Composite sync is active low
+                      </para></listitem>
+                    </varlistentry>
+                    <varlistentry>
+                      <term>DRM_MODE_FLAG_HSKEW</term>
+                      <listitem><para>
+                        hskew provided (not used?)
+                      </para></listitem>
+                    </varlistentry>
+                    <varlistentry>
+                      <term>DRM_MODE_FLAG_BCAST</term>
+                      <listitem><para>
+                        not used?
+                      </para></listitem>
+                    </varlistentry>
+                    <varlistentry>
+                      <term>DRM_MODE_FLAG_PIXMUX</term>
+                      <listitem><para>
+                        not used?
+                      </para></listitem>
+                    </varlistentry>
+                    <varlistentry>
+                      <term>DRM_MODE_FLAG_DBLCLK</term>
+                      <listitem><para>
+                        not used?
+                      </para></listitem>
+                    </varlistentry>
+                    <varlistentry>
+                      <term>DRM_MODE_FLAG_CLKDIV2</term>
+                      <listitem><para>
+                        ?
+                      </para></listitem>
+                    </varlistentry>
+                  </variablelist>
+                </para>
+                <para>
+                  Note that modes marked with the INTERLACE or DBLSCAN flags will be
+                  filtered out by
+                  <function>drm_helper_probe_single_connector_modes</function> if
+                  the connector's <structfield>interlace_allowed</structfield> or
+                  <structfield>doublescan_allowed</structfield> field is set to 0.
+                </para>
+              </listitem>
+              <listitem>
+                <synopsis>char name[DRM_DISPLAY_MODE_LEN];</synopsis>
+                <para>
+                  Mode name. The driver must call
+                  <function>drm_mode_set_name</function> to fill the mode name from
+                  <structfield>hdisplay</structfield>,
+                  <structfield>vdisplay</structfield> and interlace flag after
+                  filling the corresponding fields.
+                </para>
+              </listitem>
+            </itemizedlist>
+          </para>
+          <para>
+            The <structfield>vrefresh</structfield> value is computed by
+            <function>drm_helper_probe_single_connector_modes</function>.
+          </para>
+          <para>
+            When parsing EDID data, <function>drm_add_edid_modes</function> fill the
+            connector <structfield>display_info</structfield>
+            <structfield>width_mm</structfield> and
+            <structfield>height_mm</structfield> fields. When creating modes
+            manually the <methodname>get_modes</methodname> helper operation must
+            set the <structfield>display_info</structfield>
+            <structfield>width_mm</structfield> and
+            <structfield>height_mm</structfield> fields if they haven't been set
+            already (for instance at initilization time when a fixed-size panel is
+            attached to the connector). The mode <structfield>width_mm</structfield>
+            and <structfield>height_mm</structfield> fields are only used internally
+            during EDID parsing and should not be set when creating modes manually.
+          </para>
+        </listitem>
+        <listitem>
+          <synopsis>int (*mode_valid)(struct drm_connector *connector,
+                 struct drm_display_mode *mode);</synopsis>
+          <para>
+            Verify whether a mode is valid for the connector. Return MODE_OK for
+            supported modes and one of the enum drm_mode_status values (MODE_*)
+            for unsupported modes. This operation is mandatory.
+          </para>
+          <para>
+            As the mode rejection reason is currently not used beside for
+            immediately removing the unsupported mode, an implementation can
+            return MODE_BAD regardless of the exact reason why the mode is not
+            valid.
+          </para>
+          <note><para>
+            Note that the <methodname>mode_valid</methodname> helper operation is
+            only called for modes detected by the device, and
+            <emphasis>not</emphasis> for modes set by the user through the CRTC
+            <methodname>set_config</methodname> operation.
+          </para></note>
+        </listitem>
+      </itemizedlist>
+    </sect2>
   </sect1>
 
-  <sect1>
-    <title>Memory management</title>
+  <!-- Internals: vertical blanking -->
+
+  <sect1 id="drm-vertical-blank">
+    <title>Vertical Blanking</title>
+    <para>
+      Vertical blanking plays a major role in graphics rendering. To achieve
+      tear-free display, users must synchronize page flips and/or rendering to
+      vertical blanking. The DRM API offers ioctls to perform page flips
+      synchronized to vertical blanking and wait for vertical blanking.
+    </para>
+    <para>
+      The DRM core handles most of the vertical blanking management logic, which
+      involves filtering out spurious interrupts, keeping race-free blanking
+      counters, coping with counter wrap-around and resets and keeping use
+      counts. It relies on the driver to generate vertical blanking interrupts
+      and optionally provide a hardware vertical blanking counter. Drivers must
+      implement the following operations.
+    </para>
+    <itemizedlist>
+      <listitem>
+        <synopsis>int (*enable_vblank) (struct drm_device *dev, int crtc);
+void (*disable_vblank) (struct drm_device *dev, int crtc);</synopsis>
+        <para>
+         Enable or disable vertical blanking interrupts for the given CRTC.
+       </para>
+      </listitem>
+      <listitem>
+        <synopsis>u32 (*get_vblank_counter) (struct drm_device *dev, int crtc);</synopsis>
+        <para>
+         Retrieve the value of the vertical blanking counter for the given
+         CRTC. If the hardware maintains a vertical blanking counter its value
+         should be returned. Otherwise drivers can use the
+         <function>drm_vblank_count</function> helper function to handle this
+         operation.
+       </para>
+      </listitem>
+    </itemizedlist>
     <para>
-      The memory manager lies at the heart of many DRM operations; it
-      is required to support advanced client features like OpenGL
-      pbuffers.  The DRM currently contains two memory managers: TTM
-      and GEM.
+      Drivers must initialize the vertical blanking handling core with a call to
+      <function>drm_vblank_init</function> in their
+      <methodname>load</methodname> operation. The function will set the struct
+      <structname>drm_device</structname>
+      <structfield>vblank_disable_allowed</structfield> field to 0. This will
+      keep vertical blanking interrupts enabled permanently until the first mode
+      set operation, where <structfield>vblank_disable_allowed</structfield> is
+      set to 1. The reason behind this is not clear. Drivers can set the field
+      to 1 after <function>calling drm_vblank_init</function> to make vertical
+      blanking interrupts dynamically managed from the beginning.
     </para>
+    <para>
+      Vertical blanking interrupts can be enabled by the DRM core or by drivers
+      themselves (for instance to handle page flipping operations). The DRM core
+      maintains a vertical blanking use count to ensure that the interrupts are
+      not disabled while a user still needs them. To increment the use count,
+      drivers call <function>drm_vblank_get</function>. Upon return vertical
+      blanking interrupts are guaranteed to be enabled.
+    </para>
+    <para>
+      To decrement the use count drivers call
+      <function>drm_vblank_put</function>. Only when the use count drops to zero
+      will the DRM core disable the vertical blanking interrupts after a delay
+      by scheduling a timer. The delay is accessible through the vblankoffdelay
+      module parameter or the <varname>drm_vblank_offdelay</varname> global
+      variable and expressed in milliseconds. Its default value is 5000 ms.
+    </para>
+    <para>
+      When a vertical blanking interrupt occurs drivers only need to call the
+      <function>drm_handle_vblank</function> function to account for the
+      interrupt.
+    </para>
+    <para>
+      Resources allocated by <function>drm_vblank_init</function> must be freed
+      with a call to <function>drm_vblank_cleanup</function> in the driver
+      <methodname>unload</methodname> operation handler.
+    </para>
+  </sect1>
+
+  <!-- Internals: open/close, file operations and ioctls -->
 
+  <sect1>
+    <title>Open/Close, File Operations and IOCTLs</title>
     <sect2>
-      <title>The Translation Table Manager (TTM)</title>
+      <title>Open and Close</title>
+      <synopsis>int (*firstopen) (struct drm_device *);
+void (*lastclose) (struct drm_device *);
+int (*open) (struct drm_device *, struct drm_file *);
+void (*preclose) (struct drm_device *, struct drm_file *);
+void (*postclose) (struct drm_device *, struct drm_file *);</synopsis>
+      <abstract>Open and close handlers. None of those methods are mandatory.
+      </abstract>
       <para>
-       TTM was developed by Tungsten Graphics, primarily by Thomas
-       Hellström, and is intended to be a flexible, high performance
-       graphics memory manager.
+        The <methodname>firstopen</methodname> method is called by the DRM core
+       when an application opens a device that has no other opened file handle.
+       Similarly the <methodname>lastclose</methodname> method is called when
+       the last application holding a file handle opened on the device closes
+       it. Both methods are mostly used for UMS (User Mode Setting) drivers to
+       acquire and release device resources which should be done in the
+       <methodname>load</methodname> and <methodname>unload</methodname>
+       methods for KMS drivers.
       </para>
       <para>
-       Drivers wishing to support TTM must fill out a drm_bo_driver
-       structure.
+        Note that the <methodname>lastclose</methodname> method is also called
+       at module unload time or, for hot-pluggable devices, when the device is
+       unplugged. The <methodname>firstopen</methodname> and
+       <methodname>lastclose</methodname> calls can thus be unbalanced.
       </para>
       <para>
-       TTM design background and information belongs here.
+        The <methodname>open</methodname> method is called every time the device
+       is opened by an application. Drivers can allocate per-file private data
+       in this method and store them in the struct
+       <structname>drm_file</structname> <structfield>driver_priv</structfield>
+       field. Note that the <methodname>open</methodname> method is called
+       before <methodname>firstopen</methodname>.
+      </para>
+      <para>
+        The close operation is split into <methodname>preclose</methodname> and
+       <methodname>postclose</methodname> methods. Drivers must stop and
+       cleanup all per-file operations in the <methodname>preclose</methodname>
+       method. For instance pending vertical blanking and page flip events must
+       be cancelled. No per-file operation is allowed on the file handle after
+       returning from the <methodname>preclose</methodname> method.
+      </para>
+      <para>
+        Finally the <methodname>postclose</methodname> method is called as the
+       last step of the close operation, right before calling the
+       <methodname>lastclose</methodname> method if no other open file handle
+       exists for the device. Drivers that have allocated per-file private data
+       in the <methodname>open</methodname> method should free it here.
+      </para>
+      <para>
+        The <methodname>lastclose</methodname> method should restore CRTC and
+       plane properties to default value, so that a subsequent open of the
+       device will not inherit state from the previous user.
       </para>
     </sect2>
-
     <sect2>
-      <title>The Graphics Execution Manager (GEM)</title>
+      <title>File Operations</title>
+      <synopsis>const struct file_operations *fops</synopsis>
+      <abstract>File operations for the DRM device node.</abstract>
       <para>
-       GEM is an Intel project, authored by Eric Anholt and Keith
-       Packard.  It provides simpler interfaces than TTM, and is well
-       suited for UMA devices.
+        Drivers must define the file operations structure that forms the DRM
+       userspace API entry point, even though most of those operations are
+       implemented in the DRM core. The <methodname>open</methodname>,
+       <methodname>release</methodname> and <methodname>ioctl</methodname>
+       operations are handled by
+       <programlisting>
+       .owner = THIS_MODULE,
+       .open = drm_open,
+       .release = drm_release,
+       .unlocked_ioctl = drm_ioctl,
+  #ifdef CONFIG_COMPAT
+       .compat_ioctl = drm_compat_ioctl,
+  #endif
+        </programlisting>
       </para>
       <para>
-       GEM-enabled drivers must provide gem_init_object() and
-       gem_free_object() callbacks to support the core memory
-       allocation routines.  They should also provide several driver-specific
-       ioctls to support command execution, pinning, buffer
-       read &amp; write, mapping, and domain ownership transfers.
+        Drivers that implement private ioctls that requires 32/64bit
+       compatibility support must provide their own
+       <methodname>compat_ioctl</methodname> handler that processes private
+       ioctls and calls <function>drm_compat_ioctl</function> for core ioctls.
       </para>
       <para>
-       On a fundamental level, GEM involves several operations:
-       <itemizedlist>
-         <listitem>Memory allocation and freeing</listitem>
-         <listitem>Command execution</listitem>
-         <listitem>Aperture management at command execution time</listitem>
-       </itemizedlist>
-       Buffer object allocation is relatively
-       straightforward and largely provided by Linux's shmem layer, which
-       provides memory to back each object.  When mapped into the GTT
-       or used in a command buffer, the backing pages for an object are
-       flushed to memory and marked write combined so as to be coherent
-       with the GPU.  Likewise, if the CPU accesses an object after the GPU
-       has finished rendering to the object, then the object must be made
-       coherent with the CPU's view
-       of memory, usually involving GPU cache flushing of various kinds.
-       This core CPU&lt;-&gt;GPU coherency management is provided by a
-       device-specific ioctl, which evaluates an object's current domain and
-       performs any necessary flushing or synchronization to put the object
-       into the desired coherency domain (note that the object may be busy,
-       i.e. an active render target; in that case, setting the domain
-       blocks the client and waits for rendering to complete before
-       performing any necessary flushing operations).
-      </para>
-      <para>
-       Perhaps the most important GEM function is providing a command
-       execution interface to clients.  Client programs construct command
-       buffers containing references to previously allocated memory objects,
-       and then submit them to GEM.  At that point, GEM takes care to bind
-       all the objects into the GTT, execute the buffer, and provide
-       necessary synchronization between clients accessing the same buffers.
-       This often involves evicting some objects from the GTT and re-binding
-       others (a fairly expensive operation), and providing relocation
-       support which hides fixed GTT offsets from clients.  Clients must
-       take care not to submit command buffers that reference more objects
-       than can fit in the GTT; otherwise, GEM will reject them and no rendering
-       will occur.  Similarly, if several objects in the buffer require
-       fence registers to be allocated for correct rendering (e.g. 2D blits
-       on pre-965 chips), care must be taken not to require more fence
-       registers than are available to the client.  Such resource management
-       should be abstracted from the client in libdrm.
+        The <methodname>read</methodname> and <methodname>poll</methodname>
+       operations provide support for reading DRM events and polling them. They
+       are implemented by
+       <programlisting>
+       .poll = drm_poll,
+       .read = drm_read,
+       .fasync = drm_fasync,
+       .llseek = no_llseek,
+       </programlisting>
+      </para>
+      <para>
+        The memory mapping implementation varies depending on how the driver
+       manages memory. Pre-GEM drivers will use <function>drm_mmap</function>,
+       while GEM-aware drivers will use <function>drm_gem_mmap</function>. See
+       <xref linkend="drm-gem"/>.
+       <programlisting>
+       .mmap = drm_gem_mmap,
+       </programlisting>
+      </para>
+      <para>
+        No other file operation is supported by the DRM API.
+      </para>
+    </sect2>
+    <sect2>
+      <title>IOCTLs</title>
+      <synopsis>struct drm_ioctl_desc *ioctls;
+int num_ioctls;</synopsis>
+      <abstract>Driver-specific ioctls descriptors table.</abstract>
+      <para>
+        Driver-specific ioctls numbers start at DRM_COMMAND_BASE. The ioctls
+       descriptors table is indexed by the ioctl number offset from the base
+       value. Drivers can use the DRM_IOCTL_DEF_DRV() macro to initialize the
+       table entries.
+      </para>
+      <para>
+        <programlisting>DRM_IOCTL_DEF_DRV(ioctl, func, flags)</programlisting>
+       <para>
+         <parameter>ioctl</parameter> is the ioctl name. Drivers must define
+         the DRM_##ioctl and DRM_IOCTL_##ioctl macros to the ioctl number
+         offset from DRM_COMMAND_BASE and the ioctl number respectively. The
+         first macro is private to the device while the second must be exposed
+         to userspace in a public header.
+       </para>
+       <para>
+         <parameter>func</parameter> is a pointer to the ioctl handler function
+         compatible with the <type>drm_ioctl_t</type> type.
+         <programlisting>typedef int drm_ioctl_t(struct drm_device *dev, void *data,
+               struct drm_file *file_priv);</programlisting>
+       </para>
+       <para>
+         <parameter>flags</parameter> is a bitmask combination of the following
+         values. It restricts how the ioctl is allowed to be called.
+         <itemizedlist>
+           <listitem><para>
+             DRM_AUTH - Only authenticated callers allowed
+           </para></listitem>
+           <listitem><para>
+             DRM_MASTER - The ioctl can only be called on the master file
+             handle
+           </para></listitem>
+            <listitem><para>
+             DRM_ROOT_ONLY - Only callers with the SYSADMIN capability allowed
+           </para></listitem>
+            <listitem><para>
+             DRM_CONTROL_ALLOW - The ioctl can only be called on a control
+             device
+           </para></listitem>
+            <listitem><para>
+             DRM_UNLOCKED - The ioctl handler will be called without locking
+             the DRM global mutex
+           </para></listitem>
+         </itemizedlist>
+       </para>
       </para>
     </sect2>
-
-  </sect1>
-
-  <!-- Output management -->
-  <sect1>
-    <title>Output management</title>
-    <para>
-      At the core of the DRM output management code is a set of
-      structures representing CRTCs, encoders, and connectors.
-    </para>
-    <para>
-      A CRTC is an abstraction representing a part of the chip that
-      contains a pointer to a scanout buffer.  Therefore, the number
-      of CRTCs available determines how many independent scanout
-      buffers can be active at any given time.  The CRTC structure
-      contains several fields to support this: a pointer to some video
-      memory, a display mode, and an (x, y) offset into the video
-      memory to support panning or configurations where one piece of
-      video memory spans multiple CRTCs.
-    </para>
-    <para>
-      An encoder takes pixel data from a CRTC and converts it to a
-      format suitable for any attached connectors.  On some devices,
-      it may be possible to have a CRTC send data to more than one
-      encoder.  In that case, both encoders would receive data from
-      the same scanout buffer, resulting in a "cloned" display
-      configuration across the connectors attached to each encoder.
-    </para>
-    <para>
-      A connector is the final destination for pixel data on a device,
-      and usually connects directly to an external display device like
-      a monitor or laptop panel.  A connector can only be attached to
-      one encoder at a time.  The connector is also the structure
-      where information about the attached display is kept, so it
-      contains fields for display data, EDID data, DPMS &amp;
-      connection status, and information about modes supported on the
-      attached displays.
-    </para>
-<!--!Edrivers/char/drm/drm_crtc.c-->
-  </sect1>
-
-  <sect1>
-    <title>Framebuffer management</title>
-    <para>
-      Clients need to provide a framebuffer object which provides a source
-      of pixels for a CRTC to deliver to the encoder(s) and ultimately the
-      connector(s). A framebuffer is fundamentally a driver-specific memory
-      object, made into an opaque handle by the DRM's addfb() function.
-      Once a framebuffer has been created this way, it may be passed to the
-      KMS mode setting routines for use in a completed configuration.
-    </para>
   </sect1>
 
   <sect1>
@@ -812,15 +2355,24 @@ void intel_crt_init(struct drm_device *dev)
     </para>
   </sect1>
 
+  <!-- Internals: suspend/resume -->
+
   <sect1>
-    <title>Suspend/resume</title>
+    <title>Suspend/Resume</title>
+    <para>
+      The DRM core provides some suspend/resume code, but drivers wanting full
+      suspend/resume support should provide save() and restore() functions.
+      These are called at suspend, hibernate, or resume time, and should perform
+      any state save or restore required by your device across suspend or
+      hibernate states.
+    </para>
+    <synopsis>int (*suspend) (struct drm_device *, pm_message_t state);
+int (*resume) (struct drm_device *);</synopsis>
     <para>
-      The DRM core provides some suspend/resume code, but drivers
-      wanting full suspend/resume support should provide save() and
-      restore() functions.  These are called at suspend,
-      hibernate, or resume time, and should perform any state save or
-      restore required by your device across suspend or hibernate
-      states.
+      Those are legacy suspend and resume methods. New driver should use the
+      power management interface provided by their bus type (usually through
+      the struct <structname>device_driver</structname> dev_pm_ops) and set
+      these methods to NULL.
     </para>
   </sect1>
 
@@ -833,6 +2385,35 @@ void intel_crt_init(struct drm_device *dev)
   </sect1>
   </chapter>
 
+<!-- TODO
+
+- Add a glossary
+- Document the struct_mutex catch-all lock
+- Document connector properties
+
+- Why is the load method optional?
+- What are drivers supposed to set the initial display state to, and how?
+  Connector's DPMS states are not initialized and are thus equal to
+  DRM_MODE_DPMS_ON. The fbcon compatibility layer calls
+  drm_helper_disable_unused_functions(), which disables unused encoders and
+  CRTCs, but doesn't touch the connectors' DPMS state, and
+  drm_helper_connector_dpms() in reaction to fbdev blanking events. Do drivers
+  that don't implement (or just don't use) fbcon compatibility need to call
+  those functions themselves?
+- KMS drivers must call drm_vblank_pre_modeset() and drm_vblank_post_modeset()
+  around mode setting. Should this be done in the DRM core?
+- vblank_disable_allowed is set to 1 in the first drm_vblank_post_modeset()
+  call and never set back to 0. It seems to be safe to permanently set it to 1
+  in drm_vblank_init() for KMS driver, and it might be safe for UMS drivers as
+  well. This should be investigated.
+- crtc and connector .save and .restore operations are only used internally in
+  drivers, should they be removed from the core?
+- encoder mid-layer .save and .restore operations are only used internally in
+  drivers, should they be removed from the core?
+- encoder mid-layer .detect operation is only used internally in drivers,
+  should it be removed from the core?
+-->
+
   <!-- External interfaces -->
 
   <chapter id="drmExternals">
@@ -853,6 +2434,42 @@ void intel_crt_init(struct drm_device *dev)
       Cover generic ioctls and sysfs layout here.  We only need high-level
       info, since man pages should cover the rest.
     </para>
+
+  <!-- External: vblank handling -->
+
+    <sect1>
+      <title>VBlank event handling</title>
+      <para>
+        The DRM core exposes two vertical blank related ioctls:
+        <variablelist>
+          <varlistentry>
+            <term>DRM_IOCTL_WAIT_VBLANK</term>
+            <listitem>
+              <para>
+                This takes a struct drm_wait_vblank structure as its argument,
+                and it is used to block or request a signal when a specified
+                vblank event occurs.
+              </para>
+            </listitem>
+          </varlistentry>
+          <varlistentry>
+            <term>DRM_IOCTL_MODESET_CTL</term>
+            <listitem>
+              <para>
+                This should be called by application level drivers before and
+                after mode setting, since on many devices the vertical blank
+                counter is reset at that time.  Internally, the DRM snapshots
+                the last vblank count when the ioctl is called with the
+                _DRM_PRE_MODESET command, so that the counter won't go backwards
+                (which is dealt with when _DRM_POST_MODESET is used).
+              </para>
+            </listitem>
+          </varlistentry>
+        </variablelist>
+<!--!Edrivers/char/drm/drm_irq.c-->
+      </para>
+    </sect1>
+
   </chapter>
 
   <!-- API reference -->
index fdc0119963e70f12c4f9e1eae3a613447e7d8781..b17587d9412f04318dab5e7368f6e5306ca57b91 100644 (file)
@@ -3388,7 +3388,7 @@ M:        "Wolfram Sang (embedded platforms)" <w.sang@pengutronix.de>
 L:     linux-i2c@vger.kernel.org
 W:     http://i2c.wiki.kernel.org/
 T:     quilt kernel.org/pub/linux/kernel/people/jdelvare/linux-2.6/jdelvare-i2c/
-T:     git git://git.fluff.org/bjdooks/linux.git
+T:     git git://git.pengutronix.de/git/wsa/linux.git
 S:     Maintained
 F:     Documentation/i2c/
 F:     drivers/i2c/
@@ -3666,11 +3666,12 @@ F:      Documentation/networking/README.ipw2200
 F:     drivers/net/wireless/ipw2x00/
 
 INTEL(R) TRUSTED EXECUTION TECHNOLOGY (TXT)
-M:     Joseph Cihula <joseph.cihula@intel.com>
+M:     Richard L Maliszewski <richard.l.maliszewski@intel.com>
+M:     Gang Wei <gang.wei@intel.com>
 M:     Shane Wang <shane.wang@intel.com>
 L:     tboot-devel@lists.sourceforge.net
 W:     http://tboot.sourceforge.net
-T:     Mercurial http://www.bughost.org/repos.hg/tboot.hg
+T:     hg http://tboot.hg.sourceforge.net:8000/hgroot/tboot/tboot
 S:     Supported
 F:     Documentation/intel_txt.txt
 F:     include/linux/tboot.h
index 0f66f146d57ea3c25573bdd20280d2dd15d041ed..a3c11d589681d9a53db5cf31ca8a4e1c1b9246a5 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,8 @@
 VERSION = 3
 PATCHLEVEL = 6
 SUBLEVEL = 0
-EXTRAVERSION = -rc5
-NAME = Saber-toothed Squirrel
+EXTRAVERSION = -rc7
+NAME = Terrified Chipmunk
 
 # *DOCUMENTATION*
 # To see a list of typical targets execute "make help"
index f15f82bf3a50f808005af479ff9c334f100c1044..e968a52e4881967a01f8aa68fc586aafe61edf38 100644 (file)
@@ -356,15 +356,15 @@ choice
                  is nothing connected to read from the DCC.
 
        config DEBUG_SEMIHOSTING
-               bool "Kernel low-level debug output via semihosting I"
+               bool "Kernel low-level debug output via semihosting I/O"
                help
                  Semihosting enables code running on an ARM target to use
                  the I/O facilities on a host debugger/emulator through a
-                 simple SVC calls. The host debugger or emulator must have
+                 simple SVC call. The host debugger or emulator must have
                  semihosting enabled for the special svc call to be trapped
                  otherwise the kernel will crash.
 
-                 This is known to work with OpenOCD, as wellas
+                 This is known to work with OpenOCD, as well as
                  ARM's Fast Models, or any other controlling environment
                  that implements semihosting.
 
index 30eae87ead6d4b245bc6707040645f5182464e90..a051dfbdd7db07fb12db913d85050a5bb0ea7167 100644 (file)
@@ -284,10 +284,10 @@ zImage Image xipImage bootpImage uImage: vmlinux
 zinstall uinstall install: vmlinux
        $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $@
 
-%.dtb:
+%.dtb: scripts
        $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
 
-dtbs:
+dtbs: scripts
        $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
 
 # We use MRPROPER_FILES and CLEAN_FILES now
index b8c64b80bafc848032dfff366a9ac4428a7f82b5..bc67cbff39448ff84a3f0c14cd22876975ee1965 100644 (file)
@@ -653,16 +653,21 @@ __armv7_mmu_cache_on:
                mcrne   p15, 0, r0, c8, c7, 0   @ flush I,D TLBs
 #endif
                mrc     p15, 0, r0, c1, c0, 0   @ read control reg
+               bic     r0, r0, #1 << 28        @ clear SCTLR.TRE
                orr     r0, r0, #0x5000         @ I-cache enable, RR cache replacement
                orr     r0, r0, #0x003c         @ write buffer
 #ifdef CONFIG_MMU
 #ifdef CONFIG_CPU_ENDIAN_BE8
                orr     r0, r0, #1 << 25        @ big-endian page tables
 #endif
+               mrcne   p15, 0, r6, c2, c0, 2   @ read ttb control reg
                orrne   r0, r0, #1              @ MMU enabled
                movne   r1, #0xfffffffd         @ domain 0 = client
+               bic     r6, r6, #1 << 31        @ 32-bit translation system
+               bic     r6, r6, #3 << 0         @ use only ttbr0
                mcrne   p15, 0, r3, c2, c0, 0   @ load page table pointer
                mcrne   p15, 0, r1, c3, c0, 0   @ load domain access control
+               mcrne   p15, 0, r6, c2, c0, 2   @ load ttb control
 #endif
                mcr     p15, 0, r0, c7, c5, 4   @ ISB
                mcr     p15, 0, r0, c1, c0, 0   @ load control register
index 66389c1c6f6297e50c8ae3b944d7311dcbb26b6c..7c95f76398de401ac166af8f32d4a34a6b918e79 100644 (file)
                                #gpio-cells = <2>;
                                gpio-controller;
                                interrupt-controller;
+                               #interrupt-cells = <2>;
                        };
 
                        pioB: gpio@fffff600 {
                                #gpio-cells = <2>;
                                gpio-controller;
                                interrupt-controller;
+                               #interrupt-cells = <2>;
                        };
 
                        pioC: gpio@fffff800 {
                                #gpio-cells = <2>;
                                gpio-controller;
                                interrupt-controller;
+                               #interrupt-cells = <2>;
                        };
 
                        dbgu: serial@fffff200 {
index b460d6ce9eb592da5191871b40b2d3433156bcef..195019b7ca0e046073361a587862c885c8fbfb7e 100644 (file)
@@ -95,6 +95,7 @@
                                #gpio-cells = <2>;
                                gpio-controller;
                                interrupt-controller;
+                               #interrupt-cells = <2>;
                        };
 
                        pioB: gpio@fffff400 {
                                #gpio-cells = <2>;
                                gpio-controller;
                                interrupt-controller;
+                               #interrupt-cells = <2>;
                        };
 
                        pioC: gpio@fffff600 {
                                #gpio-cells = <2>;
                                gpio-controller;
                                interrupt-controller;
+                               #interrupt-cells = <2>;
                        };
 
                        pioD: gpio@fffff800 {
                                #gpio-cells = <2>;
                                gpio-controller;
                                interrupt-controller;
+                               #interrupt-cells = <2>;
                        };
 
                        pioE: gpio@fffffa00 {
                                #gpio-cells = <2>;
                                gpio-controller;
                                interrupt-controller;
+                               #interrupt-cells = <2>;
                        };
 
                        dbgu: serial@ffffee00 {
index bafa8806fc17293e3bddc28aff5b4829f677c178..63751b1e744b42611f8e74d17738ccd69dd1efe1 100644 (file)
                                #gpio-cells = <2>;
                                gpio-controller;
                                interrupt-controller;
+                               #interrupt-cells = <2>;
                        };
 
                        pioB: gpio@fffff400 {
                                #gpio-cells = <2>;
                                gpio-controller;
                                interrupt-controller;
+                               #interrupt-cells = <2>;
                        };
 
                        pioC: gpio@fffff600 {
                                #gpio-cells = <2>;
                                gpio-controller;
                                interrupt-controller;
+                               #interrupt-cells = <2>;
                        };
 
                        pioD: gpio@fffff800 {
                                #gpio-cells = <2>;
                                gpio-controller;
                                interrupt-controller;
+                               #interrupt-cells = <2>;
                        };
 
                        pioE: gpio@fffffa00 {
                                #gpio-cells = <2>;
                                gpio-controller;
                                interrupt-controller;
+                               #interrupt-cells = <2>;
                        };
 
                        dbgu: serial@ffffee00 {
index bfac0dfc332c7d10bc3fe2f87458ec728c48b885..ef9336ae9614f85ffb0ce2348b69fc06b0f756e5 100644 (file)
                                #gpio-cells = <2>;
                                gpio-controller;
                                interrupt-controller;
+                               #interrupt-cells = <2>;
                        };
 
                        pioB: gpio@fffff600 {
                                #gpio-cells = <2>;
                                gpio-controller;
                                interrupt-controller;
+                               #interrupt-cells = <2>;
                        };
 
                        pioC: gpio@fffff800 {
                                #gpio-cells = <2>;
                                gpio-controller;
                                interrupt-controller;
+                               #interrupt-cells = <2>;
                        };
 
                        pioD: gpio@fffffa00 {
                                #gpio-cells = <2>;
                                gpio-controller;
                                interrupt-controller;
+                               #interrupt-cells = <2>;
                        };
 
                        dbgu: serial@fffff200 {
index 4a18c393b1360f490c9796f3c598d349338ed2f5..8a387a8d61b745fc763c9e13f015896bc3e2bff7 100644 (file)
                                #gpio-cells = <2>;
                                gpio-controller;
                                interrupt-controller;
+                               #interrupt-cells = <2>;
                        };
 
                        pioB: gpio@fffff600 {
                                #gpio-cells = <2>;
                                gpio-controller;
                                interrupt-controller;
+                               #interrupt-cells = <2>;
                        };
 
                        pioC: gpio@fffff800 {
                                #gpio-cells = <2>;
                                gpio-controller;
                                interrupt-controller;
+                               #interrupt-cells = <2>;
                        };
 
                        pioD: gpio@fffffa00 {
                                #gpio-cells = <2>;
                                gpio-controller;
                                interrupt-controller;
+                               #interrupt-cells = <2>;
                        };
 
                        dbgu: serial@fffff200 {
index 03fb93621d0d6046b7b416c9c4328437380fd843..5c8b3bf4d8252f1013af6fea25848ce9c41dcfeb 100644 (file)
        .size \name , . - \name
        .endm
 
+       .macro check_uaccess, addr:req, size:req, limit:req, tmp:req, bad:req
+#ifndef CONFIG_CPU_USE_DOMAINS
+       adds    \tmp, \addr, #\size - 1
+       sbcccs  \tmp, \tmp, \limit
+       bcs     \bad
+#endif
+       .endm
+
 #endif /* __ASM_ASSEMBLER_H__ */
index e965f1b560f11e3a504814183c98f8f1b11bbf25..5f6ddcc56452998f40b1c16d7e20a0ff1ec010bd 100644 (file)
@@ -187,6 +187,7 @@ static inline unsigned long __phys_to_virt(unsigned long x)
 #define __phys_to_virt(x)      ((x) - PHYS_OFFSET + PAGE_OFFSET)
 #endif
 #endif
+#endif /* __ASSEMBLY__ */
 
 #ifndef PHYS_OFFSET
 #ifdef PLAT_PHYS_OFFSET
@@ -196,6 +197,8 @@ static inline unsigned long __phys_to_virt(unsigned long x)
 #endif
 #endif
 
+#ifndef __ASSEMBLY__
+
 /*
  * PFNs are used to describe any physical page; this means
  * PFN 0 == physical address 0.
index 314d4664eae7d9976a5fe656f74918cb8d15ab8b..99a19512ee26e2e5d99135d21f10d8b99e606226 100644 (file)
@@ -199,6 +199,9 @@ static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
 {
        pgtable_page_dtor(pte);
 
+#ifdef CONFIG_ARM_LPAE
+       tlb_add_flush(tlb, addr);
+#else
        /*
         * With the classic ARM MMU, a pte page has two corresponding pmd
         * entries, each covering 1MB.
@@ -206,6 +209,7 @@ static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
        addr &= PMD_MASK;
        tlb_add_flush(tlb, addr + SZ_1M - PAGE_SIZE);
        tlb_add_flush(tlb, addr + SZ_1M);
+#endif
 
        tlb_remove_page(tlb, pte);
 }
index 479a6352e0b5075911e91a4e0b60b7e9443fd4fd..77bd79f2ffdbd0344d096ca7fb2db809fb52d387 100644 (file)
@@ -101,28 +101,39 @@ extern int __get_user_1(void *);
 extern int __get_user_2(void *);
 extern int __get_user_4(void *);
 
-#define __get_user_x(__r2,__p,__e,__s,__i...)                          \
+#define __GUP_CLOBBER_1        "lr", "cc"
+#ifdef CONFIG_CPU_USE_DOMAINS
+#define __GUP_CLOBBER_2        "ip", "lr", "cc"
+#else
+#define __GUP_CLOBBER_2 "lr", "cc"
+#endif
+#define __GUP_CLOBBER_4        "lr", "cc"
+
+#define __get_user_x(__r2,__p,__e,__l,__s)                             \
           __asm__ __volatile__ (                                       \
                __asmeq("%0", "r0") __asmeq("%1", "r2")                 \
+               __asmeq("%3", "r1")                                     \
                "bl     __get_user_" #__s                               \
                : "=&r" (__e), "=r" (__r2)                              \
-               : "0" (__p)                                             \
-               : __i, "cc")
+               : "0" (__p), "r" (__l)                                  \
+               : __GUP_CLOBBER_##__s)
 
-#define get_user(x,p)                                                  \
+#define __get_user_check(x,p)                                                  \
        ({                                                              \
+               unsigned long __limit = current_thread_info()->addr_limit - 1; \
                register const typeof(*(p)) __user *__p asm("r0") = (p);\
                register unsigned long __r2 asm("r2");                  \
+               register unsigned long __l asm("r1") = __limit;         \
                register int __e asm("r0");                             \
                switch (sizeof(*(__p))) {                               \
                case 1:                                                 \
-                       __get_user_x(__r2, __p, __e, 1, "lr");          \
-                       break;                                          \
+                       __get_user_x(__r2, __p, __e, __l, 1);           \
+                       break;                                          \
                case 2:                                                 \
-                       __get_user_x(__r2, __p, __e, 2, "r3", "lr");    \
+                       __get_user_x(__r2, __p, __e, __l, 2);           \
                        break;                                          \
                case 4:                                                 \
-                       __get_user_x(__r2, __p, __e, 4, "lr");          \
+                       __get_user_x(__r2, __p, __e, __l, 4);           \
                        break;                                          \
                default: __e = __get_user_bad(); break;                 \
                }                                                       \
@@ -130,42 +141,57 @@ extern int __get_user_4(void *);
                __e;                                                    \
        })
 
+#define get_user(x,p)                                                  \
+       ({                                                              \
+               might_fault();                                          \
+               __get_user_check(x,p);                                  \
+        })
+
 extern int __put_user_1(void *, unsigned int);
 extern int __put_user_2(void *, unsigned int);
 extern int __put_user_4(void *, unsigned int);
 extern int __put_user_8(void *, unsigned long long);
 
-#define __put_user_x(__r2,__p,__e,__s)                                 \
+#define __put_user_x(__r2,__p,__e,__l,__s)                             \
           __asm__ __volatile__ (                                       \
                __asmeq("%0", "r0") __asmeq("%2", "r2")                 \
+               __asmeq("%3", "r1")                                     \
                "bl     __put_user_" #__s                               \
                : "=&r" (__e)                                           \
-               : "0" (__p), "r" (__r2)                                 \
+               : "0" (__p), "r" (__r2), "r" (__l)                      \
                : "ip", "lr", "cc")
 
-#define put_user(x,p)                                                  \
+#define __put_user_check(x,p)                                                  \
        ({                                                              \
+               unsigned long __limit = current_thread_info()->addr_limit - 1; \
                register const typeof(*(p)) __r2 asm("r2") = (x);       \
                register const typeof(*(p)) __user *__p asm("r0") = (p);\
+               register unsigned long __l asm("r1") = __limit;         \
                register int __e asm("r0");                             \
                switch (sizeof(*(__p))) {                               \
                case 1:                                                 \
-                       __put_user_x(__r2, __p, __e, 1);                \
+                       __put_user_x(__r2, __p, __e, __l, 1);           \
                        break;                                          \
                case 2:                                                 \
-                       __put_user_x(__r2, __p, __e, 2);                \
+                       __put_user_x(__r2, __p, __e, __l, 2);           \
                        break;                                          \
                case 4:                                                 \
-                       __put_user_x(__r2, __p, __e, 4);                \
+                       __put_user_x(__r2, __p, __e, __l, 4);           \
                        break;                                          \
                case 8:                                                 \
-                       __put_user_x(__r2, __p, __e, 8);                \
+                       __put_user_x(__r2, __p, __e, __l, 8);           \
                        break;                                          \
                default: __e = __put_user_bad(); break;                 \
                }                                                       \
                __e;                                                    \
        })
 
+#define put_user(x,p)                                                  \
+       ({                                                              \
+               might_fault();                                          \
+               __put_user_check(x,p);                                  \
+        })
+
 #else /* CONFIG_MMU */
 
 /*
@@ -219,6 +245,7 @@ do {                                                                        \
        unsigned long __gu_addr = (unsigned long)(ptr);                 \
        unsigned long __gu_val;                                         \
        __chk_user_ptr(ptr);                                            \
+       might_fault();                                                  \
        switch (sizeof(*(ptr))) {                                       \
        case 1: __get_user_asm_byte(__gu_val,__gu_addr,err);    break;  \
        case 2: __get_user_asm_half(__gu_val,__gu_addr,err);    break;  \
@@ -300,6 +327,7 @@ do {                                                                        \
        unsigned long __pu_addr = (unsigned long)(ptr);                 \
        __typeof__(*(ptr)) __pu_val = (x);                              \
        __chk_user_ptr(ptr);                                            \
+       might_fault();                                                  \
        switch (sizeof(*(ptr))) {                                       \
        case 1: __put_user_asm_byte(__pu_val,__pu_addr,err);    break;  \
        case 2: __put_user_asm_half(__pu_val,__pu_addr,err);    break;  \
index 0cab47d4a83ff97a23c7a9d2975cfb855a687e3e..2fde5fd1accedda5a00accac967e1985be9c7f08 100644 (file)
 #define __NR_setns                     (__NR_SYSCALL_BASE+375)
 #define __NR_process_vm_readv          (__NR_SYSCALL_BASE+376)
 #define __NR_process_vm_writev         (__NR_SYSCALL_BASE+377)
+                                       /* 378 for kcmp */
 
 /*
  * The following SWIs are ARM private.
  */
 #define __IGNORE_fadvise64_64
 #define __IGNORE_migrate_pages
+#define __IGNORE_kcmp
 
 #endif /* __KERNEL__ */
 #endif /* __ASM_ARM_UNISTD_H */
index 463ff4a0ec8acaa69372b8abd40a39260ee91736..e337879595e565e5666cbd8ac0c40a4a72908c0e 100644 (file)
 /* 375 */      CALL(sys_setns)
                CALL(sys_process_vm_readv)
                CALL(sys_process_vm_writev)
+               CALL(sys_ni_syscall)    /* reserved for sys_kcmp */
 #ifndef syscalls_counted
 .equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls
 #define syscalls_counted
index ba386bd94107642d9819a7d8bafb7ba04e92b83f..281bf3301241fba2a1ce1baafeaf3020b7cd57ff 100644 (file)
@@ -159,6 +159,12 @@ static int debug_arch_supported(void)
                arch >= ARM_DEBUG_ARCH_V7_1;
 }
 
+/* Can we determine the watchpoint access type from the fsr? */
+static int debug_exception_updates_fsr(void)
+{
+       return 0;
+}
+
 /* Determine number of WRP registers available. */
 static int get_num_wrp_resources(void)
 {
@@ -604,13 +610,14 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
                /* Aligned */
                break;
        case 1:
-               /* Allow single byte watchpoint. */
-               if (info->ctrl.len == ARM_BREAKPOINT_LEN_1)
-                       break;
        case 2:
                /* Allow halfword watchpoints and breakpoints. */
                if (info->ctrl.len == ARM_BREAKPOINT_LEN_2)
                        break;
+       case 3:
+               /* Allow single byte watchpoint. */
+               if (info->ctrl.len == ARM_BREAKPOINT_LEN_1)
+                       break;
        default:
                ret = -EINVAL;
                goto out;
@@ -619,18 +626,35 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
        info->address &= ~alignment_mask;
        info->ctrl.len <<= offset;
 
-       /*
-        * Currently we rely on an overflow handler to take
-        * care of single-stepping the breakpoint when it fires.
-        * In the case of userspace breakpoints on a core with V7 debug,
-        * we can use the mismatch feature as a poor-man's hardware
-        * single-step, but this only works for per-task breakpoints.
-        */
-       if (!bp->overflow_handler && (arch_check_bp_in_kernelspace(bp) ||
-           !core_has_mismatch_brps() || !bp->hw.bp_target)) {
-               pr_warning("overflow handler required but none found\n");
-               ret = -EINVAL;
+       if (!bp->overflow_handler) {
+               /*
+                * Mismatch breakpoints are required for single-stepping
+                * breakpoints.
+                */
+               if (!core_has_mismatch_brps())
+                       return -EINVAL;
+
+               /* We don't allow mismatch breakpoints in kernel space. */
+               if (arch_check_bp_in_kernelspace(bp))
+                       return -EPERM;
+
+               /*
+                * Per-cpu breakpoints are not supported by our stepping
+                * mechanism.
+                */
+               if (!bp->hw.bp_target)
+                       return -EINVAL;
+
+               /*
+                * We only support specific access types if the fsr
+                * reports them.
+                */
+               if (!debug_exception_updates_fsr() &&
+                   (info->ctrl.type == ARM_BREAKPOINT_LOAD ||
+                    info->ctrl.type == ARM_BREAKPOINT_STORE))
+                       return -EINVAL;
        }
+
 out:
        return ret;
 }
@@ -706,10 +730,12 @@ static void watchpoint_handler(unsigned long addr, unsigned int fsr,
                                goto unlock;
 
                        /* Check that the access type matches. */
-                       access = (fsr & ARM_FSR_ACCESS_MASK) ? HW_BREAKPOINT_W :
-                                HW_BREAKPOINT_R;
-                       if (!(access & hw_breakpoint_type(wp)))
-                               goto unlock;
+                       if (debug_exception_updates_fsr()) {
+                               access = (fsr & ARM_FSR_ACCESS_MASK) ?
+                                         HW_BREAKPOINT_W : HW_BREAKPOINT_R;
+                               if (!(access & hw_breakpoint_type(wp)))
+                                       goto unlock;
+                       }
 
                        /* We have a winner. */
                        info->trigger = addr;
index fef42b21cecba517e67e8ed74ef8c88d3cfd2afd..e1f906989bb8161963d8a714073a8d84a8e2c2e6 100644 (file)
@@ -11,7 +11,6 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/clk.h>
-#include <linux/cpufreq.h>
 #include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/err.h>
@@ -96,7 +95,52 @@ static void twd_timer_stop(struct clock_event_device *clk)
        disable_percpu_irq(clk->irq);
 }
 
-#ifdef CONFIG_CPU_FREQ
+#ifdef CONFIG_COMMON_CLK
+
+/*
+ * Updates clockevent frequency when the cpu frequency changes.
+ * Called on the cpu that is changing frequency with interrupts disabled.
+ */
+static void twd_update_frequency(void *new_rate)
+{
+       twd_timer_rate = *((unsigned long *) new_rate);
+
+       clockevents_update_freq(*__this_cpu_ptr(twd_evt), twd_timer_rate);
+}
+
+static int twd_rate_change(struct notifier_block *nb,
+       unsigned long flags, void *data)
+{
+       struct clk_notifier_data *cnd = data;
+
+       /*
+        * The twd clock events must be reprogrammed to account for the new
+        * frequency.  The timer is local to a cpu, so cross-call to the
+        * changing cpu.
+        */
+       if (flags == POST_RATE_CHANGE)
+               smp_call_function(twd_update_frequency,
+                                 (void *)&cnd->new_rate, 1);
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block twd_clk_nb = {
+       .notifier_call = twd_rate_change,
+};
+
+static int twd_clk_init(void)
+{
+       if (twd_evt && *__this_cpu_ptr(twd_evt) && !IS_ERR(twd_clk))
+               return clk_notifier_register(twd_clk, &twd_clk_nb);
+
+       return 0;
+}
+core_initcall(twd_clk_init);
+
+#elif defined (CONFIG_CPU_FREQ)
+
+#include <linux/cpufreq.h>
 
 /*
  * Updates clockevent frequency when the cpu frequency changes.
index f7945218b8c63a722cf08badc229345d7083b052..b0179b89a04ce26062184aaf23f86c521fb3009c 100644 (file)
@@ -420,20 +420,23 @@ asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
 #endif
                        instr = *(u32 *) pc;
        } else if (thumb_mode(regs)) {
-               get_user(instr, (u16 __user *)pc);
+               if (get_user(instr, (u16 __user *)pc))
+                       goto die_sig;
                if (is_wide_instruction(instr)) {
                        unsigned int instr2;
-                       get_user(instr2, (u16 __user *)pc+1);
+                       if (get_user(instr2, (u16 __user *)pc+1))
+                               goto die_sig;
                        instr <<= 16;
                        instr |= instr2;
                }
-       } else {
-               get_user(instr, (u32 __user *)pc);
+       } else if (get_user(instr, (u32 __user *)pc)) {
+               goto die_sig;
        }
 
        if (call_undef_hook(regs, instr) == 0)
                return;
 
+die_sig:
 #ifdef CONFIG_DEBUG_USER
        if (user_debug & UDBG_UNDEFINED) {
                printk(KERN_INFO "%s (%d): undefined instruction: pc=%p\n",
index d6dacc69254e47ddca3f399cc6f37eef12b073d8..395d5fbb8fa20c01c8b71d37dce42df700820c91 100644 (file)
@@ -59,6 +59,7 @@ void __init init_current_timer_delay(unsigned long freq)
 {
        pr_info("Switching to timer-based delay loop\n");
        lpj_fine                        = freq / HZ;
+       loops_per_jiffy                 = lpj_fine;
        arm_delay_ops.delay             = __timer_delay;
        arm_delay_ops.const_udelay      = __timer_const_udelay;
        arm_delay_ops.udelay            = __timer_udelay;
index 11093a7c3e32289e95a8c100cc01ef2bbb8d7101..9b06bb41fca659b9bbfc2f996e703ce2b8c315aa 100644 (file)
@@ -16,8 +16,9 @@
  * __get_user_X
  *
  * Inputs:     r0 contains the address
+ *             r1 contains the address limit, which must be preserved
  * Outputs:    r0 is the error code
- *             r2, r3 contains the zero-extended value
+ *             r2 contains the zero-extended value
  *             lr corrupted
  *
  * No other registers must be altered.  (see <asm/uaccess.h>
  * Note also that it is intended that __get_user_bad is not global.
  */
 #include <linux/linkage.h>
+#include <asm/assembler.h>
 #include <asm/errno.h>
 #include <asm/domain.h>
 
 ENTRY(__get_user_1)
+       check_uaccess r0, 1, r1, r2, __get_user_bad
 1: TUSER(ldrb) r2, [r0]
        mov     r0, #0
        mov     pc, lr
 ENDPROC(__get_user_1)
 
 ENTRY(__get_user_2)
-#ifdef CONFIG_THUMB2_KERNEL
-2: TUSER(ldrb) r2, [r0]
-3: TUSER(ldrb) r3, [r0, #1]
+       check_uaccess r0, 2, r1, r2, __get_user_bad
+#ifdef CONFIG_CPU_USE_DOMAINS
+rb     .req    ip
+2:     ldrbt   r2, [r0], #1
+3:     ldrbt   rb, [r0], #0
 #else
-2: TUSER(ldrb) r2, [r0], #1
-3: TUSER(ldrb) r3, [r0]
+rb     .req    r0
+2:     ldrb    r2, [r0]
+3:     ldrb    rb, [r0, #1]
 #endif
 #ifndef __ARMEB__
-       orr     r2, r2, r3, lsl #8
+       orr     r2, r2, rb, lsl #8
 #else
-       orr     r2, r3, r2, lsl #8
+       orr     r2, rb, r2, lsl #8
 #endif
        mov     r0, #0
        mov     pc, lr
 ENDPROC(__get_user_2)
 
 ENTRY(__get_user_4)
+       check_uaccess r0, 4, r1, r2, __get_user_bad
 4: TUSER(ldr)  r2, [r0]
        mov     r0, #0
        mov     pc, lr
index 7db25990c589f3d98554d9aee47cf7b5c3c486fd..3d73dcb959b0da83bc8affe3a781b7fcbdb17752 100644 (file)
@@ -16,6 +16,7 @@
  * __put_user_X
  *
  * Inputs:     r0 contains the address
+ *             r1 contains the address limit, which must be preserved
  *             r2, r3 contains the value
  * Outputs:    r0 is the error code
  *             lr corrupted
  * Note also that it is intended that __put_user_bad is not global.
  */
 #include <linux/linkage.h>
+#include <asm/assembler.h>
 #include <asm/errno.h>
 #include <asm/domain.h>
 
 ENTRY(__put_user_1)
+       check_uaccess r0, 1, r1, ip, __put_user_bad
 1: TUSER(strb) r2, [r0]
        mov     r0, #0
        mov     pc, lr
 ENDPROC(__put_user_1)
 
 ENTRY(__put_user_2)
+       check_uaccess r0, 2, r1, ip, __put_user_bad
        mov     ip, r2, lsr #8
 #ifdef CONFIG_THUMB2_KERNEL
 #ifndef __ARMEB__
@@ -60,12 +64,14 @@ ENTRY(__put_user_2)
 ENDPROC(__put_user_2)
 
 ENTRY(__put_user_4)
+       check_uaccess r0, 4, r1, ip, __put_user_bad
 4: TUSER(str)  r2, [r0]
        mov     r0, #0
        mov     pc, lr
 ENDPROC(__put_user_4)
 
 ENTRY(__put_user_8)
+       check_uaccess r0, 8, r1, ip, __put_user_bad
 #ifdef CONFIG_THUMB2_KERNEL
 5: TUSER(str)  r2, [r0]
 6: TUSER(str)  r3, [r0, #4]
index fdd8cc87c9feee388ca8a94b0fb46fa20305f33a..d20d4795f4eaebeb633c73ec441c36a661949a47 100644 (file)
@@ -222,10 +222,8 @@ int __init mx25_clocks_init(void)
        clk_register_clkdev(clk[lcdc_ipg], "ipg", "imx-fb.0");
        clk_register_clkdev(clk[lcdc_ahb], "ahb", "imx-fb.0");
        clk_register_clkdev(clk[wdt_ipg], NULL, "imx2-wdt.0");
-       clk_register_clkdev(clk[ssi1_ipg_per], "per", "imx-ssi.0");
-       clk_register_clkdev(clk[ssi1_ipg], "ipg", "imx-ssi.0");
-       clk_register_clkdev(clk[ssi2_ipg_per], "per", "imx-ssi.1");
-       clk_register_clkdev(clk[ssi2_ipg], "ipg", "imx-ssi.1");
+       clk_register_clkdev(clk[ssi1_ipg], NULL, "imx-ssi.0");
+       clk_register_clkdev(clk[ssi2_ipg], NULL, "imx-ssi.1");
        clk_register_clkdev(clk[esdhc1_ipg_per], "per", "sdhci-esdhc-imx25.0");
        clk_register_clkdev(clk[esdhc1_ipg], "ipg", "sdhci-esdhc-imx25.0");
        clk_register_clkdev(clk[esdhc1_ahb], "ahb", "sdhci-esdhc-imx25.0");
@@ -243,6 +241,6 @@ int __init mx25_clocks_init(void)
        clk_register_clkdev(clk[sdma_ahb], "ahb", "imx35-sdma");
        clk_register_clkdev(clk[iim_ipg], "iim", NULL);
 
-       mxc_timer_init(MX25_IO_ADDRESS(MX25_GPT1_BASE_ADDR), 54);
+       mxc_timer_init(MX25_IO_ADDRESS(MX25_GPT1_BASE_ADDR), MX25_INT_GPT1);
        return 0;
 }
index c6422fb10bae37756693f3323f79df62e4fc932e..65fb8bcd86cb1652385ee0320679d04a427f8295 100644 (file)
@@ -230,10 +230,8 @@ int __init mx35_clocks_init()
        clk_register_clkdev(clk[ipu_gate], NULL, "mx3_sdc_fb");
        clk_register_clkdev(clk[owire_gate], NULL, "mxc_w1");
        clk_register_clkdev(clk[sdma_gate], NULL, "imx35-sdma");
-       clk_register_clkdev(clk[ipg], "ipg", "imx-ssi.0");
-       clk_register_clkdev(clk[ssi1_div_post], "per", "imx-ssi.0");
-       clk_register_clkdev(clk[ipg], "ipg", "imx-ssi.1");
-       clk_register_clkdev(clk[ssi2_div_post], "per", "imx-ssi.1");
+       clk_register_clkdev(clk[ssi1_gate], NULL, "imx-ssi.0");
+       clk_register_clkdev(clk[ssi2_gate], NULL, "imx-ssi.1");
        /* i.mx35 has the i.mx21 type uart */
        clk_register_clkdev(clk[uart1_gate], "per", "imx21-uart.0");
        clk_register_clkdev(clk[ipg], "ipg", "imx21-uart.0");
index 2c6ab3273f9e4d4a419e54a4f7cde417fdb213a6..5985ed1b8c9875cffc99ab74233147912ae01eca 100644 (file)
@@ -526,7 +526,8 @@ static void __init armadillo5x0_init(void)
        imx31_add_mxc_nand(&armadillo5x0_nand_board_info);
 
        /* set NAND page size to 2k if not configured via boot mode pins */
-       __raw_writel(__raw_readl(MXC_CCM_RCSR) | (1 << 30), MXC_CCM_RCSR);
+       __raw_writel(__raw_readl(mx3_ccm_base + MXC_CCM_RCSR) |
+                                       (1 << 30), mx3_ccm_base + MXC_CCM_RCSR);
 
        /* RTC */
        /* Get RTC IRQ and register the chip */
index fcd4e85c4ddcce66921a83b29e45669c50704918..346fd26f3aa62bcbe29757ddaa1c98fb9929c53b 100644 (file)
@@ -232,10 +232,11 @@ config MACH_OMAP3_PANDORA
        select OMAP_PACKAGE_CBB
        select REGULATOR_FIXED_VOLTAGE if REGULATOR
 
-config MACH_OMAP3_TOUCHBOOK
+config MACH_TOUCHBOOK
        bool "OMAP3 Touch Book"
        depends on ARCH_OMAP3
        default y
+       select OMAP_PACKAGE_CBB
 
 config MACH_OMAP_3430SDP
        bool "OMAP 3430 SDP board"
index f6a24b3f9c4f7b4dd3e381eb3ff7823444d99e70..34c2c7f59f0a855b2be4ddcd5f2ad2a8995995b1 100644 (file)
@@ -255,7 +255,7 @@ obj-$(CONFIG_MACH_OMAP_3630SDP)             += board-zoom-display.o
 obj-$(CONFIG_MACH_CM_T35)              += board-cm-t35.o
 obj-$(CONFIG_MACH_CM_T3517)            += board-cm-t3517.o
 obj-$(CONFIG_MACH_IGEP0020)            += board-igep0020.o
-obj-$(CONFIG_MACH_OMAP3_TOUCHBOOK)     += board-omap3touchbook.o
+obj-$(CONFIG_MACH_TOUCHBOOK)           += board-omap3touchbook.o
 obj-$(CONFIG_MACH_OMAP_4430SDP)                += board-4430sdp.o
 obj-$(CONFIG_MACH_OMAP4_PANDA)         += board-omap4panda.o
 
index 25bbcc7ca4dce9794676001167a23f36474fbd86..ae27de8899a69207ac90aca52164bb7ccf37a582 100644 (file)
@@ -1036,13 +1036,13 @@ static struct omap_clk am33xx_clks[] = {
        CLK(NULL,       "mmu_fck",              &mmu_fck,       CK_AM33XX),
        CLK(NULL,       "smartreflex0_fck",     &smartreflex0_fck,      CK_AM33XX),
        CLK(NULL,       "smartreflex1_fck",     &smartreflex1_fck,      CK_AM33XX),
-       CLK(NULL,       "gpt1_fck",             &timer1_fck,    CK_AM33XX),
-       CLK(NULL,       "gpt2_fck",             &timer2_fck,    CK_AM33XX),
-       CLK(NULL,       "gpt3_fck",             &timer3_fck,    CK_AM33XX),
-       CLK(NULL,       "gpt4_fck",             &timer4_fck,    CK_AM33XX),
-       CLK(NULL,       "gpt5_fck",             &timer5_fck,    CK_AM33XX),
-       CLK(NULL,       "gpt6_fck",             &timer6_fck,    CK_AM33XX),
-       CLK(NULL,       "gpt7_fck",             &timer7_fck,    CK_AM33XX),
+       CLK(NULL,       "timer1_fck",           &timer1_fck,    CK_AM33XX),
+       CLK(NULL,       "timer2_fck",           &timer2_fck,    CK_AM33XX),
+       CLK(NULL,       "timer3_fck",           &timer3_fck,    CK_AM33XX),
+       CLK(NULL,       "timer4_fck",           &timer4_fck,    CK_AM33XX),
+       CLK(NULL,       "timer5_fck",           &timer5_fck,    CK_AM33XX),
+       CLK(NULL,       "timer6_fck",           &timer6_fck,    CK_AM33XX),
+       CLK(NULL,       "timer7_fck",           &timer7_fck,    CK_AM33XX),
        CLK(NULL,       "usbotg_fck",           &usbotg_fck,    CK_AM33XX),
        CLK(NULL,       "ieee5000_fck",         &ieee5000_fck,  CK_AM33XX),
        CLK(NULL,       "wdt1_fck",             &wdt1_fck,      CK_AM33XX),
index a0d68dbecfa3bb96cd52226b0f8d7965379d1252..f99e65cfb86223c77ed544d1a8fbe6a0c4ad4b51 100644 (file)
@@ -241,6 +241,52 @@ static void omap3_clkdm_deny_idle(struct clockdomain *clkdm)
                _clkdm_del_autodeps(clkdm);
 }
 
+static int omap3xxx_clkdm_clk_enable(struct clockdomain *clkdm)
+{
+       bool hwsup = false;
+
+       if (!clkdm->clktrctrl_mask)
+               return 0;
+
+       hwsup = omap2_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
+                               clkdm->clktrctrl_mask);
+
+       if (hwsup) {
+               /* Disable HW transitions when we are changing deps */
+               _disable_hwsup(clkdm);
+               _clkdm_add_autodeps(clkdm);
+               _enable_hwsup(clkdm);
+       } else {
+               if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
+                       omap3_clkdm_wakeup(clkdm);
+       }
+
+       return 0;
+}
+
+static int omap3xxx_clkdm_clk_disable(struct clockdomain *clkdm)
+{
+       bool hwsup = false;
+
+       if (!clkdm->clktrctrl_mask)
+               return 0;
+
+       hwsup = omap2_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
+                               clkdm->clktrctrl_mask);
+
+       if (hwsup) {
+               /* Disable HW transitions when we are changing deps */
+               _disable_hwsup(clkdm);
+               _clkdm_del_autodeps(clkdm);
+               _enable_hwsup(clkdm);
+       } else {
+               if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP)
+                       omap3_clkdm_sleep(clkdm);
+       }
+
+       return 0;
+}
+
 struct clkdm_ops omap2_clkdm_operations = {
        .clkdm_add_wkdep        = omap2_clkdm_add_wkdep,
        .clkdm_del_wkdep        = omap2_clkdm_del_wkdep,
@@ -267,6 +313,6 @@ struct clkdm_ops omap3_clkdm_operations = {
        .clkdm_wakeup           = omap3_clkdm_wakeup,
        .clkdm_allow_idle       = omap3_clkdm_allow_idle,
        .clkdm_deny_idle        = omap3_clkdm_deny_idle,
-       .clkdm_clk_enable       = omap2_clkdm_clk_enable,
-       .clkdm_clk_disable      = omap2_clkdm_clk_disable,
+       .clkdm_clk_enable       = omap3xxx_clkdm_clk_enable,
+       .clkdm_clk_disable      = omap3xxx_clkdm_clk_disable,
 };
index 766338fe4d347746ee04eb3961452a7b3f8f6aa9..975f6bda0e0b7a84855a2e6c90f8051df747f6a2 100644 (file)
@@ -67,6 +67,7 @@
 #define OMAP3430_EN_IVA2_DPLL_MASK                     (0x7 << 0)
 
 /* CM_IDLEST_IVA2 */
+#define OMAP3430_ST_IVA2_SHIFT                         0
 #define OMAP3430_ST_IVA2_MASK                          (1 << 0)
 
 /* CM_IDLEST_PLL_IVA2 */
index 05fdebfaa195b0e5fc87e33217f24ce1b5c09822..330d4c6e746b703819f95ba41d733a88523b555a 100644 (file)
@@ -46,7 +46,7 @@
 static void __iomem *wakeupgen_base;
 static void __iomem *sar_base;
 static DEFINE_SPINLOCK(wakeupgen_lock);
-static unsigned int irq_target_cpu[NR_IRQS];
+static unsigned int irq_target_cpu[MAX_IRQS];
 static unsigned int irq_banks = MAX_NR_REG_BANKS;
 static unsigned int max_irqs = MAX_IRQS;
 static unsigned int omap_secure_apis;
index 6ca8e519968d0c4e82e94fb384ab84da90a892b1..37afbd173c2c27969e81f37917d70a767e8fbc2d 100644 (file)
@@ -1889,6 +1889,7 @@ static int _enable(struct omap_hwmod *oh)
                        _enable_sysc(oh);
                }
        } else {
+               _omap4_disable_module(oh);
                _disable_clocks(oh);
                pr_debug("omap_hwmod: %s: _wait_target_ready: %d\n",
                         oh->name, r);
index c9e38200216b2985cb3ef997e530891b8e3b19dd..ce7e6068768f3cebbb4bb8e32ea94e4ae6aca3a3 100644 (file)
@@ -100,9 +100,9 @@ static struct omap_hwmod omap3xxx_mpu_hwmod = {
 
 /* IVA2 (IVA2) */
 static struct omap_hwmod_rst_info omap3xxx_iva_resets[] = {
-       { .name = "logic", .rst_shift = 0 },
-       { .name = "seq0", .rst_shift = 1 },
-       { .name = "seq1", .rst_shift = 2 },
+       { .name = "logic", .rst_shift = 0, .st_shift = 8 },
+       { .name = "seq0", .rst_shift = 1, .st_shift = 9 },
+       { .name = "seq1", .rst_shift = 2, .st_shift = 10 },
 };
 
 static struct omap_hwmod omap3xxx_iva_hwmod = {
@@ -112,6 +112,15 @@ static struct omap_hwmod omap3xxx_iva_hwmod = {
        .rst_lines      = omap3xxx_iva_resets,
        .rst_lines_cnt  = ARRAY_SIZE(omap3xxx_iva_resets),
        .main_clk       = "iva2_ck",
+       .prcm = {
+               .omap2 = {
+                       .module_offs = OMAP3430_IVA2_MOD,
+                       .prcm_reg_id = 1,
+                       .module_bit = OMAP3430_CM_FCLKEN_IVA2_EN_IVA2_SHIFT,
+                       .idlest_reg_id = 1,
+                       .idlest_idle_bit = OMAP3430_ST_IVA2_SHIFT,
+               }
+       },
 };
 
 /* timer class */
index 242aee498ceb21466e33ee04035ed63147e4a615..afb60917a948b64bb3b0ed9b90924facc27fc7f6 100644 (file)
@@ -4210,7 +4210,7 @@ static struct omap_hwmod_ocp_if omap44xx_dsp__iva = {
 };
 
 /* dsp -> sl2if */
-static struct omap_hwmod_ocp_if omap44xx_dsp__sl2if = {
+static struct omap_hwmod_ocp_if __maybe_unused omap44xx_dsp__sl2if = {
        .master         = &omap44xx_dsp_hwmod,
        .slave          = &omap44xx_sl2if_hwmod,
        .clk            = "dpll_iva_m5x2_ck",
@@ -4828,7 +4828,7 @@ static struct omap_hwmod_ocp_if omap44xx_l3_main_2__iss = {
 };
 
 /* iva -> sl2if */
-static struct omap_hwmod_ocp_if omap44xx_iva__sl2if = {
+static struct omap_hwmod_ocp_if __maybe_unused omap44xx_iva__sl2if = {
        .master         = &omap44xx_iva_hwmod,
        .slave          = &omap44xx_sl2if_hwmod,
        .clk            = "dpll_iva_m5x2_ck",
@@ -5362,7 +5362,7 @@ static struct omap_hwmod_ocp_if omap44xx_l4_wkup__scrm = {
 };
 
 /* l3_main_2 -> sl2if */
-static struct omap_hwmod_ocp_if omap44xx_l3_main_2__sl2if = {
+static struct omap_hwmod_ocp_if __maybe_unused omap44xx_l3_main_2__sl2if = {
        .master         = &omap44xx_l3_main_2_hwmod,
        .slave          = &omap44xx_sl2if_hwmod,
        .clk            = "l3_div_ck",
@@ -6032,7 +6032,7 @@ static struct omap_hwmod_ocp_if *omap44xx_hwmod_ocp_ifs[] __initdata = {
        &omap44xx_l4_abe__dmic,
        &omap44xx_l4_abe__dmic_dma,
        &omap44xx_dsp__iva,
-       &omap44xx_dsp__sl2if,
+       /* &omap44xx_dsp__sl2if, */
        &omap44xx_l4_cfg__dsp,
        &omap44xx_l3_main_2__dss,
        &omap44xx_l4_per__dss,
@@ -6068,7 +6068,7 @@ static struct omap_hwmod_ocp_if *omap44xx_hwmod_ocp_ifs[] __initdata = {
        &omap44xx_l4_per__i2c4,
        &omap44xx_l3_main_2__ipu,
        &omap44xx_l3_main_2__iss,
-       &omap44xx_iva__sl2if,
+       /* &omap44xx_iva__sl2if, */
        &omap44xx_l3_main_2__iva,
        &omap44xx_l4_wkup__kbd,
        &omap44xx_l4_cfg__mailbox,
@@ -6099,7 +6099,7 @@ static struct omap_hwmod_ocp_if *omap44xx_hwmod_ocp_ifs[] __initdata = {
        &omap44xx_l4_cfg__cm_core,
        &omap44xx_l4_wkup__prm,
        &omap44xx_l4_wkup__scrm,
-       &omap44xx_l3_main_2__sl2if,
+       /* &omap44xx_l3_main_2__sl2if, */
        &omap44xx_l4_abe__slimbus1,
        &omap44xx_l4_abe__slimbus1_dma,
        &omap44xx_l4_per__slimbus2,
index 2ff6d41ec6c6c004ace041b525ec1821d6389653..2ba4f57dda866df44689834370916395ab9a4c3c 100644 (file)
@@ -260,6 +260,7 @@ static u32 notrace dmtimer_read_sched_clock(void)
        return 0;
 }
 
+#ifdef CONFIG_OMAP_32K_TIMER
 /* Setup free-running counter for clocksource */
 static int __init omap2_sync32k_clocksource_init(void)
 {
@@ -299,6 +300,12 @@ static int __init omap2_sync32k_clocksource_init(void)
 
        return ret;
 }
+#else
+static inline int omap2_sync32k_clocksource_init(void)
+{
+       return -ENODEV;
+}
+#endif
 
 static void __init omap2_gptimer_clocksource_init(int gptimer_id,
                                                const char *fck_source)
index 53b7ea92c32c119bcfc6d44a96b92fac915363da..3b8a0171c3cb3e30aa7242afcdc65f13cd5c11de 100644 (file)
@@ -346,11 +346,11 @@ static struct resource sh_mmcif_resources[] = {
                .flags  = IORESOURCE_MEM,
        },
        [1] = {
-               .start  = gic_spi(141),
+               .start  = gic_spi(140),
                .flags  = IORESOURCE_IRQ,
        },
        [2] = {
-               .start  = gic_spi(140),
+               .start  = gic_spi(141),
                .flags  = IORESOURCE_IRQ,
        },
 };
index 119bc52ab93ed7675d4528a779e97270d308c70e..4e07eec1270dd3b3fa51860fbc735a5d1fb9e18b 100644 (file)
@@ -63,10 +63,11 @@ static int contextidr_notifier(struct notifier_block *unused, unsigned long cmd,
        pid = task_pid_nr(thread->task) << ASID_BITS;
        asm volatile(
        "       mrc     p15, 0, %0, c13, c0, 1\n"
-       "       bfi     %1, %0, #0, %2\n"
-       "       mcr     p15, 0, %1, c13, c0, 1\n"
+       "       and     %0, %0, %2\n"
+       "       orr     %0, %0, %1\n"
+       "       mcr     p15, 0, %0, c13, c0, 1\n"
        : "=r" (contextidr), "+r" (pid)
-       : "I" (ASID_BITS));
+       : "I" (~ASID_MASK));
        isb();
 
        return NOTIFY_OK;
index 051204fc4617670321bfa5fa7c1f3621cb49a33c..e59c4ab71bcb78282f968cebbda09c43b49809ff 100644 (file)
@@ -489,7 +489,7 @@ static bool __in_atomic_pool(void *start, size_t size)
        void *pool_start = pool->vaddr;
        void *pool_end = pool->vaddr + pool->size;
 
-       if (start < pool_start || start > pool_end)
+       if (start < pool_start || start >= pool_end)
                return false;
 
        if (end <= pool_end)
index 6776160618ef0ede79d07b4f6a0873c30df2a1d0..a8ee92da3544926138f68116fbddbc2c55b64dbf 100644 (file)
@@ -55,6 +55,9 @@ extern void __flush_dcache_page(struct address_space *mapping, struct page *page
 /* permanent static mappings from iotable_init() */
 #define VM_ARM_STATIC_MAPPING  0x40000000
 
+/* empty mapping */
+#define VM_ARM_EMPTY_MAPPING   0x20000000
+
 /* mapping type (attributes) for permanent static mappings */
 #define VM_ARM_MTYPE(mt)               ((mt) << 20)
 #define VM_ARM_MTYPE_MASK      (0x1f << 20)
index 4c2d0451e84af1c2a0347a6fe462dd2e3306db3e..c2fa21d0103e0348f2b1f48c886aa11d63809dad 100644 (file)
@@ -807,7 +807,7 @@ static void __init pmd_empty_section_gap(unsigned long addr)
        vm = early_alloc_aligned(sizeof(*vm), __alignof__(*vm));
        vm->addr = (void *)addr;
        vm->size = SECTION_SIZE;
-       vm->flags = VM_IOREMAP | VM_ARM_STATIC_MAPPING;
+       vm->flags = VM_IOREMAP | VM_ARM_EMPTY_MAPPING;
        vm->caller = pmd_empty_section_gap;
        vm_area_add_early(vm);
 }
@@ -820,7 +820,7 @@ static void __init fill_pmd_gaps(void)
 
        /* we're still single threaded hence no lock needed here */
        for (vm = vmlist; vm; vm = vm->next) {
-               if (!(vm->flags & VM_ARM_STATIC_MAPPING))
+               if (!(vm->flags & (VM_ARM_STATIC_MAPPING | VM_ARM_EMPTY_MAPPING)))
                        continue;
                addr = (unsigned long)vm->addr;
                if (addr < next)
@@ -961,8 +961,8 @@ void __init sanity_check_meminfo(void)
                 * Check whether this memory bank would partially overlap
                 * the vmalloc area.
                 */
-               if (__va(bank->start + bank->size) > vmalloc_min ||
-                   __va(bank->start + bank->size) < __va(bank->start)) {
+               if (__va(bank->start + bank->size - 1) >= vmalloc_min ||
+                   __va(bank->start + bank->size - 1) <= __va(bank->start)) {
                        unsigned long newsize = vmalloc_min - __va(bank->start);
                        printk(KERN_NOTICE "Truncating RAM at %.8llx-%.8llx "
                               "to -%.8llx (vmalloc region overlap).\n",
index 627d94f1b010d32d09091bf4f025fabb617bde03..ec466400a2002943de0ccaf840f25266db6149e0 100644 (file)
@@ -98,6 +98,7 @@
 #define MX25_INT_UART1         (NR_IRQS_LEGACY + 45)
 #define MX25_INT_GPIO2         (NR_IRQS_LEGACY + 51)
 #define MX25_INT_GPIO1         (NR_IRQS_LEGACY + 52)
+#define MX25_INT_GPT1          (NR_IRQS_LEGACY + 54)
 #define MX25_INT_FEC           (NR_IRQS_LEGACY + 57)
 
 #define MX25_DMA_REQ_SSI2_RX1  22
index 766181cb5c95c277b8495966835571059a36dda5..024f3b08db29b0046a58457120259e7b5366a1ca 100644 (file)
@@ -68,6 +68,7 @@
 
 static unsigned long omap_sram_start;
 static void __iomem *omap_sram_base;
+static unsigned long omap_sram_skip;
 static unsigned long omap_sram_size;
 static void __iomem *omap_sram_ceil;
 
@@ -106,6 +107,7 @@ static int is_sram_locked(void)
  */
 static void __init omap_detect_sram(void)
 {
+       omap_sram_skip = SRAM_BOOTLOADER_SZ;
        if (cpu_class_is_omap2()) {
                if (is_sram_locked()) {
                        if (cpu_is_omap34xx()) {
@@ -113,6 +115,7 @@ static void __init omap_detect_sram(void)
                                if ((omap_type() == OMAP2_DEVICE_TYPE_EMU) ||
                                    (omap_type() == OMAP2_DEVICE_TYPE_SEC)) {
                                        omap_sram_size = 0x7000; /* 28K */
+                                       omap_sram_skip += SZ_16K;
                                } else {
                                        omap_sram_size = 0x8000; /* 32K */
                                }
@@ -175,8 +178,10 @@ static void __init omap_map_sram(void)
                return;
 
 #ifdef CONFIG_OMAP4_ERRATA_I688
+       if (cpu_is_omap44xx()) {
                omap_sram_start += PAGE_SIZE;
                omap_sram_size -= SZ_16K;
+       }
 #endif
        if (cpu_is_omap34xx()) {
                /*
@@ -203,8 +208,8 @@ static void __init omap_map_sram(void)
         * Looks like we need to preserve some bootloader code at the
         * beginning of SRAM for jumping to flash for reboot to work...
         */
-       memset_io(omap_sram_base + SRAM_BOOTLOADER_SZ, 0,
-                 omap_sram_size - SRAM_BOOTLOADER_SZ);
+       memset_io(omap_sram_base + omap_sram_skip, 0,
+                 omap_sram_size - omap_sram_skip);
 }
 
 /*
@@ -218,7 +223,7 @@ void *omap_sram_push_address(unsigned long size)
 {
        unsigned long available, new_ceil = (unsigned long)omap_sram_ceil;
 
-       available = omap_sram_ceil - (omap_sram_base + SRAM_BOOTLOADER_SZ);
+       available = omap_sram_ceil - (omap_sram_base + omap_sram_skip);
 
        if (size > available) {
                pr_err("Not enough space in SRAM\n");
index 65c5eca475e76ef839a16aaef568173a2dc0ee1c..d1116e2dfbeaf57ccd0c2a1aff9ce413d9b57f2a 100644 (file)
@@ -144,6 +144,7 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
 
 int clk_set_rate(struct clk *clk, unsigned long rate)
 {
+       unsigned long flags;
        int ret;
 
        if (IS_ERR(clk))
@@ -159,9 +160,9 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
        if (clk->ops == NULL || clk->ops->set_rate == NULL)
                return -EINVAL;
 
-       spin_lock(&clocks_lock);
+       spin_lock_irqsave(&clocks_lock, flags);
        ret = (clk->ops->set_rate)(clk, rate);
-       spin_unlock(&clocks_lock);
+       spin_unlock_irqrestore(&clocks_lock, flags);
 
        return ret;
 }
@@ -173,17 +174,18 @@ struct clk *clk_get_parent(struct clk *clk)
 
 int clk_set_parent(struct clk *clk, struct clk *parent)
 {
+       unsigned long flags;
        int ret = 0;
 
        if (IS_ERR(clk))
                return -EINVAL;
 
-       spin_lock(&clocks_lock);
+       spin_lock_irqsave(&clocks_lock, flags);
 
        if (clk->ops && clk->ops->set_parent)
                ret = (clk->ops->set_parent)(clk, parent);
 
-       spin_unlock(&clocks_lock);
+       spin_unlock_irqrestore(&clocks_lock, flags);
 
        return ret;
 }
index 75f9ee967ea71cc696c3ed9cc5efb390b4ed209a..9cd13b4ce42ba9eb4367be63010802a632577629 100644 (file)
@@ -146,9 +146,3 @@ struct clk_ops clk_ops1 = {
 };
 #endif /* MCFPM_PPMCR1 */
 #endif /* MCFPM_PPMCR0 */
-
-struct clk *devm_clk_get(struct device *dev, const char *id)
-{
-       return NULL;
-}
-EXPORT_SYMBOL(devm_clk_get);
index e7e03ecf54959988e029e81783422fa0c5b78554..afc379ca375301f7e916a4fd46447239ad478a38 100644 (file)
@@ -102,7 +102,7 @@ static void cmp_init_secondary(void)
        c->vpe_id = (read_c0_tcbind() >> TCBIND_CURVPE_SHIFT) & TCBIND_CURVPE;
 #endif
 #ifdef CONFIG_MIPS_MT_SMTC
-       c->tc_id  = (read_c0_tcbind() >> TCBIND_CURTC_SHIFT) & TCBIND_CURTC;
+       c->tc_id  = (read_c0_tcbind() & TCBIND_CURTC) >> TCBIND_CURTC_SHIFT;
 #endif
 }
 
index 33aadbcf170bb4428020ddb87a17383180719400..dcfd573871c142348c4fa790bfcbd3dcc0c50879 100644 (file)
@@ -152,6 +152,8 @@ static int gup_huge_pud(pud_t pud, unsigned long addr, unsigned long end,
        do {
                VM_BUG_ON(compound_head(page) != head);
                pages[*nr] = page;
+               if (PageTail(page))
+                       get_huge_page_tail(page);
                (*nr)++;
                page++;
                refs++;
index 7b13a4caeea41b6377fb9410fe1397f2dce6aef2..fea823f184794a8a9003ce79afd90cb762a4ed52 100644 (file)
@@ -273,16 +273,19 @@ asmlinkage void plat_irq_dispatch(void)
        unsigned int pending = read_c0_cause() & read_c0_status() & ST0_IM;
        int irq;
 
+       if (unlikely(!pending)) {
+               spurious_interrupt();
+               return;
+       }
+
        irq = irq_ffs(pending);
 
        if (irq == MIPSCPU_INT_I8259A)
                malta_hw0_irqdispatch();
        else if (gic_present && ((1 << irq) & ipi_map[smp_processor_id()]))
                malta_ipi_irqdispatch();
-       else if (irq >= 0)
-               do_IRQ(MIPS_CPU_IRQ_BASE + irq);
        else
-               spurious_interrupt();
+               do_IRQ(MIPS_CPU_IRQ_BASE + irq);
 }
 
 #ifdef CONFIG_MIPS_MT_SMP
index 4c35301720e76c04aad4eade1d88310c107ba6fd..80562b81f0f2a2036fbf0a28781601b420c5d872 100644 (file)
@@ -138,11 +138,6 @@ static int __init malta_add_devices(void)
        if (err)
                return err;
 
-       /*
-        * Set RTC to BCD mode to support current alarm code.
-        */
-       CMOS_WRITE(CMOS_READ(RTC_CONTROL) & ~RTC_DM_BINARY, RTC_CONTROL);
-
        return 0;
 }
 
index 799ed0f1643d135c843f3a1cdc41204063289f0f..2d6e6e3805641bbc79ce1ee11aa639f48f5e2933 100644 (file)
@@ -66,16 +66,6 @@ static inline pte_t huge_ptep_get(pte_t *ptep)
        return pte;
 }
 
-static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm,
-                                           unsigned long addr, pte_t *ptep)
-{
-       pte_t pte = huge_ptep_get(ptep);
-
-       mm->context.flush_mm = 1;
-       pmd_clear((pmd_t *) ptep);
-       return pte;
-}
-
 static inline void __pmd_csp(pmd_t *pmdp)
 {
        register unsigned long reg2 asm("2") = pmd_val(*pmdp);
@@ -117,6 +107,15 @@ static inline void huge_ptep_invalidate(struct mm_struct *mm,
                __pmd_csp(pmdp);
 }
 
+static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm,
+                                           unsigned long addr, pte_t *ptep)
+{
+       pte_t pte = huge_ptep_get(ptep);
+
+       huge_ptep_invalidate(mm, addr, ptep);
+       return pte;
+}
+
 #define huge_ptep_set_access_flags(__vma, __addr, __ptep, __entry, __dirty) \
 ({                                                                         \
        int __changed = !pte_same(huge_ptep_get(__ptep), __entry);          \
@@ -131,10 +130,7 @@ static inline void huge_ptep_invalidate(struct mm_struct *mm,
 ({                                                                     \
        pte_t __pte = huge_ptep_get(__ptep);                            \
        if (pte_write(__pte)) {                                         \
-               (__mm)->context.flush_mm = 1;                           \
-               if (atomic_read(&(__mm)->context.attach_count) > 1 ||   \
-                   (__mm) != current->active_mm)                       \
-                       huge_ptep_invalidate(__mm, __addr, __ptep);     \
+               huge_ptep_invalidate(__mm, __addr, __ptep);             \
                set_huge_pte_at(__mm, __addr, __ptep,                   \
                                huge_pte_wrprotect(__pte));             \
        }                                                               \
index 9fde315f3a7cd42184a54f2174584258076e6a60..1d8fe2b17ef6f1d0069c0e3cc8ff70c7d9cc402a 100644 (file)
@@ -90,12 +90,10 @@ static inline void __tlb_flush_mm(struct mm_struct * mm)
 
 static inline void __tlb_flush_mm_cond(struct mm_struct * mm)
 {
-       spin_lock(&mm->page_table_lock);
        if (mm->context.flush_mm) {
                __tlb_flush_mm(mm);
                mm->context.flush_mm = 0;
        }
-       spin_unlock(&mm->page_table_lock);
 }
 
 /*
index f86c81e13c374be484f591faead417a7fb3ee572..40b57693de389b24a5b08decae1dbb1fedd47e6c 100644 (file)
@@ -974,11 +974,13 @@ static void __init setup_hwcaps(void)
        if (MACHINE_HAS_HPAGE)
                elf_hwcap |= HWCAP_S390_HPAGE;
 
+#if defined(CONFIG_64BIT)
        /*
         * 64-bit register support for 31-bit processes
         * HWCAP_S390_HIGH_GPRS is bit 9.
         */
        elf_hwcap |= HWCAP_S390_HIGH_GPRS;
+#endif
 
        get_cpu_id(&cpu_id);
        switch (cpu_id.machine) {
index 60ee2b8837972c27d8bfb3d95dd9fc3b0b4d0091..2d37bb861faf69cf43e30971881fae771322158a 100644 (file)
@@ -2,69 +2,82 @@
  *  User access functions based on page table walks for enhanced
  *  system layout without hardware support.
  *
- *    Copyright IBM Corp. 2006
+ *    Copyright IBM Corp. 2006, 2012
  *    Author(s): Gerald Schaefer (gerald.schaefer@de.ibm.com)
  */
 
 #include <linux/errno.h>
 #include <linux/hardirq.h>
 #include <linux/mm.h>
+#include <linux/hugetlb.h>
 #include <asm/uaccess.h>
 #include <asm/futex.h>
 #include "uaccess.h"
 
-static inline pte_t *follow_table(struct mm_struct *mm, unsigned long addr)
+
+/*
+ * Returns kernel address for user virtual address. If the returned address is
+ * >= -4095 (IS_ERR_VALUE(x) returns true), a fault has occured and the address
+ * contains the (negative) exception code.
+ */
+static __always_inline unsigned long follow_table(struct mm_struct *mm,
+                                                 unsigned long addr, int write)
 {
        pgd_t *pgd;
        pud_t *pud;
        pmd_t *pmd;
+       pte_t *ptep;
 
        pgd = pgd_offset(mm, addr);
        if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
-               return (pte_t *) 0x3a;
+               return -0x3aUL;
 
        pud = pud_offset(pgd, addr);
        if (pud_none(*pud) || unlikely(pud_bad(*pud)))
-               return (pte_t *) 0x3b;
+               return -0x3bUL;
 
        pmd = pmd_offset(pud, addr);
-       if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd)))
-               return (pte_t *) 0x10;
+       if (pmd_none(*pmd))
+               return -0x10UL;
+       if (pmd_huge(*pmd)) {
+               if (write && (pmd_val(*pmd) & _SEGMENT_ENTRY_RO))
+                       return -0x04UL;
+               return (pmd_val(*pmd) & HPAGE_MASK) + (addr & ~HPAGE_MASK);
+       }
+       if (unlikely(pmd_bad(*pmd)))
+               return -0x10UL;
+
+       ptep = pte_offset_map(pmd, addr);
+       if (!pte_present(*ptep))
+               return -0x11UL;
+       if (write && !pte_write(*ptep))
+               return -0x04UL;
 
-       return pte_offset_map(pmd, addr);
+       return (pte_val(*ptep) & PAGE_MASK) + (addr & ~PAGE_MASK);
 }
 
 static __always_inline size_t __user_copy_pt(unsigned long uaddr, void *kptr,
                                             size_t n, int write_user)
 {
        struct mm_struct *mm = current->mm;
-       unsigned long offset, pfn, done, size;
-       pte_t *pte;
+       unsigned long offset, done, size, kaddr;
        void *from, *to;
 
        done = 0;
 retry:
        spin_lock(&mm->page_table_lock);
        do {
-               pte = follow_table(mm, uaddr);
-               if ((unsigned long) pte < 0x1000)
+               kaddr = follow_table(mm, uaddr, write_user);
+               if (IS_ERR_VALUE(kaddr))
                        goto fault;
-               if (!pte_present(*pte)) {
-                       pte = (pte_t *) 0x11;
-                       goto fault;
-               } else if (write_user && !pte_write(*pte)) {
-                       pte = (pte_t *) 0x04;
-                       goto fault;
-               }
 
-               pfn = pte_pfn(*pte);
-               offset = uaddr & (PAGE_SIZE - 1);
+               offset = uaddr & ~PAGE_MASK;
                size = min(n - done, PAGE_SIZE - offset);
                if (write_user) {
-                       to = (void *)((pfn << PAGE_SHIFT) + offset);
+                       to = (void *) kaddr;
                        from = kptr + done;
                } else {
-                       from = (void *)((pfn << PAGE_SHIFT) + offset);
+                       from = (void *) kaddr;
                        to = kptr + done;
                }
                memcpy(to, from, size);
@@ -75,7 +88,7 @@ retry:
        return n - done;
 fault:
        spin_unlock(&mm->page_table_lock);
-       if (__handle_fault(uaddr, (unsigned long) pte, write_user))
+       if (__handle_fault(uaddr, -kaddr, write_user))
                return n - done;
        goto retry;
 }
@@ -84,27 +97,22 @@ fault:
  * Do DAT for user address by page table walk, return kernel address.
  * This function needs to be called with current->mm->page_table_lock held.
  */
-static __always_inline unsigned long __dat_user_addr(unsigned long uaddr)
+static __always_inline unsigned long __dat_user_addr(unsigned long uaddr,
+                                                    int write)
 {
        struct mm_struct *mm = current->mm;
-       unsigned long pfn;
-       pte_t *pte;
+       unsigned long kaddr;
        int rc;
 
 retry:
-       pte = follow_table(mm, uaddr);
-       if ((unsigned long) pte < 0x1000)
-               goto fault;
-       if (!pte_present(*pte)) {
-               pte = (pte_t *) 0x11;
+       kaddr = follow_table(mm, uaddr, write);
+       if (IS_ERR_VALUE(kaddr))
                goto fault;
-       }
 
-       pfn = pte_pfn(*pte);
-       return (pfn << PAGE_SHIFT) + (uaddr & (PAGE_SIZE - 1));
+       return kaddr;
 fault:
        spin_unlock(&mm->page_table_lock);
-       rc = __handle_fault(uaddr, (unsigned long) pte, 0);
+       rc = __handle_fault(uaddr, -kaddr, write);
        spin_lock(&mm->page_table_lock);
        if (!rc)
                goto retry;
@@ -159,11 +167,9 @@ static size_t clear_user_pt(size_t n, void __user *to)
 
 static size_t strnlen_user_pt(size_t count, const char __user *src)
 {
-       char *addr;
        unsigned long uaddr = (unsigned long) src;
        struct mm_struct *mm = current->mm;
-       unsigned long offset, pfn, done, len;
-       pte_t *pte;
+       unsigned long offset, done, len, kaddr;
        size_t len_str;
 
        if (segment_eq(get_fs(), KERNEL_DS))
@@ -172,19 +178,13 @@ static size_t strnlen_user_pt(size_t count, const char __user *src)
 retry:
        spin_lock(&mm->page_table_lock);
        do {
-               pte = follow_table(mm, uaddr);
-               if ((unsigned long) pte < 0x1000)
-                       goto fault;
-               if (!pte_present(*pte)) {
-                       pte = (pte_t *) 0x11;
+               kaddr = follow_table(mm, uaddr, 0);
+               if (IS_ERR_VALUE(kaddr))
                        goto fault;
-               }
 
-               pfn = pte_pfn(*pte);
-               offset = uaddr & (PAGE_SIZE-1);
-               addr = (char *)(pfn << PAGE_SHIFT) + offset;
+               offset = uaddr & ~PAGE_MASK;
                len = min(count - done, PAGE_SIZE - offset);
-               len_str = strnlen(addr, len);
+               len_str = strnlen((char *) kaddr, len);
                done += len_str;
                uaddr += len_str;
        } while ((len_str == len) && (done < count));
@@ -192,7 +192,7 @@ retry:
        return done + 1;
 fault:
        spin_unlock(&mm->page_table_lock);
-       if (__handle_fault(uaddr, (unsigned long) pte, 0))
+       if (__handle_fault(uaddr, -kaddr, 0))
                return 0;
        goto retry;
 }
@@ -225,11 +225,10 @@ static size_t copy_in_user_pt(size_t n, void __user *to,
                              const void __user *from)
 {
        struct mm_struct *mm = current->mm;
-       unsigned long offset_from, offset_to, offset_max, pfn_from, pfn_to,
-                     uaddr, done, size, error_code;
+       unsigned long offset_max, uaddr, done, size, error_code;
        unsigned long uaddr_from = (unsigned long) from;
        unsigned long uaddr_to = (unsigned long) to;
-       pte_t *pte_from, *pte_to;
+       unsigned long kaddr_to, kaddr_from;
        int write_user;
 
        if (segment_eq(get_fs(), KERNEL_DS)) {
@@ -242,38 +241,23 @@ retry:
        do {
                write_user = 0;
                uaddr = uaddr_from;
-               pte_from = follow_table(mm, uaddr_from);
-               error_code = (unsigned long) pte_from;
-               if (error_code < 0x1000)
-                       goto fault;
-               if (!pte_present(*pte_from)) {
-                       error_code = 0x11;
+               kaddr_from = follow_table(mm, uaddr_from, 0);
+               error_code = kaddr_from;
+               if (IS_ERR_VALUE(error_code))
                        goto fault;
-               }
 
                write_user = 1;
                uaddr = uaddr_to;
-               pte_to = follow_table(mm, uaddr_to);
-               error_code = (unsigned long) pte_to;
-               if (error_code < 0x1000)
-                       goto fault;
-               if (!pte_present(*pte_to)) {
-                       error_code = 0x11;
+               kaddr_to = follow_table(mm, uaddr_to, 1);
+               error_code = (unsigned long) kaddr_to;
+               if (IS_ERR_VALUE(error_code))
                        goto fault;
-               } else if (!pte_write(*pte_to)) {
-                       error_code = 0x04;
-                       goto fault;
-               }
 
-               pfn_from = pte_pfn(*pte_from);
-               pfn_to = pte_pfn(*pte_to);
-               offset_from = uaddr_from & (PAGE_SIZE-1);
-               offset_to = uaddr_from & (PAGE_SIZE-1);
-               offset_max = max(offset_from, offset_to);
+               offset_max = max(uaddr_from & ~PAGE_MASK,
+                                uaddr_to & ~PAGE_MASK);
                size = min(n - done, PAGE_SIZE - offset_max);
 
-               memcpy((void *)(pfn_to << PAGE_SHIFT) + offset_to,
-                      (void *)(pfn_from << PAGE_SHIFT) + offset_from, size);
+               memcpy((void *) kaddr_to, (void *) kaddr_from, size);
                done += size;
                uaddr_from += size;
                uaddr_to += size;
@@ -282,7 +266,7 @@ retry:
        return n - done;
 fault:
        spin_unlock(&mm->page_table_lock);
-       if (__handle_fault(uaddr, error_code, write_user))
+       if (__handle_fault(uaddr, -error_code, write_user))
                return n - done;
        goto retry;
 }
@@ -341,7 +325,7 @@ int futex_atomic_op_pt(int op, u32 __user *uaddr, int oparg, int *old)
                return __futex_atomic_op_pt(op, uaddr, oparg, old);
        spin_lock(&current->mm->page_table_lock);
        uaddr = (u32 __force __user *)
-               __dat_user_addr((__force unsigned long) uaddr);
+               __dat_user_addr((__force unsigned long) uaddr, 1);
        if (!uaddr) {
                spin_unlock(&current->mm->page_table_lock);
                return -EFAULT;
@@ -378,7 +362,7 @@ int futex_atomic_cmpxchg_pt(u32 *uval, u32 __user *uaddr,
                return __futex_atomic_cmpxchg_pt(uval, uaddr, oldval, newval);
        spin_lock(&current->mm->page_table_lock);
        uaddr = (u32 __force __user *)
-               __dat_user_addr((__force unsigned long) uaddr);
+               __dat_user_addr((__force unsigned long) uaddr, 1);
        if (!uaddr) {
                spin_unlock(&current->mm->page_table_lock);
                return -EFAULT;
index a1e9d69a9c90e579c2f68cea8869ae6ec26d05c4..584b93674ea43bceea2259142081ef81a02ad9aa 100644 (file)
@@ -169,7 +169,7 @@ static ssize_t hw_interval_write(struct file *file, char const __user *buf,
        if (*offset)
                return -EINVAL;
        retval = oprofilefs_ulong_from_user(&val, buf, count);
-       if (retval)
+       if (retval <= 0)
                return retval;
        if (val < oprofile_min_interval)
                oprofile_hw_interval = oprofile_min_interval;
@@ -212,7 +212,7 @@ static ssize_t hwsampler_zero_write(struct file *file, char const __user *buf,
                return -EINVAL;
 
        retval = oprofilefs_ulong_from_user(&val, buf, count);
-       if (retval)
+       if (retval <= 0)
                return retval;
        if (val != 0)
                return -EINVAL;
@@ -243,7 +243,7 @@ static ssize_t hwsampler_kernel_write(struct file *file, char const __user *buf,
                return -EINVAL;
 
        retval = oprofilefs_ulong_from_user(&val, buf, count);
-       if (retval)
+       if (retval <= 0)
                return retval;
 
        if (val != 0 && val != 1)
@@ -278,7 +278,7 @@ static ssize_t hwsampler_user_write(struct file *file, char const __user *buf,
                return -EINVAL;
 
        retval = oprofilefs_ulong_from_user(&val, buf, count);
-       if (retval)
+       if (retval <= 0)
                return retval;
 
        if (val != 0 && val != 1)
@@ -317,7 +317,7 @@ static ssize_t timer_enabled_write(struct file *file, char const __user *buf,
                return -EINVAL;
 
        retval = oprofilefs_ulong_from_user(&val, buf, count);
-       if (retval)
+       if (retval <= 0)
                return retval;
 
        if (val != 0 && val != 1)
index b7cf6a547f117c5eeae4b69f6b1ccbcdb41b37c8..7e605b95592aea7ac767b817df3a7a61af8d4f20 100644 (file)
@@ -933,7 +933,7 @@ ret_with_reschedule:
 
        pta     restore_all, tr1
 
-       movi    _TIF_SIGPENDING, r8
+       movi    (_TIF_SIGPENDING|_TIF_NOTIFY_RESUME), r8
        and     r8, r7, r8
        pta     work_notifysig, tr0
        bne     r8, ZERO, tr0
index f67601cb3f1f47da13a49ae4948021800b9a2f4d..b96489d8b27dde9e38630d51de4e9d7f04115c08 100644 (file)
@@ -139,7 +139,7 @@ work_pending:
        ! r8: current_thread_info
        ! t:  result of "tst    #_TIF_NEED_RESCHED, r0"
        bf/s    work_resched
-        tst    #_TIF_SIGPENDING, r0
+        tst    #(_TIF_SIGPENDING | _TIF_NOTIFY_RESUME), r0
 work_notifysig:
        bt/s    __restore_all
         mov    r15, r4
index 15e0a16939765148efd633663c131390daaa9893..f1ddc0d2367947d850e863aa4d0a897705074574 100644 (file)
@@ -48,9 +48,7 @@ void *module_alloc(unsigned long size)
                return NULL;
 
        ret = module_map(size);
-       if (!ret)
-               ret = ERR_PTR(-ENOMEM);
-       else
+       if (ret)
                memset(ret, 0, size);
 
        return ret;
@@ -116,6 +114,10 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
                v = sym->st_value + rel[i].r_addend;
 
                switch (ELF_R_TYPE(rel[i].r_info) & 0xff) {
+               case R_SPARC_DISP32:
+                       v -= (Elf_Addr) location;
+                       *loc32 = v;
+                       break;
 #ifdef CONFIG_SPARC64
                case R_SPARC_64:
                        location[0] = v >> 56;
@@ -128,11 +130,6 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
                        location[7] = v >>  0;
                        break;
 
-               case R_SPARC_DISP32:
-                       v -= (Elf_Addr) location;
-                       *loc32 = v;
-                       break;
-
                case R_SPARC_WDISP19:
                        v -= (Elf_Addr) location;
                        *loc32 = (*loc32 & ~0x7ffff) |
index 8ec3a1aa4abd3f612df4a5da31f30c67554d5182..50a1d1f9b6d3ac28c6b2294cd579d71cda2e75c1 100644 (file)
@@ -746,10 +746,10 @@ config SWIOTLB
        def_bool y if X86_64
        ---help---
          Support for software bounce buffers used on x86-64 systems
-         which don't have a hardware IOMMU (e.g. the current generation
-         of Intel's x86-64 CPUs). Using this PCI devices which can only
-         access 32-bits of memory can be used on systems with more than
-         3 GB of memory. If unsure, say Y.
+         which don't have a hardware IOMMU. Using this PCI devices
+         which can only access 32-bits of memory can be used on systems
+         with more than 3 GB of memory.
+         If unsure, say Y.
 
 config IOMMU_HELPER
        def_bool (CALGARY_IOMMU || GART_IOMMU || SWIOTLB || AMD_IOMMU)
index 682e9c210baacb99b86817adbd45ebbd17a30955..474ca35b1bce342356b27ac4118f133135d8c249 100644 (file)
@@ -142,7 +142,7 @@ KBUILD_CFLAGS += $(call cc-option,-mno-avx,)
 KBUILD_CFLAGS += $(mflags-y)
 KBUILD_AFLAGS += $(mflags-y)
 
-archscripts:
+archscripts: scripts_basic
        $(Q)$(MAKE) $(build)=arch/x86/tools relocs
 
 ###
index 93971e841dd5e7eaa032f5f4a8d5542040900a6d..472b9b783019a2e2f7c7b273ab7b13b96ec257b9 100644 (file)
@@ -51,7 +51,8 @@ extern unsigned long set_phys_range_identity(unsigned long pfn_s,
 
 extern int m2p_add_override(unsigned long mfn, struct page *page,
                            struct gnttab_map_grant_ref *kmap_op);
-extern int m2p_remove_override(struct page *page, bool clear_pte);
+extern int m2p_remove_override(struct page *page,
+                               struct gnttab_map_grant_ref *kmap_op);
 extern struct page *m2p_find_override(unsigned long mfn);
 extern unsigned long m2p_find_override_pfn(unsigned long mfn, unsigned long pfn);
 
index 6605a81ba3399fb8c1c9044b0e7aa8c2e22ff882..8b6defe7eefc5e6de1538984622f109c4223528d 100644 (file)
@@ -586,6 +586,8 @@ extern struct event_constraint intel_westmere_pebs_event_constraints[];
 
 extern struct event_constraint intel_snb_pebs_event_constraints[];
 
+extern struct event_constraint intel_ivb_pebs_event_constraints[];
+
 struct event_constraint *intel_pebs_constraints(struct perf_event *event);
 
 void intel_pmu_pebs_enable(struct perf_event *event);
index 7bfb5bec8630ada6580dd446e8a62de847d12288..eebd5ffe1bba0b922f9ef8481fd141f2ba9668da 100644 (file)
@@ -209,6 +209,15 @@ static int perf_ibs_precise_event(struct perf_event *event, u64 *config)
        return -EOPNOTSUPP;
 }
 
+static const struct perf_event_attr ibs_notsupp = {
+       .exclude_user   = 1,
+       .exclude_kernel = 1,
+       .exclude_hv     = 1,
+       .exclude_idle   = 1,
+       .exclude_host   = 1,
+       .exclude_guest  = 1,
+};
+
 static int perf_ibs_init(struct perf_event *event)
 {
        struct hw_perf_event *hwc = &event->hw;
@@ -229,6 +238,9 @@ static int perf_ibs_init(struct perf_event *event)
        if (event->pmu != &perf_ibs->pmu)
                return -ENOENT;
 
+       if (perf_flags(&event->attr) & perf_flags(&ibs_notsupp))
+               return -EINVAL;
+
        if (config & ~perf_ibs->config_mask)
                return -EINVAL;
 
index 7f2739e03e79a80fc1baaf203cf3a22eccec54dc..6bca492b85475bc8eab5577447ce1fa9a5133223 100644 (file)
@@ -2008,6 +2008,7 @@ __init int intel_pmu_init(void)
                break;
 
        case 28: /* Atom */
+       case 54: /* Cedariew */
                memcpy(hw_cache_event_ids, atom_hw_cache_event_ids,
                       sizeof(hw_cache_event_ids));
 
@@ -2047,7 +2048,6 @@ __init int intel_pmu_init(void)
        case 42: /* SandyBridge */
        case 45: /* SandyBridge, "Romely-EP" */
                x86_add_quirk(intel_sandybridge_quirk);
-       case 58: /* IvyBridge */
                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,
@@ -2072,6 +2072,29 @@ __init int intel_pmu_init(void)
 
                pr_cont("SandyBridge events, ");
                break;
+       case 58: /* IvyBridge */
+               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,
+                      sizeof(hw_cache_extra_regs));
+
+               intel_pmu_lbr_init_snb();
+
+               x86_pmu.event_constraints = intel_snb_event_constraints;
+               x86_pmu.pebs_constraints = intel_ivb_pebs_event_constraints;
+               x86_pmu.pebs_aliases = intel_pebs_aliases_snb;
+               x86_pmu.extra_regs = intel_snb_extra_regs;
+               /* all extra regs are per-cpu when HT is on */
+               x86_pmu.er_flags |= ERF_HAS_RSP_1;
+               x86_pmu.er_flags |= ERF_NO_HT_SHARING;
+
+               /* UOPS_ISSUED.ANY,c=1,i=1 to count stall cycles */
+               intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] =
+                       X86_CONFIG(.event=0x0e, .umask=0x01, .inv=1, .cmask=1);
+
+               pr_cont("IvyBridge events, ");
+               break;
+
 
        default:
                switch (x86_pmu.version) {
index e38d97bf4259fc579f2b070fef85980ae7aaab7a..826054a4f2ee4c54ea4cfb9d29ab117db35e7fc5 100644 (file)
@@ -407,6 +407,20 @@ struct event_constraint intel_snb_pebs_event_constraints[] = {
        EVENT_CONSTRAINT_END
 };
 
+struct event_constraint intel_ivb_pebs_event_constraints[] = {
+        INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PRECDIST */
+        INTEL_UEVENT_CONSTRAINT(0x01c2, 0xf), /* UOPS_RETIRED.ALL */
+        INTEL_UEVENT_CONSTRAINT(0x02c2, 0xf), /* UOPS_RETIRED.RETIRE_SLOTS */
+        INTEL_EVENT_CONSTRAINT(0xc4, 0xf),    /* BR_INST_RETIRED.* */
+        INTEL_EVENT_CONSTRAINT(0xc5, 0xf),    /* BR_MISP_RETIRED.* */
+        INTEL_EVENT_CONSTRAINT(0xcd, 0x8),    /* MEM_TRANS_RETIRED.* */
+        INTEL_EVENT_CONSTRAINT(0xd0, 0xf),    /* MEM_UOP_RETIRED.* */
+        INTEL_EVENT_CONSTRAINT(0xd1, 0xf),    /* MEM_LOAD_UOPS_RETIRED.* */
+        INTEL_EVENT_CONSTRAINT(0xd2, 0xf),    /* MEM_LOAD_UOPS_LLC_HIT_RETIRED.* */
+        INTEL_EVENT_CONSTRAINT(0xd3, 0xf),    /* MEM_LOAD_UOPS_LLC_MISS_RETIRED.* */
+        EVENT_CONSTRAINT_END
+};
+
 struct event_constraint *intel_pebs_constraints(struct perf_event *event)
 {
        struct event_constraint *c;
index 520b4265fcd215ee5afe240fe11c944dd6bc06aa..da02e9cc3754b4a2c1a37c1edb44865143f7f723 100644 (file)
@@ -686,7 +686,8 @@ void intel_pmu_lbr_init_atom(void)
         * to have an operational LBR which can freeze
         * on PMU interrupt
         */
-       if (boot_cpu_data.x86_mask < 10) {
+       if (boot_cpu_data.x86_model == 28
+           && boot_cpu_data.x86_mask < 10) {
                pr_cont("LBR disabled due to erratum");
                return;
        }
index 0a5571080e7453bc9d602061ffbe2c6ad81647a2..38e4894165b9cdc096e038de65fc4ee04bdb0690 100644 (file)
@@ -661,6 +661,11 @@ static void snb_uncore_msr_init_box(struct intel_uncore_box *box)
        }
 }
 
+static struct uncore_event_desc snb_uncore_events[] = {
+       INTEL_UNCORE_EVENT_DESC(clockticks, "event=0xff,umask=0x00"),
+       { /* end: all zeroes */ },
+};
+
 static struct attribute *snb_uncore_formats_attr[] = {
        &format_attr_event.attr,
        &format_attr_umask.attr,
@@ -704,6 +709,7 @@ static struct intel_uncore_type snb_uncore_cbox = {
        .constraints    = snb_uncore_cbox_constraints,
        .ops            = &snb_uncore_msr_ops,
        .format_group   = &snb_uncore_format_group,
+       .event_descs    = snb_uncore_events,
 };
 
 static struct intel_uncore_type *snb_msr_uncores[] = {
index 4873e62db6a18468b23736c5f4adfd2de8b3b85b..9e5bcf1e2376e9713adc8841df162293c859c97c 100644 (file)
@@ -225,6 +225,9 @@ static ssize_t microcode_write(struct file *file, const char __user *buf,
        if (do_microcode_update(buf, len) == 0)
                ret = (ssize_t)len;
 
+       if (ret > 0)
+               perf_check_microcode();
+
        mutex_unlock(&microcode_mutex);
        put_online_cpus();
 
index e0e6990723e982e12f425f2fdd26113725f2d52a..ab1f6a93b527c9bd50acc12a52e9ce157f2cc48b 100644 (file)
@@ -319,7 +319,7 @@ unsigned long __init_refok init_memory_mapping(unsigned long start,
  */
 int devmem_is_allowed(unsigned long pagenr)
 {
-       if (pagenr <= 256)
+       if (pagenr < 256)
                return 1;
        if (iomem_is_exclusive(pagenr << PAGE_SHIFT))
                return 0;
index 9642d4a3860239f3203a2ff12dba5543a26ccf4f..1fbe75a95f15953d3d0ed6cd0dd7c911a88eac0b 100644 (file)
@@ -1452,6 +1452,10 @@ asmlinkage void __init xen_start_kernel(void)
                pci_request_acs();
 
                xen_acpi_sleep_register();
+
+               /* Avoid searching for BIOS MP tables */
+               x86_init.mpparse.find_smp_config = x86_init_noop;
+               x86_init.mpparse.get_smp_config = x86_init_uint_noop;
        }
 #ifdef CONFIG_PCI
        /* PCI BIOS service won't work from a PV guest. */
index 76ba0e97e530131199e40aec0730554ae7b7825c..72213da605f50c07f3684ca07d351a8bbd7435ef 100644 (file)
@@ -828,9 +828,6 @@ int m2p_add_override(unsigned long mfn, struct page *page,
 
                        xen_mc_issue(PARAVIRT_LAZY_MMU);
                }
-               /* let's use dev_bus_addr to record the old mfn instead */
-               kmap_op->dev_bus_addr = page->index;
-               page->index = (unsigned long) kmap_op;
        }
        spin_lock_irqsave(&m2p_override_lock, flags);
        list_add(&page->lru,  &m2p_overrides[mfn_hash(mfn)]);
@@ -857,7 +854,8 @@ int m2p_add_override(unsigned long mfn, struct page *page,
        return 0;
 }
 EXPORT_SYMBOL_GPL(m2p_add_override);
-int m2p_remove_override(struct page *page, bool clear_pte)
+int m2p_remove_override(struct page *page,
+               struct gnttab_map_grant_ref *kmap_op)
 {
        unsigned long flags;
        unsigned long mfn;
@@ -887,10 +885,8 @@ int m2p_remove_override(struct page *page, bool clear_pte)
        WARN_ON(!PagePrivate(page));
        ClearPagePrivate(page);
 
-       if (clear_pte) {
-               struct gnttab_map_grant_ref *map_op =
-                       (struct gnttab_map_grant_ref *) page->index;
-               set_phys_to_machine(pfn, map_op->dev_bus_addr);
+       set_phys_to_machine(pfn, page->index);
+       if (kmap_op != NULL) {
                if (!PageHighMem(page)) {
                        struct multicall_space mcs;
                        struct gnttab_unmap_grant_ref *unmap_op;
@@ -902,13 +898,13 @@ int m2p_remove_override(struct page *page, bool clear_pte)
                         * issued. In this case handle is going to -1 because
                         * it hasn't been modified yet.
                         */
-                       if (map_op->handle == -1)
+                       if (kmap_op->handle == -1)
                                xen_mc_flush();
                        /*
-                        * Now if map_op->handle is negative it means that the
+                        * Now if kmap_op->handle is negative it means that the
                         * hypercall actually returned an error.
                         */
-                       if (map_op->handle == GNTST_general_error) {
+                       if (kmap_op->handle == GNTST_general_error) {
                                printk(KERN_WARNING "m2p_remove_override: "
                                                "pfn %lx mfn %lx, failed to modify kernel mappings",
                                                pfn, mfn);
@@ -918,8 +914,8 @@ int m2p_remove_override(struct page *page, bool clear_pte)
                        mcs = xen_mc_entry(
                                        sizeof(struct gnttab_unmap_grant_ref));
                        unmap_op = mcs.args;
-                       unmap_op->host_addr = map_op->host_addr;
-                       unmap_op->handle = map_op->handle;
+                       unmap_op->host_addr = kmap_op->host_addr;
+                       unmap_op->handle = kmap_op->handle;
                        unmap_op->dev_bus_addr = 0;
 
                        MULTI_grant_table_op(mcs.mc,
@@ -930,10 +926,9 @@ int m2p_remove_override(struct page *page, bool clear_pte)
                        set_pte_at(&init_mm, address, ptep,
                                        pfn_pte(pfn, PAGE_KERNEL));
                        __flush_tlb_single(address);
-                       map_op->host_addr = 0;
+                       kmap_op->host_addr = 0;
                }
-       } else
-               set_phys_to_machine(pfn, page->index);
+       }
 
        /* p2m(m2p(mfn)) == FOREIGN_FRAME(mfn): the mfn is already present
         * somewhere in this domain, even before being added to the
index 4b4dbdfbca89fe5769fd4b2f6826f305fca18e26..ee3cb3a5e2782292bba4807c0d9cc887de48a31e 100644 (file)
@@ -2254,9 +2254,11 @@ bool blk_update_request(struct request *req, int error, unsigned int nr_bytes)
                        error_type = "I/O";
                        break;
                }
-               printk(KERN_ERR "end_request: %s error, dev %s, sector %llu\n",
-                      error_type, req->rq_disk ? req->rq_disk->disk_name : "?",
-                      (unsigned long long)blk_rq_pos(req));
+               printk_ratelimited(KERN_ERR "end_request: %s error, dev %s, sector %llu\n",
+                                  error_type, req->rq_disk ?
+                                  req->rq_disk->disk_name : "?",
+                                  (unsigned long long)blk_rq_pos(req));
+
        }
 
        blk_account_io_completion(req, nr_bytes);
index 4476e0e85d1687c08b31f81e7f633ae802b14814..4a85096f5410adde92d01cd58ecba30373c808f1 100644 (file)
@@ -41,7 +41,7 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user
                            sizeof(long long) > sizeof(long)) {
                                long pstart = start, plength = length;
                                if (pstart != start || plength != length
-                                   || pstart < 0 || plength < 0)
+                                   || pstart < 0 || plength < 0 || partno > 65535)
                                        return -EINVAL;
                        }
 
index 9628652e080c590fb3f6e01dedc655584c12737b..e0596954290b8e33e20791effc0227b80fb4a0c9 100644 (file)
@@ -237,6 +237,16 @@ static int __acpi_bus_get_power(struct acpi_device *device, int *state)
        } else if (result == ACPI_STATE_D3_HOT) {
                result = ACPI_STATE_D3;
        }
+
+       /*
+        * If we were unsure about the device parent's power state up to this
+        * point, the fact that the device is in D0 implies that the parent has
+        * to be in D0 too.
+        */
+       if (device->parent && device->parent->power.state == ACPI_STATE_UNKNOWN
+           && result == ACPI_STATE_D0)
+               device->parent->power.state = ACPI_STATE_D0;
+
        *state = result;
 
  out:
index fc1803414629d8233b0d6f11819efd166838952e..40e38a06ba854fc04751ec2386ca334d01998eb8 100644 (file)
@@ -107,6 +107,7 @@ struct acpi_power_resource {
 
        /* List of devices relying on this power resource */
        struct acpi_power_resource_device *devices;
+       struct mutex devices_lock;
 };
 
 static struct list_head acpi_power_resource_list;
@@ -225,7 +226,6 @@ static void acpi_power_on_device(struct acpi_power_managed_device *device)
 
 static int __acpi_power_on(struct acpi_power_resource *resource)
 {
-       struct acpi_power_resource_device *device_list = resource->devices;
        acpi_status status = AE_OK;
 
        status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL);
@@ -238,19 +238,15 @@ static int __acpi_power_on(struct acpi_power_resource *resource)
        ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned on\n",
                          resource->name));
 
-       while (device_list) {
-               acpi_power_on_device(device_list->device);
-
-               device_list = device_list->next;
-       }
-
        return 0;
 }
 
 static int acpi_power_on(acpi_handle handle)
 {
        int result = 0;
+       bool resume_device = false;
        struct acpi_power_resource *resource = NULL;
+       struct acpi_power_resource_device *device_list;
 
        result = acpi_power_get_context(handle, &resource);
        if (result)
@@ -266,10 +262,25 @@ static int acpi_power_on(acpi_handle handle)
                result = __acpi_power_on(resource);
                if (result)
                        resource->ref_count--;
+               else
+                       resume_device = true;
        }
 
        mutex_unlock(&resource->resource_lock);
 
+       if (!resume_device)
+               return result;
+
+       mutex_lock(&resource->devices_lock);
+
+       device_list = resource->devices;
+       while (device_list) {
+               acpi_power_on_device(device_list->device);
+               device_list = device_list->next;
+       }
+
+       mutex_unlock(&resource->devices_lock);
+
        return result;
 }
 
@@ -355,7 +366,7 @@ static void __acpi_power_resource_unregister_device(struct device *dev,
        if (acpi_power_get_context(res_handle, &resource))
                return;
 
-       mutex_lock(&resource->resource_lock);
+       mutex_lock(&resource->devices_lock);
        prev = NULL;
        curr = resource->devices;
        while (curr) {
@@ -372,7 +383,7 @@ static void __acpi_power_resource_unregister_device(struct device *dev,
                prev = curr;
                curr = curr->next;
        }
-       mutex_unlock(&resource->resource_lock);
+       mutex_unlock(&resource->devices_lock);
 }
 
 /* Unlink dev from all power resources in _PR0 */
@@ -414,10 +425,10 @@ static int __acpi_power_resource_register_device(
 
        power_resource_device->device = powered_device;
 
-       mutex_lock(&resource->resource_lock);
+       mutex_lock(&resource->devices_lock);
        power_resource_device->next = resource->devices;
        resource->devices = power_resource_device;
-       mutex_unlock(&resource->resource_lock);
+       mutex_unlock(&resource->devices_lock);
 
        return 0;
 }
@@ -462,7 +473,7 @@ int acpi_power_resource_register_device(struct device *dev, acpi_handle handle)
        return ret;
 
 no_power_resource:
-       printk(KERN_WARNING PREFIX "Invalid Power Resource to register!");
+       printk(KERN_DEBUG PREFIX "Invalid Power Resource to register!");
        return -ENODEV;
 }
 EXPORT_SYMBOL_GPL(acpi_power_resource_register_device);
@@ -721,6 +732,7 @@ static int acpi_power_add(struct acpi_device *device)
 
        resource->device = device;
        mutex_init(&resource->resource_lock);
+       mutex_init(&resource->devices_lock);
        strcpy(resource->name, device->pnp.bus_id);
        strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME);
        strcpy(acpi_device_class(device), ACPI_POWER_CLASS);
index 50d5dea0ff599feb19626ff80b8143dff1ae4f6e..7862d17976b7532f48204cbf4210ffb6d4984f97 100644 (file)
@@ -268,6 +268,9 @@ static const struct pci_device_id ahci_pci_tbl[] = {
        /* JMicron 360/1/3/5/6, match class to avoid IDE function */
        { PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
          PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci_ign_iferr },
+       /* JMicron 362B and 362C have an AHCI function with IDE class code */
+       { PCI_VDEVICE(JMICRON, 0x2362), board_ahci_ign_iferr },
+       { PCI_VDEVICE(JMICRON, 0x236f), board_ahci_ign_iferr },
 
        /* ATI */
        { PCI_VDEVICE(ATI, 0x4380), board_ahci_sb600 }, /* ATI SB600 */
@@ -393,6 +396,8 @@ static const struct pci_device_id ahci_pci_tbl[] = {
          .driver_data = board_ahci_yes_fbs },                  /* 88se9125 */
        { PCI_DEVICE(0x1b4b, 0x917a),
          .driver_data = board_ahci_yes_fbs },                  /* 88se9172 */
+       { PCI_DEVICE(0x1b4b, 0x9192),
+         .driver_data = board_ahci_yes_fbs },                  /* 88se9172 on some Gigabyte */
        { PCI_DEVICE(0x1b4b, 0x91a3),
          .driver_data = board_ahci_yes_fbs },
 
@@ -400,7 +405,10 @@ static const struct pci_device_id ahci_pci_tbl[] = {
        { PCI_VDEVICE(PROMISE, 0x3f20), board_ahci },   /* PDC42819 */
 
        /* Asmedia */
-       { PCI_VDEVICE(ASMEDIA, 0x0612), board_ahci },   /* ASM1061 */
+       { PCI_VDEVICE(ASMEDIA, 0x0601), board_ahci },   /* ASM1060 */
+       { PCI_VDEVICE(ASMEDIA, 0x0602), board_ahci },   /* ASM1060 */
+       { PCI_VDEVICE(ASMEDIA, 0x0611), board_ahci },   /* ASM1061 */
+       { PCI_VDEVICE(ASMEDIA, 0x0612), board_ahci },   /* ASM1062 */
 
        /* Generic, PCI class code for AHCI */
        { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
index de0435e63b02cbd349c5dcc282682359f6f85934..887f68f6d79a9e615beba7525cb9d0e671917c4e 100644 (file)
@@ -35,6 +35,7 @@ new_skb(ulong len)
                skb_reset_mac_header(skb);
                skb_reset_network_header(skb);
                skb->protocol = __constant_htons(ETH_P_AOE);
+               skb_checksum_none_assert(skb);
        }
        return skb;
 }
index 38aa6dda6b81d0deeb355956ead087324f41f3d1..da3311129a0c5b87b8cc8b3718cc63b5bdfd3928 100644 (file)
@@ -795,6 +795,7 @@ static void complete_scsi_command(CommandList_struct *c, int timeout,
                                }
                        break;
                        case CMD_PROTOCOL_ERR:
+                               cmd->result = DID_ERROR << 16;
                                dev_warn(&h->pdev->dev,
                                        "%p has protocol error\n", c);
                         break;
index a8fddeb3d638ebcdf9a0191beda816d8f1338ccd..f946d31d6917e00aa0637df5d21cef35ebf82845 100644 (file)
@@ -1148,11 +1148,15 @@ static bool mtip_pause_ncq(struct mtip_port *port,
        reply = port->rxfis + RX_FIS_D2H_REG;
        task_file_data = readl(port->mmio+PORT_TFDATA);
 
-       if ((task_file_data & 1) || (fis->command == ATA_CMD_SEC_ERASE_UNIT))
+       if (fis->command == ATA_CMD_SEC_ERASE_UNIT)
+               clear_bit(MTIP_DDF_SEC_LOCK_BIT, &port->dd->dd_flag);
+
+       if ((task_file_data & 1))
                return false;
 
        if (fis->command == ATA_CMD_SEC_ERASE_PREP) {
                set_bit(MTIP_PF_SE_ACTIVE_BIT, &port->flags);
+               set_bit(MTIP_DDF_SEC_LOCK_BIT, &port->dd->dd_flag);
                port->ic_pause_timer = jiffies;
                return true;
        } else if ((fis->command == ATA_CMD_DOWNLOAD_MICRO) &&
@@ -1900,7 +1904,7 @@ static int exec_drive_command(struct mtip_port *port, u8 *command,
        int rv = 0, xfer_sz = command[3];
 
        if (xfer_sz) {
-               if (user_buffer)
+               if (!user_buffer)
                        return -EFAULT;
 
                buf = dmam_alloc_coherent(&port->dd->pdev->dev,
@@ -2043,7 +2047,7 @@ static void mtip_set_timeout(struct host_to_dev_fis *fis, unsigned int *timeout)
                *timeout = 240000; /* 4 minutes */
                break;
        case ATA_CMD_STANDBYNOW1:
-               *timeout = 10000;  /* 10 seconds */
+               *timeout = 120000;  /* 2 minutes */
                break;
        case 0xF7:
        case 0xFA:
@@ -2588,9 +2592,6 @@ static ssize_t mtip_hw_read_registers(struct file *f, char __user *ubuf,
        if (!len || size)
                return 0;
 
-       if (size < 0)
-               return -EINVAL;
-
        size += sprintf(&buf[size], "H/ S ACTive      : [ 0x");
 
        for (n = dd->slot_groups-1; n >= 0; n--)
@@ -2660,9 +2661,6 @@ static ssize_t mtip_hw_read_flags(struct file *f, char __user *ubuf,
        if (!len || size)
                return 0;
 
-       if (size < 0)
-               return -EINVAL;
-
        size += sprintf(&buf[size], "Flag-port : [ %08lX ]\n",
                                                        dd->port->flags);
        size += sprintf(&buf[size], "Flag-dd   : [ %08lX ]\n",
@@ -3214,8 +3212,8 @@ static int mtip_hw_init(struct driver_data *dd)
                                "Unable to check write protect progress\n");
        else
                dev_info(&dd->pdev->dev,
-                               "Write protect progress: %d%% (%d blocks)\n",
-                               attr242.cur, attr242.data);
+                               "Write protect progress: %u%% (%u blocks)\n",
+                               attr242.cur, le32_to_cpu(attr242.data));
        return rv;
 
 out3:
@@ -3619,6 +3617,10 @@ static void mtip_make_request(struct request_queue *queue, struct bio *bio)
                        bio_endio(bio, -ENODATA);
                        return;
                }
+               if (unlikely(test_bit(MTIP_DDF_SEC_LOCK_BIT, &dd->dd_flag))) {
+                       bio_endio(bio, -ENODATA);
+                       return;
+               }
        }
 
        if (unlikely(!bio_has_data(bio))) {
@@ -4168,7 +4170,13 @@ static void mtip_pci_shutdown(struct pci_dev *pdev)
 
 /* Table of device ids supported by this driver. */
 static DEFINE_PCI_DEVICE_TABLE(mtip_pci_tbl) = {
-       {  PCI_DEVICE(PCI_VENDOR_ID_MICRON, P320_DEVICE_ID) },
+       { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P320H_DEVICE_ID) },
+       { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P320M_DEVICE_ID) },
+       { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P320S_DEVICE_ID) },
+       { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P325M_DEVICE_ID) },
+       { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P420H_DEVICE_ID) },
+       { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P420M_DEVICE_ID) },
+       { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P425M_DEVICE_ID) },
        { 0 }
 };
 
@@ -4199,12 +4207,12 @@ static int __init mtip_init(void)
 {
        int error;
 
-       printk(KERN_INFO MTIP_DRV_NAME " Version " MTIP_DRV_VERSION "\n");
+       pr_info(MTIP_DRV_NAME " Version " MTIP_DRV_VERSION "\n");
 
        /* Allocate a major block device number to use with this driver. */
        error = register_blkdev(0, MTIP_DRV_NAME);
        if (error <= 0) {
-               printk(KERN_ERR "Unable to register block device (%d)\n",
+               pr_err("Unable to register block device (%d)\n",
                error);
                return -EBUSY;
        }
@@ -4213,7 +4221,7 @@ static int __init mtip_init(void)
        if (!dfs_parent) {
                dfs_parent = debugfs_create_dir("rssd", NULL);
                if (IS_ERR_OR_NULL(dfs_parent)) {
-                       printk(KERN_WARNING "Error creating debugfs parent\n");
+                       pr_warn("Error creating debugfs parent\n");
                        dfs_parent = NULL;
                }
        }
index f51fc23d17bb0e0025c74ac9f495007dcbc8b784..18627a1d04c59eff34f7cdd2313555b10a75b283 100644 (file)
 
 /* Micron Vendor ID & P320x SSD Device ID */
 #define PCI_VENDOR_ID_MICRON    0x1344
-#define P320_DEVICE_ID         0x5150
+#define P320H_DEVICE_ID                0x5150
+#define P320M_DEVICE_ID                0x5151
+#define P320S_DEVICE_ID                0x5152
+#define P325M_DEVICE_ID                0x5153
+#define P420H_DEVICE_ID                0x5160
+#define P420M_DEVICE_ID                0x5161
+#define P425M_DEVICE_ID                0x5163
 
 /* Driver name and version strings */
 #define MTIP_DRV_NAME          "mtip32xx"
@@ -131,10 +137,12 @@ enum {
        MTIP_PF_SVC_THD_STOP_BIT    = 8,
 
        /* below are bit numbers in 'dd_flag' defined in driver_data */
+       MTIP_DDF_SEC_LOCK_BIT       = 0,
        MTIP_DDF_REMOVE_PENDING_BIT = 1,
        MTIP_DDF_OVER_TEMP_BIT      = 2,
        MTIP_DDF_WRITE_PROTECT_BIT  = 3,
        MTIP_DDF_STOP_IO      = ((1 << MTIP_DDF_REMOVE_PENDING_BIT) | \
+                               (1 << MTIP_DDF_SEC_LOCK_BIT) | \
                                (1 << MTIP_DDF_OVER_TEMP_BIT) | \
                                (1 << MTIP_DDF_WRITE_PROTECT_BIT)),
 
index d07c9f7fded600d76192330ef37c35c2135c0fb5..0c03411c59eb8cba0f744e72aed93342dc9cace8 100644 (file)
@@ -449,6 +449,14 @@ static void nbd_clear_que(struct nbd_device *nbd)
                req->errors++;
                nbd_end_request(req);
        }
+
+       while (!list_empty(&nbd->waiting_queue)) {
+               req = list_entry(nbd->waiting_queue.next, struct request,
+                                queuelist);
+               list_del_init(&req->queuelist);
+               req->errors++;
+               nbd_end_request(req);
+       }
 }
 
 
@@ -598,6 +606,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
                nbd->file = NULL;
                nbd_clear_que(nbd);
                BUG_ON(!list_empty(&nbd->queue_head));
+               BUG_ON(!list_empty(&nbd->waiting_queue));
                if (file)
                        fput(file);
                return 0;
index 73f196ca713f20e037e22adccba64d0e00c936b1..c6decb901e5e16f4ba09ccef7db33675aeecb189 100644 (file)
@@ -337,7 +337,7 @@ static void xen_blkbk_unmap(struct pending_req *req)
                invcount++;
        }
 
-       ret = gnttab_unmap_refs(unmap, pages, invcount, false);
+       ret = gnttab_unmap_refs(unmap, NULL, pages, invcount);
        BUG_ON(ret);
 }
 
index 11f36e5021367d7dd7c2b0794241154a0544ad0a..fc2de5528dcc94eb65537baa17b78048a5002827 100644 (file)
@@ -86,6 +86,7 @@ static struct usb_device_id ath3k_table[] = {
 
        /* Atheros AR5BBU22 with sflash firmware */
        { USB_DEVICE(0x0489, 0xE03C) },
+       { USB_DEVICE(0x0489, 0xE036) },
 
        { }     /* Terminating entry */
 };
@@ -109,6 +110,7 @@ static struct usb_device_id ath3k_blist_tbl[] = {
 
        /* Atheros AR5BBU22 with sflash firmware */
        { USB_DEVICE(0x0489, 0xE03C), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0489, 0xE036), .driver_info = BTUSB_ATH3012 },
 
        { }     /* Terminating entry */
 };
index cef3bac1a543d83113b54939585f807748accaf7..654e248763efb98024bd81b57118cd2ad1d77cdf 100644 (file)
@@ -52,6 +52,9 @@ static struct usb_device_id btusb_table[] = {
        /* Generic Bluetooth USB device */
        { USB_DEVICE_INFO(0xe0, 0x01, 0x01) },
 
+       /* Apple-specific (Broadcom) devices */
+       { USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01) },
+
        /* Broadcom SoftSailing reporting vendor specific */
        { USB_DEVICE(0x0a5c, 0x21e1) },
 
@@ -94,16 +97,14 @@ static struct usb_device_id btusb_table[] = {
 
        /* Broadcom BCM20702A0 */
        { USB_DEVICE(0x0489, 0xe042) },
-       { USB_DEVICE(0x0a5c, 0x21e3) },
-       { USB_DEVICE(0x0a5c, 0x21e6) },
-       { USB_DEVICE(0x0a5c, 0x21e8) },
-       { USB_DEVICE(0x0a5c, 0x21f3) },
-       { USB_DEVICE(0x0a5c, 0x21f4) },
        { USB_DEVICE(0x413c, 0x8197) },
 
        /* Foxconn - Hon Hai */
        { USB_DEVICE(0x0489, 0xe033) },
 
+       /*Broadcom devices with vendor specific id */
+       { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01) },
+
        { }     /* Terminating entry */
 };
 
@@ -141,6 +142,7 @@ static struct usb_device_id blacklist_table[] = {
 
        /* Atheros AR5BBU12 with sflash firmware */
        { USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0489, 0xe036), .driver_info = BTUSB_ATH3012 },
 
        /* Broadcom BCM2035 */
        { USB_DEVICE(0x0a5c, 0x2035), .driver_info = BTUSB_WRONG_SCO_MTU },
index 58e32f7c322956209bd3a63ce16f5ce16135c59b..e01f5eaaec82a9317724c90337d9d8e45da1bb39 100644 (file)
@@ -84,40 +84,33 @@ static struct _intel_private {
 #define IS_IRONLAKE    intel_private.driver->is_ironlake
 #define HAS_PGTBL_EN   intel_private.driver->has_pgtbl_enable
 
-int intel_gtt_map_memory(struct page **pages, unsigned int num_entries,
-                        struct scatterlist **sg_list, int *num_sg)
+static int intel_gtt_map_memory(struct page **pages,
+                               unsigned int num_entries,
+                               struct sg_table *st)
 {
-       struct sg_table st;
        struct scatterlist *sg;
        int i;
 
-       if (*sg_list)
-               return 0; /* already mapped (for e.g. resume */
-
        DBG("try mapping %lu pages\n", (unsigned long)num_entries);
 
-       if (sg_alloc_table(&st, num_entries, GFP_KERNEL))
+       if (sg_alloc_table(st, num_entries, GFP_KERNEL))
                goto err;
 
-       *sg_list = sg = st.sgl;
-
-       for (i = 0 ; i < num_entries; i++, sg = sg_next(sg))
+       for_each_sg(st->sgl, sg, num_entries, i)
                sg_set_page(sg, pages[i], PAGE_SIZE, 0);
 
-       *num_sg = pci_map_sg(intel_private.pcidev, *sg_list,
-                                num_entries, PCI_DMA_BIDIRECTIONAL);
-       if (unlikely(!*num_sg))
+       if (!pci_map_sg(intel_private.pcidev,
+                       st->sgl, st->nents, PCI_DMA_BIDIRECTIONAL))
                goto err;
 
        return 0;
 
 err:
-       sg_free_table(&st);
+       sg_free_table(st);
        return -ENOMEM;
 }
-EXPORT_SYMBOL(intel_gtt_map_memory);
 
-void intel_gtt_unmap_memory(struct scatterlist *sg_list, int num_sg)
+static void intel_gtt_unmap_memory(struct scatterlist *sg_list, int num_sg)
 {
        struct sg_table st;
        DBG("try unmapping %lu pages\n", (unsigned long)mem->page_count);
@@ -130,7 +123,6 @@ void intel_gtt_unmap_memory(struct scatterlist *sg_list, int num_sg)
 
        sg_free_table(&st);
 }
-EXPORT_SYMBOL(intel_gtt_unmap_memory);
 
 static void intel_fake_agp_enable(struct agp_bridge_data *bridge, u32 mode)
 {
@@ -674,9 +666,14 @@ static int intel_gtt_init(void)
 
        gtt_map_size = intel_private.base.gtt_total_entries * 4;
 
-       intel_private.gtt = ioremap(intel_private.gtt_bus_addr,
-                                   gtt_map_size);
-       if (!intel_private.gtt) {
+       intel_private.gtt = NULL;
+       if (INTEL_GTT_GEN < 6)
+               intel_private.gtt = ioremap_wc(intel_private.gtt_bus_addr,
+                                              gtt_map_size);
+       if (intel_private.gtt == NULL)
+               intel_private.gtt = ioremap(intel_private.gtt_bus_addr,
+                                           gtt_map_size);
+       if (intel_private.gtt == NULL) {
                intel_private.driver->cleanup();
                iounmap(intel_private.registers);
                return -ENOMEM;
@@ -879,8 +876,7 @@ static bool i830_check_flags(unsigned int flags)
        return false;
 }
 
-void intel_gtt_insert_sg_entries(struct scatterlist *sg_list,
-                                unsigned int sg_len,
+void intel_gtt_insert_sg_entries(struct sg_table *st,
                                 unsigned int pg_start,
                                 unsigned int flags)
 {
@@ -892,12 +888,11 @@ void intel_gtt_insert_sg_entries(struct scatterlist *sg_list,
 
        /* sg may merge pages, but we have to separate
         * per-page addr for GTT */
-       for_each_sg(sg_list, sg, sg_len, i) {
+       for_each_sg(st->sgl, sg, st->nents, i) {
                len = sg_dma_len(sg) >> PAGE_SHIFT;
                for (m = 0; m < len; m++) {
                        dma_addr_t addr = sg_dma_address(sg) + (m << PAGE_SHIFT);
-                       intel_private.driver->write_entry(addr,
-                                                         j, flags);
+                       intel_private.driver->write_entry(addr, j, flags);
                        j++;
                }
        }
@@ -905,8 +900,10 @@ void intel_gtt_insert_sg_entries(struct scatterlist *sg_list,
 }
 EXPORT_SYMBOL(intel_gtt_insert_sg_entries);
 
-void intel_gtt_insert_pages(unsigned int first_entry, unsigned int num_entries,
-                           struct page **pages, unsigned int flags)
+static void intel_gtt_insert_pages(unsigned int first_entry,
+                                  unsigned int num_entries,
+                                  struct page **pages,
+                                  unsigned int flags)
 {
        int i, j;
 
@@ -917,7 +914,6 @@ void intel_gtt_insert_pages(unsigned int first_entry, unsigned int num_entries,
        }
        readl(intel_private.gtt+j-1);
 }
-EXPORT_SYMBOL(intel_gtt_insert_pages);
 
 static int intel_fake_agp_insert_entries(struct agp_memory *mem,
                                         off_t pg_start, int type)
@@ -953,13 +949,15 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem,
                global_cache_flush();
 
        if (intel_private.base.needs_dmar) {
-               ret = intel_gtt_map_memory(mem->pages, mem->page_count,
-                                          &mem->sg_list, &mem->num_sg);
+               struct sg_table st;
+
+               ret = intel_gtt_map_memory(mem->pages, mem->page_count, &st);
                if (ret != 0)
                        return ret;
 
-               intel_gtt_insert_sg_entries(mem->sg_list, mem->num_sg,
-                                           pg_start, type);
+               intel_gtt_insert_sg_entries(&st, pg_start, type);
+               mem->sg_list = st.sgl;
+               mem->num_sg = st.nents;
        } else
                intel_gtt_insert_pages(pg_start, mem->page_count, mem->pages,
                                       type);
index 5869ea3870545f0f7c3ab64a185e1c486243b4e1..72ce247a0e8d5c08395fe92c9163a8c376c555bc 100644 (file)
@@ -1,4 +1,5 @@
 # common clock types
+obj-$(CONFIG_HAVE_CLK)         += clk-devres.o
 obj-$(CONFIG_CLKDEV_LOOKUP)    += clkdev.o
 obj-$(CONFIG_COMMON_CLK)       += clk.o clk-fixed-rate.o clk-gate.o \
                                   clk-mux.o clk-divider.o clk-fixed-factor.o
diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c
new file mode 100644 (file)
index 0000000..8f57154
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/gfp.h>
+
+static void devm_clk_release(struct device *dev, void *res)
+{
+       clk_put(*(struct clk **)res);
+}
+
+struct clk *devm_clk_get(struct device *dev, const char *id)
+{
+       struct clk **ptr, *clk;
+
+       ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       clk = clk_get(dev, id);
+       if (!IS_ERR(clk)) {
+               *ptr = clk;
+               devres_add(dev, ptr);
+       } else {
+               devres_free(ptr);
+       }
+
+       return clk;
+}
+EXPORT_SYMBOL(devm_clk_get);
+
+static int devm_clk_match(struct device *dev, void *res, void *data)
+{
+       struct clk **c = res;
+       if (!c || !*c) {
+               WARN_ON(!c || !*c);
+               return 0;
+       }
+       return *c == data;
+}
+
+void devm_clk_put(struct device *dev, struct clk *clk)
+{
+       int ret;
+
+       ret = devres_release(dev, devm_clk_release, devm_clk_match, clk);
+
+       WARN_ON(ret);
+}
+EXPORT_SYMBOL(devm_clk_put);
index d423c9bdd71a9d4670c340c94f7f1eb08152ed41..442a31363873d35f319e0d5ee5b6b250084c02fc 100644 (file)
@@ -171,51 +171,6 @@ void clk_put(struct clk *clk)
 }
 EXPORT_SYMBOL(clk_put);
 
-static void devm_clk_release(struct device *dev, void *res)
-{
-       clk_put(*(struct clk **)res);
-}
-
-struct clk *devm_clk_get(struct device *dev, const char *id)
-{
-       struct clk **ptr, *clk;
-
-       ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);
-       if (!ptr)
-               return ERR_PTR(-ENOMEM);
-
-       clk = clk_get(dev, id);
-       if (!IS_ERR(clk)) {
-               *ptr = clk;
-               devres_add(dev, ptr);
-       } else {
-               devres_free(ptr);
-       }
-
-       return clk;
-}
-EXPORT_SYMBOL(devm_clk_get);
-
-static int devm_clk_match(struct device *dev, void *res, void *data)
-{
-       struct clk **c = res;
-       if (!c || !*c) {
-               WARN_ON(!c || !*c);
-               return 0;
-       }
-       return *c == data;
-}
-
-void devm_clk_put(struct device *dev, struct clk *clk)
-{
-       int ret;
-
-       ret = devres_destroy(dev, devm_clk_release, devm_clk_match, clk);
-
-       WARN_ON(ret);
-}
-EXPORT_SYMBOL(devm_clk_put);
-
 void clkdev_add(struct clk_lookup *cl)
 {
        mutex_lock(&clocks_mutex);
index c0e816468e300f242735f4825d09b9d291a9b522..1a40935c85fdcc1f21e976ad8dbd5298a87d6060 100644 (file)
@@ -35,7 +35,6 @@
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/cpumask.h>
-#include <linux/sched.h>       /* for current / set_cpus_allowed() */
 #include <linux/io.h>
 #include <linux/delay.h>
 
@@ -1139,16 +1138,23 @@ static int transition_frequency_pstate(struct powernow_k8_data *data,
        return res;
 }
 
-/* Driver entry point to switch to the target frequency */
-static int powernowk8_target(struct cpufreq_policy *pol,
-               unsigned targfreq, unsigned relation)
+struct powernowk8_target_arg {
+       struct cpufreq_policy           *pol;
+       unsigned                        targfreq;
+       unsigned                        relation;
+};
+
+static long powernowk8_target_fn(void *arg)
 {
-       cpumask_var_t oldmask;
+       struct powernowk8_target_arg *pta = arg;
+       struct cpufreq_policy *pol = pta->pol;
+       unsigned targfreq = pta->targfreq;
+       unsigned relation = pta->relation;
        struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu);
        u32 checkfid;
        u32 checkvid;
        unsigned int newstate;
-       int ret = -EIO;
+       int ret;
 
        if (!data)
                return -EINVAL;
@@ -1156,29 +1162,16 @@ static int powernowk8_target(struct cpufreq_policy *pol,
        checkfid = data->currfid;
        checkvid = data->currvid;
 
-       /* only run on specific CPU from here on. */
-       /* This is poor form: use a workqueue or smp_call_function_single */
-       if (!alloc_cpumask_var(&oldmask, GFP_KERNEL))
-               return -ENOMEM;
-
-       cpumask_copy(oldmask, tsk_cpus_allowed(current));
-       set_cpus_allowed_ptr(current, cpumask_of(pol->cpu));
-
-       if (smp_processor_id() != pol->cpu) {
-               printk(KERN_ERR PFX "limiting to cpu %u failed\n", pol->cpu);
-               goto err_out;
-       }
-
        if (pending_bit_stuck()) {
                printk(KERN_ERR PFX "failing targ, change pending bit set\n");
-               goto err_out;
+               return -EIO;
        }
 
        pr_debug("targ: cpu %d, %d kHz, min %d, max %d, relation %d\n",
                pol->cpu, targfreq, pol->min, pol->max, relation);
 
        if (query_current_values_with_pending_wait(data))
-               goto err_out;
+               return -EIO;
 
        if (cpu_family != CPU_HW_PSTATE) {
                pr_debug("targ: curr fid 0x%x, vid 0x%x\n",
@@ -1196,7 +1189,7 @@ static int powernowk8_target(struct cpufreq_policy *pol,
 
        if (cpufreq_frequency_table_target(pol, data->powernow_table,
                                targfreq, relation, &newstate))
-               goto err_out;
+               return -EIO;
 
        mutex_lock(&fidvid_mutex);
 
@@ -1209,9 +1202,8 @@ static int powernowk8_target(struct cpufreq_policy *pol,
                ret = transition_frequency_fidvid(data, newstate);
        if (ret) {
                printk(KERN_ERR PFX "transition frequency failed\n");
-               ret = 1;
                mutex_unlock(&fidvid_mutex);
-               goto err_out;
+               return 1;
        }
        mutex_unlock(&fidvid_mutex);
 
@@ -1220,12 +1212,25 @@ static int powernowk8_target(struct cpufreq_policy *pol,
                                data->powernow_table[newstate].index);
        else
                pol->cur = find_khz_freq_from_fid(data->currfid);
-       ret = 0;
 
-err_out:
-       set_cpus_allowed_ptr(current, oldmask);
-       free_cpumask_var(oldmask);
-       return ret;
+       return 0;
+}
+
+/* Driver entry point to switch to the target frequency */
+static int powernowk8_target(struct cpufreq_policy *pol,
+               unsigned targfreq, unsigned relation)
+{
+       struct powernowk8_target_arg pta = { .pol = pol, .targfreq = targfreq,
+                                            .relation = relation };
+
+       /*
+        * Must run on @pol->cpu.  cpufreq core is responsible for ensuring
+        * that we're bound to the current CPU and pol->cpu stays online.
+        */
+       if (smp_processor_id() == pol->cpu)
+               return powernowk8_target_fn(&pta);
+       else
+               return work_on_cpu(pol->cpu, powernowk8_target_fn, &pta);
 }
 
 /* Driver entry point to verify the policy and range of frequencies */
index 3934fcc4e00b1b02778bb02866fb86073f9b1f0a..7ab6e26664a77a1ab2b375148488d1209e2e21ba 100644 (file)
@@ -661,7 +661,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
                        flags);
 
        if (unlikely(!atslave || !sg_len)) {
-               dev_dbg(chan2dev(chan), "prep_dma_memcpy: length is zero!\n");
+               dev_dbg(chan2dev(chan), "prep_slave_sg: sg length is zero!\n");
                return NULL;
        }
 
@@ -689,6 +689,11 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
 
                        mem = sg_dma_address(sg);
                        len = sg_dma_len(sg);
+                       if (unlikely(!len)) {
+                               dev_dbg(chan2dev(chan),
+                                       "prep_slave_sg: sg(%d) data length is zero\n", i);
+                               goto err;
+                       }
                        mem_width = 2;
                        if (unlikely(mem & 3 || len & 3))
                                mem_width = 0;
@@ -724,6 +729,11 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
 
                        mem = sg_dma_address(sg);
                        len = sg_dma_len(sg);
+                       if (unlikely(!len)) {
+                               dev_dbg(chan2dev(chan),
+                                       "prep_slave_sg: sg(%d) data length is zero\n", i);
+                               goto err;
+                       }
                        mem_width = 2;
                        if (unlikely(mem & 3 || len & 3))
                                mem_width = 0;
@@ -757,6 +767,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
 
 err_desc_get:
        dev_err(chan2dev(chan), "not enough descriptors available\n");
+err:
        atc_desc_put(atchan, first);
        return NULL;
 }
index e4feba6b03c00e6f2ae412a9a1104eef21fb94f4..f5843bc80baa9b8168f6034ad167ce46a0d537bb 100644 (file)
@@ -1567,17 +1567,19 @@ static int pl330_submit_req(void *ch_id, struct pl330_req *r)
                goto xfer_exit;
        }
 
-       /* Prefer Secure Channel */
-       if (!_manager_ns(thrd))
-               r->cfg->nonsecure = 0;
-       else
-               r->cfg->nonsecure = 1;
 
        /* Use last settings, if not provided */
-       if (r->cfg)
+       if (r->cfg) {
+               /* Prefer Secure Channel */
+               if (!_manager_ns(thrd))
+                       r->cfg->nonsecure = 0;
+               else
+                       r->cfg->nonsecure = 1;
+
                ccr = _prepare_ccr(r->cfg);
-       else
+       } else {
                ccr = readl(regs + CC(thrd->id));
+       }
 
        /* If this req doesn't have valid xfer settings */
        if (!_is_valid(ccr)) {
@@ -2928,6 +2930,11 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
                num_chan = max_t(int, pi->pcfg.num_peri, pi->pcfg.num_chan);
 
        pdmac->peripherals = kzalloc(num_chan * sizeof(*pch), GFP_KERNEL);
+       if (!pdmac->peripherals) {
+               ret = -ENOMEM;
+               dev_err(&adev->dev, "unable to allocate pdmac->peripherals\n");
+               goto probe_err5;
+       }
 
        for (i = 0; i < num_chan; i++) {
                pch = &pdmac->peripherals[i];
index 616d90bcb3a4106929523c4a98fe32fb27f6a9a6..d5dc9da7f99f95fc9dd3a6edb5d4bc302e69c2c3 100644 (file)
@@ -199,6 +199,36 @@ void *edac_align_ptr(void **p, unsigned size, int n_elems)
        return (void *)(((unsigned long)ptr) + align - r);
 }
 
+static void _edac_mc_free(struct mem_ctl_info *mci)
+{
+       int i, chn, row;
+       struct csrow_info *csr;
+       const unsigned int tot_dimms = mci->tot_dimms;
+       const unsigned int tot_channels = mci->num_cschannel;
+       const unsigned int tot_csrows = mci->nr_csrows;
+
+       if (mci->dimms) {
+               for (i = 0; i < tot_dimms; i++)
+                       kfree(mci->dimms[i]);
+               kfree(mci->dimms);
+       }
+       if (mci->csrows) {
+               for (row = 0; row < tot_csrows; row++) {
+                       csr = mci->csrows[row];
+                       if (csr) {
+                               if (csr->channels) {
+                                       for (chn = 0; chn < tot_channels; chn++)
+                                               kfree(csr->channels[chn]);
+                                       kfree(csr->channels);
+                               }
+                               kfree(csr);
+                       }
+               }
+               kfree(mci->csrows);
+       }
+       kfree(mci);
+}
+
 /**
  * edac_mc_alloc: Allocate and partially fill a struct mem_ctl_info structure
  * @mc_num:            Memory controller number
@@ -413,24 +443,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
        return mci;
 
 error:
-       if (mci->dimms) {
-               for (i = 0; i < tot_dimms; i++)
-                       kfree(mci->dimms[i]);
-               kfree(mci->dimms);
-       }
-       if (mci->csrows) {
-               for (chn = 0; chn < tot_channels; chn++) {
-                       csr = mci->csrows[chn];
-                       if (csr) {
-                               for (chn = 0; chn < tot_channels; chn++)
-                                       kfree(csr->channels[chn]);
-                               kfree(csr);
-                       }
-                       kfree(mci->csrows[i]);
-               }
-               kfree(mci->csrows);
-       }
-       kfree(mci);
+       _edac_mc_free(mci);
 
        return NULL;
 }
@@ -445,6 +458,14 @@ void edac_mc_free(struct mem_ctl_info *mci)
 {
        edac_dbg(1, "\n");
 
+       /* If we're not yet registered with sysfs free only what was allocated
+        * in edac_mc_alloc().
+        */
+       if (!device_is_registered(&mci->dev)) {
+               _edac_mc_free(mci);
+               return;
+       }
+
        /* the mci instance is freed here, when the sysfs object is dropped */
        edac_unregister_sysfs(mci);
 }
index 920a609b2c35857c2fa64c8341ad6561fe6f7d65..38f9e52f358b17596b8b41177d6af493d509f84b 100644 (file)
@@ -669,13 +669,18 @@ static int __devinit max77693_muic_probe(struct platform_device *pdev)
        }
        info->dev = &pdev->dev;
        info->max77693 = max77693;
-       info->max77693->regmap_muic = regmap_init_i2c(info->max77693->muic,
-                                        &max77693_muic_regmap_config);
-       if (IS_ERR(info->max77693->regmap_muic)) {
-               ret = PTR_ERR(info->max77693->regmap_muic);
-               dev_err(max77693->dev,
-                       "failed to allocate register map: %d\n", ret);
-               goto err_regmap;
+       if (info->max77693->regmap_muic)
+               dev_dbg(&pdev->dev, "allocate register map\n");
+       else {
+               info->max77693->regmap_muic = devm_regmap_init_i2c(
+                                               info->max77693->muic,
+                                               &max77693_muic_regmap_config);
+               if (IS_ERR(info->max77693->regmap_muic)) {
+                       ret = PTR_ERR(info->max77693->regmap_muic);
+                       dev_err(max77693->dev,
+                               "failed to allocate register map: %d\n", ret);
+                       goto err_regmap;
+               }
        }
        platform_set_drvdata(pdev, info);
        mutex_init(&info->mutex);
index 90e28081712dbd7c68343f88bd10b468c10f604a..18321b68b88069f3e5588bf23fabd83c268e0359 100644 (file)
@@ -22,7 +22,7 @@ menuconfig DRM
 config DRM_USB
        tristate
        depends on DRM
-       depends on USB_ARCH_HAS_HCD
+       depends on USB_SUPPORT && USB_ARCH_HAS_HCD
        select USB
 
 config DRM_KMS_HELPER
@@ -54,6 +54,21 @@ config DRM_TTM
          GPU memory types. Will be enabled automatically if a device driver
          uses it.
 
+config DRM_GEM_CMA_HELPER
+       bool
+       depends on DRM
+       help
+         Choose this if you need the GEM CMA helper functions
+
+config DRM_KMS_CMA_HELPER
+       bool
+       select DRM_GEM_CMA_HELPER
+       select FB_SYS_FILLRECT
+       select FB_SYS_COPYAREA
+       select FB_SYS_IMAGEBLIT
+       help
+         Choose this if you need the KMS CMA helper functions
+
 config DRM_TDFX
        tristate "3dfx Banshee/Voodoo3+"
        depends on DRM && PCI
@@ -193,3 +208,5 @@ source "drivers/gpu/drm/ast/Kconfig"
 source "drivers/gpu/drm/mgag200/Kconfig"
 
 source "drivers/gpu/drm/cirrus/Kconfig"
+
+source "drivers/gpu/drm/shmobile/Kconfig"
index f65f65ed0ddfc8542f42ea578978ab5db367ceac..2ff5cefe9eadd106fe1e611c19388d097716ccb8 100644 (file)
@@ -15,11 +15,13 @@ drm-y       :=      drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
                drm_trace_points.o drm_global.o drm_prime.o
 
 drm-$(CONFIG_COMPAT) += drm_ioc32.o
+drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
 
 drm-usb-y   := drm_usb.o
 
 drm_kms_helper-y := drm_fb_helper.o drm_crtc_helper.o drm_dp_i2c_helper.o
 drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
+drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o
 
 obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
 
@@ -45,4 +47,5 @@ obj-$(CONFIG_DRM_EXYNOS) +=exynos/
 obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
 obj-$(CONFIG_DRM_AST) += ast/
+obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
 obj-y                  += i2c/
index d4af9edcbb9760e794a71a16d82d5a791eec3b7c..20a437f88780e022dd64f800f550ff4da0b882bc 100644 (file)
@@ -94,7 +94,6 @@ struct ast_private {
                struct drm_global_reference mem_global_ref;
                struct ttm_bo_global_ref bo_global_ref;
                struct ttm_bo_device bdev;
-               atomic_t validate_sequence;
        } ttm;
 
        struct drm_gem_object *cursor_cache;
index a712cafcfa1dfde6f76e5c94a77307b7c1f1041a..f3b2a7cce744db8ebf50f2fe1adf3a93a6770406 100644 (file)
@@ -737,6 +737,7 @@ static int ast_get_modes(struct drm_connector *connector)
        if (edid) {
                drm_mode_connector_update_edid_property(&ast_connector->base, edid);
                ret = drm_add_edid_modes(connector, edid);
+               kfree(edid);
                return ret;
        } else
                drm_mode_connector_update_edid_property(&ast_connector->base, NULL);
index 64ea597cb6d390219abd198a086e131b08b6d54d..5d045647a3fc76deba63355577f373ccf8da6fe7 100644 (file)
@@ -143,7 +143,6 @@ struct cirrus_device {
                struct drm_global_reference mem_global_ref;
                struct ttm_bo_global_ref bo_global_ref;
                struct ttm_bo_device bdev;
-               atomic_t validate_sequence;
        } ttm;
        bool mm_inited;
 };
index 08758e061478eb77e2db77794d1d5e54c8625474..4a4274b348b63d0509a6fc248019c7d166340b69 100644 (file)
@@ -37,12 +37,13 @@ drm_clflush_page(struct page *page)
 {
        uint8_t *page_virtual;
        unsigned int i;
+       const int size = boot_cpu_data.x86_clflush_size;
 
        if (unlikely(page == NULL))
                return;
 
        page_virtual = kmap_atomic(page);
-       for (i = 0; i < PAGE_SIZE; i += boot_cpu_data.x86_clflush_size)
+       for (i = 0; i < PAGE_SIZE; i += size)
                clflush(page_virtual + i);
        kunmap_atomic(page_virtual);
 }
@@ -99,6 +100,31 @@ drm_clflush_pages(struct page *pages[], unsigned long num_pages)
 }
 EXPORT_SYMBOL(drm_clflush_pages);
 
+void
+drm_clflush_sg(struct sg_table *st)
+{
+#if defined(CONFIG_X86)
+       if (cpu_has_clflush) {
+               struct scatterlist *sg;
+               int i;
+
+               mb();
+               for_each_sg(st->sgl, sg, st->nents, i)
+                       drm_clflush_page(sg_page(sg));
+               mb();
+
+               return;
+       }
+
+       if (on_each_cpu(drm_clflush_ipi_handler, NULL, 1) != 0)
+               printk(KERN_ERR "Timed out waiting for cache flush.\n");
+#else
+       printk(KERN_ERR "Architecture has no drm_cache.c support\n");
+       WARN_ON_ONCE(1);
+#endif
+}
+EXPORT_SYMBOL(drm_clflush_sg);
+
 void
 drm_clflush_virt_range(char *addr, unsigned long length)
 {
index 6fbfc244748fd9f5e23e05c90d57bcfbdead4b6b..9c346a50379f65de7ac8ce2787fae639936d097c 100644 (file)
@@ -294,6 +294,8 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
 {
        int ret;
 
+       kref_init(&fb->refcount);
+
        ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB);
        if (ret)
                return ret;
@@ -307,6 +309,38 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
 }
 EXPORT_SYMBOL(drm_framebuffer_init);
 
+static void drm_framebuffer_free(struct kref *kref)
+{
+       struct drm_framebuffer *fb =
+                       container_of(kref, struct drm_framebuffer, refcount);
+       fb->funcs->destroy(fb);
+}
+
+/**
+ * drm_framebuffer_unreference - unref a framebuffer
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ */
+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
+ */
+void drm_framebuffer_reference(struct drm_framebuffer *fb)
+{
+       DRM_DEBUG("FB ID: %d\n", fb->base.id);
+       kref_get(&fb->refcount);
+}
+EXPORT_SYMBOL(drm_framebuffer_reference);
+
 /**
  * drm_framebuffer_cleanup - remove a framebuffer object
  * @fb: framebuffer to remove
@@ -318,6 +352,32 @@ EXPORT_SYMBOL(drm_framebuffer_init);
  * it, setting it to NULL.
  */
 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);
+       list_del(&fb->head);
+       dev->mode_config.num_fb--;
+}
+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.
+ */
+void drm_framebuffer_remove(struct drm_framebuffer *fb)
 {
        struct drm_device *dev = fb->dev;
        struct drm_crtc *crtc;
@@ -350,11 +410,11 @@ void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
                }
        }
 
-       drm_mode_object_put(dev, &fb->base);
-       list_del(&fb->head);
-       dev->mode_config.num_fb--;
+       list_del(&fb->filp_head);
+
+       drm_framebuffer_unreference(fb);
 }
-EXPORT_SYMBOL(drm_framebuffer_cleanup);
+EXPORT_SYMBOL(drm_framebuffer_remove);
 
 /**
  * drm_crtc_init - Initialise a new CRTC object
@@ -377,6 +437,7 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
 
        crtc->dev = dev;
        crtc->funcs = funcs;
+       crtc->invert_dimensions = false;
 
        mutex_lock(&dev->mode_config.mutex);
 
@@ -1031,11 +1092,7 @@ void drm_mode_config_cleanup(struct drm_device *dev)
        }
 
        list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) {
-               fb->funcs->destroy(fb);
-       }
-
-       list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) {
-               crtc->funcs->destroy(crtc);
+               drm_framebuffer_remove(fb);
        }
 
        list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list,
@@ -1043,6 +1100,10 @@ void drm_mode_config_cleanup(struct drm_device *dev)
                plane->funcs->destroy(plane);
        }
 
+       list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) {
+               crtc->funcs->destroy(crtc);
+       }
+
        idr_remove_all(&dev->mode_config.crtc_idr);
        idr_destroy(&dev->mode_config.crtc_idr);
 }
@@ -1852,6 +1913,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
        DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
 
        if (crtc_req->mode_valid) {
+               int hdisplay, vdisplay;
                /* If we have a mode we need a framebuffer. */
                /* If we pass -1, set the mode with the currently bound fb */
                if (crtc_req->fb_id == -1) {
@@ -1887,14 +1949,20 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
 
                drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
 
-               if (mode->hdisplay > fb->width ||
-                   mode->vdisplay > fb->height ||
-                   crtc_req->x > fb->width - mode->hdisplay ||
-                   crtc_req->y > fb->height - mode->vdisplay) {
-                       DRM_DEBUG_KMS("Invalid CRTC viewport %ux%u+%u+%u for fb size %ux%u.\n",
-                                     mode->hdisplay, mode->vdisplay,
-                                     crtc_req->x, crtc_req->y,
-                                     fb->width, fb->height);
+               hdisplay = mode->hdisplay;
+               vdisplay = mode->vdisplay;
+
+               if (crtc->invert_dimensions)
+                       swap(hdisplay, vdisplay);
+
+               if (hdisplay > fb->width ||
+                   vdisplay > fb->height ||
+                   crtc_req->x > fb->width - hdisplay ||
+                   crtc_req->y > fb->height - vdisplay) {
+                       DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n",
+                                     fb->width, fb->height,
+                                     hdisplay, vdisplay, crtc_req->x, crtc_req->y,
+                                     crtc->invert_dimensions ? " (inverted)" : "");
                        ret = -ENOSPC;
                        goto out;
                }
@@ -2169,6 +2237,8 @@ static int format_check(const struct drm_mode_fb_cmd2 *r)
        case DRM_FORMAT_NV21:
        case DRM_FORMAT_NV16:
        case DRM_FORMAT_NV61:
+       case DRM_FORMAT_NV24:
+       case DRM_FORMAT_NV42:
        case DRM_FORMAT_YUV410:
        case DRM_FORMAT_YVU410:
        case DRM_FORMAT_YUV411:
@@ -2335,11 +2405,7 @@ int drm_mode_rmfb(struct drm_device *dev,
                goto out;
        }
 
-       /* TODO release all crtc connected to the framebuffer */
-       /* TODO unhock the destructor from the buffer object */
-
-       list_del(&fb->filp_head);
-       fb->funcs->destroy(fb);
+       drm_framebuffer_remove(fb);
 
 out:
        mutex_unlock(&dev->mode_config.mutex);
@@ -2489,8 +2555,7 @@ void drm_fb_release(struct drm_file *priv)
 
        mutex_lock(&dev->mode_config.mutex);
        list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
-               list_del(&fb->filp_head);
-               fb->funcs->destroy(fb);
+               drm_framebuffer_remove(fb);
        }
        mutex_unlock(&dev->mode_config.mutex);
 }
@@ -3489,6 +3554,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
        struct drm_framebuffer *fb;
        struct drm_pending_vblank_event *e = NULL;
        unsigned long flags;
+       int hdisplay, vdisplay;
        int ret = -EINVAL;
 
        if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS ||
@@ -3518,14 +3584,19 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
                goto out;
        fb = obj_to_fb(obj);
 
-       if (crtc->mode.hdisplay > fb->width ||
-           crtc->mode.vdisplay > fb->height ||
-           crtc->x > fb->width - crtc->mode.hdisplay ||
-           crtc->y > fb->height - crtc->mode.vdisplay) {
-               DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d.\n",
-                             fb->width, fb->height,
-                             crtc->mode.hdisplay, crtc->mode.vdisplay,
-                             crtc->x, crtc->y);
+       hdisplay = crtc->mode.hdisplay;
+       vdisplay = crtc->mode.vdisplay;
+
+       if (crtc->invert_dimensions)
+               swap(hdisplay, vdisplay);
+
+       if (hdisplay > fb->width ||
+           vdisplay > fb->height ||
+           crtc->x > fb->width - hdisplay ||
+           crtc->y > fb->height - vdisplay) {
+               DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n",
+                             fb->width, fb->height, hdisplay, vdisplay, crtc->x, crtc->y,
+                             crtc->invert_dimensions ? " (inverted)" : "");
                ret = -ENOSPC;
                goto out;
        }
@@ -3718,6 +3789,8 @@ int drm_format_num_planes(uint32_t format)
        case DRM_FORMAT_NV21:
        case DRM_FORMAT_NV16:
        case DRM_FORMAT_NV61:
+       case DRM_FORMAT_NV24:
+       case DRM_FORMAT_NV42:
                return 2;
        default:
                return 1;
@@ -3751,6 +3824,8 @@ int drm_format_plane_cpp(uint32_t format, int plane)
        case DRM_FORMAT_NV21:
        case DRM_FORMAT_NV16:
        case DRM_FORMAT_NV61:
+       case DRM_FORMAT_NV24:
+       case DRM_FORMAT_NV42:
                return plane ? 2 : 1;
        case DRM_FORMAT_YUV410:
        case DRM_FORMAT_YVU410:
index 9238de4009fa15a31613d6f86185e0624810f69e..a2c6eb6e0dd82cf3bd14ca847df7db3c966f2361 100644 (file)
@@ -140,10 +140,10 @@ static struct drm_ioctl_desc drm_ioctls[] = {
        DRM_IOCTL_DEF(DRM_IOCTL_PRIME_HANDLE_TO_FD, drm_prime_handle_to_fd_ioctl, DRM_AUTH|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_PRIME_FD_TO_HANDLE, drm_prime_fd_to_handle_ioctl, DRM_AUTH|DRM_UNLOCKED),
 
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANERESOURCES, drm_mode_getplane_res, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANERESOURCES, drm_mode_getplane_res, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANE, drm_mode_getplane, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANE, drm_mode_getplane, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPLANE, drm_mode_setplane, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_UNLOCKED),
@@ -152,19 +152,19 @@ static struct drm_ioctl_desc drm_ioctls[] = {
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_mode_attachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_mode_detachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_MASTER | DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_mode_connector_property_set_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
 };
 
index b7ee230572b7c053a20c6bf115d4fd98a836f820..289c9b18c0d3b4137c28055561d608d50e10e72a 100644 (file)
@@ -161,7 +161,7 @@ MODULE_PARM_DESC(edid_fixup,
  * Sanity check the EDID block (base or extension).  Return 0 if the block
  * doesn't check out, or 1 if it's valid.
  */
-bool drm_edid_block_valid(u8 *raw_edid, int block)
+bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid)
 {
        int i;
        u8 csum = 0;
@@ -184,7 +184,9 @@ bool drm_edid_block_valid(u8 *raw_edid, int block)
        for (i = 0; i < EDID_LENGTH; i++)
                csum += raw_edid[i];
        if (csum) {
-               DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum);
+               if (print_bad_edid) {
+                       DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum);
+               }
 
                /* allow CEA to slide through, switches mangle this */
                if (raw_edid[0] != 0x02)
@@ -210,7 +212,7 @@ bool drm_edid_block_valid(u8 *raw_edid, int block)
        return 1;
 
 bad:
-       if (raw_edid) {
+       if (raw_edid && print_bad_edid) {
                printk(KERN_ERR "Raw EDID:\n");
                print_hex_dump(KERN_ERR, " \t", DUMP_PREFIX_NONE, 16, 1,
                               raw_edid, EDID_LENGTH, false);
@@ -234,7 +236,7 @@ bool drm_edid_is_valid(struct edid *edid)
                return false;
 
        for (i = 0; i <= edid->extensions; i++)
-               if (!drm_edid_block_valid(raw + i * EDID_LENGTH, i))
+               if (!drm_edid_block_valid(raw + i * EDID_LENGTH, i, true))
                        return false;
 
        return true;
@@ -257,6 +259,8 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf,
                      int block, int len)
 {
        unsigned char start = block * EDID_LENGTH;
+       unsigned char segment = block >> 1;
+       unsigned char xfers = segment ? 3 : 2;
        int ret, retries = 5;
 
        /* The core i2c driver will automatically retry the transfer if the
@@ -268,6 +272,11 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf,
        do {
                struct i2c_msg msgs[] = {
                        {
+                               .addr   = DDC_SEGMENT_ADDR,
+                               .flags  = 0,
+                               .len    = 1,
+                               .buf    = &segment,
+                       }, {
                                .addr   = DDC_ADDR,
                                .flags  = 0,
                                .len    = 1,
@@ -279,15 +288,21 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf,
                                .buf    = buf,
                        }
                };
-               ret = i2c_transfer(adapter, msgs, 2);
+
+       /*
+        * Avoid sending the segment addr to not upset non-compliant ddc
+        * monitors.
+        */
+               ret = i2c_transfer(adapter, &msgs[3 - xfers], xfers);
+
                if (ret == -ENXIO) {
                        DRM_DEBUG_KMS("drm: skipping non-existent adapter %s\n",
                                        adapter->name);
                        break;
                }
-       } while (ret != 2 && --retries);
+       } while (ret != xfers && --retries);
 
-       return ret == 2 ? 0 : -1;
+       return ret == xfers ? 0 : -1;
 }
 
 static bool drm_edid_is_zero(u8 *in_edid, int length)
@@ -306,6 +321,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
 {
        int i, j = 0, valid_extensions = 0;
        u8 *block, *new;
+       bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS);
 
        if ((block = kmalloc(EDID_LENGTH, GFP_KERNEL)) == NULL)
                return NULL;
@@ -314,7 +330,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
        for (i = 0; i < 4; i++) {
                if (drm_do_probe_ddc_edid(adapter, block, 0, EDID_LENGTH))
                        goto out;
-               if (drm_edid_block_valid(block, 0))
+               if (drm_edid_block_valid(block, 0, print_bad_edid))
                        break;
                if (i == 0 && drm_edid_is_zero(block, EDID_LENGTH)) {
                        connector->null_edid_counter++;
@@ -339,7 +355,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
                                  block + (valid_extensions + 1) * EDID_LENGTH,
                                  j, EDID_LENGTH))
                                goto out;
-                       if (drm_edid_block_valid(block + (valid_extensions + 1) * EDID_LENGTH, j)) {
+                       if (drm_edid_block_valid(block + (valid_extensions + 1) * EDID_LENGTH, j, print_bad_edid)) {
                                valid_extensions++;
                                break;
                        }
@@ -362,8 +378,11 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
        return block;
 
 carp:
-       dev_warn(connector->dev->dev, "%s: EDID block %d invalid.\n",
-                drm_get_connector_name(connector), j);
+       if (print_bad_edid) {
+               dev_warn(connector->dev->dev, "%s: EDID block %d invalid.\n",
+                        drm_get_connector_name(connector), j);
+       }
+       connector->bad_edid_counter++;
 
 out:
        kfree(block);
@@ -402,10 +421,7 @@ struct edid *drm_get_edid(struct drm_connector *connector,
        if (drm_probe_ddc(adapter))
                edid = (struct edid *)drm_do_get_edid(connector, adapter);
 
-       connector->display_info.raw_edid = (char *)edid;
-
        return edid;
-
 }
 EXPORT_SYMBOL(drm_get_edid);
 
@@ -1522,6 +1538,40 @@ do_cea_modes (struct drm_connector *connector, u8 *db, u8 len)
        return modes;
 }
 
+static int
+cea_db_payload_len(const u8 *db)
+{
+       return db[0] & 0x1f;
+}
+
+static int
+cea_db_tag(const u8 *db)
+{
+       return db[0] >> 5;
+}
+
+static int
+cea_revision(const u8 *cea)
+{
+       return cea[1];
+}
+
+static int
+cea_db_offsets(const u8 *cea, int *start, int *end)
+{
+       /* Data block offset in CEA extension block */
+       *start = 4;
+       *end = cea[2];
+       if (*end == 0)
+               *end = 127;
+       if (*end < 4 || *end > 127)
+               return -ERANGE;
+       return 0;
+}
+
+#define for_each_cea_db(cea, i, start, end) \
+       for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1)
+
 static int
 add_cea_modes(struct drm_connector *connector, struct edid *edid)
 {
@@ -1529,10 +1579,17 @@ add_cea_modes(struct drm_connector *connector, struct edid *edid)
        u8 * db, dbl;
        int modes = 0;
 
-       if (cea && cea[1] >= 3) {
-               for (db = cea + 4; db < cea + cea[2]; db += dbl + 1) {
-                       dbl = db[0] & 0x1f;
-                       if (((db[0] & 0xe0) >> 5) == VIDEO_BLOCK)
+       if (cea && cea_revision(cea) >= 3) {
+               int i, start, end;
+
+               if (cea_db_offsets(cea, &start, &end))
+                       return 0;
+
+               for_each_cea_db(cea, i, start, end) {
+                       db = &cea[i];
+                       dbl = cea_db_payload_len(db);
+
+                       if (cea_db_tag(db) == VIDEO_BLOCK)
                                modes += do_cea_modes (connector, db+1, dbl);
                }
        }
@@ -1541,19 +1598,28 @@ add_cea_modes(struct drm_connector *connector, struct edid *edid)
 }
 
 static void
-parse_hdmi_vsdb(struct drm_connector *connector, uint8_t *db)
+parse_hdmi_vsdb(struct drm_connector *connector, const u8 *db)
 {
-       connector->eld[5] |= (db[6] >> 7) << 1;  /* Supports_AI */
-
-       connector->dvi_dual = db[6] & 1;
-       connector->max_tmds_clock = db[7] * 5;
+       u8 len = cea_db_payload_len(db);
 
-       connector->latency_present[0] = db[8] >> 7;
-       connector->latency_present[1] = (db[8] >> 6) & 1;
-       connector->video_latency[0] = db[9];
-       connector->audio_latency[0] = db[10];
-       connector->video_latency[1] = db[11];
-       connector->audio_latency[1] = db[12];
+       if (len >= 6) {
+               connector->eld[5] |= (db[6] >> 7) << 1;  /* Supports_AI */
+               connector->dvi_dual = db[6] & 1;
+       }
+       if (len >= 7)
+               connector->max_tmds_clock = db[7] * 5;
+       if (len >= 8) {
+               connector->latency_present[0] = db[8] >> 7;
+               connector->latency_present[1] = (db[8] >> 6) & 1;
+       }
+       if (len >= 9)
+               connector->video_latency[0] = db[9];
+       if (len >= 10)
+               connector->audio_latency[0] = db[10];
+       if (len >= 11)
+               connector->video_latency[1] = db[11];
+       if (len >= 12)
+               connector->audio_latency[1] = db[12];
 
        DRM_LOG_KMS("HDMI: DVI dual %d, "
                    "max TMDS clock %d, "
@@ -1577,6 +1643,21 @@ monitor_name(struct detailed_timing *t, void *data)
                *(u8 **)data = t->data.other_data.data.str.str;
 }
 
+static bool cea_db_is_hdmi_vsdb(const u8 *db)
+{
+       int hdmi_id;
+
+       if (cea_db_tag(db) != VENDOR_BLOCK)
+               return false;
+
+       if (cea_db_payload_len(db) < 5)
+               return false;
+
+       hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16);
+
+       return hdmi_id == HDMI_IDENTIFIER;
+}
+
 /**
  * drm_edid_to_eld - build ELD from EDID
  * @connector: connector corresponding to the HDMI/DP sink
@@ -1623,29 +1704,40 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
        eld[18] = edid->prod_code[0];
        eld[19] = edid->prod_code[1];
 
-       if (cea[1] >= 3)
-               for (db = cea + 4; db < cea + cea[2]; db += dbl + 1) {
-                       dbl = db[0] & 0x1f;
-                       
-                       switch ((db[0] & 0xe0) >> 5) {
+       if (cea_revision(cea) >= 3) {
+               int i, start, end;
+
+               if (cea_db_offsets(cea, &start, &end)) {
+                       start = 0;
+                       end = 0;
+               }
+
+               for_each_cea_db(cea, i, start, end) {
+                       db = &cea[i];
+                       dbl = cea_db_payload_len(db);
+
+                       switch (cea_db_tag(db)) {
                        case AUDIO_BLOCK:
                                /* Audio Data Block, contains SADs */
                                sad_count = dbl / 3;
-                               memcpy(eld + 20 + mnl, &db[1], dbl);
+                               if (dbl >= 1)
+                                       memcpy(eld + 20 + mnl, &db[1], dbl);
                                break;
                        case SPEAKER_BLOCK:
-                                /* Speaker Allocation Data Block */
-                               eld[7] = db[1];
+                               /* Speaker Allocation Data Block */
+                               if (dbl >= 1)
+                                       eld[7] = db[1];
                                break;
                        case VENDOR_BLOCK:
                                /* HDMI Vendor-Specific Data Block */
-                               if (db[1] == 0x03 && db[2] == 0x0c && db[3] == 0)
+                               if (cea_db_is_hdmi_vsdb(db))
                                        parse_hdmi_vsdb(connector, db);
                                break;
                        default:
                                break;
                        }
                }
+       }
        eld[5] |= sad_count << 4;
        eld[2] = (20 + mnl + sad_count * 3 + 3) / 4;
 
@@ -1723,38 +1815,26 @@ EXPORT_SYMBOL(drm_select_eld);
 bool drm_detect_hdmi_monitor(struct edid *edid)
 {
        u8 *edid_ext;
-       int i, hdmi_id;
+       int i;
        int start_offset, end_offset;
-       bool is_hdmi = false;
 
        edid_ext = drm_find_cea_extension(edid);
        if (!edid_ext)
-               goto end;
+               return false;
 
-       /* Data block offset in CEA extension block */
-       start_offset = 4;
-       end_offset = edid_ext[2];
+       if (cea_db_offsets(edid_ext, &start_offset, &end_offset))
+               return false;
 
        /*
         * Because HDMI identifier is in Vendor Specific Block,
         * search it from all data blocks of CEA extension.
         */
-       for (i = start_offset; i < end_offset;
-               /* Increased by data block len */
-               i += ((edid_ext[i] & 0x1f) + 1)) {
-               /* Find vendor specific block */
-               if ((edid_ext[i] >> 5) == VENDOR_BLOCK) {
-                       hdmi_id = edid_ext[i + 1] | (edid_ext[i + 2] << 8) |
-                                 edid_ext[i + 3] << 16;
-                       /* Find HDMI identifier */
-                       if (hdmi_id == HDMI_IDENTIFIER)
-                               is_hdmi = true;
-                       break;
-               }
+       for_each_cea_db(edid_ext, i, start_offset, end_offset) {
+               if (cea_db_is_hdmi_vsdb(&edid_ext[i]))
+                       return true;
        }
 
-end:
-       return is_hdmi;
+       return false;
 }
 EXPORT_SYMBOL(drm_detect_hdmi_monitor);
 
@@ -1786,15 +1866,13 @@ bool drm_detect_monitor_audio(struct edid *edid)
                goto end;
        }
 
-       /* Data block offset in CEA extension block */
-       start_offset = 4;
-       end_offset = edid_ext[2];
+       if (cea_db_offsets(edid_ext, &start_offset, &end_offset))
+               goto end;
 
-       for (i = start_offset; i < end_offset;
-                       i += ((edid_ext[i] & 0x1f) + 1)) {
-               if ((edid_ext[i] >> 5) == AUDIO_BLOCK) {
+       for_each_cea_db(edid_ext, i, start_offset, end_offset) {
+               if (cea_db_tag(&edid_ext[i]) == AUDIO_BLOCK) {
                        has_audio = true;
-                       for (j = 1; j < (edid_ext[i] & 0x1f); j += 3)
+                       for (j = 1; j < cea_db_payload_len(&edid_ext[i]) + 1; j += 3)
                                DRM_DEBUG_KMS("CEA audio format %d\n",
                                              (edid_ext[i + j] >> 3) & 0xf);
                        goto end;
index 0303935d10e2ca45165509a7e9e8165962c99774..ea9cdab3d43164fb55ea81bf55338ec26a408478 100644 (file)
@@ -114,8 +114,8 @@ static u8 generic_edid[GENERIC_EDIDS][128] = {
        },
 };
 
-static int edid_load(struct drm_connector *connector, char *name,
-                    char *connector_name)
+static u8 *edid_load(struct drm_connector *connector, char *name,
+                       char *connector_name)
 {
        const struct firmware *fw;
        struct platform_device *pdev;
@@ -123,6 +123,7 @@ static int edid_load(struct drm_connector *connector, char *name,
        int fwsize, expected;
        int builtin = 0, err = 0;
        int i, valid_extensions = 0;
+       bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS);
 
        pdev = platform_device_register_simple(connector_name, -1, NULL, 0);
        if (IS_ERR(pdev)) {
@@ -173,7 +174,8 @@ static int edid_load(struct drm_connector *connector, char *name,
        }
        memcpy(edid, fwdata, fwsize);
 
-       if (!drm_edid_block_valid(edid, 0)) {
+       if (!drm_edid_block_valid(edid, 0, print_bad_edid)) {
+               connector->bad_edid_counter++;
                DRM_ERROR("Base block of EDID firmware \"%s\" is invalid ",
                    name);
                kfree(edid);
@@ -185,7 +187,7 @@ static int edid_load(struct drm_connector *connector, char *name,
                if (i != valid_extensions + 1)
                        memcpy(edid + (valid_extensions + 1) * EDID_LENGTH,
                            edid + i * EDID_LENGTH, EDID_LENGTH);
-               if (drm_edid_block_valid(edid + i * EDID_LENGTH, i))
+               if (drm_edid_block_valid(edid + i * EDID_LENGTH, i, print_bad_edid))
                        valid_extensions++;
        }
 
@@ -205,7 +207,6 @@ static int edid_load(struct drm_connector *connector, char *name,
                edid = new_edid;
        }
 
-       connector->display_info.raw_edid = edid;
        DRM_INFO("Got %s EDID base block and %d extension%s from "
            "\"%s\" for connector \"%s\"\n", builtin ? "built-in" :
            "external", valid_extensions, valid_extensions == 1 ? "" : "s",
@@ -215,7 +216,10 @@ relfw_out:
        release_firmware(fw);
 
 out:
-       return err;
+       if (err)
+               return ERR_PTR(err);
+
+       return edid;
 }
 
 int drm_load_edid_firmware(struct drm_connector *connector)
@@ -223,6 +227,7 @@ int drm_load_edid_firmware(struct drm_connector *connector)
        char *connector_name = drm_get_connector_name(connector);
        char *edidname = edid_firmware, *last, *colon;
        int ret;
+       struct edid *edid;
 
        if (*edidname == '\0')
                return 0;
@@ -240,13 +245,13 @@ int drm_load_edid_firmware(struct drm_connector *connector)
        if (*last == '\n')
                *last = '\0';
 
-       ret = edid_load(connector, edidname, connector_name);
-       if (ret)
+       edid = (struct edid *) edid_load(connector, edidname, connector_name);
+       if (IS_ERR_OR_NULL(edid))
                return 0;
 
-       drm_mode_connector_update_edid_property(connector,
-           (struct edid *) connector->display_info.raw_edid);
+       drm_mode_connector_update_edid_property(connector, edid);
+       ret = drm_add_edid_modes(connector, edid);
+       kfree(edid);
 
-       return drm_add_edid_modes(connector, (struct edid *)
-           connector->display_info.raw_edid);
+       return ret;
 }
index ff98a7eb38ddd24ff82358b784e1569c92eaf05f..57459b316adc9cb25ee7e592d91de45e9323bb91 100644 (file)
@@ -89,7 +89,7 @@ static const struct drm_display_mode drm_dmt_modes[] = {
                   976, 1088, 0, 480, 486, 494, 517, 0,
                   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
        /* 1024x768@43Hz, interlace */
-       { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 44900, 1024, 1032,
+       { DRM_MODE("1024x768i", DRM_MODE_TYPE_DRIVER, 44900, 1024, 1032,
                   1208, 1264, 0, 768, 768, 772, 817, 0,
                   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
                        DRM_MODE_FLAG_INTERLACE) },
@@ -395,7 +395,7 @@ static const struct drm_display_mode edid_est_modes[] = {
        { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
                   1184, 1344, 0,  768, 771, 777, 806, 0,
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */
-       { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032,
+       { DRM_MODE("1024x768i", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032,
                   1208, 1264, 0, 768, 768, 776, 817, 0,
                   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */
        { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864,
@@ -506,17 +506,17 @@ static const struct drm_display_mode edid_cea_modes[] = {
                   1430, 1650, 0, 720, 725, 730, 750, 0,
                   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
        /* 5 - 1920x1080i@60Hz */
-       { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
+       { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
                   2052, 2200, 0, 1080, 1084, 1094, 1125, 0,
                   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
                        DRM_MODE_FLAG_INTERLACE) },
        /* 6 - 1440x480i@60Hz */
-       { DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478,
+       { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478,
                   1602, 1716, 0, 480, 488, 494, 525, 0,
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
                        DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) },
        /* 7 - 1440x480i@60Hz */
-       { DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478,
+       { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478,
                   1602, 1716, 0, 480, 488, 494, 525, 0,
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
                        DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) },
@@ -531,12 +531,12 @@ static const struct drm_display_mode edid_cea_modes[] = {
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
                        DRM_MODE_FLAG_DBLCLK) },
        /* 10 - 2880x480i@60Hz */
-       { DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
+       { DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
                   3204, 3432, 0, 480, 488, 494, 525, 0,
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
                        DRM_MODE_FLAG_INTERLACE) },
        /* 11 - 2880x480i@60Hz */
-       { DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
+       { DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
                   3204, 3432, 0, 480, 488, 494, 525, 0,
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
                        DRM_MODE_FLAG_INTERLACE) },
@@ -573,17 +573,17 @@ static const struct drm_display_mode edid_cea_modes[] = {
                   1760, 1980, 0, 720, 725, 730, 750, 0,
                   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
        /* 20 - 1920x1080i@50Hz */
-       { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
+       { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
                   2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
                   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
                        DRM_MODE_FLAG_INTERLACE) },
        /* 21 - 1440x576i@50Hz */
-       { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464,
+       { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464,
                   1590, 1728, 0, 576, 580, 586, 625, 0,
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
                        DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) },
        /* 22 - 1440x576i@50Hz */
-       { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464,
+       { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464,
                   1590, 1728, 0, 576, 580, 586, 625, 0,
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
                        DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) },
@@ -598,12 +598,12 @@ static const struct drm_display_mode edid_cea_modes[] = {
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
                        DRM_MODE_FLAG_DBLCLK) },
        /* 25 - 2880x576i@50Hz */
-       { DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
+       { DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
                   3180, 3456, 0, 576, 580, 586, 625, 0,
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
                        DRM_MODE_FLAG_INTERLACE) },
        /* 26 - 2880x576i@50Hz */
-       { DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
+       { DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
                   3180, 3456, 0, 576, 580, 586, 625, 0,
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
                        DRM_MODE_FLAG_INTERLACE) },
@@ -656,12 +656,12 @@ static const struct drm_display_mode edid_cea_modes[] = {
                   3184, 3456, 0, 576, 581, 586, 625, 0,
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
        /* 39 - 1920x1080i@50Hz */
-       { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 72000, 1920, 1952,
+       { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 72000, 1920, 1952,
                   2120, 2304, 0, 1080, 1126, 1136, 1250, 0,
                   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC |
                        DRM_MODE_FLAG_INTERLACE) },
        /* 40 - 1920x1080i@100Hz */
-       { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
+       { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
                   2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
                   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
                        DRM_MODE_FLAG_INTERLACE) },
@@ -688,7 +688,7 @@ static const struct drm_display_mode edid_cea_modes[] = {
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
                        DRM_MODE_FLAG_DBLCLK) },
        /* 46 - 1920x1080i@120Hz */
-       { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
+       { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
                   2052, 2200, 0, 1080, 1084, 1094, 1125, 0,
                   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
                        DRM_MODE_FLAG_INTERLACE) },
@@ -705,12 +705,12 @@ static const struct drm_display_mode edid_cea_modes[] = {
                   798, 858, 0, 480, 489, 495, 525, 0,
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
        /* 50 - 1440x480i@120Hz */
-       { DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478,
+       { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478,
                   1602, 1716, 0, 480, 488, 494, 525, 0,
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
                        DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) },
        /* 51 - 1440x480i@120Hz */
-       { DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478,
+       { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478,
                   1602, 1716, 0, 480, 488, 494, 525, 0,
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
                        DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) },
@@ -723,12 +723,12 @@ static const struct drm_display_mode edid_cea_modes[] = {
                   796, 864, 0, 576, 581, 586, 625, 0,
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
        /* 54 - 1440x576i@200Hz */
-       { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464,
+       { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464,
                   1590, 1728, 0, 576, 580, 586, 625, 0,
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
                        DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) },
        /* 55 - 1440x576i@200Hz */
-       { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464,
+       { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464,
                   1590, 1728, 0, 576, 580, 586, 625, 0,
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
                        DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) },
@@ -741,12 +741,12 @@ static const struct drm_display_mode edid_cea_modes[] = {
                   798, 858, 0, 480, 489, 495, 525, 0,
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
        /* 58 - 1440x480i@240 */
-       { DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478,
+       { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478,
                   1602, 1716, 0, 480, 488, 494, 525, 0,
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
                        DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) },
        /* 59 - 1440x480i@240 */
-       { DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478,
+       { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478,
                   1602, 1716, 0, 480, 488, 494, 525, 0,
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
                        DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) },
diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c
new file mode 100644 (file)
index 0000000..09e11a5
--- /dev/null
@@ -0,0 +1,406 @@
+/*
+ * drm kms/fb cma (contiguous memory allocator) helper functions
+ *
+ * Copyright (C) 2012 Analog Device Inc.
+ *   Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Based on udl_fbdev.c
+ *  Copyright (C) 2012 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <linux/module.h>
+
+struct drm_fb_cma {
+       struct drm_framebuffer          fb;
+       struct drm_gem_cma_object       *obj[4];
+};
+
+struct drm_fbdev_cma {
+       struct drm_fb_helper    fb_helper;
+       struct drm_fb_cma       *fb;
+};
+
+static inline struct drm_fbdev_cma *to_fbdev_cma(struct drm_fb_helper *helper)
+{
+       return container_of(helper, struct drm_fbdev_cma, fb_helper);
+}
+
+static inline struct drm_fb_cma *to_fb_cma(struct drm_framebuffer *fb)
+{
+       return container_of(fb, struct drm_fb_cma, fb);
+}
+
+static void drm_fb_cma_destroy(struct drm_framebuffer *fb)
+{
+       struct drm_fb_cma *fb_cma = to_fb_cma(fb);
+       int i;
+
+       for (i = 0; i < 4; i++) {
+               if (fb_cma->obj[i])
+                       drm_gem_object_unreference_unlocked(&fb_cma->obj[i]->base);
+       }
+
+       drm_framebuffer_cleanup(fb);
+       kfree(fb_cma);
+}
+
+static int drm_fb_cma_create_handle(struct drm_framebuffer *fb,
+       struct drm_file *file_priv, unsigned int *handle)
+{
+       struct drm_fb_cma *fb_cma = to_fb_cma(fb);
+
+       return drm_gem_handle_create(file_priv,
+                       &fb_cma->obj[0]->base, handle);
+}
+
+static struct drm_framebuffer_funcs drm_fb_cma_funcs = {
+       .destroy        = drm_fb_cma_destroy,
+       .create_handle  = drm_fb_cma_create_handle,
+};
+
+static struct drm_fb_cma *drm_fb_cma_alloc(struct drm_device *dev,
+       struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_cma_object **obj,
+       unsigned int num_planes)
+{
+       struct drm_fb_cma *fb_cma;
+       int ret;
+       int i;
+
+       fb_cma = kzalloc(sizeof(*fb_cma), GFP_KERNEL);
+       if (!fb_cma)
+               return ERR_PTR(-ENOMEM);
+
+       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);
+               kfree(fb_cma);
+               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;
+}
+
+/**
+ * drm_fb_cma_create() - (struct drm_mode_config_funcs *)->fb_create callback function
+ *
+ * If your hardware has special alignment or pitch requirements these should be
+ * checked before calling this function.
+ */
+struct drm_framebuffer *drm_fb_cma_create(struct drm_device *dev,
+       struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd)
+{
+       struct drm_fb_cma *fb_cma;
+       struct drm_gem_cma_object *objs[4];
+       struct drm_gem_object *obj;
+       unsigned int hsub;
+       unsigned int vsub;
+       int ret;
+       int i;
+
+       hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
+       vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
+
+       for (i = 0; i < drm_format_num_planes(mode_cmd->pixel_format); i++) {
+               unsigned int width = mode_cmd->width / (i ? hsub : 1);
+               unsigned int height = mode_cmd->height / (i ? vsub : 1);
+               unsigned int min_size;
+
+               obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[i]);
+               if (!obj) {
+                       dev_err(dev->dev, "Failed to lookup GEM object\n");
+                       ret = -ENXIO;
+                       goto err_gem_object_unreference;
+               }
+
+               min_size = (height - 1) * mode_cmd->pitches[i]
+                        + width * drm_format_plane_cpp(mode_cmd->pixel_format, i)
+                        + mode_cmd->offsets[i];
+
+               if (obj->size < min_size) {
+                       drm_gem_object_unreference_unlocked(obj);
+                       ret = -EINVAL;
+                       goto err_gem_object_unreference;
+               }
+               objs[i] = to_drm_gem_cma_obj(obj);
+       }
+
+       fb_cma = drm_fb_cma_alloc(dev, mode_cmd, objs, i);
+       if (IS_ERR(fb_cma)) {
+               ret = PTR_ERR(fb_cma);
+               goto err_gem_object_unreference;
+       }
+
+       return &fb_cma->fb;
+
+err_gem_object_unreference:
+       for (i--; i >= 0; i--)
+               drm_gem_object_unreference_unlocked(&objs[i]->base);
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(drm_fb_cma_create);
+
+/**
+ * drm_fb_cma_get_gem_obj() - Get CMA GEM object for framebuffer
+ * @fb: The framebuffer
+ * @plane: Which plane
+ *
+ * Return the CMA GEM object for given framebuffer.
+ *
+ * This function will usually be called from the CRTC callback functions.
+ */
+struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb,
+       unsigned int plane)
+{
+       struct drm_fb_cma *fb_cma = to_fb_cma(fb);
+
+       if (plane >= 4)
+               return NULL;
+
+       return fb_cma->obj[plane];
+}
+EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj);
+
+static struct fb_ops drm_fbdev_cma_ops = {
+       .owner          = THIS_MODULE,
+       .fb_fillrect    = sys_fillrect,
+       .fb_copyarea    = sys_copyarea,
+       .fb_imageblit   = sys_imageblit,
+       .fb_check_var   = drm_fb_helper_check_var,
+       .fb_set_par     = drm_fb_helper_set_par,
+       .fb_blank       = drm_fb_helper_blank,
+       .fb_pan_display = drm_fb_helper_pan_display,
+       .fb_setcmap     = drm_fb_helper_setcmap,
+};
+
+static int drm_fbdev_cma_create(struct drm_fb_helper *helper,
+       struct drm_fb_helper_surface_size *sizes)
+{
+       struct drm_fbdev_cma *fbdev_cma = to_fbdev_cma(helper);
+       struct drm_mode_fb_cmd2 mode_cmd = { 0 };
+       struct drm_device *dev = helper->dev;
+       struct drm_gem_cma_object *obj;
+       struct drm_framebuffer *fb;
+       unsigned int bytes_per_pixel;
+       unsigned long offset;
+       struct fb_info *fbi;
+       size_t size;
+       int ret;
+
+       DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n",
+                       sizes->surface_width, sizes->surface_height,
+                       sizes->surface_bpp);
+
+       bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
+
+       mode_cmd.width = sizes->surface_width;
+       mode_cmd.height = sizes->surface_height;
+       mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel;
+       mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+               sizes->surface_depth);
+
+       size = mode_cmd.pitches[0] * mode_cmd.height;
+       obj = drm_gem_cma_create(dev, size);
+       if (!obj)
+               return -ENOMEM;
+
+       fbi = framebuffer_alloc(0, dev->dev);
+       if (!fbi) {
+               dev_err(dev->dev, "Failed to allocate framebuffer info.\n");
+               ret = -ENOMEM;
+               goto err_drm_gem_cma_free_object;
+       }
+
+       fbdev_cma->fb = drm_fb_cma_alloc(dev, &mode_cmd, &obj, 1);
+       if (IS_ERR(fbdev_cma->fb)) {
+               dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n");
+               ret = PTR_ERR(fbdev_cma->fb);
+               goto err_framebuffer_release;
+       }
+
+       fb = &fbdev_cma->fb->fb;
+       helper->fb = fb;
+       helper->fbdev = fbi;
+
+       fbi->par = helper;
+       fbi->flags = FBINFO_FLAG_DEFAULT;
+       fbi->fbops = &drm_fbdev_cma_ops;
+
+       ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
+       if (ret) {
+               dev_err(dev->dev, "Failed to allocate color map.\n");
+               goto err_drm_fb_cma_destroy;
+       }
+
+       drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
+       drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
+
+       offset = fbi->var.xoffset * bytes_per_pixel;
+       offset += fbi->var.yoffset * fb->pitches[0];
+
+       dev->mode_config.fb_base = (resource_size_t)obj->paddr;
+       fbi->screen_base = obj->vaddr + offset;
+       fbi->fix.smem_start = (unsigned long)(obj->paddr + offset);
+       fbi->screen_size = size;
+       fbi->fix.smem_len = size;
+
+       return 0;
+
+err_drm_fb_cma_destroy:
+       drm_fb_cma_destroy(fb);
+err_framebuffer_release:
+       framebuffer_release(fbi);
+err_drm_gem_cma_free_object:
+       drm_gem_cma_free_object(&obj->base);
+       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,
+};
+
+/**
+ * drm_fbdev_cma_init() - Allocate and initializes a drm_fbdev_cma struct
+ * @dev: DRM device
+ * @preferred_bpp: Preferred bits per pixel for the device
+ * @num_crtc: Number of CRTCs
+ * @max_conn_count: Maximum number of connectors
+ *
+ * Returns a newly allocated drm_fbdev_cma struct or a ERR_PTR.
+ */
+struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
+       unsigned int preferred_bpp, unsigned int num_crtc,
+       unsigned int max_conn_count)
+{
+       struct drm_fbdev_cma *fbdev_cma;
+       struct drm_fb_helper *helper;
+       int ret;
+
+       fbdev_cma = kzalloc(sizeof(*fbdev_cma), GFP_KERNEL);
+       if (!fbdev_cma) {
+               dev_err(dev->dev, "Failed to allocate drm fbdev.\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       fbdev_cma->fb_helper.funcs = &drm_fb_cma_helper_funcs;
+       helper = &fbdev_cma->fb_helper;
+
+       ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count);
+       if (ret < 0) {
+               dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
+               goto err_free;
+       }
+
+       ret = drm_fb_helper_single_add_all_connectors(helper);
+       if (ret < 0) {
+               dev_err(dev->dev, "Failed to add connectors.\n");
+               goto err_drm_fb_helper_fini;
+
+       }
+
+       ret = drm_fb_helper_initial_config(helper, preferred_bpp);
+       if (ret < 0) {
+               dev_err(dev->dev, "Failed to set inital hw configuration.\n");
+               goto err_drm_fb_helper_fini;
+       }
+
+       return fbdev_cma;
+
+err_drm_fb_helper_fini:
+       drm_fb_helper_fini(helper);
+err_free:
+       kfree(fbdev_cma);
+
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(drm_fbdev_cma_init);
+
+/**
+ * drm_fbdev_cma_fini() - Free drm_fbdev_cma struct
+ * @fbdev_cma: The drm_fbdev_cma struct
+ */
+void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma)
+{
+       if (fbdev_cma->fb_helper.fbdev) {
+               struct fb_info *info;
+               int ret;
+
+               info = fbdev_cma->fb_helper.fbdev;
+               ret = unregister_framebuffer(info);
+               if (ret < 0)
+                       DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
+
+               if (info->cmap.len)
+                       fb_dealloc_cmap(&info->cmap);
+
+               framebuffer_release(info);
+       }
+
+       if (fbdev_cma->fb)
+               drm_fb_cma_destroy(&fbdev_cma->fb->fb);
+
+       drm_fb_helper_fini(&fbdev_cma->fb_helper);
+       kfree(fbdev_cma);
+}
+EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini);
+
+/**
+ * drm_fbdev_cma_restore_mode() - Restores initial framebuffer mode
+ * @fbdev_cma: The drm_fbdev_cma struct, may be NULL
+ *
+ * This function is usually called from the DRM drivers lastclose callback.
+ */
+void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma)
+{
+       if (fbdev_cma)
+               drm_fb_helper_restore_fbdev_mode(&fbdev_cma->fb_helper);
+}
+EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode);
+
+/**
+ * drm_fbdev_cma_hotplug_event() - Poll for hotpulug events
+ * @fbdev_cma: The drm_fbdev_cma struct, may be NULL
+ *
+ * This function is usually called from the DRM drivers output_poll_changed
+ * callback.
+ */
+void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma)
+{
+       if (fbdev_cma)
+               drm_fb_helper_hotplug_event(&fbdev_cma->fb_helper);
+}
+EXPORT_SYMBOL_GPL(drm_fbdev_cma_hotplug_event);
index f546d1e8af82f10e1cfe1d9c6c66ec9015bc1a68..b5d05f58c15a28de22ccc6b7cf1ca7b17f6598f7 100644 (file)
@@ -236,7 +236,7 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
 }
 EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode);
 
-bool drm_fb_helper_force_kernel_mode(void)
+static bool drm_fb_helper_force_kernel_mode(void)
 {
        bool ret, error = false;
        struct drm_fb_helper *helper;
@@ -330,7 +330,7 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
                /* Walk the connectors & encoders on this fb turning them on/off */
                for (j = 0; j < fb_helper->connector_count; j++) {
                        connector = fb_helper->connector_info[j]->connector;
-                       drm_helper_connector_dpms(connector, dpms_mode);
+                       connector->funcs->dpms(connector, dpms_mode);
                        drm_connector_property_set_value(connector,
                                dev->mode_config.dpms_property, dpms_mode);
                }
@@ -1230,7 +1230,6 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
        struct drm_device *dev = fb_helper->dev;
        struct drm_fb_helper_crtc **crtcs;
        struct drm_display_mode **modes;
-       struct drm_encoder *encoder;
        struct drm_mode_set *modeset;
        bool *enabled;
        int width, height;
@@ -1241,11 +1240,6 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
        width = dev->mode_config.max_width;
        height = dev->mode_config.max_height;
 
-       /* clean out all the encoder/crtc combos */
-       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
-               encoder->crtc = NULL;
-       }
-
        crtcs = kcalloc(dev->mode_config.num_connector,
                        sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
        modes = kcalloc(dev->mode_config.num_connector,
diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c
new file mode 100644 (file)
index 0000000..1aa8fee
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * drm gem CMA (contiguous memory allocator) helper functions
+ *
+ * Copyright (C) 2012 Sascha Hauer, Pengutronix
+ *
+ * Based on Samsung Exynos code
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/export.h>
+#include <linux/dma-mapping.h>
+
+#include <drm/drmP.h>
+#include <drm/drm.h>
+#include <drm/drm_gem_cma_helper.h>
+
+static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj)
+{
+       return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT;
+}
+
+static void drm_gem_cma_buf_destroy(struct drm_device *drm,
+               struct drm_gem_cma_object *cma_obj)
+{
+       dma_free_writecombine(drm->dev, cma_obj->base.size, cma_obj->vaddr,
+                       cma_obj->paddr);
+}
+
+/*
+ * drm_gem_cma_create - allocate an object with the given size
+ *
+ * returns a struct drm_gem_cma_object* on success or ERR_PTR values
+ * on failure.
+ */
+struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm,
+               unsigned int size)
+{
+       struct drm_gem_cma_object *cma_obj;
+       struct drm_gem_object *gem_obj;
+       int ret;
+
+       size = round_up(size, PAGE_SIZE);
+
+       cma_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL);
+       if (!cma_obj)
+               return ERR_PTR(-ENOMEM);
+
+       cma_obj->vaddr = dma_alloc_writecombine(drm->dev, size,
+                       &cma_obj->paddr, GFP_KERNEL | __GFP_NOWARN);
+       if (!cma_obj->vaddr) {
+               dev_err(drm->dev, "failed to allocate buffer with size %d\n", size);
+               ret = -ENOMEM;
+               goto err_dma_alloc;
+       }
+
+       gem_obj = &cma_obj->base;
+
+       ret = drm_gem_object_init(drm, gem_obj, size);
+       if (ret)
+               goto err_obj_init;
+
+       ret = drm_gem_create_mmap_offset(gem_obj);
+       if (ret)
+               goto err_create_mmap_offset;
+
+       return cma_obj;
+
+err_create_mmap_offset:
+       drm_gem_object_release(gem_obj);
+
+err_obj_init:
+       drm_gem_cma_buf_destroy(drm, cma_obj);
+
+err_dma_alloc:
+       kfree(cma_obj);
+
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_create);
+
+/*
+ * drm_gem_cma_create_with_handle - allocate an object with the given
+ * size and create a gem handle on it
+ *
+ * returns a struct drm_gem_cma_object* on success or ERR_PTR values
+ * on failure.
+ */
+static struct drm_gem_cma_object *drm_gem_cma_create_with_handle(
+               struct drm_file *file_priv,
+               struct drm_device *drm, unsigned int size,
+               unsigned int *handle)
+{
+       struct drm_gem_cma_object *cma_obj;
+       struct drm_gem_object *gem_obj;
+       int ret;
+
+       cma_obj = drm_gem_cma_create(drm, size);
+       if (IS_ERR(cma_obj))
+               return cma_obj;
+
+       gem_obj = &cma_obj->base;
+
+       /*
+        * allocate a id of idr table where the obj is registered
+        * and handle has the id what user can see.
+        */
+       ret = drm_gem_handle_create(file_priv, gem_obj, handle);
+       if (ret)
+               goto err_handle_create;
+
+       /* drop reference from allocate - handle holds it now. */
+       drm_gem_object_unreference_unlocked(gem_obj);
+
+       return cma_obj;
+
+err_handle_create:
+       drm_gem_cma_free_object(gem_obj);
+
+       return ERR_PTR(ret);
+}
+
+/*
+ * drm_gem_cma_free_object - (struct drm_driver)->gem_free_object callback
+ * function
+ */
+void drm_gem_cma_free_object(struct drm_gem_object *gem_obj)
+{
+       struct drm_gem_cma_object *cma_obj;
+
+       if (gem_obj->map_list.map)
+               drm_gem_free_mmap_offset(gem_obj);
+
+       drm_gem_object_release(gem_obj);
+
+       cma_obj = to_drm_gem_cma_obj(gem_obj);
+
+       drm_gem_cma_buf_destroy(gem_obj->dev, cma_obj);
+
+       kfree(cma_obj);
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_free_object);
+
+/*
+ * drm_gem_cma_dumb_create - (struct drm_driver)->dumb_create callback
+ * function
+ *
+ * This aligns the pitch and size arguments to the minimum required. wrap
+ * this into your own function if you need bigger alignment.
+ */
+int drm_gem_cma_dumb_create(struct drm_file *file_priv,
+               struct drm_device *dev, struct drm_mode_create_dumb *args)
+{
+       struct drm_gem_cma_object *cma_obj;
+       int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+
+       if (args->pitch < min_pitch)
+               args->pitch = min_pitch;
+
+       if (args->size < args->pitch * args->height)
+               args->size = args->pitch * args->height;
+
+       cma_obj = drm_gem_cma_create_with_handle(file_priv, dev,
+                       args->size, &args->handle);
+       if (IS_ERR(cma_obj))
+               return PTR_ERR(cma_obj);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_create);
+
+/*
+ * drm_gem_cma_dumb_map_offset - (struct drm_driver)->dumb_map_offset callback
+ * function
+ */
+int drm_gem_cma_dumb_map_offset(struct drm_file *file_priv,
+               struct drm_device *drm, uint32_t handle, uint64_t *offset)
+{
+       struct drm_gem_object *gem_obj;
+
+       mutex_lock(&drm->struct_mutex);
+
+       gem_obj = drm_gem_object_lookup(drm, file_priv, handle);
+       if (!gem_obj) {
+               dev_err(drm->dev, "failed to lookup gem object\n");
+               mutex_unlock(&drm->struct_mutex);
+               return -EINVAL;
+       }
+
+       *offset = get_gem_mmap_offset(gem_obj);
+
+       drm_gem_object_unreference(gem_obj);
+
+       mutex_unlock(&drm->struct_mutex);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_map_offset);
+
+const struct vm_operations_struct drm_gem_cma_vm_ops = {
+       .open = drm_gem_vm_open,
+       .close = drm_gem_vm_close,
+};
+EXPORT_SYMBOL_GPL(drm_gem_cma_vm_ops);
+
+/*
+ * drm_gem_cma_mmap - (struct file_operation)->mmap callback function
+ */
+int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+       struct drm_gem_object *gem_obj;
+       struct drm_gem_cma_object *cma_obj;
+       int ret;
+
+       ret = drm_gem_mmap(filp, vma);
+       if (ret)
+               return ret;
+
+       gem_obj = vma->vm_private_data;
+       cma_obj = to_drm_gem_cma_obj(gem_obj);
+
+       ret = remap_pfn_range(vma, vma->vm_start, cma_obj->paddr >> PAGE_SHIFT,
+                       vma->vm_end - vma->vm_start, vma->vm_page_prot);
+       if (ret)
+               drm_gem_vm_close(vma);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_mmap);
+
+/*
+ * drm_gem_cma_dumb_destroy - (struct drm_driver)->dumb_destroy callback function
+ */
+int drm_gem_cma_dumb_destroy(struct drm_file *file_priv,
+               struct drm_device *drm, unsigned int handle)
+{
+       return drm_gem_handle_delete(file_priv, handle);
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_destroy);
index 03f16f352fe2e9e9b309008c182b33954fc46f93..076c4a86ff846fcfa8cde61340ac2b770b613e76 100644 (file)
@@ -1236,7 +1236,7 @@ done:
        return ret;
 }
 
-void drm_handle_vblank_events(struct drm_device *dev, int crtc)
+static void drm_handle_vblank_events(struct drm_device *dev, int crtc)
 {
        struct drm_pending_vblank_event *e, *t;
        struct timeval now;
index 961ee08927fe873cc791dcaa003beb437dd3b0aa..6fed215023132981c1af48c8fa8521ce59a210d0 100644 (file)
@@ -62,7 +62,7 @@ static pgprot_t drm_io_prot(uint32_t map_type, struct vm_area_struct *vma)
                tmp = pgprot_writecombine(tmp);
        else
                tmp = pgprot_noncached(tmp);
-#elif defined(__sparc__) || defined(__arm__)
+#elif defined(__sparc__) || defined(__arm__) || defined(__mips__)
        tmp = pgprot_noncached(tmp);
 #endif
        return tmp;
@@ -619,20 +619,11 @@ int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
                offset = drm_core_get_reg_ofs(dev);
                vma->vm_flags |= VM_IO; /* not in core dump */
                vma->vm_page_prot = drm_io_prot(map->type, vma);
-#if !defined(__arm__)
                if (io_remap_pfn_range(vma, vma->vm_start,
                                       (map->offset + offset) >> PAGE_SHIFT,
                                       vma->vm_end - vma->vm_start,
                                       vma->vm_page_prot))
                        return -EAGAIN;
-#else
-               if (remap_pfn_range(vma, vma->vm_start,
-                                       (map->offset + offset) >> PAGE_SHIFT,
-                                       vma->vm_end - vma->vm_start,
-                                       vma->vm_page_prot))
-                       return -EAGAIN;
-#endif
-
                DRM_DEBUG("   Type = %d; start = 0x%lx, end = 0x%lx,"
                          " offset = 0x%llx\n",
                          map->type,
index d9568198c3008b1518096c92ee5ac54f77a70676..9dce3b9c38964aa16942b4a1cf264c1d58312399 100644 (file)
@@ -147,9 +147,7 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
 
                drm_mode_connector_update_edid_property(connector, edid);
                count = drm_add_edid_modes(connector, edid);
-
-               kfree(connector->display_info.raw_edid);
-               connector->display_info.raw_edid = edid;
+               kfree(edid);
        } else {
                struct drm_display_mode *mode = drm_mode_create(connector->dev);
                struct exynos_drm_panel_info *panel;
index d5586cc751635e9ab858d2b3880e39b2c672e1e4..f4ac433565839c5de38c94d30559c0bd9ac076ff 100644 (file)
@@ -266,8 +266,8 @@ 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 && fb->funcs->destroy)
-                       fb->funcs->destroy(fb);
+               if (fb)
+                       drm_framebuffer_remove(fb);
        }
 
        /* release linux framebuffer */
index 537027a74fd54f06b49c1387c1650bf90abc11f1..e364165f1a2acc52a2a2f0ba8a62b18e5ea43706 100644 (file)
@@ -102,7 +102,6 @@ static int vidi_get_edid(struct device *dev, struct drm_connector *connector,
                                u8 *edid, int len)
 {
        struct vidi_context *ctx = get_vidi_context(dev);
-       struct edid *raw_edid;
 
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
@@ -115,18 +114,6 @@ static int vidi_get_edid(struct device *dev, struct drm_connector *connector,
                return -EFAULT;
        }
 
-       raw_edid = kzalloc(len, GFP_KERNEL);
-       if (!raw_edid) {
-               DRM_DEBUG_KMS("failed to allocate raw_edid.\n");
-               return -ENOMEM;
-       }
-
-       memcpy(raw_edid, ctx->raw_edid, min((1 + ctx->raw_edid->extensions)
-                                               * EDID_LENGTH, len));
-
-       /* attach the edid data to connector. */
-       connector->display_info.raw_edid = (char *)raw_edid;
-
        memcpy(edid, ctx->raw_edid, min((1 + ctx->raw_edid->extensions)
                                        * EDID_LENGTH, len));
 
index abfa2a93f0d0326bba0b8d7e14c9c0b69909d56c..7a2d40a5c1e181e9406fdd76c1b6403d11a32a73 100644 (file)
@@ -3,7 +3,7 @@
 #
 ccflags-y += -I$(srctree)/include/drm
 
-gma500_gfx-y += gem_glue.o \
+gma500_gfx-y += \
          accel_2d.o \
          backlight.o \
          framebuffer.o \
@@ -30,7 +30,8 @@ gma500_gfx-$(CONFIG_DRM_GMA3600) +=  cdv_device.o \
          cdv_intel_crt.o \
          cdv_intel_display.o \
          cdv_intel_hdmi.o \
-         cdv_intel_lvds.o
+         cdv_intel_lvds.o \
+         cdv_intel_dp.o
 
 gma500_gfx-$(CONFIG_DRM_GMA600) += oaktrail_device.o \
          oaktrail_crtc.o \
index 20793951fcac777134300fbe4b890df06b1c9980..143eba3309c5fc7685a48a9ec56a875580f4f038 100644 (file)
 #include "intel_bios.h"
 #include "power.h"
 
+static void do_gma_backlight_set(struct drm_device *dev)
+{
+#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       backlight_update_status(dev_priv->backlight_device);
+#endif 
+}
+
+void gma_backlight_enable(struct drm_device *dev)
+{
+#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       dev_priv->backlight_enabled = true;
+       if (dev_priv->backlight_device) {
+               dev_priv->backlight_device->props.brightness = dev_priv->backlight_level;
+               do_gma_backlight_set(dev);
+       }
+#endif 
+}
+
+void gma_backlight_disable(struct drm_device *dev)
+{
+#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       dev_priv->backlight_enabled = false;
+       if (dev_priv->backlight_device) {
+               dev_priv->backlight_device->props.brightness = 0;
+               do_gma_backlight_set(dev);
+       }
+#endif 
+}
+
+void gma_backlight_set(struct drm_device *dev, int v)
+{
+#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       dev_priv->backlight_level = v;
+       if (dev_priv->backlight_device && dev_priv->backlight_enabled) {
+               dev_priv->backlight_device->props.brightness = v;
+               do_gma_backlight_set(dev);
+       }
+#endif 
+}
+
 int gma_backlight_init(struct drm_device *dev)
 {
 #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
        struct drm_psb_private *dev_priv = dev->dev_private;
+       dev_priv->backlight_enabled = true;
        return dev_priv->ops->backlight_init(dev);
 #else
        return 0;
index b7e7b49d8f627b77c290bf9491c87d508b7e3377..bfc2f397019a0444c11f5239740e3d4e11c6cd43 100644 (file)
@@ -58,10 +58,17 @@ static int cdv_output_init(struct drm_device *dev)
        cdv_intel_lvds_init(dev, &dev_priv->mode_dev);
 
        /* These bits indicate HDMI not SDVO on CDV */
-       if (REG_READ(SDVOB) & SDVO_DETECTED)
+       if (REG_READ(SDVOB) & SDVO_DETECTED) {
                cdv_hdmi_init(dev, &dev_priv->mode_dev, SDVOB);
-       if (REG_READ(SDVOC) & SDVO_DETECTED)
+               if (REG_READ(DP_B) & DP_DETECTED)
+                       cdv_intel_dp_init(dev, &dev_priv->mode_dev, DP_B);
+       }
+
+       if (REG_READ(SDVOC) & SDVO_DETECTED) {
                cdv_hdmi_init(dev, &dev_priv->mode_dev, SDVOC);
+               if (REG_READ(DP_C) & DP_DETECTED)
+                       cdv_intel_dp_init(dev, &dev_priv->mode_dev, DP_C);
+       }
        return 0;
 }
 
@@ -163,6 +170,7 @@ static int cdv_backlight_init(struct drm_device *dev)
                        cdv_get_brightness(cdv_backlight_device);
        backlight_update_status(cdv_backlight_device);
        dev_priv->backlight_device = cdv_backlight_device;
+       dev_priv->backlight_enabled = true;
        return 0;
 }
 
@@ -449,6 +457,7 @@ static void cdv_get_core_freq(struct drm_device *dev)
        case 6:
        case 7:
                dev_priv->core_freq = 266;
+               break;
        default:
                dev_priv->core_freq = 0;
        }
@@ -488,6 +497,65 @@ static void cdv_hotplug_enable(struct drm_device *dev, bool on)
        }       
 }
 
+static const char *force_audio_names[] = {
+       "off",
+       "auto",
+       "on",
+};
+
+void cdv_intel_attach_force_audio_property(struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       struct drm_property *prop;
+       int i;
+
+       prop = dev_priv->force_audio_property;
+       if (prop == NULL) {
+               prop = drm_property_create(dev, DRM_MODE_PROP_ENUM,
+                                          "audio",
+                                          ARRAY_SIZE(force_audio_names));
+               if (prop == NULL)
+                       return;
+
+               for (i = 0; i < ARRAY_SIZE(force_audio_names); i++)
+                       drm_property_add_enum(prop, i, i-1, force_audio_names[i]);
+
+               dev_priv->force_audio_property = prop;
+       }
+       drm_connector_attach_property(connector, prop, 0);
+}
+
+
+static const char *broadcast_rgb_names[] = {
+       "Full",
+       "Limited 16:235",
+};
+
+void cdv_intel_attach_broadcast_rgb_property(struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       struct drm_property *prop;
+       int i;
+
+       prop = dev_priv->broadcast_rgb_property;
+       if (prop == NULL) {
+               prop = drm_property_create(dev, DRM_MODE_PROP_ENUM,
+                                          "Broadcast RGB",
+                                          ARRAY_SIZE(broadcast_rgb_names));
+               if (prop == NULL)
+                       return;
+
+               for (i = 0; i < ARRAY_SIZE(broadcast_rgb_names); i++)
+                       drm_property_add_enum(prop, i, i, broadcast_rgb_names[i]);
+
+               dev_priv->broadcast_rgb_property = prop;
+       }
+
+       drm_connector_attach_property(connector, prop, 0);
+}
+
 /* Cedarview */
 static const struct psb_offset cdv_regmap[2] = {
        {
index a68509ba22a8ae705421307e17c6a9c13d08331c..3cfd0931fbfb1ce41013475ef70c62343b8b4cd8 100644 (file)
@@ -57,15 +57,26 @@ struct cdv_intel_clock_t {
 struct cdv_intel_limit_t {
        struct cdv_intel_range_t dot, vco, n, m, m1, m2, p, p1;
        struct cdv_intel_p2_t p2;
+       bool (*find_pll)(const struct cdv_intel_limit_t *, struct drm_crtc *,
+                       int, int, struct cdv_intel_clock_t *);
 };
 
+static bool cdv_intel_find_best_PLL(const struct cdv_intel_limit_t *limit,
+       struct drm_crtc *crtc, int target, int refclk,
+       struct cdv_intel_clock_t *best_clock);
+static bool cdv_intel_find_dp_pll(const struct cdv_intel_limit_t *limit, struct drm_crtc *crtc, int target,
+                               int refclk,
+                               struct cdv_intel_clock_t *best_clock);
+
 #define CDV_LIMIT_SINGLE_LVDS_96       0
 #define CDV_LIMIT_SINGLE_LVDS_100      1
 #define CDV_LIMIT_DAC_HDMI_27          2
 #define CDV_LIMIT_DAC_HDMI_96          3
+#define CDV_LIMIT_DP_27                        4
+#define CDV_LIMIT_DP_100               5
 
 static const struct cdv_intel_limit_t cdv_intel_limits[] = {
-       {                       /* CDV_SIGNLE_LVDS_96MHz */
+       {                       /* CDV_SINGLE_LVDS_96MHz */
         .dot = {.min = 20000, .max = 115500},
         .vco = {.min = 1800000, .max = 3600000},
         .n = {.min = 2, .max = 6},
@@ -76,6 +87,7 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = {
         .p1 = {.min = 2, .max = 10},
         .p2 = {.dot_limit = 200000,
                .p2_slow = 14, .p2_fast = 14},
+               .find_pll = cdv_intel_find_best_PLL,
         },
        {                       /* CDV_SINGLE_LVDS_100MHz */
         .dot = {.min = 20000, .max = 115500},
@@ -90,6 +102,7 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = {
          * is 80-224Mhz.  Prefer single channel as much as possible.
          */
         .p2 = {.dot_limit = 200000, .p2_slow = 14, .p2_fast = 14},
+       .find_pll = cdv_intel_find_best_PLL,
         },
        {                       /* CDV_DAC_HDMI_27MHz */
         .dot = {.min = 20000, .max = 400000},
@@ -101,6 +114,7 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = {
         .p = {.min = 5, .max = 90},
         .p1 = {.min = 1, .max = 9},
         .p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 5},
+       .find_pll = cdv_intel_find_best_PLL,
         },
        {                       /* CDV_DAC_HDMI_96MHz */
         .dot = {.min = 20000, .max = 400000},
@@ -112,7 +126,32 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = {
         .p = {.min = 5, .max = 100},
         .p1 = {.min = 1, .max = 10},
         .p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 5},
+       .find_pll = cdv_intel_find_best_PLL,
+        },
+       {                       /* CDV_DP_27MHz */
+        .dot = {.min = 160000, .max = 272000},
+        .vco = {.min = 1809000, .max = 3564000},
+        .n = {.min = 1, .max = 1},
+        .m = {.min = 67, .max = 132},
+        .m1 = {.min = 0, .max = 0},
+        .m2 = {.min = 65, .max = 130},
+        .p = {.min = 5, .max = 90},
+        .p1 = {.min = 1, .max = 9},
+        .p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 10},
+        .find_pll = cdv_intel_find_dp_pll,
         },
+       {                       /* CDV_DP_100MHz */
+        .dot = {.min = 160000, .max = 272000},
+        .vco = {.min = 1800000, .max = 3600000},
+        .n = {.min = 2, .max = 6},
+        .m = {.min = 60, .max = 164},
+        .m1 = {.min = 0, .max = 0},
+        .m2 = {.min = 58, .max = 162},
+        .p = {.min = 5, .max = 100},
+        .p1 = {.min = 1, .max = 10},
+        .p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 10},
+        .find_pll = cdv_intel_find_dp_pll,
+        }      
 };
 
 #define _wait_for(COND, MS, W) ({ \
@@ -132,7 +171,7 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = {
 #define wait_for(COND, MS) _wait_for(COND, MS, 1)
 
 
-static int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val)
+int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val)
 {
        int ret;
 
@@ -159,7 +198,7 @@ static int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val)
        return 0;
 }
 
-static int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val)
+int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val)
 {
        int ret;
        static bool dpio_debug = true;
@@ -201,7 +240,7 @@ static int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val)
 /* Reset the DPIO configuration register.  The BIOS does this at every
  * mode set.
  */
-static void cdv_sb_reset(struct drm_device *dev)
+void cdv_sb_reset(struct drm_device *dev)
 {
 
        REG_WRITE(DPIO_CFG, 0);
@@ -216,7 +255,7 @@ static void cdv_sb_reset(struct drm_device *dev)
  */
 static int
 cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc,
-                              struct cdv_intel_clock_t *clock, bool is_lvds)
+                              struct cdv_intel_clock_t *clock, bool is_lvds, u32 ddi_select)
 {
        struct psb_intel_crtc *psb_crtc = to_psb_intel_crtc(crtc);
        int pipe = psb_crtc->pipe;
@@ -259,7 +298,7 @@ cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc,
        ref_value &= ~(REF_CLK_MASK);
 
        /* use DPLL_A for pipeB on CRT/HDMI */
-       if (pipe == 1 && !is_lvds) {
+       if (pipe == 1 && !is_lvds && !(ddi_select & DP_MASK)) {
                DRM_DEBUG_KMS("use DPLLA for pipe B\n");
                ref_value |= REF_CLK_DPLLA;
        } else {
@@ -336,30 +375,33 @@ cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc,
        if (ret)
                return ret;
 
-       lane_reg = PSB_LANE0;
-       cdv_sb_read(dev, lane_reg, &lane_value);
-       lane_value &= ~(LANE_PLL_MASK);
-       lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe);
-       cdv_sb_write(dev, lane_reg, lane_value);
-
-       lane_reg = PSB_LANE1;
-       cdv_sb_read(dev, lane_reg, &lane_value);
-       lane_value &= ~(LANE_PLL_MASK);
-       lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe);
-       cdv_sb_write(dev, lane_reg, lane_value);
-
-       lane_reg = PSB_LANE2;
-       cdv_sb_read(dev, lane_reg, &lane_value);
-       lane_value &= ~(LANE_PLL_MASK);
-       lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe);
-       cdv_sb_write(dev, lane_reg, lane_value);
-
-       lane_reg = PSB_LANE3;
-       cdv_sb_read(dev, lane_reg, &lane_value);
-       lane_value &= ~(LANE_PLL_MASK);
-       lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe);
-       cdv_sb_write(dev, lane_reg, lane_value);
-
+       if (ddi_select) {
+               if ((ddi_select & DDI_MASK) == DDI0_SELECT) {
+                       lane_reg = PSB_LANE0;
+                       cdv_sb_read(dev, lane_reg, &lane_value);
+                       lane_value &= ~(LANE_PLL_MASK);
+                       lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe);
+                       cdv_sb_write(dev, lane_reg, lane_value);
+
+                       lane_reg = PSB_LANE1;
+                       cdv_sb_read(dev, lane_reg, &lane_value);
+                       lane_value &= ~(LANE_PLL_MASK);
+                       lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe);
+                       cdv_sb_write(dev, lane_reg, lane_value);
+               } else {
+                       lane_reg = PSB_LANE2;
+                       cdv_sb_read(dev, lane_reg, &lane_value);
+                       lane_value &= ~(LANE_PLL_MASK);
+                       lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe);
+                       cdv_sb_write(dev, lane_reg, lane_value);
+
+                       lane_reg = PSB_LANE3;
+                       cdv_sb_read(dev, lane_reg, &lane_value);
+                       lane_value &= ~(LANE_PLL_MASK);
+                       lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe);
+                       cdv_sb_write(dev, lane_reg, lane_value);
+               }
+       }
        return 0;
 }
 
@@ -396,6 +438,12 @@ static const struct cdv_intel_limit_t *cdv_intel_limit(struct drm_crtc *crtc,
                        limit = &cdv_intel_limits[CDV_LIMIT_SINGLE_LVDS_96];
                else
                        limit = &cdv_intel_limits[CDV_LIMIT_SINGLE_LVDS_100];
+       } else if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) ||
+                       psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) {
+               if (refclk == 27000)
+                       limit = &cdv_intel_limits[CDV_LIMIT_DP_27];
+               else
+                       limit = &cdv_intel_limits[CDV_LIMIT_DP_100];
        } else {
                if (refclk == 27000)
                        limit = &cdv_intel_limits[CDV_LIMIT_DAC_HDMI_27];
@@ -438,13 +486,12 @@ static bool cdv_intel_PLL_is_valid(struct drm_crtc *crtc,
        return true;
 }
 
-static bool cdv_intel_find_best_PLL(struct drm_crtc *crtc, int target,
-                               int refclk,
-                               struct cdv_intel_clock_t *best_clock)
+static bool cdv_intel_find_best_PLL(const struct cdv_intel_limit_t *limit,
+       struct drm_crtc *crtc, int target, int refclk,
+       struct cdv_intel_clock_t *best_clock)
 {
        struct drm_device *dev = crtc->dev;
        struct cdv_intel_clock_t clock;
-       const struct cdv_intel_limit_t *limit = cdv_intel_limit(crtc, refclk);
        int err = target;
 
 
@@ -498,6 +545,49 @@ static bool cdv_intel_find_best_PLL(struct drm_crtc *crtc, int target,
        return err != target;
 }
 
+static bool cdv_intel_find_dp_pll(const struct cdv_intel_limit_t *limit, struct drm_crtc *crtc, int target,
+                               int refclk,
+                               struct cdv_intel_clock_t *best_clock)
+{
+       struct cdv_intel_clock_t clock;
+       if (refclk == 27000) {
+               if (target < 200000) {
+                       clock.p1 = 2;
+                       clock.p2 = 10;
+                       clock.n = 1;
+                       clock.m1 = 0;
+                       clock.m2 = 118;
+               } else {
+                       clock.p1 = 1;
+                       clock.p2 = 10;
+                       clock.n = 1;
+                       clock.m1 = 0;
+                       clock.m2 = 98;
+               }
+       } else if (refclk == 100000) {
+               if (target < 200000) {
+                       clock.p1 = 2;
+                       clock.p2 = 10;
+                       clock.n = 5;
+                       clock.m1 = 0;
+                       clock.m2 = 160;
+               } else {
+                       clock.p1 = 1;
+                       clock.p2 = 10;
+                       clock.n = 5;
+                       clock.m1 = 0;
+                       clock.m2 = 133;
+               }
+       } else
+               return false;
+       clock.m = clock.m2 + 2;
+       clock.p = clock.p1 * clock.p2;
+       clock.vco = (refclk * clock.m) / clock.n;
+       clock.dot = clock.vco / clock.p;
+       memcpy(best_clock, &clock, sizeof(struct cdv_intel_clock_t));
+       return true;
+}
+
 static int cdv_intel_pipe_set_base(struct drm_crtc *crtc,
                            int x, int y, struct drm_framebuffer *old_fb)
 {
@@ -791,7 +881,7 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode)
        case DRM_MODE_DPMS_STANDBY:
        case DRM_MODE_DPMS_SUSPEND:
                if (psb_intel_crtc->active)
-                       return;
+                       break;
 
                psb_intel_crtc->active = true;
 
@@ -835,17 +925,15 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode)
                REG_WRITE(map->status, temp);
                REG_READ(map->status);
 
-               cdv_intel_update_watermark(dev, crtc);
                cdv_intel_crtc_load_lut(crtc);
 
                /* Give the overlay scaler a chance to enable
                 * if it's on this pipe */
                /* psb_intel_crtc_dpms_video(crtc, true); TODO */
-               psb_intel_crtc->crtc_enable = true;
                break;
        case DRM_MODE_DPMS_OFF:
                if (!psb_intel_crtc->active)
-                       return;
+                       break;
 
                psb_intel_crtc->active = false;
 
@@ -892,10 +980,9 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode)
 
                /* Wait for the clocks to turn off. */
                udelay(150);
-               cdv_intel_update_watermark(dev, crtc);
-               psb_intel_crtc->crtc_enable = false;
                break;
        }
+       cdv_intel_update_watermark(dev, crtc);
        /*Set FIFO Watermarks*/
        REG_WRITE(DSPARB, 0x3F3E);
 }
@@ -952,9 +1039,12 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
        u32 dpll = 0, dspcntr, pipeconf;
        bool ok;
        bool is_crt = false, is_lvds = false, is_tv = false;
-       bool is_hdmi = false;
+       bool is_hdmi = false, is_dp = false;
        struct drm_mode_config *mode_config = &dev->mode_config;
        struct drm_connector *connector;
+       const struct cdv_intel_limit_t *limit;
+       u32 ddi_select = 0;
+       bool is_edp = false;
 
        list_for_each_entry(connector, &mode_config->connector_list, head) {
                struct psb_intel_encoder *psb_intel_encoder =
@@ -964,6 +1054,7 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
                    || connector->encoder->crtc != crtc)
                        continue;
 
+               ddi_select = psb_intel_encoder->ddi_select;
                switch (psb_intel_encoder->type) {
                case INTEL_OUTPUT_LVDS:
                        is_lvds = true;
@@ -977,6 +1068,15 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
                case INTEL_OUTPUT_HDMI:
                        is_hdmi = true;
                        break;
+               case INTEL_OUTPUT_DISPLAYPORT:
+                       is_dp = true;
+                       break;
+               case INTEL_OUTPUT_EDP:
+                       is_edp = true;
+                       break;
+               default:
+                       DRM_ERROR("invalid output type.\n");
+                       return 0;
                }
        }
 
@@ -986,6 +1086,20 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
        else
                /* high-end sku, 27/100 mhz */
                refclk = 27000;
+       if (is_dp || is_edp) {
+               /*
+                * Based on the spec the low-end SKU has only CRT/LVDS. So it is
+                * unnecessary to consider it for DP/eDP.
+                * On the high-end SKU, it will use the 27/100M reference clk
+                * for DP/eDP. When using SSC clock, the ref clk is 100MHz.Otherwise
+                * it will be 27MHz. From the VBIOS code it seems that the pipe A choose
+                * 27MHz for DP/eDP while the Pipe B chooses the 100MHz.
+                */ 
+               if (pipe == 0)
+                       refclk = 27000;
+               else
+                       refclk = 100000;
+       }
 
        if (is_lvds && dev_priv->lvds_use_ssc) {
                refclk = dev_priv->lvds_ssc_freq * 1000;
@@ -993,8 +1107,10 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
        }
 
        drm_mode_debug_printmodeline(adjusted_mode);
+       
+       limit = cdv_intel_limit(crtc, refclk);
 
-       ok = cdv_intel_find_best_PLL(crtc, adjusted_mode->clock, refclk,
+       ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk,
                                 &clock);
        if (!ok) {
                dev_err(dev->dev, "Couldn't find PLL settings for mode!\n");
@@ -1009,6 +1125,15 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
        }
 /*             dpll |= PLL_REF_INPUT_DREFCLK; */
 
+       if (is_dp || is_edp) {
+               cdv_intel_dp_set_m_n(crtc, mode, adjusted_mode);
+       } else {
+               REG_WRITE(PIPE_GMCH_DATA_M(pipe), 0);
+               REG_WRITE(PIPE_GMCH_DATA_N(pipe), 0);
+               REG_WRITE(PIPE_DP_LINK_M(pipe), 0);
+               REG_WRITE(PIPE_DP_LINK_N(pipe), 0);
+       }
+
        dpll |= DPLL_SYNCLOCK_ENABLE;
 /*     if (is_lvds)
                dpll |= DPLLB_MODE_LVDS;
@@ -1019,6 +1144,31 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
        /* setup pipeconf */
        pipeconf = REG_READ(map->conf);
 
+       pipeconf &= ~(PIPE_BPC_MASK);
+       if (is_edp) {
+               switch (dev_priv->edp.bpp) {
+               case 24:
+                       pipeconf |= PIPE_8BPC;
+                       break;
+               case 18:
+                       pipeconf |= PIPE_6BPC;
+                       break;
+               case 30:
+                       pipeconf |= PIPE_10BPC;
+                       break;
+               default:
+                       pipeconf |= PIPE_8BPC;
+                       break;
+               }
+       } else if (is_lvds) {
+               /* the BPC will be 6 if it is 18-bit LVDS panel */
+               if ((REG_READ(LVDS) & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP)
+                       pipeconf |= PIPE_8BPC;
+               else
+                       pipeconf |= PIPE_6BPC;
+       } else
+               pipeconf |= PIPE_8BPC;
+                       
        /* Set up the display plane register */
        dspcntr = DISPPLANE_GAMMA_ENABLE;
 
@@ -1033,7 +1183,7 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
        REG_WRITE(map->dpll, dpll | DPLL_VGA_MODE_DIS | DPLL_SYNCLOCK_ENABLE);
        REG_READ(map->dpll);
 
-       cdv_dpll_set_clock_cdv(dev, crtc, &clock, is_lvds);
+       cdv_dpll_set_clock_cdv(dev, crtc, &clock, is_lvds, ddi_select);
 
        udelay(150);
 
diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c
new file mode 100644 (file)
index 0000000..c9abc06
--- /dev/null
@@ -0,0 +1,1951 @@
+/*
+ * Copyright Â© 2012 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Keith Packard <keithp@keithp.com>
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+#include "psb_drv.h"
+#include "psb_intel_drv.h"
+#include "psb_intel_reg.h"
+#include "drm_dp_helper.h"
+
+#define _wait_for(COND, MS, W) ({ \
+        unsigned long timeout__ = jiffies + msecs_to_jiffies(MS);       \
+        int ret__ = 0;                                                  \
+        while (! (COND)) {                                              \
+                if (time_after(jiffies, timeout__)) {                   \
+                        ret__ = -ETIMEDOUT;                             \
+                        break;                                          \
+                }                                                       \
+                if (W && !in_dbg_master()) msleep(W);                   \
+        }                                                               \
+        ret__;                                                          \
+})      
+
+#define wait_for(COND, MS) _wait_for(COND, MS, 1)
+
+#define DP_LINK_STATUS_SIZE    6
+#define DP_LINK_CHECK_TIMEOUT  (10 * 1000)
+
+#define DP_LINK_CONFIGURATION_SIZE     9
+
+#define CDV_FAST_LINK_TRAIN    1
+
+struct cdv_intel_dp {
+       uint32_t output_reg;
+       uint32_t DP;
+       uint8_t  link_configuration[DP_LINK_CONFIGURATION_SIZE];
+       bool has_audio;
+       int force_audio;
+       uint32_t color_range;
+       uint8_t link_bw;
+       uint8_t lane_count;
+       uint8_t dpcd[4];
+       struct psb_intel_encoder *encoder;
+       struct i2c_adapter adapter;
+       struct i2c_algo_dp_aux_data algo;
+       uint8_t train_set[4];
+       uint8_t link_status[DP_LINK_STATUS_SIZE];
+       int panel_power_up_delay;
+       int panel_power_down_delay;
+       int panel_power_cycle_delay;
+       int backlight_on_delay;
+       int backlight_off_delay;
+       struct drm_display_mode *panel_fixed_mode;  /* for eDP */
+       bool panel_on;
+};
+
+struct ddi_regoff {
+       uint32_t        PreEmph1;
+       uint32_t        PreEmph2;
+       uint32_t        VSwing1;
+       uint32_t        VSwing2;
+       uint32_t        VSwing3;
+       uint32_t        VSwing4;
+       uint32_t        VSwing5;
+};
+
+static struct ddi_regoff ddi_DP_train_table[] = {
+       {.PreEmph1 = 0x812c, .PreEmph2 = 0x8124, .VSwing1 = 0x8154,
+       .VSwing2 = 0x8148, .VSwing3 = 0x814C, .VSwing4 = 0x8150,
+       .VSwing5 = 0x8158,},
+       {.PreEmph1 = 0x822c, .PreEmph2 = 0x8224, .VSwing1 = 0x8254,
+       .VSwing2 = 0x8248, .VSwing3 = 0x824C, .VSwing4 = 0x8250,
+       .VSwing5 = 0x8258,},
+};
+
+static uint32_t dp_vswing_premph_table[] = {
+        0x55338954,    0x4000,
+        0x554d8954,    0x2000,
+        0x55668954,    0,
+        0x559ac0d4,    0x6000,
+};
+/**
+ * is_edp - is the given port attached to an eDP panel (either CPU or PCH)
+ * @intel_dp: DP struct
+ *
+ * If a CPU or PCH DP output is attached to an eDP panel, this function
+ * will return true, and false otherwise.
+ */
+static bool is_edp(struct psb_intel_encoder *encoder)
+{
+       return encoder->type == INTEL_OUTPUT_EDP;
+}
+
+
+static void cdv_intel_dp_start_link_train(struct psb_intel_encoder *encoder);
+static void cdv_intel_dp_complete_link_train(struct psb_intel_encoder *encoder);
+static void cdv_intel_dp_link_down(struct psb_intel_encoder *encoder);
+
+static int
+cdv_intel_dp_max_lane_count(struct psb_intel_encoder *encoder)
+{
+       struct cdv_intel_dp *intel_dp = encoder->dev_priv;
+       int max_lane_count = 4;
+
+       if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11) {
+               max_lane_count = intel_dp->dpcd[DP_MAX_LANE_COUNT] & 0x1f;
+               switch (max_lane_count) {
+               case 1: case 2: case 4:
+                       break;
+               default:
+                       max_lane_count = 4;
+               }
+       }
+       return max_lane_count;
+}
+
+static int
+cdv_intel_dp_max_link_bw(struct psb_intel_encoder *encoder)
+{
+       struct cdv_intel_dp *intel_dp = encoder->dev_priv;
+       int max_link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE];
+
+       switch (max_link_bw) {
+       case DP_LINK_BW_1_62:
+       case DP_LINK_BW_2_7:
+               break;
+       default:
+               max_link_bw = DP_LINK_BW_1_62;
+               break;
+       }
+       return max_link_bw;
+}
+
+static int
+cdv_intel_dp_link_clock(uint8_t link_bw)
+{
+       if (link_bw == DP_LINK_BW_2_7)
+               return 270000;
+       else
+               return 162000;
+}
+
+static int
+cdv_intel_dp_link_required(int pixel_clock, int bpp)
+{
+       return (pixel_clock * bpp + 7) / 8;
+}
+
+static int
+cdv_intel_dp_max_data_rate(int max_link_clock, int max_lanes)
+{
+       return (max_link_clock * max_lanes * 19) / 20;
+}
+
+static void cdv_intel_edp_panel_vdd_on(struct psb_intel_encoder *intel_encoder)
+{
+       struct drm_device *dev = intel_encoder->base.dev;
+       struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv;
+       u32 pp;
+
+       if (intel_dp->panel_on) {
+               DRM_DEBUG_KMS("Skip VDD on because of panel on\n");
+               return;
+       }       
+       DRM_DEBUG_KMS("\n");
+
+       pp = REG_READ(PP_CONTROL);
+
+       pp |= EDP_FORCE_VDD;
+       REG_WRITE(PP_CONTROL, pp);
+       REG_READ(PP_CONTROL);
+       msleep(intel_dp->panel_power_up_delay);
+}
+
+static void cdv_intel_edp_panel_vdd_off(struct psb_intel_encoder *intel_encoder)
+{
+       struct drm_device *dev = intel_encoder->base.dev;
+       u32 pp;
+
+       DRM_DEBUG_KMS("\n");
+       pp = REG_READ(PP_CONTROL);
+
+       pp &= ~EDP_FORCE_VDD;
+       REG_WRITE(PP_CONTROL, pp);
+       REG_READ(PP_CONTROL);
+
+}
+
+/* Returns true if the panel was already on when called */
+static bool cdv_intel_edp_panel_on(struct psb_intel_encoder *intel_encoder)
+{
+       struct drm_device *dev = intel_encoder->base.dev;
+       struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv;
+       u32 pp, idle_on_mask = PP_ON | PP_SEQUENCE_NONE;
+
+       if (intel_dp->panel_on)
+               return true;
+
+       DRM_DEBUG_KMS("\n");
+       pp = REG_READ(PP_CONTROL);
+       pp &= ~PANEL_UNLOCK_MASK;
+
+       pp |= (PANEL_UNLOCK_REGS | POWER_TARGET_ON);
+       REG_WRITE(PP_CONTROL, pp);
+       REG_READ(PP_CONTROL);
+
+       if (wait_for(((REG_READ(PP_STATUS) & idle_on_mask) == idle_on_mask), 1000)) {
+               DRM_DEBUG_KMS("Error in Powering up eDP panel, status %x\n", REG_READ(PP_STATUS));
+               intel_dp->panel_on = false;
+       } else
+               intel_dp->panel_on = true;      
+       msleep(intel_dp->panel_power_up_delay);
+
+       return false;
+}
+
+static void cdv_intel_edp_panel_off (struct psb_intel_encoder *intel_encoder)
+{
+       struct drm_device *dev = intel_encoder->base.dev;
+       u32 pp, idle_off_mask = PP_ON ;
+       struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv;
+
+       DRM_DEBUG_KMS("\n");
+
+       pp = REG_READ(PP_CONTROL);
+
+       if ((pp & POWER_TARGET_ON) == 0) 
+               return;
+
+       intel_dp->panel_on = false;
+       pp &= ~PANEL_UNLOCK_MASK;
+       /* ILK workaround: disable reset around power sequence */
+
+       pp &= ~POWER_TARGET_ON;
+       pp &= ~EDP_FORCE_VDD;
+       pp &= ~EDP_BLC_ENABLE;
+       REG_WRITE(PP_CONTROL, pp);
+       REG_READ(PP_CONTROL);
+       DRM_DEBUG_KMS("PP_STATUS %x\n", REG_READ(PP_STATUS));
+
+       if (wait_for((REG_READ(PP_STATUS) & idle_off_mask) == 0, 1000)) {
+               DRM_DEBUG_KMS("Error in turning off Panel\n");  
+       }
+
+       msleep(intel_dp->panel_power_cycle_delay);
+       DRM_DEBUG_KMS("Over\n");
+}
+
+static void cdv_intel_edp_backlight_on (struct psb_intel_encoder *intel_encoder)
+{
+       struct drm_device *dev = intel_encoder->base.dev;
+       u32 pp;
+
+       DRM_DEBUG_KMS("\n");
+       /*
+        * If we enable the backlight right away following a panel power
+        * on, we may see slight flicker as the panel syncs with the eDP
+        * link.  So delay a bit to make sure the image is solid before
+        * allowing it to appear.
+        */
+       msleep(300);
+       pp = REG_READ(PP_CONTROL);
+
+       pp |= EDP_BLC_ENABLE;
+       REG_WRITE(PP_CONTROL, pp);
+       gma_backlight_enable(dev);
+}
+
+static void cdv_intel_edp_backlight_off (struct psb_intel_encoder *intel_encoder)
+{
+       struct drm_device *dev = intel_encoder->base.dev;
+       struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv;
+       u32 pp;
+
+       DRM_DEBUG_KMS("\n");
+       gma_backlight_disable(dev);
+       msleep(10);
+       pp = REG_READ(PP_CONTROL);
+
+       pp &= ~EDP_BLC_ENABLE;
+       REG_WRITE(PP_CONTROL, pp);
+       msleep(intel_dp->backlight_off_delay);
+}
+
+static int
+cdv_intel_dp_mode_valid(struct drm_connector *connector,
+                   struct drm_display_mode *mode)
+{
+       struct psb_intel_encoder *encoder = psb_intel_attached_encoder(connector);
+       struct cdv_intel_dp *intel_dp = encoder->dev_priv;
+       int max_link_clock = cdv_intel_dp_link_clock(cdv_intel_dp_max_link_bw(encoder));
+       int max_lanes = cdv_intel_dp_max_lane_count(encoder);
+       struct drm_psb_private *dev_priv = connector->dev->dev_private;
+
+       if (is_edp(encoder) && intel_dp->panel_fixed_mode) {
+               if (mode->hdisplay > intel_dp->panel_fixed_mode->hdisplay)
+                       return MODE_PANEL;
+               if (mode->vdisplay > intel_dp->panel_fixed_mode->vdisplay)
+                       return MODE_PANEL;
+       }
+
+       /* only refuse the mode on non eDP since we have seen some weird eDP panels
+          which are outside spec tolerances but somehow work by magic */
+       if (!is_edp(encoder) &&
+           (cdv_intel_dp_link_required(mode->clock, dev_priv->edp.bpp)
+            > cdv_intel_dp_max_data_rate(max_link_clock, max_lanes)))
+               return MODE_CLOCK_HIGH;
+
+       if (is_edp(encoder)) {
+           if (cdv_intel_dp_link_required(mode->clock, 24)
+               > cdv_intel_dp_max_data_rate(max_link_clock, max_lanes))
+               return MODE_CLOCK_HIGH;
+               
+       }
+       if (mode->clock < 10000)
+               return MODE_CLOCK_LOW;
+
+       return MODE_OK;
+}
+
+static uint32_t
+pack_aux(uint8_t *src, int src_bytes)
+{
+       int     i;
+       uint32_t v = 0;
+
+       if (src_bytes > 4)
+               src_bytes = 4;
+       for (i = 0; i < src_bytes; i++)
+               v |= ((uint32_t) src[i]) << ((3-i) * 8);
+       return v;
+}
+
+static void
+unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes)
+{
+       int i;
+       if (dst_bytes > 4)
+               dst_bytes = 4;
+       for (i = 0; i < dst_bytes; i++)
+               dst[i] = src >> ((3-i) * 8);
+}
+
+static int
+cdv_intel_dp_aux_ch(struct psb_intel_encoder *encoder,
+               uint8_t *send, int send_bytes,
+               uint8_t *recv, int recv_size)
+{
+       struct cdv_intel_dp *intel_dp = encoder->dev_priv;
+       uint32_t output_reg = intel_dp->output_reg;
+       struct drm_device *dev = encoder->base.dev;
+       uint32_t ch_ctl = output_reg + 0x10;
+       uint32_t ch_data = ch_ctl + 4;
+       int i;
+       int recv_bytes;
+       uint32_t status;
+       uint32_t aux_clock_divider;
+       int try, precharge;
+
+       /* The clock divider is based off the hrawclk,
+        * and would like to run at 2MHz. So, take the
+        * hrawclk value and divide by 2 and use that
+        * On CDV platform it uses 200MHz as hrawclk.
+        *
+        */
+       aux_clock_divider = 200 / 2;
+
+       precharge = 4;
+       if (is_edp(encoder))
+               precharge = 10;
+
+       if (REG_READ(ch_ctl) & DP_AUX_CH_CTL_SEND_BUSY) {
+               DRM_ERROR("dp_aux_ch not started status 0x%08x\n",
+                         REG_READ(ch_ctl));
+               return -EBUSY;
+       }
+
+       /* Must try at least 3 times according to DP spec */
+       for (try = 0; try < 5; try++) {
+               /* Load the send data into the aux channel data registers */
+               for (i = 0; i < send_bytes; i += 4)
+                       REG_WRITE(ch_data + i,
+                                  pack_aux(send + i, send_bytes - i));
+       
+               /* Send the command and wait for it to complete */
+               REG_WRITE(ch_ctl,
+                          DP_AUX_CH_CTL_SEND_BUSY |
+                          DP_AUX_CH_CTL_TIME_OUT_400us |
+                          (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
+                          (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
+                          (aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT) |
+                          DP_AUX_CH_CTL_DONE |
+                          DP_AUX_CH_CTL_TIME_OUT_ERROR |
+                          DP_AUX_CH_CTL_RECEIVE_ERROR);
+               for (;;) {
+                       status = REG_READ(ch_ctl);
+                       if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0)
+                               break;
+                       udelay(100);
+               }
+       
+               /* Clear done status and any errors */
+               REG_WRITE(ch_ctl,
+                          status |
+                          DP_AUX_CH_CTL_DONE |
+                          DP_AUX_CH_CTL_TIME_OUT_ERROR |
+                          DP_AUX_CH_CTL_RECEIVE_ERROR);
+               if (status & DP_AUX_CH_CTL_DONE)
+                       break;
+       }
+
+       if ((status & DP_AUX_CH_CTL_DONE) == 0) {
+               DRM_ERROR("dp_aux_ch not done status 0x%08x\n", status);
+               return -EBUSY;
+       }
+
+       /* Check for timeout or receive error.
+        * Timeouts occur when the sink is not connected
+        */
+       if (status & DP_AUX_CH_CTL_RECEIVE_ERROR) {
+               DRM_ERROR("dp_aux_ch receive error status 0x%08x\n", status);
+               return -EIO;
+       }
+
+       /* 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;
+       }
+
+       /* Unload any bytes sent back from the other side */
+       recv_bytes = ((status & DP_AUX_CH_CTL_MESSAGE_SIZE_MASK) >>
+                     DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT);
+       if (recv_bytes > recv_size)
+               recv_bytes = recv_size;
+       
+       for (i = 0; i < recv_bytes; i += 4)
+               unpack_aux(REG_READ(ch_data + i),
+                          recv + i, recv_bytes - i);
+
+       return recv_bytes;
+}
+
+/* Write data to the aux channel in native mode */
+static int
+cdv_intel_dp_aux_native_write(struct psb_intel_encoder *encoder,
+                         uint16_t address, uint8_t *send, int send_bytes)
+{
+       int ret;
+       uint8_t msg[20];
+       int msg_bytes;
+       uint8_t ack;
+
+       if (send_bytes > 16)
+               return -1;
+       msg[0] = AUX_NATIVE_WRITE << 4;
+       msg[1] = address >> 8;
+       msg[2] = address & 0xff;
+       msg[3] = send_bytes - 1;
+       memcpy(&msg[4], send, send_bytes);
+       msg_bytes = send_bytes + 4;
+       for (;;) {
+               ret = cdv_intel_dp_aux_ch(encoder, msg, msg_bytes, &ack, 1);
+               if (ret < 0)
+                       return ret;
+               if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK)
+                       break;
+               else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER)
+                       udelay(100);
+               else
+                       return -EIO;
+       }
+       return send_bytes;
+}
+
+/* Write a single byte to the aux channel in native mode */
+static int
+cdv_intel_dp_aux_native_write_1(struct psb_intel_encoder *encoder,
+                           uint16_t address, uint8_t byte)
+{
+       return cdv_intel_dp_aux_native_write(encoder, address, &byte, 1);
+}
+
+/* read bytes from a native aux channel */
+static int
+cdv_intel_dp_aux_native_read(struct psb_intel_encoder *encoder,
+                        uint16_t address, uint8_t *recv, int recv_bytes)
+{
+       uint8_t msg[4];
+       int msg_bytes;
+       uint8_t reply[20];
+       int reply_bytes;
+       uint8_t ack;
+       int ret;
+
+       msg[0] = AUX_NATIVE_READ << 4;
+       msg[1] = address >> 8;
+       msg[2] = address & 0xff;
+       msg[3] = recv_bytes - 1;
+
+       msg_bytes = 4;
+       reply_bytes = recv_bytes + 1;
+
+       for (;;) {
+               ret = cdv_intel_dp_aux_ch(encoder, msg, msg_bytes,
+                                     reply, reply_bytes);
+               if (ret == 0)
+                       return -EPROTO;
+               if (ret < 0)
+                       return ret;
+               ack = reply[0];
+               if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK) {
+                       memcpy(recv, reply + 1, ret - 1);
+                       return ret - 1;
+               }
+               else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER)
+                       udelay(100);
+               else
+                       return -EIO;
+       }
+}
+
+static int
+cdv_intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
+                   uint8_t write_byte, uint8_t *read_byte)
+{
+       struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
+       struct cdv_intel_dp *intel_dp = container_of(adapter,
+                                               struct cdv_intel_dp,
+                                               adapter);
+       struct psb_intel_encoder *encoder = intel_dp->encoder;
+       uint16_t address = algo_data->address;
+       uint8_t msg[5];
+       uint8_t reply[2];
+       unsigned retry;
+       int msg_bytes;
+       int reply_bytes;
+       int ret;
+
+       /* Set up the command byte */
+       if (mode & MODE_I2C_READ)
+               msg[0] = AUX_I2C_READ << 4;
+       else
+               msg[0] = AUX_I2C_WRITE << 4;
+
+       if (!(mode & MODE_I2C_STOP))
+               msg[0] |= AUX_I2C_MOT << 4;
+
+       msg[1] = address >> 8;
+       msg[2] = address;
+
+       switch (mode) {
+       case MODE_I2C_WRITE:
+               msg[3] = 0;
+               msg[4] = write_byte;
+               msg_bytes = 5;
+               reply_bytes = 1;
+               break;
+       case MODE_I2C_READ:
+               msg[3] = 0;
+               msg_bytes = 4;
+               reply_bytes = 2;
+               break;
+       default:
+               msg_bytes = 3;
+               reply_bytes = 1;
+               break;
+       }
+
+       for (retry = 0; retry < 5; retry++) {
+               ret = cdv_intel_dp_aux_ch(encoder,
+                                     msg, msg_bytes,
+                                     reply, reply_bytes);
+               if (ret < 0) {
+                       DRM_DEBUG_KMS("aux_ch failed %d\n", ret);
+                       return ret;
+               }
+
+               switch (reply[0] & AUX_NATIVE_REPLY_MASK) {
+               case AUX_NATIVE_REPLY_ACK:
+                       /* I2C-over-AUX Reply field is only valid
+                        * when paired with AUX ACK.
+                        */
+                       break;
+               case AUX_NATIVE_REPLY_NACK:
+                       DRM_DEBUG_KMS("aux_ch native nack\n");
+                       return -EREMOTEIO;
+               case AUX_NATIVE_REPLY_DEFER:
+                       udelay(100);
+                       continue;
+               default:
+                       DRM_ERROR("aux_ch invalid native reply 0x%02x\n",
+                                 reply[0]);
+                       return -EREMOTEIO;
+               }
+
+               switch (reply[0] & AUX_I2C_REPLY_MASK) {
+               case AUX_I2C_REPLY_ACK:
+                       if (mode == MODE_I2C_READ) {
+                               *read_byte = reply[1];
+                       }
+                       return reply_bytes - 1;
+               case AUX_I2C_REPLY_NACK:
+                       DRM_DEBUG_KMS("aux_i2c nack\n");
+                       return -EREMOTEIO;
+               case AUX_I2C_REPLY_DEFER:
+                       DRM_DEBUG_KMS("aux_i2c defer\n");
+                       udelay(100);
+                       break;
+               default:
+                       DRM_ERROR("aux_i2c invalid reply 0x%02x\n", reply[0]);
+                       return -EREMOTEIO;
+               }
+       }
+
+       DRM_ERROR("too many retries, giving up\n");
+       return -EREMOTEIO;
+}
+
+static int
+cdv_intel_dp_i2c_init(struct psb_intel_connector *connector, struct psb_intel_encoder *encoder, const char *name)
+{
+       struct cdv_intel_dp *intel_dp = encoder->dev_priv;
+       int ret;
+
+       DRM_DEBUG_KMS("i2c_init %s\n", name);
+
+       intel_dp->algo.running = false;
+       intel_dp->algo.address = 0;
+       intel_dp->algo.aux_ch = cdv_intel_dp_i2c_aux_ch;
+
+       memset(&intel_dp->adapter, '\0', sizeof (intel_dp->adapter));
+       intel_dp->adapter.owner = THIS_MODULE;
+       intel_dp->adapter.class = I2C_CLASS_DDC;
+       strncpy (intel_dp->adapter.name, name, sizeof(intel_dp->adapter.name) - 1);
+       intel_dp->adapter.name[sizeof(intel_dp->adapter.name) - 1] = '\0';
+       intel_dp->adapter.algo_data = &intel_dp->algo;
+       intel_dp->adapter.dev.parent = &connector->base.kdev;
+
+       if (is_edp(encoder))
+               cdv_intel_edp_panel_vdd_on(encoder);
+       ret = i2c_dp_aux_add_bus(&intel_dp->adapter);
+       if (is_edp(encoder))
+               cdv_intel_edp_panel_vdd_off(encoder);
+       
+       return ret;
+}
+
+void cdv_intel_fixed_panel_mode(struct drm_display_mode *fixed_mode,
+       struct drm_display_mode *adjusted_mode)
+{
+       adjusted_mode->hdisplay = fixed_mode->hdisplay;
+       adjusted_mode->hsync_start = fixed_mode->hsync_start;
+       adjusted_mode->hsync_end = fixed_mode->hsync_end;
+       adjusted_mode->htotal = fixed_mode->htotal;
+
+       adjusted_mode->vdisplay = fixed_mode->vdisplay;
+       adjusted_mode->vsync_start = fixed_mode->vsync_start;
+       adjusted_mode->vsync_end = fixed_mode->vsync_end;
+       adjusted_mode->vtotal = fixed_mode->vtotal;
+
+       adjusted_mode->clock = fixed_mode->clock;
+
+       drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
+}
+
+static bool
+cdv_intel_dp_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode,
+                   struct drm_display_mode *adjusted_mode)
+{
+       struct drm_psb_private *dev_priv = encoder->dev->dev_private;
+       struct psb_intel_encoder *intel_encoder = to_psb_intel_encoder(encoder);
+       struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv;
+       int lane_count, clock;
+       int max_lane_count = cdv_intel_dp_max_lane_count(intel_encoder);
+       int max_clock = cdv_intel_dp_max_link_bw(intel_encoder) == DP_LINK_BW_2_7 ? 1 : 0;
+       static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 };
+       int refclock = mode->clock;
+       int bpp = 24;
+
+       if (is_edp(intel_encoder) && intel_dp->panel_fixed_mode) {
+               cdv_intel_fixed_panel_mode(intel_dp->panel_fixed_mode, adjusted_mode);
+               refclock = intel_dp->panel_fixed_mode->clock;
+               bpp = dev_priv->edp.bpp;
+       }
+
+       for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) {
+               for (clock = max_clock; clock >= 0; clock--) {
+                       int link_avail = cdv_intel_dp_max_data_rate(cdv_intel_dp_link_clock(bws[clock]), lane_count);
+
+                       if (cdv_intel_dp_link_required(refclock, bpp) <= link_avail) {
+                               intel_dp->link_bw = bws[clock];
+                               intel_dp->lane_count = lane_count;
+                               adjusted_mode->clock = cdv_intel_dp_link_clock(intel_dp->link_bw);
+                               DRM_DEBUG_KMS("Display port link bw %02x lane "
+                                               "count %d clock %d\n",
+                                      intel_dp->link_bw, intel_dp->lane_count,
+                                      adjusted_mode->clock);
+                               return true;
+                       }
+               }
+       }
+       if (is_edp(intel_encoder)) {
+               /* okay we failed just pick the highest */
+               intel_dp->lane_count = max_lane_count;
+               intel_dp->link_bw = bws[max_clock];
+               adjusted_mode->clock = cdv_intel_dp_link_clock(intel_dp->link_bw);
+               DRM_DEBUG_KMS("Force picking display port link bw %02x lane "
+                             "count %d clock %d\n",
+                             intel_dp->link_bw, intel_dp->lane_count,
+                             adjusted_mode->clock);
+
+               return true;
+       }
+       return false;
+}
+
+struct cdv_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
+cdv_intel_reduce_ratio(uint32_t *num, uint32_t *den)
+{
+       /*
+       while (*num > 0xffffff || *den > 0xffffff) {
+               *num >>= 1;
+               *den >>= 1;
+       }*/
+       uint64_t value, m;
+       m = *num;
+       value = m * (0x800000);
+       m = do_div(value, *den);
+       *num = value;
+       *den = 0x800000;
+}
+
+static void
+cdv_intel_dp_compute_m_n(int bpp,
+                    int nlanes,
+                    int pixel_clock,
+                    int link_clock,
+                    struct cdv_intel_dp_m_n *m_n)
+{
+       m_n->tu = 64;
+       m_n->gmch_m = (pixel_clock * bpp + 7) >> 3;
+       m_n->gmch_n = link_clock * nlanes;
+       cdv_intel_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n);
+       m_n->link_m = pixel_clock;
+       m_n->link_n = link_clock;
+       cdv_intel_reduce_ratio(&m_n->link_m, &m_n->link_n);
+}
+
+void
+cdv_intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
+                struct drm_display_mode *adjusted_mode)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       struct drm_mode_config *mode_config = &dev->mode_config;
+       struct drm_encoder *encoder;
+       struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc);
+       int lane_count = 4, bpp = 24;
+       struct cdv_intel_dp_m_n m_n;
+       int pipe = intel_crtc->pipe;
+
+       /*
+        * Find the lane count in the intel_encoder private
+        */
+       list_for_each_entry(encoder, &mode_config->encoder_list, head) {
+               struct psb_intel_encoder *intel_encoder;
+               struct cdv_intel_dp *intel_dp;
+
+               if (encoder->crtc != crtc)
+                       continue;
+
+               intel_encoder = to_psb_intel_encoder(encoder);
+               intel_dp = intel_encoder->dev_priv;
+               if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT) {
+                       lane_count = intel_dp->lane_count;
+                       break;
+               } else if (is_edp(intel_encoder)) {
+                       lane_count = intel_dp->lane_count;
+                       bpp = dev_priv->edp.bpp;
+                       break;
+               }
+       }
+
+       /*
+        * Compute the GMCH and Link ratios. The '3' here is
+        * the number of bytes_per_pixel post-LUT, which we always
+        * set up for 8-bits of R/G/B, or 3 bytes total.
+        */
+       cdv_intel_dp_compute_m_n(bpp, lane_count,
+                            mode->clock, adjusted_mode->clock, &m_n);
+
+       {
+               REG_WRITE(PIPE_GMCH_DATA_M(pipe),
+                          ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
+                          m_n.gmch_m);
+               REG_WRITE(PIPE_GMCH_DATA_N(pipe), m_n.gmch_n);
+               REG_WRITE(PIPE_DP_LINK_M(pipe), m_n.link_m);
+               REG_WRITE(PIPE_DP_LINK_N(pipe), m_n.link_n);
+       }
+}
+
+static void
+cdv_intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
+                 struct drm_display_mode *adjusted_mode)
+{
+       struct psb_intel_encoder *intel_encoder = to_psb_intel_encoder(encoder);
+       struct drm_crtc *crtc = encoder->crtc;
+       struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc);
+       struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv;
+       struct drm_device *dev = encoder->dev;
+
+       intel_dp->DP = DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0;
+       intel_dp->DP |= intel_dp->color_range;
+
+       if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
+               intel_dp->DP |= DP_SYNC_HS_HIGH;
+       if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
+               intel_dp->DP |= DP_SYNC_VS_HIGH;
+
+       intel_dp->DP |= DP_LINK_TRAIN_OFF;
+
+       switch (intel_dp->lane_count) {
+       case 1:
+               intel_dp->DP |= DP_PORT_WIDTH_1;
+               break;
+       case 2:
+               intel_dp->DP |= DP_PORT_WIDTH_2;
+               break;
+       case 4:
+               intel_dp->DP |= DP_PORT_WIDTH_4;
+               break;
+       }
+       if (intel_dp->has_audio)
+               intel_dp->DP |= DP_AUDIO_OUTPUT_ENABLE;
+
+       memset(intel_dp->link_configuration, 0, DP_LINK_CONFIGURATION_SIZE);
+       intel_dp->link_configuration[0] = intel_dp->link_bw;
+       intel_dp->link_configuration[1] = intel_dp->lane_count;
+
+       /*
+        * Check for DPCD version > 1.1 and enhanced framing support
+        */
+       if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 &&
+           (intel_dp->dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP)) {
+               intel_dp->link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+               intel_dp->DP |= DP_ENHANCED_FRAMING;
+       }
+
+       /* CPT DP's pipe select is decided in TRANS_DP_CTL */
+       if (intel_crtc->pipe == 1)
+               intel_dp->DP |= DP_PIPEB_SELECT;
+
+       REG_WRITE(intel_dp->output_reg, (intel_dp->DP | DP_PORT_EN));
+       DRM_DEBUG_KMS("DP expected reg is %x\n", intel_dp->DP);
+       if (is_edp(intel_encoder)) {
+               uint32_t pfit_control;
+               cdv_intel_edp_panel_on(intel_encoder);
+
+               if (mode->hdisplay != adjusted_mode->hdisplay ||
+                           mode->vdisplay != adjusted_mode->vdisplay)
+                       pfit_control = PFIT_ENABLE;
+               else
+                       pfit_control = 0;
+
+               pfit_control |= intel_crtc->pipe << PFIT_PIPE_SHIFT;
+
+               REG_WRITE(PFIT_CONTROL, pfit_control);
+       }
+}
+
+
+/* If the sink supports it, try to set the power state appropriately */
+static void cdv_intel_dp_sink_dpms(struct psb_intel_encoder *encoder, int mode)
+{
+       struct cdv_intel_dp *intel_dp = encoder->dev_priv;
+       int ret, i;
+
+       /* Should have a valid DPCD by this point */
+       if (intel_dp->dpcd[DP_DPCD_REV] < 0x11)
+               return;
+
+       if (mode != DRM_MODE_DPMS_ON) {
+               ret = cdv_intel_dp_aux_native_write_1(encoder, DP_SET_POWER,
+                                                 DP_SET_POWER_D3);
+               if (ret != 1)
+                       DRM_DEBUG_DRIVER("failed to write sink power state\n");
+       } else {
+               /*
+                * When turning on, we need to retry for 1ms to give the sink
+                * time to wake up.
+                */
+               for (i = 0; i < 3; i++) {
+                       ret = cdv_intel_dp_aux_native_write_1(encoder,
+                                                         DP_SET_POWER,
+                                                         DP_SET_POWER_D0);
+                       if (ret == 1)
+                               break;
+                       udelay(1000);
+               }
+       }
+}
+
+static void cdv_intel_dp_prepare(struct drm_encoder *encoder)
+{
+       struct psb_intel_encoder *intel_encoder = to_psb_intel_encoder(encoder);
+       int edp = is_edp(intel_encoder);
+
+       if (edp) {
+               cdv_intel_edp_backlight_off(intel_encoder);
+               cdv_intel_edp_panel_off(intel_encoder);
+               cdv_intel_edp_panel_vdd_on(intel_encoder);
+        }
+       /* Wake up the sink first */
+       cdv_intel_dp_sink_dpms(intel_encoder, DRM_MODE_DPMS_ON);
+       cdv_intel_dp_link_down(intel_encoder);
+       if (edp)
+               cdv_intel_edp_panel_vdd_off(intel_encoder);
+}
+
+static void cdv_intel_dp_commit(struct drm_encoder *encoder)
+{
+       struct psb_intel_encoder *intel_encoder = to_psb_intel_encoder(encoder);
+       int edp = is_edp(intel_encoder);
+
+       if (edp)
+               cdv_intel_edp_panel_on(intel_encoder);
+       cdv_intel_dp_start_link_train(intel_encoder);
+       cdv_intel_dp_complete_link_train(intel_encoder);
+       if (edp)
+               cdv_intel_edp_backlight_on(intel_encoder);
+}
+
+static void
+cdv_intel_dp_dpms(struct drm_encoder *encoder, int mode)
+{
+       struct psb_intel_encoder *intel_encoder = to_psb_intel_encoder(encoder);
+       struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv;
+       struct drm_device *dev = encoder->dev;
+       uint32_t dp_reg = REG_READ(intel_dp->output_reg);
+       int edp = is_edp(intel_encoder);
+
+       if (mode != DRM_MODE_DPMS_ON) {
+               if (edp) {
+                       cdv_intel_edp_backlight_off(intel_encoder);
+                       cdv_intel_edp_panel_vdd_on(intel_encoder);
+               }
+               cdv_intel_dp_sink_dpms(intel_encoder, mode);
+               cdv_intel_dp_link_down(intel_encoder);
+               if (edp) {
+                       cdv_intel_edp_panel_vdd_off(intel_encoder);
+                       cdv_intel_edp_panel_off(intel_encoder);
+               }
+       } else {
+               if (edp)
+                       cdv_intel_edp_panel_on(intel_encoder);
+               cdv_intel_dp_sink_dpms(intel_encoder, mode);
+               if (!(dp_reg & DP_PORT_EN)) {
+                       cdv_intel_dp_start_link_train(intel_encoder);
+                       cdv_intel_dp_complete_link_train(intel_encoder);
+               }
+               if (edp)
+                       cdv_intel_edp_backlight_on(intel_encoder);
+       }
+}
+
+/*
+ * Native read with retry for link status and receiver capability reads for
+ * cases where the sink may still be asleep.
+ */
+static bool
+cdv_intel_dp_aux_native_read_retry(struct psb_intel_encoder *encoder, uint16_t address,
+                              uint8_t *recv, int recv_bytes)
+{
+       int ret, i;
+
+       /*
+        * Sinks are *supposed* to come up within 1ms from an off state,
+        * but we're also supposed to retry 3 times per the spec.
+        */
+       for (i = 0; i < 3; i++) {
+               ret = cdv_intel_dp_aux_native_read(encoder, address, recv,
+                                              recv_bytes);
+               if (ret == recv_bytes)
+                       return true;
+               udelay(1000);
+       }
+
+       return false;
+}
+
+/*
+ * Fetch AUX CH registers 0x202 - 0x207 which contain
+ * link status information
+ */
+static bool
+cdv_intel_dp_get_link_status(struct psb_intel_encoder *encoder)
+{
+       struct cdv_intel_dp *intel_dp = encoder->dev_priv;
+       return cdv_intel_dp_aux_native_read_retry(encoder,
+                                             DP_LANE0_1_STATUS,
+                                             intel_dp->link_status,
+                                             DP_LINK_STATUS_SIZE);
+}
+
+static uint8_t
+cdv_intel_dp_link_status(uint8_t link_status[DP_LINK_STATUS_SIZE],
+                    int r)
+{
+       return link_status[r - DP_LANE0_1_STATUS];
+}
+
+static uint8_t
+cdv_intel_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE],
+                                int lane)
+{
+       int         i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
+       int         s = ((lane & 1) ?
+                        DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
+                        DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
+       uint8_t l = cdv_intel_dp_link_status(link_status, i);
+
+       return ((l >> s) & 3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
+}
+
+static uint8_t
+cdv_intel_get_adjust_request_pre_emphasis(uint8_t link_status[DP_LINK_STATUS_SIZE],
+                                     int lane)
+{
+       int         i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
+       int         s = ((lane & 1) ?
+                        DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
+                        DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
+       uint8_t l = cdv_intel_dp_link_status(link_status, i);
+
+       return ((l >> s) & 3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
+}
+
+
+#if 0
+static char    *voltage_names[] = {
+       "0.4V", "0.6V", "0.8V", "1.2V"
+};
+static char    *pre_emph_names[] = {
+       "0dB", "3.5dB", "6dB", "9.5dB"
+};
+static char    *link_train_names[] = {
+       "pattern 1", "pattern 2", "idle", "off"
+};
+#endif
+
+#define CDV_DP_VOLTAGE_MAX         DP_TRAIN_VOLTAGE_SWING_1200
+/*
+static uint8_t
+cdv_intel_dp_pre_emphasis_max(uint8_t voltage_swing)
+{
+       switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
+       case DP_TRAIN_VOLTAGE_SWING_400:
+               return DP_TRAIN_PRE_EMPHASIS_6;
+       case DP_TRAIN_VOLTAGE_SWING_600:
+               return DP_TRAIN_PRE_EMPHASIS_6;
+       case DP_TRAIN_VOLTAGE_SWING_800:
+               return DP_TRAIN_PRE_EMPHASIS_3_5;
+       case DP_TRAIN_VOLTAGE_SWING_1200:
+       default:
+               return DP_TRAIN_PRE_EMPHASIS_0;
+       }
+}
+*/
+static void
+cdv_intel_get_adjust_train(struct psb_intel_encoder *encoder)
+{
+       struct cdv_intel_dp *intel_dp = encoder->dev_priv;
+       uint8_t v = 0;
+       uint8_t p = 0;
+       int lane;
+
+       for (lane = 0; lane < intel_dp->lane_count; lane++) {
+               uint8_t this_v = cdv_intel_get_adjust_request_voltage(intel_dp->link_status, lane);
+               uint8_t this_p = cdv_intel_get_adjust_request_pre_emphasis(intel_dp->link_status, lane);
+
+               if (this_v > v)
+                       v = this_v;
+               if (this_p > p)
+                       p = this_p;
+       }
+       
+       if (v >= CDV_DP_VOLTAGE_MAX)
+               v = CDV_DP_VOLTAGE_MAX | DP_TRAIN_MAX_SWING_REACHED;
+
+       if (p == DP_TRAIN_PRE_EMPHASIS_MASK)
+               p |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+               
+       for (lane = 0; lane < 4; lane++)
+               intel_dp->train_set[lane] = v | p;
+}
+
+
+static uint8_t
+cdv_intel_get_lane_status(uint8_t link_status[DP_LINK_STATUS_SIZE],
+                     int lane)
+{
+       int i = DP_LANE0_1_STATUS + (lane >> 1);
+       int s = (lane & 1) * 4;
+       uint8_t l = cdv_intel_dp_link_status(link_status, i);
+
+       return (l >> s) & 0xf;
+}
+
+/* Check for clock recovery is done on all channels */
+static bool
+cdv_intel_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count)
+{
+       int lane;
+       uint8_t lane_status;
+
+       for (lane = 0; lane < lane_count; lane++) {
+               lane_status = cdv_intel_get_lane_status(link_status, lane);
+               if ((lane_status & DP_LANE_CR_DONE) == 0)
+                       return false;
+       }
+       return true;
+}
+
+/* Check to see if channel eq is done on all channels */
+#define CHANNEL_EQ_BITS (DP_LANE_CR_DONE|\
+                        DP_LANE_CHANNEL_EQ_DONE|\
+                        DP_LANE_SYMBOL_LOCKED)
+static bool
+cdv_intel_channel_eq_ok(struct psb_intel_encoder *encoder)
+{
+       struct cdv_intel_dp *intel_dp = encoder->dev_priv;
+       uint8_t lane_align;
+       uint8_t lane_status;
+       int lane;
+
+       lane_align = cdv_intel_dp_link_status(intel_dp->link_status,
+                                         DP_LANE_ALIGN_STATUS_UPDATED);
+       if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0)
+               return false;
+       for (lane = 0; lane < intel_dp->lane_count; lane++) {
+               lane_status = cdv_intel_get_lane_status(intel_dp->link_status, lane);
+               if ((lane_status & CHANNEL_EQ_BITS) != CHANNEL_EQ_BITS)
+                       return false;
+       }
+       return true;
+}
+
+static bool
+cdv_intel_dp_set_link_train(struct psb_intel_encoder *encoder,
+                       uint32_t dp_reg_value,
+                       uint8_t dp_train_pat)
+{
+       
+       struct drm_device *dev = encoder->base.dev;
+       int ret;
+       struct cdv_intel_dp *intel_dp = encoder->dev_priv;
+
+       REG_WRITE(intel_dp->output_reg, dp_reg_value);
+       REG_READ(intel_dp->output_reg);
+
+       ret = cdv_intel_dp_aux_native_write_1(encoder,
+                                   DP_TRAINING_PATTERN_SET,
+                                   dp_train_pat);
+
+       if (ret != 1) {
+               DRM_DEBUG_KMS("Failure in setting link pattern %x\n",
+                               dp_train_pat);
+               return false;
+       }
+
+       return true;
+}
+
+
+static bool
+cdv_intel_dplink_set_level(struct psb_intel_encoder *encoder,
+                       uint8_t dp_train_pat)
+{
+       
+       int ret;
+       struct cdv_intel_dp *intel_dp = encoder->dev_priv;
+
+       ret = cdv_intel_dp_aux_native_write(encoder,
+                                       DP_TRAINING_LANE0_SET,
+                                       intel_dp->train_set,
+                                       intel_dp->lane_count);
+
+       if (ret != intel_dp->lane_count) {
+               DRM_DEBUG_KMS("Failure in setting level %d, lane_cnt= %d\n",
+                               intel_dp->train_set[0], intel_dp->lane_count);
+               return false;
+       }
+       return true;
+}
+
+static void
+cdv_intel_dp_set_vswing_premph(struct psb_intel_encoder *encoder, uint8_t signal_level)
+{
+       struct drm_device *dev = encoder->base.dev;
+       struct cdv_intel_dp *intel_dp = encoder->dev_priv;
+       struct ddi_regoff *ddi_reg;
+       int vswing, premph, index;
+
+       if (intel_dp->output_reg == DP_B)
+               ddi_reg = &ddi_DP_train_table[0];
+       else
+               ddi_reg = &ddi_DP_train_table[1];
+
+       vswing = (signal_level & DP_TRAIN_VOLTAGE_SWING_MASK);
+       premph = ((signal_level & DP_TRAIN_PRE_EMPHASIS_MASK)) >>
+                               DP_TRAIN_PRE_EMPHASIS_SHIFT;
+
+       if (vswing + premph > 3)
+               return;
+#ifdef CDV_FAST_LINK_TRAIN
+       return;
+#endif
+       DRM_DEBUG_KMS("Test2\n");
+       //return ;
+       cdv_sb_reset(dev);
+       /* ;Swing voltage programming
+        ;gfx_dpio_set_reg(0xc058, 0x0505313A) */
+       cdv_sb_write(dev, ddi_reg->VSwing5, 0x0505313A);
+
+       /* ;gfx_dpio_set_reg(0x8154, 0x43406055) */
+       cdv_sb_write(dev, ddi_reg->VSwing1, 0x43406055);
+
+       /* ;gfx_dpio_set_reg(0x8148, 0x55338954)
+        * The VSwing_PreEmph table is also considered based on the vswing/premp
+        */
+       index = (vswing + premph) * 2;
+       if (premph == 1 && vswing == 1) {
+               cdv_sb_write(dev, ddi_reg->VSwing2, 0x055738954);
+       } else
+               cdv_sb_write(dev, ddi_reg->VSwing2, dp_vswing_premph_table[index]);
+
+       /* ;gfx_dpio_set_reg(0x814c, 0x40802040) */
+       if ((vswing + premph) == DP_TRAIN_VOLTAGE_SWING_1200)
+               cdv_sb_write(dev, ddi_reg->VSwing3, 0x70802040);
+       else
+               cdv_sb_write(dev, ddi_reg->VSwing3, 0x40802040);
+
+       /* ;gfx_dpio_set_reg(0x8150, 0x2b405555) */
+       /* cdv_sb_write(dev, ddi_reg->VSwing4, 0x2b405555); */
+
+       /* ;gfx_dpio_set_reg(0x8154, 0xc3406055) */
+       cdv_sb_write(dev, ddi_reg->VSwing1, 0xc3406055);
+
+       /* ;Pre emphasis programming
+        * ;gfx_dpio_set_reg(0xc02c, 0x1f030040)
+        */
+       cdv_sb_write(dev, ddi_reg->PreEmph1, 0x1f030040);
+
+       /* ;gfx_dpio_set_reg(0x8124, 0x00004000) */
+       index = 2 * premph + 1;
+       cdv_sb_write(dev, ddi_reg->PreEmph2, dp_vswing_premph_table[index]);
+       return; 
+}
+
+
+/* Enable corresponding port and start training pattern 1 */
+static void
+cdv_intel_dp_start_link_train(struct psb_intel_encoder *encoder)
+{
+       struct drm_device *dev = encoder->base.dev;
+       struct cdv_intel_dp *intel_dp = encoder->dev_priv;
+       int i;
+       uint8_t voltage;
+       bool clock_recovery = false;
+       int tries;
+       u32 reg;
+       uint32_t DP = intel_dp->DP;
+
+       DP |= DP_PORT_EN;
+       DP &= ~DP_LINK_TRAIN_MASK;
+               
+       reg = DP;       
+       reg |= DP_LINK_TRAIN_PAT_1;
+       /* Enable output, wait for it to become active */
+       REG_WRITE(intel_dp->output_reg, reg);
+       REG_READ(intel_dp->output_reg);
+       psb_intel_wait_for_vblank(dev);
+
+       DRM_DEBUG_KMS("Link config\n");
+       /* Write the link configuration data */
+       cdv_intel_dp_aux_native_write(encoder, DP_LINK_BW_SET,
+                                 intel_dp->link_configuration,
+                                 2);
+
+       memset(intel_dp->train_set, 0, 4);
+       voltage = 0;
+       tries = 0;
+       clock_recovery = false;
+
+       DRM_DEBUG_KMS("Start train\n");
+               reg = DP | DP_LINK_TRAIN_PAT_1;
+
+
+       for (;;) {
+               /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */
+               DRM_DEBUG_KMS("DP Link Train Set %x, Link_config %x, %x\n",
+                               intel_dp->train_set[0],
+                               intel_dp->link_configuration[0],
+                               intel_dp->link_configuration[1]);
+
+               if (!cdv_intel_dp_set_link_train(encoder, reg, DP_TRAINING_PATTERN_1)) {
+                       DRM_DEBUG_KMS("Failure in aux-transfer setting pattern 1\n");
+               }
+               cdv_intel_dp_set_vswing_premph(encoder, intel_dp->train_set[0]);
+               /* Set training pattern 1 */
+
+               cdv_intel_dplink_set_level(encoder, DP_TRAINING_PATTERN_1);
+
+               udelay(200);
+               if (!cdv_intel_dp_get_link_status(encoder))
+                       break;
+
+               DRM_DEBUG_KMS("DP Link status %x, %x, %x, %x, %x, %x\n",
+                               intel_dp->link_status[0], intel_dp->link_status[1], intel_dp->link_status[2],
+                               intel_dp->link_status[3], intel_dp->link_status[4], intel_dp->link_status[5]);
+
+               if (cdv_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) {
+                       DRM_DEBUG_KMS("PT1 train is done\n");
+                       clock_recovery = true;
+                       break;
+               }
+
+               /* Check to see if we've tried the max voltage */
+               for (i = 0; i < intel_dp->lane_count; i++)
+                       if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
+                               break;
+               if (i == intel_dp->lane_count)
+                       break;
+
+               /* Check to see if we've tried the same voltage 5 times */
+               if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
+                       ++tries;
+                       if (tries == 5)
+                               break;
+               } else
+                       tries = 0;
+               voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
+
+               /* Compute new intel_dp->train_set as requested by target */
+               cdv_intel_get_adjust_train(encoder);
+
+       }
+
+       if (!clock_recovery) {
+               DRM_DEBUG_KMS("failure in DP patter 1 training, train set %x\n", intel_dp->train_set[0]);
+       }
+       
+       intel_dp->DP = DP;
+}
+
+static void
+cdv_intel_dp_complete_link_train(struct psb_intel_encoder *encoder)
+{
+       struct drm_device *dev = encoder->base.dev;
+       struct cdv_intel_dp *intel_dp = encoder->dev_priv;
+       bool channel_eq = false;
+       int tries, cr_tries;
+       u32 reg;
+       uint32_t DP = intel_dp->DP;
+
+       /* channel equalization */
+       tries = 0;
+       cr_tries = 0;
+       channel_eq = false;
+
+       DRM_DEBUG_KMS("\n");
+               reg = DP | DP_LINK_TRAIN_PAT_2;
+
+       for (;;) {
+
+               DRM_DEBUG_KMS("DP Link Train Set %x, Link_config %x, %x\n",
+                               intel_dp->train_set[0],
+                               intel_dp->link_configuration[0],
+                               intel_dp->link_configuration[1]);
+               /* channel eq pattern */
+
+               if (!cdv_intel_dp_set_link_train(encoder, reg,
+                                            DP_TRAINING_PATTERN_2)) {
+                       DRM_DEBUG_KMS("Failure in aux-transfer setting pattern 2\n");
+               }
+               /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */
+
+               if (cr_tries > 5) {
+                       DRM_ERROR("failed to train DP, aborting\n");
+                       cdv_intel_dp_link_down(encoder);
+                       break;
+               }
+
+               cdv_intel_dp_set_vswing_premph(encoder, intel_dp->train_set[0]);
+
+               cdv_intel_dplink_set_level(encoder, DP_TRAINING_PATTERN_2);
+
+               udelay(1000);
+               if (!cdv_intel_dp_get_link_status(encoder))
+                       break;
+
+               DRM_DEBUG_KMS("DP Link status %x, %x, %x, %x, %x, %x\n",
+                               intel_dp->link_status[0], intel_dp->link_status[1], intel_dp->link_status[2],
+                               intel_dp->link_status[3], intel_dp->link_status[4], intel_dp->link_status[5]);
+
+               /* Make sure clock is still ok */
+               if (!cdv_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) {
+                       cdv_intel_dp_start_link_train(encoder);
+                       cr_tries++;
+                       continue;
+               }
+
+               if (cdv_intel_channel_eq_ok(encoder)) {
+                       DRM_DEBUG_KMS("PT2 train is done\n");
+                       channel_eq = true;
+                       break;
+               }
+
+               /* Try 5 times, then try clock recovery if that fails */
+               if (tries > 5) {
+                       cdv_intel_dp_link_down(encoder);
+                       cdv_intel_dp_start_link_train(encoder);
+                       tries = 0;
+                       cr_tries++;
+                       continue;
+               }
+
+               /* Compute new intel_dp->train_set as requested by target */
+               cdv_intel_get_adjust_train(encoder);
+               ++tries;
+
+       }
+
+       reg = DP | DP_LINK_TRAIN_OFF;
+
+       REG_WRITE(intel_dp->output_reg, reg);
+       REG_READ(intel_dp->output_reg);
+       cdv_intel_dp_aux_native_write_1(encoder,
+                                   DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE);
+}
+
+static void
+cdv_intel_dp_link_down(struct psb_intel_encoder *encoder)
+{
+       struct drm_device *dev = encoder->base.dev;
+       struct cdv_intel_dp *intel_dp = encoder->dev_priv;
+       uint32_t DP = intel_dp->DP;
+
+       if ((REG_READ(intel_dp->output_reg) & DP_PORT_EN) == 0)
+               return;
+
+       DRM_DEBUG_KMS("\n");
+
+
+       {
+               DP &= ~DP_LINK_TRAIN_MASK;
+               REG_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE);
+       }
+       REG_READ(intel_dp->output_reg);
+
+       msleep(17);
+
+       REG_WRITE(intel_dp->output_reg, DP & ~DP_PORT_EN);
+       REG_READ(intel_dp->output_reg);
+}
+
+static enum drm_connector_status
+cdv_dp_detect(struct psb_intel_encoder *encoder)
+{
+       struct cdv_intel_dp *intel_dp = encoder->dev_priv;
+       enum drm_connector_status status;
+
+       status = connector_status_disconnected;
+       if (cdv_intel_dp_aux_native_read(encoder, 0x000, intel_dp->dpcd,
+                                    sizeof (intel_dp->dpcd)) == sizeof (intel_dp->dpcd))
+       {
+               if (intel_dp->dpcd[DP_DPCD_REV] != 0)
+                       status = connector_status_connected;
+       }
+       if (status == connector_status_connected)
+               DRM_DEBUG_KMS("DPCD: Rev=%x LN_Rate=%x LN_CNT=%x LN_DOWNSP=%x\n",
+                       intel_dp->dpcd[0], intel_dp->dpcd[1],
+                       intel_dp->dpcd[2], intel_dp->dpcd[3]);
+       return status;
+}
+
+/**
+ * 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
+cdv_intel_dp_detect(struct drm_connector *connector, bool force)
+{
+       struct psb_intel_encoder *encoder = psb_intel_attached_encoder(connector);
+       struct cdv_intel_dp *intel_dp = encoder->dev_priv;
+       enum drm_connector_status status;
+       struct edid *edid = NULL;
+       int edp = is_edp(encoder);
+
+       intel_dp->has_audio = false;
+
+       if (edp)
+               cdv_intel_edp_panel_vdd_on(encoder);
+       status = cdv_dp_detect(encoder);
+       if (status != connector_status_connected) {
+               if (edp)
+                       cdv_intel_edp_panel_vdd_off(encoder);
+               return status;
+        }
+
+       if (intel_dp->force_audio) {
+               intel_dp->has_audio = intel_dp->force_audio > 0;
+       } else {
+               edid = drm_get_edid(connector, &intel_dp->adapter);
+               if (edid) {
+                       intel_dp->has_audio = drm_detect_monitor_audio(edid);
+                       kfree(edid);
+               }
+       }
+       if (edp)
+               cdv_intel_edp_panel_vdd_off(encoder);
+
+       return connector_status_connected;
+}
+
+static int cdv_intel_dp_get_modes(struct drm_connector *connector)
+{
+       struct psb_intel_encoder *intel_encoder = psb_intel_attached_encoder(connector);
+       struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv;
+       struct edid *edid = NULL;
+       int ret = 0;
+       int edp = is_edp(intel_encoder);
+
+
+       edid = drm_get_edid(connector, &intel_dp->adapter);
+       if (edid) {
+               drm_mode_connector_update_edid_property(connector, edid);
+               ret = drm_add_edid_modes(connector, edid);
+               kfree(edid);
+       }
+
+       if (is_edp(intel_encoder)) {
+               struct drm_device *dev = connector->dev;
+               struct drm_psb_private *dev_priv = dev->dev_private;
+               
+               cdv_intel_edp_panel_vdd_off(intel_encoder);
+               if (ret) {
+                       if (edp && !intel_dp->panel_fixed_mode) {
+                               struct drm_display_mode *newmode;
+                               list_for_each_entry(newmode, &connector->probed_modes,
+                                           head) {
+                                       if (newmode->type & DRM_MODE_TYPE_PREFERRED) {
+                                               intel_dp->panel_fixed_mode =
+                                                       drm_mode_duplicate(dev, newmode);
+                                               break;
+                                       }
+                               }
+                       }
+
+                       return ret;
+               }
+               if (!intel_dp->panel_fixed_mode && dev_priv->lfp_lvds_vbt_mode) {
+                       intel_dp->panel_fixed_mode =
+                               drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode);
+                       if (intel_dp->panel_fixed_mode) {
+                               intel_dp->panel_fixed_mode->type |=
+                                       DRM_MODE_TYPE_PREFERRED;
+                       }
+               }
+               if (intel_dp->panel_fixed_mode != NULL) {
+                       struct drm_display_mode *mode;
+                       mode = drm_mode_duplicate(dev, intel_dp->panel_fixed_mode);
+                       drm_mode_probed_add(connector, mode);
+                       return 1;
+               }
+       }
+
+       return ret;
+}
+
+static bool
+cdv_intel_dp_detect_audio(struct drm_connector *connector)
+{
+       struct psb_intel_encoder *encoder = psb_intel_attached_encoder(connector);
+       struct cdv_intel_dp *intel_dp = encoder->dev_priv;
+       struct edid *edid;
+       bool has_audio = false;
+       int edp = is_edp(encoder);
+
+       if (edp)
+               cdv_intel_edp_panel_vdd_on(encoder);
+
+       edid = drm_get_edid(connector, &intel_dp->adapter);
+       if (edid) {
+               has_audio = drm_detect_monitor_audio(edid);
+               kfree(edid);
+       }
+       if (edp)
+               cdv_intel_edp_panel_vdd_off(encoder);
+
+       return has_audio;
+}
+
+static int
+cdv_intel_dp_set_property(struct drm_connector *connector,
+                     struct drm_property *property,
+                     uint64_t val)
+{
+       struct drm_psb_private *dev_priv = connector->dev->dev_private;
+       struct psb_intel_encoder *encoder = psb_intel_attached_encoder(connector);
+       struct cdv_intel_dp *intel_dp = encoder->dev_priv;
+       int ret;
+
+       ret = drm_connector_property_set_value(connector, property, val);
+       if (ret)
+               return ret;
+
+       if (property == dev_priv->force_audio_property) {
+               int i = val;
+               bool has_audio;
+
+               if (i == intel_dp->force_audio)
+                       return 0;
+
+               intel_dp->force_audio = i;
+
+               if (i == 0)
+                       has_audio = cdv_intel_dp_detect_audio(connector);
+               else
+                       has_audio = i > 0;
+
+               if (has_audio == intel_dp->has_audio)
+                       return 0;
+
+               intel_dp->has_audio = has_audio;
+               goto done;
+       }
+
+       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;
+               goto done;
+       }
+
+       return -EINVAL;
+
+done:
+       if (encoder->base.crtc) {
+               struct drm_crtc *crtc = encoder->base.crtc;
+               drm_crtc_helper_set_mode(crtc, &crtc->mode,
+                                        crtc->x, crtc->y,
+                                        crtc->fb);
+       }
+
+       return 0;
+}
+
+static void
+cdv_intel_dp_destroy(struct drm_connector *connector)
+{
+       struct psb_intel_encoder *psb_intel_encoder =
+                                       psb_intel_attached_encoder(connector);
+       struct cdv_intel_dp *intel_dp = psb_intel_encoder->dev_priv;
+
+       if (is_edp(psb_intel_encoder)) {
+       /*      cdv_intel_panel_destroy_backlight(connector->dev); */
+               if (intel_dp->panel_fixed_mode) {
+                       kfree(intel_dp->panel_fixed_mode);
+                       intel_dp->panel_fixed_mode = NULL;
+               }
+       }
+       i2c_del_adapter(&intel_dp->adapter);
+       drm_sysfs_connector_remove(connector);
+       drm_connector_cleanup(connector);
+       kfree(connector);
+}
+
+static void cdv_intel_dp_encoder_destroy(struct drm_encoder *encoder)
+{
+       drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_helper_funcs cdv_intel_dp_helper_funcs = {
+       .dpms = cdv_intel_dp_dpms,
+       .mode_fixup = cdv_intel_dp_mode_fixup,
+       .prepare = cdv_intel_dp_prepare,
+       .mode_set = cdv_intel_dp_mode_set,
+       .commit = cdv_intel_dp_commit,
+};
+
+static const struct drm_connector_funcs cdv_intel_dp_connector_funcs = {
+       .dpms = drm_helper_connector_dpms,
+       .detect = cdv_intel_dp_detect,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .set_property = cdv_intel_dp_set_property,
+       .destroy = cdv_intel_dp_destroy,
+};
+
+static const struct drm_connector_helper_funcs cdv_intel_dp_connector_helper_funcs = {
+       .get_modes = cdv_intel_dp_get_modes,
+       .mode_valid = cdv_intel_dp_mode_valid,
+       .best_encoder = psb_intel_best_encoder,
+};
+
+static const struct drm_encoder_funcs cdv_intel_dp_enc_funcs = {
+       .destroy = cdv_intel_dp_encoder_destroy,
+};
+
+
+static void cdv_intel_dp_add_properties(struct drm_connector *connector)
+{
+       cdv_intel_attach_force_audio_property(connector);
+       cdv_intel_attach_broadcast_rgb_property(connector);
+}
+
+/* check the VBT to see whether the eDP is on DP-D port */
+static bool cdv_intel_dpc_is_edp(struct drm_device *dev)
+{
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       struct child_device_config *p_child;
+       int i;
+
+       if (!dev_priv->child_dev_num)
+               return false;
+
+       for (i = 0; i < dev_priv->child_dev_num; i++) {
+               p_child = dev_priv->child_dev + i;
+
+               if (p_child->dvo_port == PORT_IDPC &&
+                   p_child->device_type == DEVICE_TYPE_eDP)
+                       return true;
+       }
+       return false;
+}
+
+/* Cedarview display clock gating
+
+   We need this disable dot get correct behaviour while enabling
+   DP/eDP. TODO - investigate if we can turn it back to normality
+   after enabling */
+static void cdv_disable_intel_clock_gating(struct drm_device *dev)
+{
+       u32 reg_value;
+       reg_value = REG_READ(DSPCLK_GATE_D);
+
+       reg_value |= (DPUNIT_PIPEB_GATE_DISABLE |
+                       DPUNIT_PIPEA_GATE_DISABLE |
+                       DPCUNIT_CLOCK_GATE_DISABLE |
+                       DPLSUNIT_CLOCK_GATE_DISABLE |
+                       DPOUNIT_CLOCK_GATE_DISABLE |
+                       DPIOUNIT_CLOCK_GATE_DISABLE);   
+
+       REG_WRITE(DSPCLK_GATE_D, reg_value);
+
+       udelay(500);            
+}
+
+void
+cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int output_reg)
+{
+       struct psb_intel_encoder *psb_intel_encoder;
+       struct psb_intel_connector *psb_intel_connector;
+       struct drm_connector *connector;
+       struct drm_encoder *encoder;
+       struct cdv_intel_dp *intel_dp;
+       const char *name = NULL;
+       int type = DRM_MODE_CONNECTOR_DisplayPort;
+
+       psb_intel_encoder = kzalloc(sizeof(struct psb_intel_encoder), GFP_KERNEL);
+       if (!psb_intel_encoder)
+               return;
+        psb_intel_connector = kzalloc(sizeof(struct psb_intel_connector), GFP_KERNEL);
+        if (!psb_intel_connector)
+                goto err_connector;
+       intel_dp = kzalloc(sizeof(struct cdv_intel_dp), GFP_KERNEL);
+       if (!intel_dp)
+               goto err_priv;
+
+       if ((output_reg == DP_C) && cdv_intel_dpc_is_edp(dev))
+               type = DRM_MODE_CONNECTOR_eDP;
+
+       connector = &psb_intel_connector->base;
+       encoder = &psb_intel_encoder->base;
+
+       drm_connector_init(dev, connector, &cdv_intel_dp_connector_funcs, type);
+       drm_encoder_init(dev, encoder, &cdv_intel_dp_enc_funcs, DRM_MODE_ENCODER_TMDS);
+
+       psb_intel_connector_attach_encoder(psb_intel_connector, psb_intel_encoder);
+
+       if (type == DRM_MODE_CONNECTOR_DisplayPort)
+               psb_intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
+        else
+               psb_intel_encoder->type = INTEL_OUTPUT_EDP;
+
+
+       psb_intel_encoder->dev_priv=intel_dp;
+       intel_dp->encoder = psb_intel_encoder;
+       intel_dp->output_reg = output_reg;
+       
+       drm_encoder_helper_add(encoder, &cdv_intel_dp_helper_funcs);
+       drm_connector_helper_add(connector, &cdv_intel_dp_connector_helper_funcs);
+
+       connector->polled = DRM_CONNECTOR_POLL_HPD;
+       connector->interlace_allowed = false;
+       connector->doublescan_allowed = false;
+
+       drm_sysfs_connector_add(connector);
+
+       /* Set up the DDC bus. */
+       switch (output_reg) {
+               case DP_B:
+                       name = "DPDDC-B";
+                       psb_intel_encoder->ddi_select = (DP_MASK | DDI0_SELECT);
+                       break;
+               case DP_C:
+                       name = "DPDDC-C";
+                       psb_intel_encoder->ddi_select = (DP_MASK | DDI1_SELECT);
+                       break;
+       }
+
+       cdv_disable_intel_clock_gating(dev);
+
+       cdv_intel_dp_i2c_init(psb_intel_connector, psb_intel_encoder, name);
+        /* FIXME:fail check */
+       cdv_intel_dp_add_properties(connector);
+
+       if (is_edp(psb_intel_encoder)) {
+               int ret;
+               struct edp_power_seq cur;
+                u32 pp_on, pp_off, pp_div;
+               u32 pwm_ctrl;
+
+               pp_on = REG_READ(PP_CONTROL);
+               pp_on &= ~PANEL_UNLOCK_MASK;
+               pp_on |= PANEL_UNLOCK_REGS;
+               
+               REG_WRITE(PP_CONTROL, pp_on);
+
+               pwm_ctrl = REG_READ(BLC_PWM_CTL2);
+               pwm_ctrl |= PWM_PIPE_B;
+               REG_WRITE(BLC_PWM_CTL2, pwm_ctrl);
+
+                pp_on = REG_READ(PP_ON_DELAYS);
+                pp_off = REG_READ(PP_OFF_DELAYS);
+                pp_div = REG_READ(PP_DIVISOR);
+       
+               /* Pull timing values out of registers */
+                cur.t1_t3 = (pp_on & PANEL_POWER_UP_DELAY_MASK) >>
+                        PANEL_POWER_UP_DELAY_SHIFT;
+
+                cur.t8 = (pp_on & PANEL_LIGHT_ON_DELAY_MASK) >>
+                        PANEL_LIGHT_ON_DELAY_SHIFT;
+
+                cur.t9 = (pp_off & PANEL_LIGHT_OFF_DELAY_MASK) >>
+                        PANEL_LIGHT_OFF_DELAY_SHIFT;
+
+                cur.t10 = (pp_off & PANEL_POWER_DOWN_DELAY_MASK) >>
+                        PANEL_POWER_DOWN_DELAY_SHIFT;
+
+                cur.t11_t12 = ((pp_div & PANEL_POWER_CYCLE_DELAY_MASK) >>
+                               PANEL_POWER_CYCLE_DELAY_SHIFT);
+
+                DRM_DEBUG_KMS("cur t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n",
+                              cur.t1_t3, cur.t8, cur.t9, cur.t10, cur.t11_t12);
+
+
+               intel_dp->panel_power_up_delay = cur.t1_t3 / 10;
+                intel_dp->backlight_on_delay = cur.t8 / 10;
+                intel_dp->backlight_off_delay = cur.t9 / 10;
+                intel_dp->panel_power_down_delay = cur.t10 / 10;
+                intel_dp->panel_power_cycle_delay = (cur.t11_t12 - 1) * 100;
+
+                DRM_DEBUG_KMS("panel power up delay %d, power down delay %d, power cycle delay %d\n",
+                              intel_dp->panel_power_up_delay, intel_dp->panel_power_down_delay,
+                              intel_dp->panel_power_cycle_delay);
+
+                DRM_DEBUG_KMS("backlight on delay %d, off delay %d\n",
+                              intel_dp->backlight_on_delay, intel_dp->backlight_off_delay);
+
+
+               cdv_intel_edp_panel_vdd_on(psb_intel_encoder);
+               ret = cdv_intel_dp_aux_native_read(psb_intel_encoder, DP_DPCD_REV,
+                                              intel_dp->dpcd,
+                                              sizeof(intel_dp->dpcd));
+               cdv_intel_edp_panel_vdd_off(psb_intel_encoder);
+               if (ret == 0) {
+                       /* if this fails, presume the device is a ghost */
+                       DRM_INFO("failed to retrieve link info, disabling eDP\n");
+                       cdv_intel_dp_encoder_destroy(encoder);
+                       cdv_intel_dp_destroy(connector);
+                       goto err_priv;
+               } else {
+                       DRM_DEBUG_KMS("DPCD: Rev=%x LN_Rate=%x LN_CNT=%x LN_DOWNSP=%x\n",
+                               intel_dp->dpcd[0], intel_dp->dpcd[1], 
+                               intel_dp->dpcd[2], intel_dp->dpcd[3]);
+                       
+               }
+               /* The CDV reference driver moves pnale backlight setup into the displays that
+                  have a backlight: this is a good idea and one we should probably adopt, however
+                  we need to migrate all the drivers before we can do that */
+                /*cdv_intel_panel_setup_backlight(dev); */
+       }
+       return;
+
+err_priv:
+       kfree(psb_intel_connector);
+err_connector:
+       kfree(psb_intel_encoder);
+}
index a86f87b9ddde8cc1595c3980263a1261c8aefc4b..7272a461edfe8f91fb0686db7118024efbac4e55 100644 (file)
@@ -139,8 +139,6 @@ static enum drm_connector_status cdv_hdmi_detect(
 {
        struct psb_intel_encoder *psb_intel_encoder =
                                        psb_intel_attached_encoder(connector);
-       struct psb_intel_connector *psb_intel_connector =
-                                       to_psb_intel_connector(connector);
        struct mid_intel_hdmi_priv *hdmi_priv = psb_intel_encoder->dev_priv;
        struct edid *edid = NULL;
        enum drm_connector_status status = connector_status_disconnected;
@@ -157,8 +155,6 @@ static enum drm_connector_status cdv_hdmi_detect(
                        hdmi_priv->has_hdmi_audio =
                                                drm_detect_monitor_audio(edid);
                }
-
-               psb_intel_connector->base.display_info.raw_edid = NULL;
                kfree(edid);
        }
        return status;
@@ -352,9 +348,11 @@ void cdv_hdmi_init(struct drm_device *dev,
        switch (reg) {
        case SDVOB:
                ddc_bus = GPIOE;
+               psb_intel_encoder->ddi_select = DDI0_SELECT;
                break;
        case SDVOC:
                ddc_bus = GPIOD;
+               psb_intel_encoder->ddi_select = DDI1_SELECT;
                break;
        default:
                DRM_ERROR("unknown reg 0x%x for HDMI\n", reg);
index c7f9468b74ba8d5fab7b5cc6d1066dca10a97d8f..b362dd39bf5aab2462a15ce9872e8744263a1ab7 100644 (file)
@@ -506,16 +506,8 @@ static int cdv_intel_lvds_set_property(struct drm_connector *connector,
                                                        property,
                                                        value))
                        return -1;
-               else {
-#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
-                       struct drm_psb_private *dev_priv =
-                                               encoder->dev->dev_private;
-                       struct backlight_device *bd =
-                                               dev_priv->backlight_device;
-                       bd->props.brightness = value;
-                       backlight_update_status(bd);
-#endif
-               }
+               else
+                        gma_backlight_set(encoder->dev, value);
        } else if (!strcmp(property->name, "DPMS") && encoder) {
                struct drm_encoder_helper_funcs *helpers =
                                        encoder->helper_private;
index 5732b5702e1cef5f002ac707fcb34302c33f53de..884ba73ac6ce0f42dbafd82799110cd2fdfcd15f 100644 (file)
@@ -764,6 +764,13 @@ static void psb_setup_outputs(struct drm_device *dev)
                        crtc_mask = dev_priv->ops->hdmi_mask;
                        clone_mask = (1 << INTEL_OUTPUT_HDMI);
                        break;
+               case INTEL_OUTPUT_DISPLAYPORT:
+                       crtc_mask = (1 << 0) | (1 << 1);
+                       clone_mask = (1 << INTEL_OUTPUT_DISPLAYPORT);
+                       break;
+               case INTEL_OUTPUT_EDP:
+                       crtc_mask = (1 << 1);
+                       clone_mask = (1 << INTEL_OUTPUT_EDP);
                }
                encoder->possible_crtcs = crtc_mask;
                encoder->possible_clones =
index fc7d144bc2d3c1a2eeeb01d5749052c1cf708080..df20546a2a347e8c29a6e5cfaa40e921fdb7a6a5 100644 (file)
@@ -36,7 +36,12 @@ int psb_gem_init_object(struct drm_gem_object *obj)
 void psb_gem_free_object(struct drm_gem_object *obj)
 {
        struct gtt_range *gtt = container_of(obj, struct gtt_range, gem);
-       drm_gem_object_release_wrap(obj);
+
+       /* Remove the list map if one is present */
+       if (obj->map_list.map)
+               drm_gem_free_mmap_offset(obj);
+       drm_gem_object_release(obj);
+
        /* This must occur last as it frees up the memory of the GEM object */
        psb_gtt_free_range(obj->dev, gtt);
 }
@@ -77,7 +82,7 @@ int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev,
 
        /* Make it mmapable */
        if (!obj->map_list.map) {
-               ret = gem_create_mmap_offset(obj);
+               ret = drm_gem_create_mmap_offset(obj);
                if (ret)
                        goto out;
        }
diff --git a/drivers/gpu/drm/gma500/gem_glue.c b/drivers/gpu/drm/gma500/gem_glue.c
deleted file mode 100644 (file)
index 3c17634..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-/**************************************************************************
- * Copyright (c) 2011, Intel Corporation.
- * All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
- **************************************************************************/
-
-#include <drm/drmP.h>
-#include <drm/drm.h>
-#include "gem_glue.h"
-
-void drm_gem_object_release_wrap(struct drm_gem_object *obj)
-{
-       /* Remove the list map if one is present */
-       if (obj->map_list.map) {
-               struct drm_gem_mm *mm = obj->dev->mm_private;
-               struct drm_map_list *list = &obj->map_list;
-               drm_ht_remove_item(&mm->offset_hash, &list->hash);
-               drm_mm_put_block(list->file_offset_node);
-               kfree(list->map);
-               list->map = NULL;
-       }
-       drm_gem_object_release(obj);
-}
-
-/**
- *     gem_create_mmap_offset          -       invent an mmap offset
- *     @obj: our object
- *
- *     Standard implementation of offset generation for mmap as is
- *     duplicated in several drivers. This belongs in GEM.
- */
-int gem_create_mmap_offset(struct drm_gem_object *obj)
-{
-       struct drm_device *dev = obj->dev;
-       struct drm_gem_mm *mm = dev->mm_private;
-       struct drm_map_list *list;
-       struct drm_local_map *map;
-       int ret;
-
-       list = &obj->map_list;
-       list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL);
-       if (list->map == NULL)
-               return -ENOMEM;
-       map = list->map;
-       map->type = _DRM_GEM;
-       map->size = obj->size;
-       map->handle = obj;
-
-       list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
-                                       obj->size / PAGE_SIZE, 0, 0);
-       if (!list->file_offset_node) {
-               dev_err(dev->dev, "failed to allocate offset for bo %d\n",
-                                                               obj->name);
-               ret = -ENOSPC;
-               goto free_it;
-       }
-       list->file_offset_node = drm_mm_get_block(list->file_offset_node,
-                                       obj->size / PAGE_SIZE, 0);
-       if (!list->file_offset_node) {
-               ret = -ENOMEM;
-               goto free_it;
-       }
-       list->hash.key = list->file_offset_node->start;
-       ret = drm_ht_insert_item(&mm->offset_hash, &list->hash);
-       if (ret) {
-               dev_err(dev->dev, "failed to add to map hash\n");
-               goto free_mm;
-       }
-       return 0;
-
-free_mm:
-       drm_mm_put_block(list->file_offset_node);
-free_it:
-       kfree(list->map);
-       list->map = NULL;
-       return ret;
-}
diff --git a/drivers/gpu/drm/gma500/gem_glue.h b/drivers/gpu/drm/gma500/gem_glue.h
deleted file mode 100644 (file)
index ce5ce30..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-extern void drm_gem_object_release_wrap(struct drm_gem_object *obj);
-extern int gem_create_mmap_offset(struct drm_gem_object *obj);
index 8d7caf0f363e2381a9777dc3a35bc21780a38b76..4fb79cf00ed80132058fddf349fe5e34857ce62e 100644 (file)
@@ -54,6 +54,98 @@ static void *find_section(struct bdb_header *bdb, int section_id)
        return NULL;
 }
 
+static void
+parse_edp(struct drm_psb_private *dev_priv, struct bdb_header *bdb)
+{
+       struct bdb_edp *edp;
+       struct edp_power_seq *edp_pps;
+       struct edp_link_params *edp_link_params;
+       uint8_t panel_type;
+
+       edp = find_section(bdb, BDB_EDP);
+       
+       dev_priv->edp.bpp = 18;
+       if (!edp) {
+               if (dev_priv->edp.support) {
+                       DRM_DEBUG_KMS("No eDP BDB found but eDP panel supported, assume %dbpp panel color depth.\n",
+                                     dev_priv->edp.bpp);
+               }
+               return;
+       }
+
+       panel_type = dev_priv->panel_type;
+       switch ((edp->color_depth >> (panel_type * 2)) & 3) {
+       case EDP_18BPP:
+               dev_priv->edp.bpp = 18;
+               break;
+       case EDP_24BPP:
+               dev_priv->edp.bpp = 24;
+               break;
+       case EDP_30BPP:
+               dev_priv->edp.bpp = 30;
+               break;
+       }
+
+       /* Get the eDP sequencing and link info */
+       edp_pps = &edp->power_seqs[panel_type];
+       edp_link_params = &edp->link_params[panel_type];
+
+       dev_priv->edp.pps = *edp_pps;
+
+       DRM_DEBUG_KMS("EDP timing in vbt t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n",
+                               dev_priv->edp.pps.t1_t3, dev_priv->edp.pps.t8, 
+                               dev_priv->edp.pps.t9, dev_priv->edp.pps.t10,
+                               dev_priv->edp.pps.t11_t12);
+
+       dev_priv->edp.rate = edp_link_params->rate ? DP_LINK_BW_2_7 :
+               DP_LINK_BW_1_62;
+       switch (edp_link_params->lanes) {
+       case 0:
+               dev_priv->edp.lanes = 1;
+               break;
+       case 1:
+               dev_priv->edp.lanes = 2;
+               break;
+       case 3:
+       default:
+               dev_priv->edp.lanes = 4;
+               break;
+       }
+       DRM_DEBUG_KMS("VBT reports EDP: Lane_count %d, Lane_rate %d, Bpp %d\n",
+                       dev_priv->edp.lanes, dev_priv->edp.rate, dev_priv->edp.bpp);
+
+       switch (edp_link_params->preemphasis) {
+       case 0:
+               dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_0;
+               break;
+       case 1:
+               dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_3_5;
+               break;
+       case 2:
+               dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_6;
+               break;
+       case 3:
+               dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_9_5;
+               break;
+       }
+       switch (edp_link_params->vswing) {
+       case 0:
+               dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_400;
+               break;
+       case 1:
+               dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_600;
+               break;
+       case 2:
+               dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_800;
+               break;
+       case 3:
+               dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_1200;
+               break;
+       }
+       DRM_DEBUG_KMS("VBT reports EDP: VSwing  %d, Preemph %d\n",
+                       dev_priv->edp.vswing, dev_priv->edp.preemphasis);
+}
+
 static u16
 get_blocksize(void *p)
 {
@@ -154,6 +246,8 @@ static void parse_lfp_panel_data(struct drm_psb_private *dev_priv,
                return;
 
        dev_priv->lvds_dither = lvds_options->pixel_dither;
+       dev_priv->panel_type = lvds_options->panel_type;
+
        if (lvds_options->panel_type == 0xff)
                return;
 
@@ -340,6 +434,9 @@ parse_driver_features(struct drm_psb_private *dev_priv,
        if (!driver)
                return;
 
+       if (driver->lvds_config == BDB_DRIVER_FEATURE_EDP)
+               dev_priv->edp.support = 1;
+
        /* This bit means to use 96Mhz for DPLL_A or not */
        if (driver->primary_lfp_id)
                dev_priv->dplla_96mhz = true;
@@ -437,6 +534,9 @@ int psb_intel_init_bios(struct drm_device *dev)
        size_t size;
        int i;
 
+
+       dev_priv->panel_type = 0xff;
+
        /* XXX Should this validation be moved to intel_opregion.c? */
        if (dev_priv->opregion.vbt) {
                struct vbt_header *vbt = dev_priv->opregion.vbt;
@@ -477,6 +577,7 @@ int psb_intel_init_bios(struct drm_device *dev)
        parse_sdvo_device_mapping(dev_priv, bdb);
        parse_device_mapping(dev_priv, bdb);
        parse_backlight_data(dev_priv, bdb);
+       parse_edp(dev_priv, bdb);
 
        if (bios)
                pci_unmap_rom(pdev, bios);
index 2e95523b84b19803aeab511a4d70fa15923d726a..c6267c98c9e733079803f550daaff99b407a15f1 100644 (file)
@@ -23,6 +23,7 @@
 #define _I830_BIOS_H_
 
 #include <drm/drmP.h>
+#include <drm/drm_dp_helper.h>
 
 struct vbt_header {
        u8 signature[20];               /**< Always starts with 'VBT$' */
@@ -93,6 +94,7 @@ struct vbios_data {
 #define BDB_SDVO_LVDS_PNP_IDS   24
 #define BDB_SDVO_LVDS_POWER_SEQ         25
 #define BDB_TV_OPTIONS          26
+#define BDB_EDP                         27
 #define BDB_LVDS_OPTIONS        40
 #define BDB_LVDS_LFP_DATA_PTRS  41
 #define BDB_LVDS_LFP_DATA       42
@@ -391,6 +393,11 @@ struct bdb_sdvo_lvds_options {
        u8 panel_misc_bits_4;
 } __attribute__((packed));
 
+#define BDB_DRIVER_FEATURE_NO_LVDS             0
+#define BDB_DRIVER_FEATURE_INT_LVDS            1
+#define BDB_DRIVER_FEATURE_SDVO_LVDS           2
+#define BDB_DRIVER_FEATURE_EDP                 3
+
 struct bdb_driver_features {
        u8 boot_dev_algorithm:1;
        u8 block_display_switch:1;
@@ -431,6 +438,45 @@ struct bdb_driver_features {
        u8 custom_vbt_version;
 } __attribute__((packed));
 
+#define EDP_18BPP      0
+#define EDP_24BPP      1
+#define EDP_30BPP      2
+#define EDP_RATE_1_62  0
+#define EDP_RATE_2_7   1
+#define EDP_LANE_1     0
+#define EDP_LANE_2     1
+#define EDP_LANE_4     3
+#define EDP_PREEMPHASIS_NONE   0
+#define EDP_PREEMPHASIS_3_5dB  1
+#define EDP_PREEMPHASIS_6dB    2
+#define EDP_PREEMPHASIS_9_5dB  3
+#define EDP_VSWING_0_4V                0
+#define EDP_VSWING_0_6V                1
+#define EDP_VSWING_0_8V                2
+#define EDP_VSWING_1_2V                3
+
+struct edp_power_seq {
+       u16 t1_t3;
+       u16 t8;
+       u16 t9;
+       u16 t10;
+       u16 t11_t12;
+} __attribute__ ((packed));
+
+struct edp_link_params {
+       u8 rate:4;
+       u8 lanes:4;
+       u8 preemphasis:4;
+       u8 vswing:4;
+} __attribute__ ((packed));
+
+struct bdb_edp {
+       struct edp_power_seq power_seqs[16];
+       u32 color_depth;
+       u32 sdrrs_msa_timing_delay;
+       struct edp_link_params link_params[16];
+} __attribute__ ((packed));
+
 extern int psb_intel_init_bios(struct drm_device *dev);
 extern void psb_intel_destroy_bios(struct drm_device *dev);
 
index 5675d93b420567b3c3c5f2730c915434da9be21a..32dba2ab53e1dbf2e33476f7d3a226a7fb1e3d44 100644 (file)
@@ -299,17 +299,8 @@ static int mdfld_dsi_connector_set_property(struct drm_connector *connector,
                if (drm_connector_property_set_value(connector, property,
                                                                        value))
                        goto set_prop_error;
-               else {
-#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
-                       struct backlight_device *psb_bd;
-
-                       psb_bd = mdfld_get_backlight_device();
-                       if (psb_bd) {
-                               psb_bd->props.brightness = value;
-                               mdfld_set_brightness(psb_bd);
-                       }
-#endif
-               }
+               else
+                       gma_backlight_set(encoder->dev, value);
        }
 set_prop_done:
        return 0;
index b2a790bd9899d16b76cd05b964514065b267be96..850cd3fbb9690de68fae5e1dcb56f835e4cf20ad 100644 (file)
@@ -118,20 +118,20 @@ static void mid_get_pci_revID(struct drm_psb_private *dev_priv)
                                        dev_priv->platform_rev_id);
 }
 
-struct vbt_header {
+struct mid_vbt_header {
        u32 signature;
        u8 revision;
 } __packed;
 
 /* The same for r0 and r1 */
 struct vbt_r0 {
-       struct vbt_header vbt_header;
+       struct mid_vbt_header vbt_header;
        u8 size;
        u8 checksum;
 } __packed;
 
 struct vbt_r10 {
-       struct vbt_header vbt_header;
+       struct mid_vbt_header vbt_header;
        u8 checksum;
        u16 size;
        u8 panel_count;
@@ -281,7 +281,7 @@ static void mid_get_vbt_data(struct drm_psb_private *dev_priv)
        struct drm_device *dev = dev_priv->dev;
        u32 addr;
        u8 __iomem *vbt_virtual;
-       struct vbt_header vbt_header;
+       struct mid_vbt_header vbt_header;
        struct pci_dev *pci_gfx_root = pci_get_bus_and_slot(0, PCI_DEVFN(2, 0));
        int ret = -1;
 
index 2eb3dc4e9c9b03186c2734a5818aacec70892f47..69e51e903f3585f3600e46c6f3baee6a3fed1ab0 100644 (file)
@@ -252,7 +252,6 @@ static int oaktrail_hdmi_get_modes(struct drm_connector *connector)
        if (edid) {
                drm_mode_connector_update_edid_property(connector, edid);
                ret = drm_add_edid_modes(connector, edid);
-               connector->display_info.raw_edid = NULL;
        }
 
        /*
index c430bd424681c0b28625972a796aeb96b0524423..ad0d6de938f3654aae0ccef2f02ba2d4abb6a1ef 100644 (file)
@@ -166,8 +166,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
 
        if (config_enabled(CONFIG_BACKLIGHT_CLASS_DEVICE)) {
                int max = bd->props.max_brightness;
-               bd->props.brightness = bclp * max / 255;
-               backlight_update_status(bd);
+               gma_backlight_set(dev, bclp * max / 255);
        }
 
        asle->cblv = (bclp * 0x64) / 0xff | ASLE_CBLV_VALID;
index 5971bc82b765cd87e6b32a347719ee3a5a218a64..f1432f096e5873ce3f3b0d2b39f3ef8322a91af3 100644 (file)
@@ -290,6 +290,7 @@ static void psb_get_core_freq(struct drm_device *dev)
        case 6:
        case 7:
                dev_priv->core_freq = 266;
+               break;
        default:
                dev_priv->core_freq = 0;
        }
index 1bd115ecefe142cf84890c6e2db1f1a3ebe00c94..223ff5b1b5c4842b2623da75f802c201cd82f03b 100644 (file)
 
 #include <drm/drmP.h>
 #include "drm_global.h"
-#include "gem_glue.h"
 #include "gma_drm.h"
 #include "psb_reg.h"
 #include "psb_intel_drv.h"
+#include "intel_bios.h"
 #include "gtt.h"
 #include "power.h"
 #include "opregion.h"
@@ -613,6 +613,8 @@ struct drm_psb_private {
         */
        struct backlight_device *backlight_device;
        struct drm_property *backlight_property;
+       bool backlight_enabled;
+       int backlight_level;
        uint32_t blc_adj1;
        uint32_t blc_adj2;
 
@@ -640,6 +642,19 @@ struct drm_psb_private {
        int mdfld_panel_id;
 
        bool dplla_96mhz;       /* DPLL data from the VBT */
+
+       struct {
+               int rate;
+               int lanes;
+               int preemphasis;
+               int vswing;
+
+               bool initialized;
+               bool support;
+               int bpp;
+               struct edp_power_seq pps;
+       } edp;
+       uint8_t panel_type;
 };
 
 
@@ -796,6 +811,9 @@ extern int psb_fbdev_init(struct drm_device *dev);
 /* backlight.c */
 int gma_backlight_init(struct drm_device *dev);
 void gma_backlight_exit(struct drm_device *dev);
+void gma_backlight_disable(struct drm_device *dev);
+void gma_backlight_enable(struct drm_device *dev);
+void gma_backlight_set(struct drm_device *dev, int v);
 
 /* oaktrail_crtc.c */
 extern const struct drm_crtc_helper_funcs oaktrail_helper_funcs;
index ebe1a28f60e1502240c798a0eccdb4a6b799c800..90f2d11e686ba3f18a9ea9e4c08849abc8ab8914 100644 (file)
  * Display related stuff
  */
 
-/* store information about an Ixxx DVO */
-/* The i830->i865 use multiple DVOs with multiple i2cs */
-/* the i915, i945 have a single sDVO i2c bus - which is different */
-#define MAX_OUTPUTS 6
 /* maximum connectors per crtcs in the mode set */
 #define INTELFB_CONN_LIMIT 4
 
@@ -69,6 +65,8 @@
 #define INTEL_OUTPUT_HDMI 6
 #define INTEL_OUTPUT_MIPI 7
 #define INTEL_OUTPUT_MIPI2 8
+#define INTEL_OUTPUT_DISPLAYPORT 9
+#define INTEL_OUTPUT_EDP 10
 
 #define INTEL_DVO_CHIP_NONE 0
 #define INTEL_DVO_CHIP_LVDS 1
@@ -133,6 +131,11 @@ struct psb_intel_encoder {
        void (*hot_plug)(struct psb_intel_encoder *);
        int crtc_mask;
        int clone_mask;
+       u32 ddi_select; /* Channel info */
+#define DDI0_SELECT    0x01
+#define DDI1_SELECT    0x02
+#define DP_MASK                0x8000
+#define DDI_MASK       0x03
        void *dev_priv; /* For sdvo_priv, lvds_priv, etc... */
 
        /* FIXME: Either make SDVO and LVDS store it's i2c here or give CDV it's
@@ -190,7 +193,6 @@ struct psb_intel_crtc {
        u32 mode_flags;
 
        bool active;
-       bool crtc_enable;
 
        /* Saved Crtc HW states */
        struct psb_intel_crtc_state *crtc_state;
@@ -285,4 +287,20 @@ extern void gma_intel_gmbus_set_speed(struct i2c_adapter *adapter, int speed);
 extern void gma_intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit);
 extern void gma_intel_teardown_gmbus(struct drm_device *dev);
 
+/* DP support */
+extern void cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int output_reg);
+extern void cdv_intel_dp_set_m_n(struct drm_crtc *crtc,
+                                       struct drm_display_mode *mode,
+                                       struct drm_display_mode *adjusted_mode);
+
+extern void psb_intel_attach_force_audio_property(struct drm_connector *connector);
+extern void psb_intel_attach_broadcast_rgb_property(struct drm_connector *connector);
+
+extern int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val);
+extern int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val);
+extern void cdv_sb_reset(struct drm_device *dev);
+
+extern void cdv_intel_attach_force_audio_property(struct drm_connector *connector);
+extern void cdv_intel_attach_broadcast_rgb_property(struct drm_connector *connector);
+
 #endif                         /* __INTEL_DRV_H__ */
index 37adc9edf9744ec44e779503eaefc336e34013ca..2a4c3a9e33e37a5e9c756e1b5bc17afa1fd394c6 100644 (file)
@@ -630,17 +630,8 @@ int psb_intel_lvds_set_property(struct drm_connector *connector,
                                                        property,
                                                        value))
                        goto set_prop_error;
-               else {
-#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
-                       struct drm_psb_private *devp =
-                                               encoder->dev->dev_private;
-                       struct backlight_device *bd = devp->backlight_device;
-                       if (bd) {
-                               bd->props.brightness = value;
-                               backlight_update_status(bd);
-                       }
-#endif
-               }
+               else
+                        gma_backlight_set(encoder->dev, value);
        } else if (!strcmp(property->name, "DPMS")) {
                struct drm_encoder_helper_funcs *hfuncs
                                                = encoder->helper_private;
index 8e8c8efb0a89852ec2c9d2b623f43ca25ac2273e..d914719c4b6010d7f596f982050d70c0de4e0b9c 100644 (file)
 #define PP_SEQUENCE_ON                 (1 << 28)
 #define PP_SEQUENCE_OFF                        (2 << 28)
 #define PP_SEQUENCE_MASK               0x30000000
+#define        PP_CYCLE_DELAY_ACTIVE           (1 << 27)
+#define        PP_SEQUENCE_STATE_ON_IDLE       (1 << 3)
+#define        PP_SEQUENCE_STATE_MASK          0x0000000f
+
 #define PP_CONTROL             0x61204
 #define POWER_TARGET_ON                        (1 << 0)
-
+#define        PANEL_UNLOCK_REGS               (0xabcd << 16)
+#define        PANEL_UNLOCK_MASK               (0xffff << 16)
+#define        EDP_FORCE_VDD                   (1 << 3)
+#define        EDP_BLC_ENABLE                  (1 << 2)
+#define        PANEL_POWER_RESET               (1 << 1)
+#define        PANEL_POWER_OFF                 (0 << 0)
+#define        PANEL_POWER_ON                  (1 << 0)
+
+/* Poulsbo/Oaktrail */
 #define LVDSPP_ON              0x61208
 #define LVDSPP_OFF             0x6120c
 #define PP_CYCLE               0x61210
 
+/* Cedartrail */
 #define PP_ON_DELAYS           0x61208         /* Cedartrail */
+#define PANEL_PORT_SELECT_MASK                 (3 << 30)
+#define PANEL_PORT_SELECT_LVDS                 (0 << 30)
+#define PANEL_PORT_SELECT_EDP          (1 << 30)
+#define PANEL_POWER_UP_DELAY_MASK      (0x1fff0000)
+#define PANEL_POWER_UP_DELAY_SHIFT     16
+#define PANEL_LIGHT_ON_DELAY_MASK      (0x1fff)
+#define PANEL_LIGHT_ON_DELAY_SHIFT     0
+
 #define PP_OFF_DELAYS          0x6120c         /* Cedartrail */
+#define PANEL_POWER_DOWN_DELAY_MASK    (0x1fff0000)
+#define PANEL_POWER_DOWN_DELAY_SHIFT   16
+#define PANEL_LIGHT_OFF_DELAY_MASK     (0x1fff)
+#define PANEL_LIGHT_OFF_DELAY_SHIFT    0
+
+#define PP_DIVISOR             0x61210         /* Cedartrail */
+#define  PP_REFERENCE_DIVIDER_MASK     (0xffffff00)
+#define  PP_REFERENCE_DIVIDER_SHIFT    8
+#define  PANEL_POWER_CYCLE_DELAY_MASK  (0x1f)
+#define  PANEL_POWER_CYCLE_DELAY_SHIFT 0
 
 #define PFIT_CONTROL           0x61230
 #define PFIT_ENABLE                    (1 << 31)
@@ -1282,6 +1313,10 @@ No status bits are changed.
 # define VRHUNIT_CLOCK_GATE_DISABLE            (1 << 28) /* Fixed value on CDV */
 # define DPOUNIT_CLOCK_GATE_DISABLE            (1 << 11)
 # define DPIOUNIT_CLOCK_GATE_DISABLE           (1 << 6)
+# define DPUNIT_PIPEB_GATE_DISABLE             (1 << 30)
+# define DPUNIT_PIPEA_GATE_DISABLE             (1 << 25)
+# define DPCUNIT_CLOCK_GATE_DISABLE            (1 << 24)
+# define DPLSUNIT_CLOCK_GATE_DISABLE           (1 << 13)
 
 #define RAMCLK_GATE_D          0x6210
 
@@ -1347,5 +1382,165 @@ No status bits are changed.
 #define LANE_PLL_ENABLE                (0x3 << 20)
 #define LANE_PLL_PIPE(p)       (((p) == 0) ? (1 << 21) : (0 << 21))
 
+#define DP_B                           0x64100
+#define DP_C                           0x64200
+
+#define   DP_PORT_EN                   (1 << 31)
+#define   DP_PIPEB_SELECT              (1 << 30)
+#define   DP_PIPE_MASK                 (1 << 30)
+
+/* Link training mode - select a suitable mode for each stage */
+#define   DP_LINK_TRAIN_PAT_1          (0 << 28)
+#define   DP_LINK_TRAIN_PAT_2          (1 << 28)
+#define   DP_LINK_TRAIN_PAT_IDLE       (2 << 28)
+#define   DP_LINK_TRAIN_OFF            (3 << 28)
+#define   DP_LINK_TRAIN_MASK           (3 << 28)
+#define   DP_LINK_TRAIN_SHIFT          28
+
+/* Signal voltages. These are mostly controlled by the other end */
+#define   DP_VOLTAGE_0_4               (0 << 25)
+#define   DP_VOLTAGE_0_6               (1 << 25)
+#define   DP_VOLTAGE_0_8               (2 << 25)
+#define   DP_VOLTAGE_1_2               (3 << 25)
+#define   DP_VOLTAGE_MASK              (7 << 25)
+#define   DP_VOLTAGE_SHIFT             25
+
+/* Signal pre-emphasis levels, like voltages, the other end tells us what
+ * they want
+ */
+#define   DP_PRE_EMPHASIS_0            (0 << 22)
+#define   DP_PRE_EMPHASIS_3_5          (1 << 22)
+#define   DP_PRE_EMPHASIS_6            (2 << 22)
+#define   DP_PRE_EMPHASIS_9_5          (3 << 22)
+#define   DP_PRE_EMPHASIS_MASK         (7 << 22)
+#define   DP_PRE_EMPHASIS_SHIFT                22
+
+/* How many wires to use. I guess 3 was too hard */
+#define   DP_PORT_WIDTH_1              (0 << 19)
+#define   DP_PORT_WIDTH_2              (1 << 19)
+#define   DP_PORT_WIDTH_4              (3 << 19)
+#define   DP_PORT_WIDTH_MASK           (7 << 19)
+
+/* Mystic DPCD version 1.1 special mode */
+#define   DP_ENHANCED_FRAMING          (1 << 18)
+
+/** locked once port is enabled */
+#define   DP_PORT_REVERSAL             (1 << 15)
+
+/** sends the clock on lane 15 of the PEG for debug */
+#define   DP_CLOCK_OUTPUT_ENABLE       (1 << 13)
+
+#define   DP_SCRAMBLING_DISABLE                (1 << 12)
+#define   DP_SCRAMBLING_DISABLE_IRONLAKE       (1 << 7)
+
+/** limit RGB values to avoid confusing TVs */
+#define   DP_COLOR_RANGE_16_235                (1 << 8)
+
+/** Turn on the audio link */
+#define   DP_AUDIO_OUTPUT_ENABLE       (1 << 6)
+
+/** vs and hs sync polarity */
+#define   DP_SYNC_VS_HIGH              (1 << 4)
+#define   DP_SYNC_HS_HIGH              (1 << 3)
+
+/** A fantasy */
+#define   DP_DETECTED                  (1 << 2)
+
+/** The aux channel provides a way to talk to the
+ * signal sink for DDC etc. Max packet size supported
+ * is 20 bytes in each direction, hence the 5 fixed
+ * data registers
+ */
+#define DPB_AUX_CH_CTL                 0x64110
+#define DPB_AUX_CH_DATA1               0x64114
+#define DPB_AUX_CH_DATA2               0x64118
+#define DPB_AUX_CH_DATA3               0x6411c
+#define DPB_AUX_CH_DATA4               0x64120
+#define DPB_AUX_CH_DATA5               0x64124
+
+#define DPC_AUX_CH_CTL                 0x64210
+#define DPC_AUX_CH_DATA1               0x64214
+#define DPC_AUX_CH_DATA2               0x64218
+#define DPC_AUX_CH_DATA3               0x6421c
+#define DPC_AUX_CH_DATA4               0x64220
+#define DPC_AUX_CH_DATA5               0x64224
+
+#define   DP_AUX_CH_CTL_SEND_BUSY          (1 << 31)
+#define   DP_AUX_CH_CTL_DONE               (1 << 30)
+#define   DP_AUX_CH_CTL_INTERRUPT          (1 << 29)
+#define   DP_AUX_CH_CTL_TIME_OUT_ERROR     (1 << 28)
+#define   DP_AUX_CH_CTL_TIME_OUT_400us     (0 << 26)
+#define   DP_AUX_CH_CTL_TIME_OUT_600us     (1 << 26)
+#define   DP_AUX_CH_CTL_TIME_OUT_800us     (2 << 26)
+#define   DP_AUX_CH_CTL_TIME_OUT_1600us            (3 << 26)
+#define   DP_AUX_CH_CTL_TIME_OUT_MASK      (3 << 26)
+#define   DP_AUX_CH_CTL_RECEIVE_ERROR      (1 << 25)
+#define   DP_AUX_CH_CTL_MESSAGE_SIZE_MASK    (0x1f << 20)
+#define   DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT   20
+#define   DP_AUX_CH_CTL_PRECHARGE_2US_MASK   (0xf << 16)
+#define   DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT  16
+#define   DP_AUX_CH_CTL_AUX_AKSV_SELECT            (1 << 15)
+#define   DP_AUX_CH_CTL_MANCHESTER_TEST            (1 << 14)
+#define   DP_AUX_CH_CTL_SYNC_TEST          (1 << 13)
+#define   DP_AUX_CH_CTL_DEGLITCH_TEST      (1 << 12)
+#define   DP_AUX_CH_CTL_PRECHARGE_TEST     (1 << 11)
+#define   DP_AUX_CH_CTL_BIT_CLOCK_2X_MASK    (0x7ff)
+#define   DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT   0
+
+/*
+ * Computing GMCH M and N values for the Display Port link
+ *
+ * GMCH M/N = dot clock * bytes per pixel / ls_clk * # of lanes
+ *
+ * ls_clk (we assume) is the DP link clock (1.62 or 2.7 GHz)
+ *
+ * The GMCH value is used internally
+ *
+ * bytes_per_pixel is the number of bytes coming out of the plane,
+ * which is after the LUTs, so we want the bytes for our color format.
+ * For our current usage, this is always 3, one byte for R, G and B.
+ */
+
+#define _PIPEA_GMCH_DATA_M                     0x70050
+#define _PIPEB_GMCH_DATA_M                     0x71050
+
+/* Transfer unit size for display port - 1, default is 0x3f (for TU size 64) */
+#define   PIPE_GMCH_DATA_M_TU_SIZE_MASK                (0x3f << 25)
+#define   PIPE_GMCH_DATA_M_TU_SIZE_SHIFT       25
+
+#define   PIPE_GMCH_DATA_M_MASK                        (0xffffff)
+
+#define _PIPEA_GMCH_DATA_N                     0x70054
+#define _PIPEB_GMCH_DATA_N                     0x71054
+#define   PIPE_GMCH_DATA_N_MASK                        (0xffffff)
+
+/*
+ * Computing Link M and N values for the Display Port link
+ *
+ * Link M / N = pixel_clock / ls_clk
+ *
+ * (the DP spec calls pixel_clock the 'strm_clk')
+ *
+ * The Link value is transmitted in the Main Stream
+ * Attributes and VB-ID.
+ */
+
+#define _PIPEA_DP_LINK_M                               0x70060
+#define _PIPEB_DP_LINK_M                               0x71060
+#define   PIPEA_DP_LINK_M_MASK                 (0xffffff)
+
+#define _PIPEA_DP_LINK_N                               0x70064
+#define _PIPEB_DP_LINK_N                               0x71064
+#define   PIPEA_DP_LINK_N_MASK                 (0xffffff)
+
+#define PIPE_GMCH_DATA_M(pipe) _PIPE(pipe, _PIPEA_GMCH_DATA_M, _PIPEB_GMCH_DATA_M)
+#define PIPE_GMCH_DATA_N(pipe) _PIPE(pipe, _PIPEA_GMCH_DATA_N, _PIPEB_GMCH_DATA_N)
+#define PIPE_DP_LINK_M(pipe) _PIPE(pipe, _PIPEA_DP_LINK_M, _PIPEB_DP_LINK_M)
+#define PIPE_DP_LINK_N(pipe) _PIPE(pipe, _PIPEA_DP_LINK_N, _PIPEB_DP_LINK_N)
+
+#define   PIPE_BPC_MASK                                (7 << 5)
+#define   PIPE_8BPC                            (0 << 5)
+#define   PIPE_10BPC                           (1 << 5)
+#define   PIPE_6BPC                            (2 << 5)
 
 #endif
index 0466c7b985f849bba2b7adf6faa7d022e4a19f88..d35f93ba3a859de94c799a5930a94ddc7f606a8a 100644 (file)
@@ -1292,7 +1292,6 @@ psb_intel_sdvo_get_analog_edid(struct drm_connector *connector)
 
        return drm_get_edid(connector,
                            &dev_priv->gmbus[dev_priv->crt_ddc_pin].adapter);
-       return NULL;
 }
 
 static enum drm_connector_status
@@ -1343,7 +1342,6 @@ psb_intel_sdvo_hdmi_sink_detect(struct drm_connector *connector)
                        }
                } else
                        status = connector_status_disconnected;
-               connector->display_info.raw_edid = NULL;
                kfree(edid);
        }
 
@@ -1404,7 +1402,6 @@ psb_intel_sdvo_detect(struct drm_connector *connector, bool force)
                                ret = connector_status_disconnected;
                        else
                                ret = connector_status_connected;
-                       connector->display_info.raw_edid = NULL;
                        kfree(edid);
                } else
                        ret = connector_status_connected;
@@ -1453,7 +1450,6 @@ static void psb_intel_sdvo_get_ddc_modes(struct drm_connector *connector)
                        drm_add_edid_modes(connector, edid);
                }
 
-               connector->display_info.raw_edid = NULL;
                kfree(edid);
        }
 }
index 36d952280c506d0e808fc924f9b15751862d5395..599099fe76e305372e1494fa67b5a9030db6a399 100644 (file)
@@ -427,15 +427,10 @@ static int ch7006_remove(struct i2c_client *client)
        return 0;
 }
 
-static int ch7006_suspend(struct i2c_client *client, pm_message_t mesg)
+static int ch7006_resume(struct device *dev)
 {
-       ch7006_dbg(client, "\n");
-
-       return 0;
-}
+       struct i2c_client *client = to_i2c_client(dev);
 
-static int ch7006_resume(struct i2c_client *client)
-{
        ch7006_dbg(client, "\n");
 
        ch7006_write(client, 0x3d, 0x0);
@@ -499,15 +494,18 @@ static struct i2c_device_id ch7006_ids[] = {
 };
 MODULE_DEVICE_TABLE(i2c, ch7006_ids);
 
+static const struct dev_pm_ops ch7006_pm_ops = {
+       .resume = ch7006_resume,
+};
+
 static struct drm_i2c_encoder_driver ch7006_driver = {
        .i2c_driver = {
                .probe = ch7006_probe,
                .remove = ch7006_remove,
-               .suspend = ch7006_suspend,
-               .resume = ch7006_resume,
 
                .driver = {
                        .name = "ch7006",
+                       .pm = &ch7006_pm_ops,
                },
 
                .id_table = ch7006_ids,
index b0bacdba6d7e98da5bdd22a9b72dfc1eb10349fc..0f2c5493242b2a7ff6522f8f0424a0717f347a57 100644 (file)
@@ -40,6 +40,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \
          dvo_ivch.o \
          dvo_tfp410.o \
          dvo_sil164.o \
+         dvo_ns2501.o \
          i915_gem_dmabuf.o
 
 i915-$(CONFIG_COMPAT)   += i915_ioc32.o
index 58914691a77b83e6ab9a3569f813962a2a922ca0..74b5efccfdb1185f9ff52ceaa6b6623e236608f7 100644 (file)
@@ -58,13 +58,12 @@ struct intel_dvo_dev_ops {
        void (*create_resources)(struct intel_dvo_device *dvo);
 
        /*
-        * Turn on/off output or set intermediate power levels if available.
+        * Turn on/off output.
         *
-        * Unsupported intermediate modes drop to the lower power setting.
-        * If the  mode is DPMSModeOff, the output must be disabled,
-        * as the DPLL may be disabled afterwards.
+        * Because none of our dvo drivers support an intermediate power levels,
+        * we don't expose this in the interfac.
         */
-       void (*dpms)(struct intel_dvo_device *dvo, int mode);
+       void (*dpms)(struct intel_dvo_device *dvo, bool enable);
 
        /*
         * Callback for testing a video mode for a given output.
@@ -115,6 +114,12 @@ struct intel_dvo_dev_ops {
         */
        enum drm_connector_status (*detect)(struct intel_dvo_device *dvo);
 
+       /*
+        * Probe the current hw status, returning true if the connected output
+        * is active.
+        */
+       bool (*get_hw_state)(struct intel_dvo_device *dev);
+
        /**
         * Query the device for the modes it provides.
         *
@@ -140,5 +145,6 @@ extern struct intel_dvo_dev_ops ch7xxx_ops;
 extern struct intel_dvo_dev_ops ivch_ops;
 extern struct intel_dvo_dev_ops tfp410_ops;
 extern struct intel_dvo_dev_ops ch7017_ops;
+extern struct intel_dvo_dev_ops ns2501_ops;
 
 #endif /* _INTEL_DVO_H */
index 1ca799a1e1fcd6abb19cf50be0cc51b796f20b5e..86b27d1d90c22d5c899d4da78410ce80752c7bd6 100644 (file)
@@ -163,7 +163,7 @@ struct ch7017_priv {
 };
 
 static void ch7017_dump_regs(struct intel_dvo_device *dvo);
-static void ch7017_dpms(struct intel_dvo_device *dvo, int mode);
+static void ch7017_dpms(struct intel_dvo_device *dvo, bool enable);
 
 static bool ch7017_read(struct intel_dvo_device *dvo, u8 addr, u8 *val)
 {
@@ -309,7 +309,7 @@ static void ch7017_mode_set(struct intel_dvo_device *dvo,
        lvds_power_down = CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED |
                          (mode->hdisplay & 0x0700) >> 8;
 
-       ch7017_dpms(dvo, DRM_MODE_DPMS_OFF);
+       ch7017_dpms(dvo, false);
        ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT,
                        horizontal_active_pixel_input);
        ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT,
@@ -331,7 +331,7 @@ static void ch7017_mode_set(struct intel_dvo_device *dvo,
 }
 
 /* set the CH7017 power state */
-static void ch7017_dpms(struct intel_dvo_device *dvo, int mode)
+static void ch7017_dpms(struct intel_dvo_device *dvo, bool enable)
 {
        uint8_t val;
 
@@ -345,7 +345,7 @@ static void ch7017_dpms(struct intel_dvo_device *dvo, int mode)
                        CH7017_DAC3_POWER_DOWN |
                        CH7017_TV_POWER_DOWN_EN);
 
-       if (mode == DRM_MODE_DPMS_ON) {
+       if (enable) {
                /* Turn on the LVDS */
                ch7017_write(dvo, CH7017_LVDS_POWER_DOWN,
                             val & ~CH7017_LVDS_POWER_DOWN_EN);
@@ -359,6 +359,18 @@ static void ch7017_dpms(struct intel_dvo_device *dvo, int mode)
        msleep(20);
 }
 
+static bool ch7017_get_hw_state(struct intel_dvo_device *dvo)
+{
+       uint8_t val;
+
+       ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &val);
+
+       if (val & CH7017_LVDS_POWER_DOWN_EN)
+               return false;
+       else
+               return true;
+}
+
 static void ch7017_dump_regs(struct intel_dvo_device *dvo)
 {
        uint8_t val;
@@ -396,6 +408,7 @@ struct intel_dvo_dev_ops ch7017_ops = {
        .mode_valid = ch7017_mode_valid,
        .mode_set = ch7017_mode_set,
        .dpms = ch7017_dpms,
+       .get_hw_state = ch7017_get_hw_state,
        .dump_regs = ch7017_dump_regs,
        .destroy = ch7017_destroy,
 };
index 4a036600e806bf34260d3930d923772a2c73a92e..38f3a6cb8c7d72272c9cffae2466818b440282e9 100644 (file)
@@ -289,14 +289,26 @@ static void ch7xxx_mode_set(struct intel_dvo_device *dvo,
 }
 
 /* set the CH7xxx power state */
-static void ch7xxx_dpms(struct intel_dvo_device *dvo, int mode)
+static void ch7xxx_dpms(struct intel_dvo_device *dvo, bool enable)
 {
-       if (mode == DRM_MODE_DPMS_ON)
+       if (enable)
                ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP);
        else
                ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD);
 }
 
+static bool ch7xxx_get_hw_state(struct intel_dvo_device *dvo)
+{
+       u8 val;
+
+       ch7xxx_readb(dvo, CH7xxx_PM, &val);
+
+       if (val & CH7xxx_PM_FPD)
+               return false;
+       else
+               return true;
+}
+
 static void ch7xxx_dump_regs(struct intel_dvo_device *dvo)
 {
        int i;
@@ -326,6 +338,7 @@ struct intel_dvo_dev_ops ch7xxx_ops = {
        .mode_valid = ch7xxx_mode_valid,
        .mode_set = ch7xxx_mode_set,
        .dpms = ch7xxx_dpms,
+       .get_hw_state = ch7xxx_get_hw_state,
        .dump_regs = ch7xxx_dump_regs,
        .destroy = ch7xxx_destroy,
 };
index 04f2893d5e3ce91b039d86cb3a77c7eca7d0169e..baaf65bf0bdd1a1430b565fce6c60302c4f95a2f 100644 (file)
@@ -288,7 +288,7 @@ static enum drm_mode_status ivch_mode_valid(struct intel_dvo_device *dvo,
 }
 
 /** Sets the power state of the panel connected to the ivch */
-static void ivch_dpms(struct intel_dvo_device *dvo, int mode)
+static void ivch_dpms(struct intel_dvo_device *dvo, bool enable)
 {
        int i;
        uint16_t vr01, vr30, backlight;
@@ -297,13 +297,13 @@ static void ivch_dpms(struct intel_dvo_device *dvo, int mode)
        if (!ivch_read(dvo, VR01, &vr01))
                return;
 
-       if (mode == DRM_MODE_DPMS_ON)
+       if (enable)
                backlight = 1;
        else
                backlight = 0;
        ivch_write(dvo, VR80, backlight);
 
-       if (mode == DRM_MODE_DPMS_ON)
+       if (enable)
                vr01 |= VR01_LCD_ENABLE | VR01_DVO_ENABLE;
        else
                vr01 &= ~(VR01_LCD_ENABLE | VR01_DVO_ENABLE);
@@ -315,7 +315,7 @@ static void ivch_dpms(struct intel_dvo_device *dvo, int mode)
                if (!ivch_read(dvo, VR30, &vr30))
                        break;
 
-               if (((vr30 & VR30_PANEL_ON) != 0) == (mode == DRM_MODE_DPMS_ON))
+               if (((vr30 & VR30_PANEL_ON) != 0) == enable)
                        break;
                udelay(1000);
        }
@@ -323,6 +323,20 @@ static void ivch_dpms(struct intel_dvo_device *dvo, int mode)
        udelay(16 * 1000);
 }
 
+static bool ivch_get_hw_state(struct intel_dvo_device *dvo)
+{
+       uint16_t vr01;
+
+       /* Set the new power state of the panel. */
+       if (!ivch_read(dvo, VR01, &vr01))
+               return false;
+
+       if (vr01 & VR01_LCD_ENABLE)
+               return true;
+       else
+               return false;
+}
+
 static void ivch_mode_set(struct intel_dvo_device *dvo,
                          struct drm_display_mode *mode,
                          struct drm_display_mode *adjusted_mode)
@@ -413,6 +427,7 @@ static void ivch_destroy(struct intel_dvo_device *dvo)
 struct intel_dvo_dev_ops ivch_ops = {
        .init = ivch_init,
        .dpms = ivch_dpms,
+       .get_hw_state = ivch_get_hw_state,
        .mode_valid = ivch_mode_valid,
        .mode_set = ivch_mode_set,
        .detect = ivch_detect,
diff --git a/drivers/gpu/drm/i915/dvo_ns2501.c b/drivers/gpu/drm/i915/dvo_ns2501.c
new file mode 100644 (file)
index 0000000..c4a255b
--- /dev/null
@@ -0,0 +1,588 @@
+/*
+ *
+ * Copyright (c) 2012 Gilles Dartiguelongue, Thomas Richter
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "dvo.h"
+#include "i915_reg.h"
+#include "i915_drv.h"
+
+#define NS2501_VID 0x1305
+#define NS2501_DID 0x6726
+
+#define NS2501_VID_LO 0x00
+#define NS2501_VID_HI 0x01
+#define NS2501_DID_LO 0x02
+#define NS2501_DID_HI 0x03
+#define NS2501_REV 0x04
+#define NS2501_RSVD 0x05
+#define NS2501_FREQ_LO 0x06
+#define NS2501_FREQ_HI 0x07
+
+#define NS2501_REG8 0x08
+#define NS2501_8_VEN (1<<5)
+#define NS2501_8_HEN (1<<4)
+#define NS2501_8_DSEL (1<<3)
+#define NS2501_8_BPAS (1<<2)
+#define NS2501_8_RSVD (1<<1)
+#define NS2501_8_PD (1<<0)
+
+#define NS2501_REG9 0x09
+#define NS2501_9_VLOW (1<<7)
+#define NS2501_9_MSEL_MASK (0x7<<4)
+#define NS2501_9_TSEL (1<<3)
+#define NS2501_9_RSEN (1<<2)
+#define NS2501_9_RSVD (1<<1)
+#define NS2501_9_MDI (1<<0)
+
+#define NS2501_REGC 0x0c
+
+struct ns2501_priv {
+       //I2CDevRec d;
+       bool quiet;
+       int reg_8_shadow;
+       int reg_8_set;
+       // Shadow registers for i915
+       int dvoc;
+       int pll_a;
+       int srcdim;
+       int fw_blc;
+};
+
+#define NSPTR(d) ((NS2501Ptr)(d->DriverPrivate.ptr))
+
+/*
+ * For reasons unclear to me, the ns2501 at least on the Fujitsu/Siemens
+ * laptops does not react on the i2c bus unless
+ * both the PLL is running and the display is configured in its native
+ * resolution.
+ * This function forces the DVO on, and stores the registers it touches.
+ * Afterwards, registers are restored to regular values.
+ *
+ * This is pretty much a hack, though it works.
+ * Without that, ns2501_readb and ns2501_writeb fail
+ * when switching the resolution.
+ */
+
+static void enable_dvo(struct intel_dvo_device *dvo)
+{
+       struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
+       struct i2c_adapter *adapter = dvo->i2c_bus;
+       struct intel_gmbus *bus = container_of(adapter,
+                                              struct intel_gmbus,
+                                              adapter);
+       struct drm_i915_private *dev_priv = bus->dev_priv;
+
+       DRM_DEBUG_KMS("%s: Trying to re-enable the DVO\n", __FUNCTION__);
+
+       ns->dvoc = I915_READ(DVO_C);
+       ns->pll_a = I915_READ(_DPLL_A);
+       ns->srcdim = I915_READ(DVOC_SRCDIM);
+       ns->fw_blc = I915_READ(FW_BLC);
+
+       I915_WRITE(DVOC, 0x10004084);
+       I915_WRITE(_DPLL_A, 0xd0820000);
+       I915_WRITE(DVOC_SRCDIM, 0x400300);      // 1024x768
+       I915_WRITE(FW_BLC, 0x1080304);
+
+       I915_WRITE(DVOC, 0x90004084);
+}
+
+/*
+ * Restore the I915 registers modified by the above
+ * trigger function.
+ */
+static void restore_dvo(struct intel_dvo_device *dvo)
+{
+       struct i2c_adapter *adapter = dvo->i2c_bus;
+       struct intel_gmbus *bus = container_of(adapter,
+                                              struct intel_gmbus,
+                                              adapter);
+       struct drm_i915_private *dev_priv = bus->dev_priv;
+       struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
+
+       I915_WRITE(DVOC, ns->dvoc);
+       I915_WRITE(_DPLL_A, ns->pll_a);
+       I915_WRITE(DVOC_SRCDIM, ns->srcdim);
+       I915_WRITE(FW_BLC, ns->fw_blc);
+}
+
+/*
+** Read a register from the ns2501.
+** Returns true if successful, false otherwise.
+** If it returns false, it might be wise to enable the
+** DVO with the above function.
+*/
+static bool ns2501_readb(struct intel_dvo_device *dvo, int addr, uint8_t * ch)
+{
+       struct ns2501_priv *ns = dvo->dev_priv;
+       struct i2c_adapter *adapter = dvo->i2c_bus;
+       u8 out_buf[2];
+       u8 in_buf[2];
+
+       struct i2c_msg msgs[] = {
+               {
+                .addr = dvo->slave_addr,
+                .flags = 0,
+                .len = 1,
+                .buf = out_buf,
+                },
+               {
+                .addr = dvo->slave_addr,
+                .flags = I2C_M_RD,
+                .len = 1,
+                .buf = in_buf,
+                }
+       };
+
+       out_buf[0] = addr;
+       out_buf[1] = 0;
+
+       if (i2c_transfer(adapter, msgs, 2) == 2) {
+               *ch = in_buf[0];
+               return true;
+       };
+
+       if (!ns->quiet) {
+               DRM_DEBUG_KMS
+                   ("Unable to read register 0x%02x from %s:0x%02x.\n", addr,
+                    adapter->name, dvo->slave_addr);
+       }
+
+       return false;
+}
+
+/*
+** Write a register to the ns2501.
+** Returns true if successful, false otherwise.
+** If it returns false, it might be wise to enable the
+** DVO with the above function.
+*/
+static bool ns2501_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
+{
+       struct ns2501_priv *ns = dvo->dev_priv;
+       struct i2c_adapter *adapter = dvo->i2c_bus;
+       uint8_t out_buf[2];
+
+       struct i2c_msg msg = {
+               .addr = dvo->slave_addr,
+               .flags = 0,
+               .len = 2,
+               .buf = out_buf,
+       };
+
+       out_buf[0] = addr;
+       out_buf[1] = ch;
+
+       if (i2c_transfer(adapter, &msg, 1) == 1) {
+               return true;
+       }
+
+       if (!ns->quiet) {
+               DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d\n",
+                             addr, adapter->name, dvo->slave_addr);
+       }
+
+       return false;
+}
+
+/* National Semiconductor 2501 driver for chip on i2c bus
+ * scan for the chip on the bus.
+ * Hope the VBIOS initialized the PLL correctly so we can
+ * talk to it. If not, it will not be seen and not detected.
+ * Bummer!
+ */
+static bool ns2501_init(struct intel_dvo_device *dvo,
+                       struct i2c_adapter *adapter)
+{
+       /* this will detect the NS2501 chip on the specified i2c bus */
+       struct ns2501_priv *ns;
+       unsigned char ch;
+
+       ns = kzalloc(sizeof(struct ns2501_priv), GFP_KERNEL);
+       if (ns == NULL)
+               return false;
+
+       dvo->i2c_bus = adapter;
+       dvo->dev_priv = ns;
+       ns->quiet = true;
+
+       if (!ns2501_readb(dvo, NS2501_VID_LO, &ch))
+               goto out;
+
+       if (ch != (NS2501_VID & 0xff)) {
+               DRM_DEBUG_KMS("ns2501 not detected got %d: from %s Slave %d.\n",
+                             ch, adapter->name, dvo->slave_addr);
+               goto out;
+       }
+
+       if (!ns2501_readb(dvo, NS2501_DID_LO, &ch))
+               goto out;
+
+       if (ch != (NS2501_DID & 0xff)) {
+               DRM_DEBUG_KMS("ns2501 not detected got %d: from %s Slave %d.\n",
+                             ch, adapter->name, dvo->slave_addr);
+               goto out;
+       }
+       ns->quiet = false;
+       ns->reg_8_set = 0;
+       ns->reg_8_shadow =
+           NS2501_8_PD | NS2501_8_BPAS | NS2501_8_VEN | NS2501_8_HEN;
+
+       DRM_DEBUG_KMS("init ns2501 dvo controller successfully!\n");
+       return true;
+
+out:
+       kfree(ns);
+       return false;
+}
+
+static enum drm_connector_status ns2501_detect(struct intel_dvo_device *dvo)
+{
+       /*
+        * This is a Laptop display, it doesn't have hotplugging.
+        * Even if not, the detection bit of the 2501 is unreliable as
+        * it only works for some display types.
+        * It is even more unreliable as the PLL must be active for
+        * allowing reading from the chiop.
+        */
+       return connector_status_connected;
+}
+
+static enum drm_mode_status ns2501_mode_valid(struct intel_dvo_device *dvo,
+                                             struct drm_display_mode *mode)
+{
+       DRM_DEBUG_KMS
+           ("%s: is mode valid (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d)\n",
+            __FUNCTION__, mode->hdisplay, mode->htotal, mode->vdisplay,
+            mode->vtotal);
+
+       /*
+        * Currently, these are all the modes I have data from.
+        * More might exist. Unclear how to find the native resolution
+        * of the panel in here so we could always accept it
+        * by disabling the scaler.
+        */
+       if ((mode->hdisplay == 800 && mode->vdisplay == 600) ||
+           (mode->hdisplay == 640 && mode->vdisplay == 480) ||
+           (mode->hdisplay == 1024 && mode->vdisplay == 768)) {
+               return MODE_OK;
+       } else {
+               return MODE_ONE_SIZE;   /* Is this a reasonable error? */
+       }
+}
+
+static void ns2501_mode_set(struct intel_dvo_device *dvo,
+                           struct drm_display_mode *mode,
+                           struct drm_display_mode *adjusted_mode)
+{
+       bool ok;
+       bool restore = false;
+       struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
+
+       DRM_DEBUG_KMS
+           ("%s: set mode (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d).\n",
+            __FUNCTION__, mode->hdisplay, mode->htotal, mode->vdisplay,
+            mode->vtotal);
+
+       /*
+        * Where do I find the native resolution for which scaling is not required???
+        *
+        * First trigger the DVO on as otherwise the chip does not appear on the i2c
+        * bus.
+        */
+       do {
+               ok = true;
+
+               if (mode->hdisplay == 800 && mode->vdisplay == 600) {
+                       /* mode 277 */
+                       ns->reg_8_shadow &= ~NS2501_8_BPAS;
+                       DRM_DEBUG_KMS("%s: switching to 800x600\n",
+                                     __FUNCTION__);
+
+                       /*
+                        * No, I do not know where this data comes from.
+                        * It is just what the video bios left in the DVO, so
+                        * I'm just copying it here over.
+                        * This also means that I cannot support any other modes
+                        * except the ones supported by the bios.
+                        */
+                       ok &= ns2501_writeb(dvo, 0x11, 0xc8);   // 0xc7 also works.
+                       ok &= ns2501_writeb(dvo, 0x1b, 0x19);
+                       ok &= ns2501_writeb(dvo, 0x1c, 0x62);   // VBIOS left 0x64 here, but 0x62 works nicer
+                       ok &= ns2501_writeb(dvo, 0x1d, 0x02);
+
+                       ok &= ns2501_writeb(dvo, 0x34, 0x03);
+                       ok &= ns2501_writeb(dvo, 0x35, 0xff);
+
+                       ok &= ns2501_writeb(dvo, 0x80, 0x27);
+                       ok &= ns2501_writeb(dvo, 0x81, 0x03);
+                       ok &= ns2501_writeb(dvo, 0x82, 0x41);
+                       ok &= ns2501_writeb(dvo, 0x83, 0x05);
+
+                       ok &= ns2501_writeb(dvo, 0x8d, 0x02);
+                       ok &= ns2501_writeb(dvo, 0x8e, 0x04);
+                       ok &= ns2501_writeb(dvo, 0x8f, 0x00);
+
+                       ok &= ns2501_writeb(dvo, 0x90, 0xfe);   /* vertical. VBIOS left 0xff here, but 0xfe works better */
+                       ok &= ns2501_writeb(dvo, 0x91, 0x07);
+                       ok &= ns2501_writeb(dvo, 0x94, 0x00);
+                       ok &= ns2501_writeb(dvo, 0x95, 0x00);
+
+                       ok &= ns2501_writeb(dvo, 0x96, 0x00);
+
+                       ok &= ns2501_writeb(dvo, 0x99, 0x00);
+                       ok &= ns2501_writeb(dvo, 0x9a, 0x88);
+
+                       ok &= ns2501_writeb(dvo, 0x9c, 0x23);   /* Looks like first and last line of the image. */
+                       ok &= ns2501_writeb(dvo, 0x9d, 0x00);
+                       ok &= ns2501_writeb(dvo, 0x9e, 0x25);
+                       ok &= ns2501_writeb(dvo, 0x9f, 0x03);
+
+                       ok &= ns2501_writeb(dvo, 0xa4, 0x80);
+
+                       ok &= ns2501_writeb(dvo, 0xb6, 0x00);
+
+                       ok &= ns2501_writeb(dvo, 0xb9, 0xc8);   /* horizontal? */
+                       ok &= ns2501_writeb(dvo, 0xba, 0x00);   /* horizontal? */
+
+                       ok &= ns2501_writeb(dvo, 0xc0, 0x05);   /* horizontal? */
+                       ok &= ns2501_writeb(dvo, 0xc1, 0xd7);
+
+                       ok &= ns2501_writeb(dvo, 0xc2, 0x00);
+                       ok &= ns2501_writeb(dvo, 0xc3, 0xf8);
+
+                       ok &= ns2501_writeb(dvo, 0xc4, 0x03);
+                       ok &= ns2501_writeb(dvo, 0xc5, 0x1a);
+
+                       ok &= ns2501_writeb(dvo, 0xc6, 0x00);
+                       ok &= ns2501_writeb(dvo, 0xc7, 0x73);
+                       ok &= ns2501_writeb(dvo, 0xc8, 0x02);
+
+               } else if (mode->hdisplay == 640 && mode->vdisplay == 480) {
+                       /* mode 274 */
+                       DRM_DEBUG_KMS("%s: switching to 640x480\n",
+                                     __FUNCTION__);
+                       /*
+                        * No, I do not know where this data comes from.
+                        * It is just what the video bios left in the DVO, so
+                        * I'm just copying it here over.
+                        * This also means that I cannot support any other modes
+                        * except the ones supported by the bios.
+                        */
+                       ns->reg_8_shadow &= ~NS2501_8_BPAS;
+
+                       ok &= ns2501_writeb(dvo, 0x11, 0xa0);
+                       ok &= ns2501_writeb(dvo, 0x1b, 0x11);
+                       ok &= ns2501_writeb(dvo, 0x1c, 0x54);
+                       ok &= ns2501_writeb(dvo, 0x1d, 0x03);
+
+                       ok &= ns2501_writeb(dvo, 0x34, 0x03);
+                       ok &= ns2501_writeb(dvo, 0x35, 0xff);
+
+                       ok &= ns2501_writeb(dvo, 0x80, 0xff);
+                       ok &= ns2501_writeb(dvo, 0x81, 0x07);
+                       ok &= ns2501_writeb(dvo, 0x82, 0x3d);
+                       ok &= ns2501_writeb(dvo, 0x83, 0x05);
+
+                       ok &= ns2501_writeb(dvo, 0x8d, 0x02);
+                       ok &= ns2501_writeb(dvo, 0x8e, 0x10);
+                       ok &= ns2501_writeb(dvo, 0x8f, 0x00);
+
+                       ok &= ns2501_writeb(dvo, 0x90, 0xff);   /* vertical */
+                       ok &= ns2501_writeb(dvo, 0x91, 0x07);
+                       ok &= ns2501_writeb(dvo, 0x94, 0x00);
+                       ok &= ns2501_writeb(dvo, 0x95, 0x00);
+
+                       ok &= ns2501_writeb(dvo, 0x96, 0x05);
+
+                       ok &= ns2501_writeb(dvo, 0x99, 0x00);
+                       ok &= ns2501_writeb(dvo, 0x9a, 0x88);
+
+                       ok &= ns2501_writeb(dvo, 0x9c, 0x24);
+                       ok &= ns2501_writeb(dvo, 0x9d, 0x00);
+                       ok &= ns2501_writeb(dvo, 0x9e, 0x25);
+                       ok &= ns2501_writeb(dvo, 0x9f, 0x03);
+
+                       ok &= ns2501_writeb(dvo, 0xa4, 0x84);
+
+                       ok &= ns2501_writeb(dvo, 0xb6, 0x09);
+
+                       ok &= ns2501_writeb(dvo, 0xb9, 0xa0);   /* horizontal? */
+                       ok &= ns2501_writeb(dvo, 0xba, 0x00);   /* horizontal? */
+
+                       ok &= ns2501_writeb(dvo, 0xc0, 0x05);   /* horizontal? */
+                       ok &= ns2501_writeb(dvo, 0xc1, 0x90);
+
+                       ok &= ns2501_writeb(dvo, 0xc2, 0x00);
+                       ok &= ns2501_writeb(dvo, 0xc3, 0x0f);
+
+                       ok &= ns2501_writeb(dvo, 0xc4, 0x03);
+                       ok &= ns2501_writeb(dvo, 0xc5, 0x16);
+
+                       ok &= ns2501_writeb(dvo, 0xc6, 0x00);
+                       ok &= ns2501_writeb(dvo, 0xc7, 0x02);
+                       ok &= ns2501_writeb(dvo, 0xc8, 0x02);
+
+               } else if (mode->hdisplay == 1024 && mode->vdisplay == 768) {
+                       /* mode 280 */
+                       DRM_DEBUG_KMS("%s: switching to 1024x768\n",
+                                     __FUNCTION__);
+                       /*
+                        * This might or might not work, actually. I'm silently
+                        * assuming here that the native panel resolution is
+                        * 1024x768. If not, then this leaves the scaler disabled
+                        * generating a picture that is likely not the expected.
+                        *
+                        * Problem is that I do not know where to take the panel
+                        * dimensions from.
+                        *
+                        * Enable the bypass, scaling not required.
+                        *
+                        * The scaler registers are irrelevant here....
+                        *
+                        */
+                       ns->reg_8_shadow |= NS2501_8_BPAS;
+                       ok &= ns2501_writeb(dvo, 0x37, 0x44);
+               } else {
+                       /*
+                        * Data not known. Bummer!
+                        * Hopefully, the code should not go here
+                        * as mode_OK delivered no other modes.
+                        */
+                       ns->reg_8_shadow |= NS2501_8_BPAS;
+               }
+               ok &= ns2501_writeb(dvo, NS2501_REG8, ns->reg_8_shadow);
+
+               if (!ok) {
+                       if (restore)
+                               restore_dvo(dvo);
+                       enable_dvo(dvo);
+                       restore = true;
+               }
+       } while (!ok);
+       /*
+        * Restore the old i915 registers before
+        * forcing the ns2501 on.
+        */
+       if (restore)
+               restore_dvo(dvo);
+}
+
+/* set the NS2501 power state */
+static bool ns2501_get_hw_state(struct intel_dvo_device *dvo)
+{
+       unsigned char ch;
+
+       if (!ns2501_readb(dvo, NS2501_REG8, &ch))
+               return false;
+
+       if (ch & NS2501_8_PD)
+               return true;
+       else
+               return false;
+}
+
+/* set the NS2501 power state */
+static void ns2501_dpms(struct intel_dvo_device *dvo, bool enable)
+{
+       bool ok;
+       bool restore = false;
+       struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
+       unsigned char ch;
+
+       DRM_DEBUG_KMS("%s: Trying set the dpms of the DVO to %i\n",
+                     __FUNCTION__, enable);
+
+       ch = ns->reg_8_shadow;
+
+       if (enable)
+               ch |= NS2501_8_PD;
+       else
+               ch &= ~NS2501_8_PD;
+
+       if (ns->reg_8_set == 0 || ns->reg_8_shadow != ch) {
+               ns->reg_8_set = 1;
+               ns->reg_8_shadow = ch;
+
+               do {
+                       ok = true;
+                       ok &= ns2501_writeb(dvo, NS2501_REG8, ch);
+                       ok &=
+                           ns2501_writeb(dvo, 0x34,
+                                         enable ? 0x03 : 0x00);
+                       ok &=
+                           ns2501_writeb(dvo, 0x35,
+                                         enable ? 0xff : 0x00);
+                       if (!ok) {
+                               if (restore)
+                                       restore_dvo(dvo);
+                               enable_dvo(dvo);
+                               restore = true;
+                       }
+               } while (!ok);
+
+               if (restore)
+                       restore_dvo(dvo);
+       }
+}
+
+static void ns2501_dump_regs(struct intel_dvo_device *dvo)
+{
+       uint8_t val;
+
+       ns2501_readb(dvo, NS2501_FREQ_LO, &val);
+       DRM_LOG_KMS("NS2501_FREQ_LO: 0x%02x\n", val);
+       ns2501_readb(dvo, NS2501_FREQ_HI, &val);
+       DRM_LOG_KMS("NS2501_FREQ_HI: 0x%02x\n", val);
+       ns2501_readb(dvo, NS2501_REG8, &val);
+       DRM_LOG_KMS("NS2501_REG8: 0x%02x\n", val);
+       ns2501_readb(dvo, NS2501_REG9, &val);
+       DRM_LOG_KMS("NS2501_REG9: 0x%02x\n", val);
+       ns2501_readb(dvo, NS2501_REGC, &val);
+       DRM_LOG_KMS("NS2501_REGC: 0x%02x\n", val);
+}
+
+static void ns2501_destroy(struct intel_dvo_device *dvo)
+{
+       struct ns2501_priv *ns = dvo->dev_priv;
+
+       if (ns) {
+               kfree(ns);
+               dvo->dev_priv = NULL;
+       }
+}
+
+struct intel_dvo_dev_ops ns2501_ops = {
+       .init = ns2501_init,
+       .detect = ns2501_detect,
+       .mode_valid = ns2501_mode_valid,
+       .mode_set = ns2501_mode_set,
+       .dpms = ns2501_dpms,
+       .get_hw_state = ns2501_get_hw_state,
+       .dump_regs = ns2501_dump_regs,
+       .destroy = ns2501_destroy,
+};
index a0b13a6f619d585eb85d6c307966b7dd137192f6..4debd32e3e4ce195e201b8869170a2533a7fd73e 100644 (file)
@@ -208,7 +208,7 @@ static void sil164_mode_set(struct intel_dvo_device *dvo,
 }
 
 /* set the SIL164 power state */
-static void sil164_dpms(struct intel_dvo_device *dvo, int mode)
+static void sil164_dpms(struct intel_dvo_device *dvo, bool enable)
 {
        int ret;
        unsigned char ch;
@@ -217,7 +217,7 @@ static void sil164_dpms(struct intel_dvo_device *dvo, int mode)
        if (ret == false)
                return;
 
-       if (mode == DRM_MODE_DPMS_ON)
+       if (enable)
                ch |= SIL164_8_PD;
        else
                ch &= ~SIL164_8_PD;
@@ -226,6 +226,21 @@ static void sil164_dpms(struct intel_dvo_device *dvo, int mode)
        return;
 }
 
+static bool sil164_get_hw_state(struct intel_dvo_device *dvo)
+{
+       int ret;
+       unsigned char ch;
+
+       ret = sil164_readb(dvo, SIL164_REG8, &ch);
+       if (ret == false)
+               return false;
+
+       if (ch & SIL164_8_PD)
+               return true;
+       else
+               return false;
+}
+
 static void sil164_dump_regs(struct intel_dvo_device *dvo)
 {
        uint8_t val;
@@ -258,6 +273,7 @@ struct intel_dvo_dev_ops sil164_ops = {
        .mode_valid = sil164_mode_valid,
        .mode_set = sil164_mode_set,
        .dpms = sil164_dpms,
+       .get_hw_state = sil164_get_hw_state,
        .dump_regs = sil164_dump_regs,
        .destroy = sil164_destroy,
 };
index aa2cd3ec54aa97d59bd736d67591048ad2da56a5..e17f1b07e915f924244032bc77c3ba2a5919adc8 100644 (file)
@@ -234,14 +234,14 @@ static void tfp410_mode_set(struct intel_dvo_device *dvo,
 }
 
 /* set the tfp410 power state */
-static void tfp410_dpms(struct intel_dvo_device *dvo, int mode)
+static void tfp410_dpms(struct intel_dvo_device *dvo, bool enable)
 {
        uint8_t ctl1;
 
        if (!tfp410_readb(dvo, TFP410_CTL_1, &ctl1))
                return;
 
-       if (mode == DRM_MODE_DPMS_ON)
+       if (enable)
                ctl1 |= TFP410_CTL_1_PD;
        else
                ctl1 &= ~TFP410_CTL_1_PD;
@@ -249,6 +249,19 @@ static void tfp410_dpms(struct intel_dvo_device *dvo, int mode)
        tfp410_writeb(dvo, TFP410_CTL_1, ctl1);
 }
 
+static bool tfp410_get_hw_state(struct intel_dvo_device *dvo)
+{
+       uint8_t ctl1;
+
+       if (!tfp410_readb(dvo, TFP410_CTL_1, &ctl1))
+               return false;
+
+       if (ctl1 & TFP410_CTL_1_PD)
+               return true;
+       else
+               return false;
+}
+
 static void tfp410_dump_regs(struct intel_dvo_device *dvo)
 {
        uint8_t val, val2;
@@ -299,6 +312,7 @@ struct intel_dvo_dev_ops tfp410_ops = {
        .mode_valid = tfp410_mode_valid,
        .mode_set = tfp410_mode_set,
        .dpms = tfp410_dpms,
+       .get_hw_state = tfp410_get_hw_state,
        .dump_regs = tfp410_dump_regs,
        .destroy = tfp410_destroy,
 };
index 359f6e8b9b006de01533e1ddc5df6a7e370406c0..7a4b3f3aad3f2f668c0af3fd6ab4b182731f47e7 100644 (file)
@@ -44,7 +44,6 @@
 
 enum {
        ACTIVE_LIST,
-       FLUSHING_LIST,
        INACTIVE_LIST,
        PINNED_LIST,
 };
@@ -62,28 +61,11 @@ static int i915_capabilities(struct seq_file *m, void *data)
 
        seq_printf(m, "gen: %d\n", info->gen);
        seq_printf(m, "pch: %d\n", INTEL_PCH_TYPE(dev));
-#define B(x) seq_printf(m, #x ": %s\n", yesno(info->x))
-       B(is_mobile);
-       B(is_i85x);
-       B(is_i915g);
-       B(is_i945gm);
-       B(is_g33);
-       B(need_gfx_hws);
-       B(is_g4x);
-       B(is_pineview);
-       B(is_broadwater);
-       B(is_crestline);
-       B(has_fbc);
-       B(has_pipe_cxsr);
-       B(has_hotplug);
-       B(cursor_needs_physical);
-       B(has_overlay);
-       B(overlay_needs_physical);
-       B(supports_tv);
-       B(has_bsd_ring);
-       B(has_blt_ring);
-       B(has_llc);
-#undef B
+#define DEV_INFO_FLAG(x) seq_printf(m, #x ": %s\n", yesno(info->x))
+#define DEV_INFO_SEP ;
+       DEV_INFO_FLAGS;
+#undef DEV_INFO_FLAG
+#undef DEV_INFO_SEP
 
        return 0;
 }
@@ -121,20 +103,23 @@ 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%s%s%s",
+       seq_printf(m, "%p: %s%s %8zdKiB %04x %04x %d %d %d%s%s%s",
                   &obj->base,
                   get_pin_flag(obj),
                   get_tiling_flag(obj),
                   obj->base.size / 1024,
                   obj->base.read_domains,
                   obj->base.write_domain,
-                  obj->last_rendering_seqno,
+                  obj->last_read_seqno,
+                  obj->last_write_seqno,
                   obj->last_fenced_seqno,
                   cache_level_str(obj->cache_level),
                   obj->dirty ? " dirty" : "",
                   obj->madv == I915_MADV_DONTNEED ? " purgeable" : "");
        if (obj->base.name)
                seq_printf(m, " (name: %d)", obj->base.name);
+       if (obj->pin_count)
+               seq_printf(m, " (pinned x %d)", obj->pin_count);
        if (obj->fence_reg != I915_FENCE_REG_NONE)
                seq_printf(m, " (fence: %d)", obj->fence_reg);
        if (obj->gtt_space != NULL)
@@ -177,10 +162,6 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data)
                seq_printf(m, "Inactive:\n");
                head = &dev_priv->mm.inactive_list;
                break;
-       case FLUSHING_LIST:
-               seq_printf(m, "Flushing:\n");
-               head = &dev_priv->mm.flushing_list;
-               break;
        default:
                mutex_unlock(&dev->struct_mutex);
                return -EINVAL;
@@ -218,8 +199,8 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
        struct drm_info_node *node = (struct drm_info_node *) m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 count, mappable_count;
-       size_t size, mappable_size;
+       u32 count, mappable_count, purgeable_count;
+       size_t size, mappable_size, purgeable_size;
        struct drm_i915_gem_object *obj;
        int ret;
 
@@ -232,13 +213,12 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
                   dev_priv->mm.object_memory);
 
        size = count = mappable_size = mappable_count = 0;
-       count_objects(&dev_priv->mm.gtt_list, gtt_list);
+       count_objects(&dev_priv->mm.bound_list, gtt_list);
        seq_printf(m, "%u [%u] objects, %zu [%zu] bytes in gtt\n",
                   count, mappable_count, size, mappable_size);
 
        size = count = mappable_size = mappable_count = 0;
        count_objects(&dev_priv->mm.active_list, mm_list);
-       count_objects(&dev_priv->mm.flushing_list, mm_list);
        seq_printf(m, "  %u [%u] active objects, %zu [%zu] bytes\n",
                   count, mappable_count, size, mappable_size);
 
@@ -247,8 +227,16 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
        seq_printf(m, "  %u [%u] inactive objects, %zu [%zu] bytes\n",
                   count, mappable_count, size, mappable_size);
 
+       size = count = purgeable_size = purgeable_count = 0;
+       list_for_each_entry(obj, &dev_priv->mm.unbound_list, gtt_list) {
+               size += obj->base.size, ++count;
+               if (obj->madv == I915_MADV_DONTNEED)
+                       purgeable_size += obj->base.size, ++purgeable_count;
+       }
+       seq_printf(m, "%u unbound objects, %zu bytes\n", count, size);
+
        size = count = mappable_size = mappable_count = 0;
-       list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) {
+       list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) {
                if (obj->fault_mappable) {
                        size += obj->gtt_space->size;
                        ++count;
@@ -257,7 +245,13 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
                        mappable_size += obj->gtt_space->size;
                        ++mappable_count;
                }
+               if (obj->madv == I915_MADV_DONTNEED) {
+                       purgeable_size += obj->base.size;
+                       ++purgeable_count;
+               }
        }
+       seq_printf(m, "%u purgeable objects, %zu bytes\n",
+                  purgeable_count, purgeable_size);
        seq_printf(m, "%u pinned mappable objects, %zu bytes\n",
                   mappable_count, mappable_size);
        seq_printf(m, "%u fault mappable objects, %zu bytes\n",
@@ -286,7 +280,7 @@ static int i915_gem_gtt_info(struct seq_file *m, void* data)
                return ret;
 
        total_obj_size = total_gtt_size = count = 0;
-       list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) {
+       list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) {
                if (list == PINNED_LIST && obj->pin_count == 0)
                        continue;
 
@@ -359,40 +353,22 @@ static int i915_gem_request_info(struct seq_file *m, void *data)
        struct drm_info_node *node = (struct drm_info_node *) m->private;
        struct drm_device *dev = node->minor->dev;
        drm_i915_private_t *dev_priv = dev->dev_private;
+       struct intel_ring_buffer *ring;
        struct drm_i915_gem_request *gem_request;
-       int ret, count;
+       int ret, count, i;
 
        ret = mutex_lock_interruptible(&dev->struct_mutex);
        if (ret)
                return ret;
 
        count = 0;
-       if (!list_empty(&dev_priv->ring[RCS].request_list)) {
-               seq_printf(m, "Render requests:\n");
-               list_for_each_entry(gem_request,
-                                   &dev_priv->ring[RCS].request_list,
-                                   list) {
-                       seq_printf(m, "    %d @ %d\n",
-                                  gem_request->seqno,
-                                  (int) (jiffies - gem_request->emitted_jiffies));
-               }
-               count++;
-       }
-       if (!list_empty(&dev_priv->ring[VCS].request_list)) {
-               seq_printf(m, "BSD requests:\n");
-               list_for_each_entry(gem_request,
-                                   &dev_priv->ring[VCS].request_list,
-                                   list) {
-                       seq_printf(m, "    %d @ %d\n",
-                                  gem_request->seqno,
-                                  (int) (jiffies - gem_request->emitted_jiffies));
-               }
-               count++;
-       }
-       if (!list_empty(&dev_priv->ring[BCS].request_list)) {
-               seq_printf(m, "BLT requests:\n");
+       for_each_ring(ring, dev_priv, i) {
+               if (list_empty(&ring->request_list))
+                       continue;
+
+               seq_printf(m, "%s requests:\n", ring->name);
                list_for_each_entry(gem_request,
-                                   &dev_priv->ring[BCS].request_list,
+                                   &ring->request_list,
                                    list) {
                        seq_printf(m, "    %d @ %d\n",
                                   gem_request->seqno,
@@ -413,7 +389,7 @@ static void i915_ring_seqno_info(struct seq_file *m,
 {
        if (ring->get_seqno) {
                seq_printf(m, "Current sequence (%s): %d\n",
-                          ring->name, ring->get_seqno(ring));
+                          ring->name, ring->get_seqno(ring, false));
        }
 }
 
@@ -422,14 +398,15 @@ static int i915_gem_seqno_info(struct seq_file *m, void *data)
        struct drm_info_node *node = (struct drm_info_node *) m->private;
        struct drm_device *dev = node->minor->dev;
        drm_i915_private_t *dev_priv = dev->dev_private;
+       struct intel_ring_buffer *ring;
        int ret, i;
 
        ret = mutex_lock_interruptible(&dev->struct_mutex);
        if (ret)
                return ret;
 
-       for (i = 0; i < I915_NUM_RINGS; i++)
-               i915_ring_seqno_info(m, &dev_priv->ring[i]);
+       for_each_ring(ring, dev_priv, i)
+               i915_ring_seqno_info(m, ring);
 
        mutex_unlock(&dev->struct_mutex);
 
@@ -442,6 +419,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
        struct drm_info_node *node = (struct drm_info_node *) m->private;
        struct drm_device *dev = node->minor->dev;
        drm_i915_private_t *dev_priv = dev->dev_private;
+       struct intel_ring_buffer *ring;
        int ret, i, pipe;
 
        ret = mutex_lock_interruptible(&dev->struct_mutex);
@@ -519,13 +497,13 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
        }
        seq_printf(m, "Interrupts received: %d\n",
                   atomic_read(&dev_priv->irq_received));
-       for (i = 0; i < I915_NUM_RINGS; i++) {
+       for_each_ring(ring, dev_priv, i) {
                if (IS_GEN6(dev) || IS_GEN7(dev)) {
-                       seq_printf(m, "Graphics Interrupt mask (%s):    %08x\n",
-                                  dev_priv->ring[i].name,
-                                  I915_READ_IMR(&dev_priv->ring[i]));
+                       seq_printf(m,
+                                  "Graphics Interrupt mask (%s):       %08x\n",
+                                  ring->name, I915_READ_IMR(ring));
                }
-               i915_ring_seqno_info(m, &dev_priv->ring[i]);
+               i915_ring_seqno_info(m, ring);
        }
        mutex_unlock(&dev->struct_mutex);
 
@@ -548,7 +526,8 @@ static int i915_gem_fence_regs_info(struct seq_file *m, void *data)
        for (i = 0; i < dev_priv->num_fence_regs; i++) {
                struct drm_i915_gem_object *obj = dev_priv->fence_regs[i].obj;
 
-               seq_printf(m, "Fenced object[%2d] = ", i);
+               seq_printf(m, "Fence %d, pin count = %d, object = ",
+                          i, dev_priv->fence_regs[i].pin_count);
                if (obj == NULL)
                        seq_printf(m, "unused");
                else
@@ -630,12 +609,12 @@ 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 %08x%s%s%s%s%s%s%s",
+               seq_printf(m, "  %08x %8u %04x %04x %x %x%s%s%s%s%s%s%s",
                           err->gtt_offset,
                           err->size,
                           err->read_domains,
                           err->write_domain,
-                          err->seqno,
+                          err->rseqno, err->wseqno,
                           pin_flag(err->pinned),
                           tiling_flag(err->tiling),
                           dirty_flag(err->dirty),
@@ -667,10 +646,9 @@ static void i915_ring_error_state(struct seq_file *m,
        seq_printf(m, "  IPEIR: 0x%08x\n", error->ipeir[ring]);
        seq_printf(m, "  IPEHR: 0x%08x\n", error->ipehr[ring]);
        seq_printf(m, "  INSTDONE: 0x%08x\n", error->instdone[ring]);
-       if (ring == RCS && INTEL_INFO(dev)->gen >= 4) {
-               seq_printf(m, "  INSTDONE1: 0x%08x\n", error->instdone1);
+       if (ring == RCS && INTEL_INFO(dev)->gen >= 4)
                seq_printf(m, "  BBADDR: 0x%08llx\n", error->bbaddr);
-       }
+
        if (INTEL_INFO(dev)->gen >= 4)
                seq_printf(m, "  INSTPS: 0x%08x\n", error->instps[ring]);
        seq_printf(m, "  INSTPM: 0x%08x\n", error->instpm[ring]);
@@ -719,11 +697,17 @@ static int i915_error_state(struct seq_file *m, void *unused)
        for (i = 0; i < dev_priv->num_fence_regs; i++)
                seq_printf(m, "  fence[%d] = %08llx\n", i, error->fence[i]);
 
+       for (i = 0; i < ARRAY_SIZE(error->extra_instdone); i++)
+               seq_printf(m, "  INSTDONE_%d: 0x%08x\n", i, error->extra_instdone[i]);
+
        if (INTEL_INFO(dev)->gen >= 6) {
                seq_printf(m, "ERROR: 0x%08x\n", error->error);
                seq_printf(m, "DONE_REG: 0x%08x\n", error->done_reg);
        }
 
+       if (INTEL_INFO(dev)->gen == 7)
+               seq_printf(m, "ERR_INT: 0x%08x\n", error->err_int);
+
        for_each_ring(ring, dev_priv, i)
                i915_ring_error_state(m, dev, error, i);
 
@@ -799,10 +783,14 @@ i915_error_state_write(struct file *filp,
        struct seq_file *m = filp->private_data;
        struct i915_error_state_file_priv *error_priv = m->private;
        struct drm_device *dev = error_priv->dev;
+       int ret;
 
        DRM_DEBUG_DRIVER("Resetting error state\n");
 
-       mutex_lock(&dev->struct_mutex);
+       ret = mutex_lock_interruptible(&dev->struct_mutex);
+       if (ret)
+               return ret;
+
        i915_destroy_error_state(dev);
        mutex_unlock(&dev->struct_mutex);
 
@@ -926,7 +914,7 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
                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) * 50);
+                                               GEN6_CAGF_SHIFT) * GT_FREQUENCY_MULTIPLIER);
                seq_printf(m, "RP CUR UP EI: %dus\n", rpupei &
                           GEN6_CURICONT_MASK);
                seq_printf(m, "RP CUR UP: %dus\n", rpcurup &
@@ -942,15 +930,15 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
 
                max_freq = (rp_state_cap & 0xff0000) >> 16;
                seq_printf(m, "Lowest (RPN) frequency: %dMHz\n",
-                          max_freq * 50);
+                          max_freq * GT_FREQUENCY_MULTIPLIER);
 
                max_freq = (rp_state_cap & 0xff00) >> 8;
                seq_printf(m, "Nominal (RP1) frequency: %dMHz\n",
-                          max_freq * 50);
+                          max_freq * GT_FREQUENCY_MULTIPLIER);
 
                max_freq = rp_state_cap & 0xff;
                seq_printf(m, "Max non-overclocked (RP0) frequency: %dMHz\n",
-                          max_freq * 50);
+                          max_freq * GT_FREQUENCY_MULTIPLIER);
        } else {
                seq_printf(m, "no P-state info available\n");
        }
@@ -1292,7 +1280,8 @@ static int i915_ring_freq_table(struct seq_file *m, void *unused)
 
        seq_printf(m, "GPU freq (MHz)\tEffective CPU freq (MHz)\n");
 
-       for (gpu_freq = dev_priv->min_delay; gpu_freq <= dev_priv->max_delay;
+       for (gpu_freq = dev_priv->rps.min_delay;
+            gpu_freq <= dev_priv->rps.max_delay;
             gpu_freq++) {
                I915_WRITE(GEN6_PCODE_DATA, gpu_freq);
                I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_PCODE_READY |
@@ -1303,7 +1292,7 @@ static int i915_ring_freq_table(struct seq_file *m, void *unused)
                        continue;
                }
                ia_freq = I915_READ(GEN6_PCODE_DATA);
-               seq_printf(m, "%d\t\t%d\n", gpu_freq * 50, ia_freq * 100);
+               seq_printf(m, "%d\t\t%d\n", gpu_freq * GT_FREQUENCY_MULTIPLIER, ia_freq * 100);
        }
 
        mutex_unlock(&dev->struct_mutex);
@@ -1472,8 +1461,12 @@ static int i915_swizzle_info(struct seq_file *m, void *data)
        struct drm_info_node *node = (struct drm_info_node *) m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       int ret;
+
+       ret = mutex_lock_interruptible(&dev->struct_mutex);
+       if (ret)
+               return ret;
 
-       mutex_lock(&dev->struct_mutex);
        seq_printf(m, "bit6 swizzle for X-tiling = %s\n",
                   swizzle_string(dev_priv->mm.bit_6_swizzle_x));
        seq_printf(m, "bit6 swizzle for Y-tiling = %s\n",
@@ -1520,9 +1513,7 @@ static int i915_ppgtt_info(struct seq_file *m, void *data)
        if (INTEL_INFO(dev)->gen == 6)
                seq_printf(m, "GFX_MODE: 0x%08x\n", I915_READ(GFX_MODE));
 
-       for (i = 0; i < I915_NUM_RINGS; i++) {
-               ring = &dev_priv->ring[i];
-
+       for_each_ring(ring, dev_priv, i) {
                seq_printf(m, "%s\n", ring->name);
                if (INTEL_INFO(dev)->gen == 7)
                        seq_printf(m, "GFX_MODE: 0x%08x\n", I915_READ(RING_MODE_GEN7(ring)));
@@ -1674,7 +1665,7 @@ i915_ring_stop_write(struct file *filp,
        struct drm_device *dev = filp->private_data;
        struct drm_i915_private *dev_priv = dev->dev_private;
        char buf[20];
-       int val = 0;
+       int val = 0, ret;
 
        if (cnt > 0) {
                if (cnt > sizeof(buf) - 1)
@@ -1689,7 +1680,10 @@ i915_ring_stop_write(struct file *filp,
 
        DRM_DEBUG_DRIVER("Stopping rings 0x%08x\n", val);
 
-       mutex_lock(&dev->struct_mutex);
+       ret = mutex_lock_interruptible(&dev->struct_mutex);
+       if (ret)
+               return ret;
+
        dev_priv->stop_rings = val;
        mutex_unlock(&dev->struct_mutex);
 
@@ -1713,10 +1707,18 @@ i915_max_freq_read(struct file *filp,
        struct drm_device *dev = filp->private_data;
        drm_i915_private_t *dev_priv = dev->dev_private;
        char buf[80];
-       int len;
+       int len, ret;
+
+       if (!(IS_GEN6(dev) || IS_GEN7(dev)))
+               return -ENODEV;
+
+       ret = mutex_lock_interruptible(&dev->struct_mutex);
+       if (ret)
+               return ret;
 
        len = snprintf(buf, sizeof(buf),
-                      "max freq: %d\n", dev_priv->max_delay * 50);
+                      "max freq: %d\n", dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER);
+       mutex_unlock(&dev->struct_mutex);
 
        if (len > sizeof(buf))
                len = sizeof(buf);
@@ -1733,7 +1735,10 @@ i915_max_freq_write(struct file *filp,
        struct drm_device *dev = filp->private_data;
        struct drm_i915_private *dev_priv = dev->dev_private;
        char buf[20];
-       int val = 1;
+       int val = 1, ret;
+
+       if (!(IS_GEN6(dev) || IS_GEN7(dev)))
+               return -ENODEV;
 
        if (cnt > 0) {
                if (cnt > sizeof(buf) - 1)
@@ -1748,12 +1753,17 @@ i915_max_freq_write(struct file *filp,
 
        DRM_DEBUG_DRIVER("Manually setting max freq to %d\n", val);
 
+       ret = mutex_lock_interruptible(&dev->struct_mutex);
+       if (ret)
+               return ret;
+
        /*
         * Turbo will still be enabled, but won't go above the set value.
         */
-       dev_priv->max_delay = val / 50;
+       dev_priv->rps.max_delay = val / GT_FREQUENCY_MULTIPLIER;
 
-       gen6_set_rps(dev, val / 50);
+       gen6_set_rps(dev, val / GT_FREQUENCY_MULTIPLIER);
+       mutex_unlock(&dev->struct_mutex);
 
        return cnt;
 }
@@ -1773,10 +1783,18 @@ i915_min_freq_read(struct file *filp, char __user *ubuf, size_t max,
        struct drm_device *dev = filp->private_data;
        drm_i915_private_t *dev_priv = dev->dev_private;
        char buf[80];
-       int len;
+       int len, ret;
+
+       if (!(IS_GEN6(dev) || IS_GEN7(dev)))
+               return -ENODEV;
+
+       ret = mutex_lock_interruptible(&dev->struct_mutex);
+       if (ret)
+               return ret;
 
        len = snprintf(buf, sizeof(buf),
-                      "min freq: %d\n", dev_priv->min_delay * 50);
+                      "min freq: %d\n", dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER);
+       mutex_unlock(&dev->struct_mutex);
 
        if (len > sizeof(buf))
                len = sizeof(buf);
@@ -1791,7 +1809,10 @@ i915_min_freq_write(struct file *filp, const char __user *ubuf, size_t cnt,
        struct drm_device *dev = filp->private_data;
        struct drm_i915_private *dev_priv = dev->dev_private;
        char buf[20];
-       int val = 1;
+       int val = 1, ret;
+
+       if (!(IS_GEN6(dev) || IS_GEN7(dev)))
+               return -ENODEV;
 
        if (cnt > 0) {
                if (cnt > sizeof(buf) - 1)
@@ -1806,12 +1827,17 @@ i915_min_freq_write(struct file *filp, const char __user *ubuf, size_t cnt,
 
        DRM_DEBUG_DRIVER("Manually setting min freq to %d\n", val);
 
+       ret = mutex_lock_interruptible(&dev->struct_mutex);
+       if (ret)
+               return ret;
+
        /*
         * Turbo will still be enabled, but won't go below the set value.
         */
-       dev_priv->min_delay = val / 50;
+       dev_priv->rps.min_delay = val / GT_FREQUENCY_MULTIPLIER;
 
-       gen6_set_rps(dev, val / 50);
+       gen6_set_rps(dev, val / GT_FREQUENCY_MULTIPLIER);
+       mutex_unlock(&dev->struct_mutex);
 
        return cnt;
 }
@@ -1834,9 +1860,15 @@ i915_cache_sharing_read(struct file *filp,
        drm_i915_private_t *dev_priv = dev->dev_private;
        char buf[80];
        u32 snpcr;
-       int len;
+       int len, ret;
+
+       if (!(IS_GEN6(dev) || IS_GEN7(dev)))
+               return -ENODEV;
+
+       ret = mutex_lock_interruptible(&dev->struct_mutex);
+       if (ret)
+               return ret;
 
-       mutex_lock(&dev_priv->dev->struct_mutex);
        snpcr = I915_READ(GEN6_MBCUNIT_SNPCR);
        mutex_unlock(&dev_priv->dev->struct_mutex);
 
@@ -1862,6 +1894,9 @@ i915_cache_sharing_write(struct file *filp,
        u32 snpcr;
        int val = 1;
 
+       if (!(IS_GEN6(dev) || IS_GEN7(dev)))
+               return -ENODEV;
+
        if (cnt > 0) {
                if (cnt > sizeof(buf) - 1)
                        return -EINVAL;
@@ -1925,16 +1960,11 @@ static int i915_forcewake_open(struct inode *inode, struct file *file)
 {
        struct drm_device *dev = inode->i_private;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       int ret;
 
        if (INTEL_INFO(dev)->gen < 6)
                return 0;
 
-       ret = mutex_lock_interruptible(&dev->struct_mutex);
-       if (ret)
-               return ret;
        gen6_gt_force_wake_get(dev_priv);
-       mutex_unlock(&dev->struct_mutex);
 
        return 0;
 }
@@ -1947,16 +1977,7 @@ static int i915_forcewake_release(struct inode *inode, struct file *file)
        if (INTEL_INFO(dev)->gen < 6)
                return 0;
 
-       /*
-        * It's bad that we can potentially hang userspace if struct_mutex gets
-        * forever stuck.  However, if we cannot acquire this lock it means that
-        * almost certainly the driver has hung, is not unload-able. Therefore
-        * hanging here is probably a minor inconvenience not to be seen my
-        * almost every user.
-        */
-       mutex_lock(&dev->struct_mutex);
        gen6_gt_force_wake_put(dev_priv);
-       mutex_unlock(&dev->struct_mutex);
 
        return 0;
 }
@@ -2006,7 +2027,6 @@ static struct drm_info_list i915_debugfs_list[] = {
        {"i915_gem_gtt", i915_gem_gtt_info, 0},
        {"i915_gem_pinned", i915_gem_gtt_info, 0, (void *) PINNED_LIST},
        {"i915_gem_active", i915_gem_object_list_info, 0, (void *) ACTIVE_LIST},
-       {"i915_gem_flushing", i915_gem_object_list_info, 0, (void *) FLUSHING_LIST},
        {"i915_gem_inactive", i915_gem_object_list_info, 0, (void *) INACTIVE_LIST},
        {"i915_gem_pageflip", i915_gem_pageflip_info, 0},
        {"i915_gem_request", i915_gem_request_info, 0},
@@ -2067,6 +2087,7 @@ int i915_debugfs_init(struct drm_minor *minor)
                                  &i915_cache_sharing_fops);
        if (ret)
                return ret;
+
        ret = i915_debugfs_create(minor->debugfs_root, minor,
                                  "i915_ring_stop",
                                  &i915_ring_stop_fops);
index 914c0dfabe6048113abc150b38e06a107eb33c5b..ffbc9156c792c23122e2272b58d9dbced3fe0663 100644 (file)
@@ -235,10 +235,10 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
                }
        }
 
-       dev_priv->cpp = init->cpp;
-       dev_priv->back_offset = init->back_offset;
-       dev_priv->front_offset = init->front_offset;
-       dev_priv->current_page = 0;
+       dev_priv->dri1.cpp = init->cpp;
+       dev_priv->dri1.back_offset = init->back_offset;
+       dev_priv->dri1.front_offset = init->front_offset;
+       dev_priv->dri1.current_page = 0;
        if (master_priv->sarea_priv)
                master_priv->sarea_priv->pf_current_page = 0;
 
@@ -575,7 +575,7 @@ static int i915_dispatch_flip(struct drm_device * dev)
 
        DRM_DEBUG_DRIVER("%s: page=%d pfCurrentPage=%d\n",
                          __func__,
-                        dev_priv->current_page,
+                        dev_priv->dri1.current_page,
                         master_priv->sarea_priv->pf_current_page);
 
        i915_kernel_lost_context(dev);
@@ -589,12 +589,12 @@ static int i915_dispatch_flip(struct drm_device * dev)
 
        OUT_RING(CMD_OP_DISPLAYBUFFER_INFO | ASYNC_FLIP);
        OUT_RING(0);
-       if (dev_priv->current_page == 0) {
-               OUT_RING(dev_priv->back_offset);
-               dev_priv->current_page = 1;
+       if (dev_priv->dri1.current_page == 0) {
+               OUT_RING(dev_priv->dri1.back_offset);
+               dev_priv->dri1.current_page = 1;
        } else {
-               OUT_RING(dev_priv->front_offset);
-               dev_priv->current_page = 0;
+               OUT_RING(dev_priv->dri1.front_offset);
+               dev_priv->dri1.current_page = 0;
        }
        OUT_RING(0);
 
@@ -613,7 +613,7 @@ static int i915_dispatch_flip(struct drm_device * dev)
                ADVANCE_LP_RING();
        }
 
-       master_priv->sarea_priv->pf_current_page = dev_priv->current_page;
+       master_priv->sarea_priv->pf_current_page = dev_priv->dri1.current_page;
        return 0;
 }
 
@@ -1009,6 +1009,12 @@ static int i915_getparam(struct drm_device *dev, void *data,
        case I915_PARAM_HAS_WAIT_TIMEOUT:
                value = 1;
                break;
+       case I915_PARAM_HAS_SEMAPHORES:
+               value = i915_semaphore_is_enabled(dev);
+               break;
+       case I915_PARAM_HAS_PRIME_VMAP_FLUSH:
+               value = 1;
+               break;
        default:
                DRM_DEBUG_DRIVER("Unknown parameter %d\n",
                                 param->param);
@@ -1425,6 +1431,21 @@ static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv)
        kfree(ap);
 }
 
+static void i915_dump_device_info(struct drm_i915_private *dev_priv)
+{
+       const struct intel_device_info *info = dev_priv->info;
+
+#define DEV_INFO_FLAG(name) info->name ? #name "," : ""
+#define DEV_INFO_SEP ,
+       DRM_DEBUG_DRIVER("i915 device info: gen=%i, pciid=0x%04x flags="
+                        "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+                        info->gen,
+                        dev_priv->dev->pdev->device,
+                        DEV_INFO_FLAGS);
+#undef DEV_INFO_FLAG
+#undef DEV_INFO_SEP
+}
+
 /**
  * i915_driver_load - setup chip and create an initial config
  * @dev: DRM device
@@ -1440,7 +1461,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
 {
        struct drm_i915_private *dev_priv;
        struct intel_device_info *info;
-       int ret = 0, mmio_bar;
+       int ret = 0, mmio_bar, mmio_size;
        uint32_t aperture_size;
 
        info = (struct intel_device_info *) flags;
@@ -1449,7 +1470,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
        if (info->gen >= 6 && !drm_core_check_feature(dev, DRIVER_MODESET))
                return -ENODEV;
 
-
        /* i915 has 4 more counters */
        dev->counters += 4;
        dev->types[6] = _DRM_STAT_IRQ;
@@ -1465,6 +1485,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
        dev_priv->dev = dev;
        dev_priv->info = info;
 
+       i915_dump_device_info(dev_priv);
+
        if (i915_get_bridge_dev(dev)) {
                ret = -EIO;
                goto free_priv;
@@ -1504,7 +1526,19 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
                dma_set_coherent_mask(&dev->pdev->dev, DMA_BIT_MASK(32));
 
        mmio_bar = IS_GEN2(dev) ? 1 : 0;
-       dev_priv->regs = pci_iomap(dev->pdev, mmio_bar, 0);
+       /* Before gen4, the registers and the GTT are behind different BARs.
+        * However, from gen4 onwards, the registers and the GTT are shared
+        * in the same BAR, so we want to restrict this ioremap from
+        * clobbering the GTT which we want ioremap_wc instead. Fortunately,
+        * the register BAR remains the same size for all the earlier
+        * generations up to Ironlake.
+        */
+       if (info->gen < 5)
+               mmio_size = 512*1024;
+       else
+               mmio_size = 2*1024*1024;
+
+       dev_priv->regs = pci_iomap(dev->pdev, mmio_bar, mmio_size);
        if (!dev_priv->regs) {
                DRM_ERROR("failed to map registers\n");
                ret = -EIO;
@@ -1536,11 +1570,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
         *
         * All tasks on the workqueue are expected to acquire the dev mutex
         * so there is no point in running more than one instance of the
-        * workqueue at any time: max_active = 1 and NON_REENTRANT.
+        * workqueue at any time.  Use an ordered one.
         */
-       dev_priv->wq = alloc_workqueue("i915",
-                                      WQ_UNBOUND | WQ_NON_REENTRANT,
-                                      1);
+       dev_priv->wq = alloc_ordered_workqueue("i915", 0);
        if (dev_priv->wq == NULL) {
                DRM_ERROR("Failed to create our workqueue.\n");
                ret = -ENOMEM;
@@ -1586,7 +1618,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
 
        spin_lock_init(&dev_priv->irq_lock);
        spin_lock_init(&dev_priv->error_lock);
-       spin_lock_init(&dev_priv->rps_lock);
+       spin_lock_init(&dev_priv->rps.lock);
        spin_lock_init(&dev_priv->dpio_lock);
 
        if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev))
@@ -1836,6 +1868,8 @@ struct drm_ioctl_desc i915_ioctls[] = {
        DRM_IOCTL_DEF_DRV(I915_GEM_PIN, i915_gem_pin_ioctl, DRM_AUTH|DRM_ROOT_ONLY|DRM_UNLOCKED),
        DRM_IOCTL_DEF_DRV(I915_GEM_UNPIN, i915_gem_unpin_ioctl, DRM_AUTH|DRM_ROOT_ONLY|DRM_UNLOCKED),
        DRM_IOCTL_DEF_DRV(I915_GEM_BUSY, i915_gem_busy_ioctl, DRM_AUTH|DRM_UNLOCKED),
+       DRM_IOCTL_DEF_DRV(I915_GEM_SET_CACHING, i915_gem_set_caching_ioctl, DRM_UNLOCKED),
+       DRM_IOCTL_DEF_DRV(I915_GEM_GET_CACHING, i915_gem_get_caching_ioctl, DRM_UNLOCKED),
        DRM_IOCTL_DEF_DRV(I915_GEM_THROTTLE, i915_gem_throttle_ioctl, DRM_AUTH|DRM_UNLOCKED),
        DRM_IOCTL_DEF_DRV(I915_GEM_ENTERVT, i915_gem_entervt_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY|DRM_UNLOCKED),
        DRM_IOCTL_DEF_DRV(I915_GEM_LEAVEVT, i915_gem_leavevt_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY|DRM_UNLOCKED),
@@ -1858,6 +1892,7 @@ struct drm_ioctl_desc i915_ioctls[] = {
        DRM_IOCTL_DEF_DRV(I915_GEM_WAIT, i915_gem_wait_ioctl, DRM_AUTH|DRM_UNLOCKED),
        DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_CREATE, i915_gem_context_create_ioctl, DRM_UNLOCKED),
        DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_DESTROY, i915_gem_context_destroy_ioctl, DRM_UNLOCKED),
+       DRM_IOCTL_DEF_DRV(I915_REG_READ, i915_reg_read_ioctl, DRM_UNLOCKED),
 };
 
 int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls);
index a24ffbe97c01cffd8a8fa5093a5ba15ff04db30b..a7837e556945b96a85c9e1aecefba1663ae35318 100644 (file)
@@ -470,6 +470,9 @@ static int i915_drm_freeze(struct drm_device *dev)
                                "GEM idle failed, resume might fail\n");
                        return error;
                }
+
+               intel_modeset_disable(dev);
+
                drm_irq_uninstall(dev);
        }
 
@@ -543,13 +546,9 @@ static int i915_drm_thaw(struct drm_device *dev)
                mutex_unlock(&dev->struct_mutex);
 
                intel_modeset_init_hw(dev);
+               intel_modeset_setup_hw_state(dev);
                drm_mode_config_reset(dev);
                drm_irq_install(dev);
-
-               /* Resume the modeset for every activated CRTC */
-               mutex_lock(&dev->mode_config.mutex);
-               drm_helper_resume_force_mode(dev);
-               mutex_unlock(&dev->mode_config.mutex);
        }
 
        intel_opregion_init(dev);
@@ -1060,7 +1059,7 @@ 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 >= 0x180000)
+       if (reg >= VLV_DISPLAY_BASE)
                return false;
 
        if (reg >= RENDER_RING_BASE &&
@@ -1174,9 +1173,59 @@ void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val) { \
        if (unlikely(__fifo_ret)) { \
                gen6_gt_check_fifodbg(dev_priv); \
        } \
+       if (IS_HASWELL(dev_priv->dev) && (I915_READ_NOTRACE(GEN7_ERR_INT) & ERR_INT_MMIO_UNCLAIMED)) { \
+               DRM_ERROR("Unclaimed write to %x\n", reg); \
+               writel(ERR_INT_MMIO_UNCLAIMED, dev_priv->regs + GEN7_ERR_INT);  \
+       } \
 }
 __i915_write(8, b)
 __i915_write(16, w)
 __i915_write(32, l)
 __i915_write(64, q)
 #undef __i915_write
+
+static const struct register_whitelist {
+       uint64_t offset;
+       uint32_t size;
+       uint32_t gen_bitmask; /* support gens, 0x10 for 4, 0x30 for 4 and 5, etc. */
+} whitelist[] = {
+       { RING_TIMESTAMP(RENDER_RING_BASE), 8, 0xF0 },
+};
+
+int i915_reg_read_ioctl(struct drm_device *dev,
+                       void *data, struct drm_file *file)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_i915_reg_read *reg = data;
+       struct register_whitelist const *entry = whitelist;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(whitelist); i++, entry++) {
+               if (entry->offset == reg->offset &&
+                   (1 << INTEL_INFO(dev)->gen & entry->gen_bitmask))
+                       break;
+       }
+
+       if (i == ARRAY_SIZE(whitelist))
+               return -EINVAL;
+
+       switch (entry->size) {
+       case 8:
+               reg->val = I915_READ64(reg->offset);
+               break;
+       case 4:
+               reg->val = I915_READ(reg->offset);
+               break;
+       case 2:
+               reg->val = I915_READ16(reg->offset);
+               break;
+       case 1:
+               reg->val = I915_READ8(reg->offset);
+               break;
+       default:
+               WARN_ON(1);
+               return -EINVAL;
+       }
+
+       return 0;
+}
index 627fe35781b4b92b1272a8bec3282dae2a3a8bc9..4f2831aa5fedd517a4f49189bc19b4803e69b71a 100644 (file)
@@ -109,6 +109,7 @@ struct intel_pch_pll {
 
 #define WATCH_COHERENCY        0
 #define WATCH_LISTS    0
+#define WATCH_GTT      0
 
 #define I915_GEM_PHYS_CURSOR_0 1
 #define I915_GEM_PHYS_CURSOR_1 2
@@ -195,9 +196,10 @@ struct drm_i915_error_state {
        u32 cpu_ring_head[I915_NUM_RINGS];
        u32 cpu_ring_tail[I915_NUM_RINGS];
        u32 error; /* gen6+ */
+       u32 err_int; /* gen7 */
        u32 instpm[I915_NUM_RINGS];
        u32 instps[I915_NUM_RINGS];
-       u32 instdone1;
+       u32 extra_instdone[I915_NUM_INSTDONE_REG];
        u32 seqno[I915_NUM_RINGS];
        u64 bbaddr;
        u32 fault_reg[I915_NUM_RINGS];
@@ -221,7 +223,7 @@ struct drm_i915_error_state {
        struct drm_i915_error_buffer {
                u32 size;
                u32 name;
-               u32 seqno;
+               u32 rseqno, wseqno;
                u32 gtt_offset;
                u32 read_domains;
                u32 write_domain;
@@ -239,7 +241,6 @@ struct drm_i915_error_state {
 };
 
 struct drm_i915_display_funcs {
-       void (*dpms)(struct drm_crtc *crtc, int mode);
        bool (*fbc_enabled)(struct drm_device *dev);
        void (*enable_fbc)(struct drm_crtc *crtc, unsigned long interval);
        void (*disable_fbc)(struct drm_device *dev);
@@ -248,7 +249,6 @@ struct drm_i915_display_funcs {
        void (*update_wm)(struct drm_device *dev);
        void (*update_sprite_wm)(struct drm_device *dev, int pipe,
                                 uint32_t sprite_width, int pixel_size);
-       void (*sanitize_pm)(struct drm_device *dev);
        void (*update_linetime_wm)(struct drm_device *dev, int pipe,
                                 struct drm_display_mode *mode);
        int (*crtc_mode_set)(struct drm_crtc *crtc,
@@ -256,6 +256,8 @@ struct drm_i915_display_funcs {
                             struct drm_display_mode *adjusted_mode,
                             int x, int y,
                             struct drm_framebuffer *old_fb);
+       void (*crtc_enable)(struct drm_crtc *crtc);
+       void (*crtc_disable)(struct drm_crtc *crtc);
        void (*off)(struct drm_crtc *crtc);
        void (*write_eld)(struct drm_connector *connector,
                          struct drm_crtc *crtc);
@@ -279,6 +281,32 @@ struct drm_i915_gt_funcs {
        void (*force_wake_put)(struct drm_i915_private *dev_priv);
 };
 
+#define DEV_INFO_FLAGS \
+       DEV_INFO_FLAG(is_mobile) DEV_INFO_SEP \
+       DEV_INFO_FLAG(is_i85x) DEV_INFO_SEP \
+       DEV_INFO_FLAG(is_i915g) DEV_INFO_SEP \
+       DEV_INFO_FLAG(is_i945gm) DEV_INFO_SEP \
+       DEV_INFO_FLAG(is_g33) DEV_INFO_SEP \
+       DEV_INFO_FLAG(need_gfx_hws) DEV_INFO_SEP \
+       DEV_INFO_FLAG(is_g4x) DEV_INFO_SEP \
+       DEV_INFO_FLAG(is_pineview) DEV_INFO_SEP \
+       DEV_INFO_FLAG(is_broadwater) DEV_INFO_SEP \
+       DEV_INFO_FLAG(is_crestline) DEV_INFO_SEP \
+       DEV_INFO_FLAG(is_ivybridge) DEV_INFO_SEP \
+       DEV_INFO_FLAG(is_valleyview) DEV_INFO_SEP \
+       DEV_INFO_FLAG(is_haswell) DEV_INFO_SEP \
+       DEV_INFO_FLAG(has_force_wake) DEV_INFO_SEP \
+       DEV_INFO_FLAG(has_fbc) DEV_INFO_SEP \
+       DEV_INFO_FLAG(has_pipe_cxsr) DEV_INFO_SEP \
+       DEV_INFO_FLAG(has_hotplug) DEV_INFO_SEP \
+       DEV_INFO_FLAG(cursor_needs_physical) DEV_INFO_SEP \
+       DEV_INFO_FLAG(has_overlay) DEV_INFO_SEP \
+       DEV_INFO_FLAG(overlay_needs_physical) DEV_INFO_SEP \
+       DEV_INFO_FLAG(supports_tv) DEV_INFO_SEP \
+       DEV_INFO_FLAG(has_bsd_ring) DEV_INFO_SEP \
+       DEV_INFO_FLAG(has_blt_ring) DEV_INFO_SEP \
+       DEV_INFO_FLAG(has_llc)
+
 struct intel_device_info {
        u8 gen;
        u8 is_mobile:1;
@@ -402,12 +430,6 @@ typedef struct drm_i915_private {
 
        struct resource mch_res;
 
-       unsigned int cpp;
-       int back_offset;
-       int front_offset;
-       int current_page;
-       int page_flipping;
-
        atomic_t irq_received;
 
        /* protects the irq masks */
@@ -425,7 +447,6 @@ typedef struct drm_i915_private {
        u32 hotplug_supported_mask;
        struct work_struct hotplug_work;
 
-       unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds;
        int num_pipe;
        int num_pch_pll;
 
@@ -434,8 +455,7 @@ typedef struct drm_i915_private {
        struct timer_list hangcheck_timer;
        int hangcheck_count;
        uint32_t last_acthd[I915_NUM_RINGS];
-       uint32_t last_instdone;
-       uint32_t last_instdone1;
+       uint32_t prev_instdone[I915_NUM_INSTDONE_REG];
 
        unsigned int stop_rings;
 
@@ -666,7 +686,13 @@ typedef struct drm_i915_private {
                struct drm_mm gtt_space;
                /** List of all objects in gtt_space. Used to restore gtt
                 * mappings on resume */
-               struct list_head gtt_list;
+               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;
@@ -695,17 +721,6 @@ typedef struct drm_i915_private {
                 */
                struct list_head active_list;
 
-               /**
-                * List of objects which are not in the ringbuffer but which
-                * still have a write_domain which needs to be flushed before
-                * unbinding.
-                *
-                * last_rendering_seqno is 0 while an object is in this list.
-                *
-                * A reference is held on the buffer while on this list.
-                */
-               struct list_head flushing_list;
-
                /**
                 * LRU list of objects which are not in the ringbuffer and
                 * are ready to unbind, but are still in the GTT.
@@ -775,6 +790,12 @@ typedef struct drm_i915_private {
        struct {
                unsigned allow_batchbuffer : 1;
                u32 __iomem *gfx_hws_cpu_addr;
+
+               unsigned int cpp;
+               int back_offset;
+               int front_offset;
+               int current_page;
+               int page_flipping;
        } dri1;
 
        /* Kernel Modesetting */
@@ -796,9 +817,6 @@ typedef struct drm_i915_private {
        bool lvds_downclock_avail;
        /* indicates the reduced downclock for LVDS*/
        int lvds_downclock;
-       struct work_struct idle_work;
-       struct timer_list idle_timer;
-       bool busy;
        u16 orig_clock;
        int child_dev_num;
        struct child_device_config *child_dev;
@@ -807,26 +825,41 @@ typedef struct drm_i915_private {
 
        bool mchbar_need_disable;
 
-       struct work_struct rps_work;
-       spinlock_t rps_lock;
-       u32 pm_iir;
-
-       u8 cur_delay;
-       u8 min_delay;
-       u8 max_delay;
-       u8 fmax;
-       u8 fstart;
-
-       u64 last_count1;
-       unsigned long last_time1;
-       unsigned long chipset_power;
-       u64 last_count2;
-       struct timespec last_time2;
-       unsigned long gfx_power;
-       int c_m;
-       int r_t;
-       u8 corr;
-       spinlock_t *mchdev_lock;
+       /* gen6+ rps state */
+       struct {
+               struct work_struct work;
+               u32 pm_iir;
+               /* lock - irqsave spinlock that protectects the work_struct and
+                * pm_iir. */
+               spinlock_t lock;
+
+               /* The below variables an all the rps hw state are protected by
+                * dev->struct mutext. */
+               u8 cur_delay;
+               u8 min_delay;
+               u8 max_delay;
+       } rps;
+
+       /* ilk-only ips/rps state. Everything in here is protected by the global
+        * mchdev_lock in intel_pm.c */
+       struct {
+               u8 cur_delay;
+               u8 min_delay;
+               u8 max_delay;
+               u8 fmax;
+               u8 fstart;
+
+               u64 last_count1;
+               unsigned long last_time1;
+               unsigned long chipset_power;
+               u64 last_count2;
+               struct timespec last_time2;
+               unsigned long gfx_power;
+               u8 corr;
+
+               int c_m;
+               int r_t;
+       } ips;
 
        enum no_fbc_reason no_fbc_reason;
 
@@ -861,30 +894,48 @@ enum hdmi_force_audio {
 };
 
 enum i915_cache_level {
-       I915_CACHE_NONE,
+       I915_CACHE_NONE = 0,
        I915_CACHE_LLC,
-       I915_CACHE_LLC_MLC, /* gen6+ */
+       I915_CACHE_LLC_MLC, /* gen6+, in docs at least! */
+};
+
+struct drm_i915_gem_object_ops {
+       /* Interface between the GEM object and its backing storage.
+        * get_pages() is called once prior to the use of the associated set
+        * of pages before to binding them into the GTT, and put_pages() is
+        * called after we no longer need them. As we expect there to be
+        * associated cost with migrating pages between the backing storage
+        * and making them available for the GPU (e.g. clflush), we may hold
+        * onto the pages after they are no longer referenced by the GPU
+        * in case they may be used again shortly (for example migrating the
+        * pages to a different memory domain within the GTT). put_pages()
+        * will therefore most likely be called when the object itself is
+        * being released or under memory pressure (where we attempt to
+        * reap pages for the shrinker).
+        */
+       int (*get_pages)(struct drm_i915_gem_object *);
+       void (*put_pages)(struct drm_i915_gem_object *);
 };
 
 struct drm_i915_gem_object {
        struct drm_gem_object base;
 
+       const struct drm_i915_gem_object_ops *ops;
+
        /** Current space allocated to this object in the GTT, if any. */
        struct drm_mm_node *gtt_space;
        struct list_head gtt_list;
 
-       /** This object's place on the active/flushing/inactive lists */
+       /** This object's place on the active/inactive lists */
        struct list_head ring_list;
        struct list_head mm_list;
-       /** This object's place on GPU write list */
-       struct list_head gpu_write_list;
        /** This object's place in the batchbuffer or on the eviction list */
        struct list_head exec_list;
 
        /**
-        * This is set if the object is on the active or flushing lists
-        * (has pending rendering), and is not set if it's on inactive (ready
-        * to be unbound).
+        * This is set if the object is on the active lists (has pending
+        * rendering and so a non-zero seqno), and is not set if it i s on
+        * inactive (ready to be unbound) list.
         */
        unsigned int active:1;
 
@@ -894,12 +945,6 @@ struct drm_i915_gem_object {
         */
        unsigned int dirty:1;
 
-       /**
-        * This is set if the object has been written to since the last
-        * GPU flush.
-        */
-       unsigned int pending_gpu_write:1;
-
        /**
         * Fence register bits (if any) for this object.  Will be set
         * as needed when mapped into the GTT.
@@ -961,17 +1006,12 @@ struct drm_i915_gem_object {
 
        unsigned int has_aliasing_ppgtt_mapping:1;
        unsigned int has_global_gtt_mapping:1;
+       unsigned int has_dma_mapping:1;
 
-       struct page **pages;
-
-       /**
-        * DMAR support
-        */
-       struct scatterlist *sg_list;
-       int num_sg;
+       struct sg_table *pages;
+       int pages_pin_count;
 
        /* prime dma-buf support */
-       struct sg_table *sg_table;
        void *dma_buf_vmapping;
        int vmapping_count;
 
@@ -992,7 +1032,8 @@ struct drm_i915_gem_object {
        struct intel_ring_buffer *ring;
 
        /** Breadcrumb of last rendering to the buffer. */
-       uint32_t last_rendering_seqno;
+       uint32_t last_read_seqno;
+       uint32_t last_write_seqno;
        /** Breadcrumb of last fenced GPU access to the buffer. */
        uint32_t last_fenced_seqno;
 
@@ -1135,6 +1176,10 @@ struct drm_i915_file_private {
 
 #define HAS_FORCE_WAKE(dev) (INTEL_INFO(dev)->has_force_wake)
 
+#define HAS_L3_GPU_CACHE(dev) (IS_IVYBRIDGE(dev) || IS_HASWELL(dev))
+
+#define GT_FREQUENCY_MULTIPLIER 50
+
 #include "i915_trace.h"
 
 /**
@@ -1256,6 +1301,10 @@ int i915_gem_unpin_ioctl(struct drm_device *dev, void *data,
                         struct drm_file *file_priv);
 int i915_gem_busy_ioctl(struct drm_device *dev, void *data,
                        struct drm_file *file_priv);
+int i915_gem_get_caching_ioctl(struct drm_device *dev, void *data,
+                              struct drm_file *file);
+int i915_gem_set_caching_ioctl(struct drm_device *dev, void *data,
+                              struct drm_file *file);
 int i915_gem_throttle_ioctl(struct drm_device *dev, void *data,
                            struct drm_file *file_priv);
 int i915_gem_madvise_ioctl(struct drm_device *dev, void *data,
@@ -1274,24 +1323,42 @@ int i915_gem_wait_ioctl(struct drm_device *dev, void *data,
                        struct drm_file *file_priv);
 void i915_gem_load(struct drm_device *dev);
 int i915_gem_init_object(struct drm_gem_object *obj);
-int __must_check i915_gem_flush_ring(struct intel_ring_buffer *ring,
-                                    uint32_t invalidate_domains,
-                                    uint32_t flush_domains);
+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 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);
 void i915_gem_release_mmap(struct drm_i915_gem_object *obj);
 void i915_gem_lastclose(struct drm_device *dev);
 
-int i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj,
-                                 gfp_t gfpmask);
+int __must_check i915_gem_object_get_pages(struct drm_i915_gem_object *obj);
+static inline struct page *i915_gem_object_get_page(struct drm_i915_gem_object *obj, int n)
+{
+       struct scatterlist *sg = obj->pages->sgl;
+       while (n >= SG_MAX_SINGLE_ALLOC) {
+               sg = sg_chain_ptr(sg + SG_MAX_SINGLE_ALLOC - 1);
+               n -= SG_MAX_SINGLE_ALLOC - 1;
+       }
+       return sg_page(sg+n);
+}
+static inline void i915_gem_object_pin_pages(struct drm_i915_gem_object *obj)
+{
+       BUG_ON(obj->pages == NULL);
+       obj->pages_pin_count++;
+}
+static inline void i915_gem_object_unpin_pages(struct drm_i915_gem_object *obj)
+{
+       BUG_ON(obj->pages_pin_count == 0);
+       obj->pages_pin_count--;
+}
+
 int __must_check i915_mutex_lock_interruptible(struct drm_device *dev);
-int __must_check i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj);
 int i915_gem_object_sync(struct drm_i915_gem_object *obj,
                         struct intel_ring_buffer *to);
 void i915_gem_object_move_to_active(struct drm_i915_gem_object *obj,
@@ -1358,9 +1425,9 @@ void i915_gem_init_ppgtt(struct drm_device *dev);
 void i915_gem_cleanup_ringbuffer(struct drm_device *dev);
 int __must_check i915_gpu_idle(struct drm_device *dev);
 int __must_check i915_gem_idle(struct drm_device *dev);
-int __must_check i915_add_request(struct intel_ring_buffer *ring,
-                                 struct drm_file *file,
-                                 struct drm_i915_gem_request *request);
+int i915_add_request(struct intel_ring_buffer *ring,
+                    struct drm_file *file,
+                    struct drm_i915_gem_request *request);
 int __must_check i915_wait_seqno(struct intel_ring_buffer *ring,
                                 uint32_t seqno);
 int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
@@ -1429,8 +1496,11 @@ void i915_gem_init_global_gtt(struct drm_device *dev,
 
 /* i915_gem_evict.c */
 int __must_check i915_gem_evict_something(struct drm_device *dev, int min_size,
-                                         unsigned alignment, bool mappable);
-int i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only);
+                                         unsigned alignment,
+                                         unsigned cache_level,
+                                         bool mappable,
+                                         bool nonblock);
+int i915_gem_evict_everything(struct drm_device *dev);
 
 /* i915_gem_stolen.c */
 int i915_gem_init_stolen(struct drm_device *dev);
@@ -1519,6 +1589,7 @@ extern void intel_modeset_init(struct drm_device *dev);
 extern void intel_modeset_gem_init(struct drm_device *dev);
 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);
 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);
@@ -1529,6 +1600,8 @@ extern int intel_trans_dp_port_sel(struct drm_crtc *crtc);
 extern int intel_enable_rc6(const struct drm_device *dev);
 
 extern bool i915_semaphore_is_enabled(struct drm_device *dev);
+int i915_reg_read_ioctl(struct drm_device *dev, void *data,
+                       struct drm_file *file);
 
 /* overlay */
 #ifdef CONFIG_DEBUG_FS
index 489e2b162b2736b4fc3d43e171ebddb25c66b12e..365a7dc8a4a896bb3321715f32f203e51f90aa96 100644 (file)
 #include <linux/pci.h>
 #include <linux/dma-buf.h>
 
-static __must_check int i915_gem_object_flush_gpu_write_domain(struct drm_i915_gem_object *obj);
 static void i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *obj);
 static void i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj);
 static __must_check int i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
                                                    unsigned alignment,
-                                                   bool map_and_fenceable);
+                                                   bool map_and_fenceable,
+                                                   bool nonblocking);
 static int i915_gem_phys_pwrite(struct drm_device *dev,
                                struct drm_i915_gem_object *obj,
                                struct drm_i915_gem_pwrite *args,
@@ -56,6 +56,8 @@ static void i915_gem_object_update_fence(struct drm_i915_gem_object *obj,
 
 static int i915_gem_inactive_shrink(struct shrinker *shrinker,
                                    struct shrink_control *sc);
+static long i915_gem_purge(struct drm_i915_private *dev_priv, long target);
+static void i915_gem_shrink_all(struct drm_i915_private *dev_priv);
 static void i915_gem_object_truncate(struct drm_i915_gem_object *obj);
 
 static inline void i915_gem_object_fence_lost(struct drm_i915_gem_object *obj)
@@ -141,7 +143,7 @@ int i915_mutex_lock_interruptible(struct drm_device *dev)
 static inline bool
 i915_gem_object_is_inactive(struct drm_i915_gem_object *obj)
 {
-       return !obj->active;
+       return obj->gtt_space && !obj->active;
 }
 
 int
@@ -180,7 +182,7 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
 
        pinned = 0;
        mutex_lock(&dev->struct_mutex);
-       list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list)
+       list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list)
                if (obj->pin_count)
                        pinned += obj->gtt_space->size;
        mutex_unlock(&dev->struct_mutex);
@@ -341,7 +343,7 @@ shmem_pread_fast(struct page *page, int shmem_page_offset, int page_length,
                                      page_length);
        kunmap_atomic(vaddr);
 
-       return ret;
+       return ret ? -EFAULT : 0;
 }
 
 static void
@@ -392,7 +394,7 @@ shmem_pread_slow(struct page *page, int shmem_page_offset, int page_length,
                                     page_length);
        kunmap(page);
 
-       return ret;
+       return ret ? - EFAULT : 0;
 }
 
 static int
@@ -401,7 +403,6 @@ i915_gem_shmem_pread(struct drm_device *dev,
                     struct drm_i915_gem_pread *args,
                     struct drm_file *file)
 {
-       struct address_space *mapping = obj->base.filp->f_path.dentry->d_inode->i_mapping;
        char __user *user_data;
        ssize_t remain;
        loff_t offset;
@@ -410,7 +411,8 @@ i915_gem_shmem_pread(struct drm_device *dev,
        int hit_slowpath = 0;
        int prefaulted = 0;
        int needs_clflush = 0;
-       int release_page;
+       struct scatterlist *sg;
+       int i;
 
        user_data = (char __user *) (uintptr_t) args->data_ptr;
        remain = args->size;
@@ -424,16 +426,30 @@ i915_gem_shmem_pread(struct drm_device *dev,
                 * anyway again before the next pread happens. */
                if (obj->cache_level == I915_CACHE_NONE)
                        needs_clflush = 1;
-               ret = i915_gem_object_set_to_gtt_domain(obj, false);
-               if (ret)
-                       return ret;
+               if (obj->gtt_space) {
+                       ret = i915_gem_object_set_to_gtt_domain(obj, false);
+                       if (ret)
+                               return ret;
+               }
        }
 
+       ret = i915_gem_object_get_pages(obj);
+       if (ret)
+               return ret;
+
+       i915_gem_object_pin_pages(obj);
+
        offset = args->offset;
 
-       while (remain > 0) {
+       for_each_sg(obj->pages->sgl, sg, obj->pages->nents, i) {
                struct page *page;
 
+               if (i < offset >> PAGE_SHIFT)
+                       continue;
+
+               if (remain <= 0)
+                       break;
+
                /* Operation in this page
                 *
                 * shmem_page_offset = offset within page in shmem file
@@ -444,18 +460,7 @@ i915_gem_shmem_pread(struct drm_device *dev,
                if ((shmem_page_offset + page_length) > PAGE_SIZE)
                        page_length = PAGE_SIZE - shmem_page_offset;
 
-               if (obj->pages) {
-                       page = obj->pages[offset >> PAGE_SHIFT];
-                       release_page = 0;
-               } else {
-                       page = shmem_read_mapping_page(mapping, offset >> PAGE_SHIFT);
-                       if (IS_ERR(page)) {
-                               ret = PTR_ERR(page);
-                               goto out;
-                       }
-                       release_page = 1;
-               }
-
+               page = sg_page(sg);
                page_do_bit17_swizzling = obj_do_bit17_swizzling &&
                        (page_to_phys(page) & (1 << 17)) != 0;
 
@@ -466,7 +471,6 @@ i915_gem_shmem_pread(struct drm_device *dev,
                        goto next_page;
 
                hit_slowpath = 1;
-               page_cache_get(page);
                mutex_unlock(&dev->struct_mutex);
 
                if (!prefaulted) {
@@ -484,16 +488,12 @@ i915_gem_shmem_pread(struct drm_device *dev,
                                       needs_clflush);
 
                mutex_lock(&dev->struct_mutex);
-               page_cache_release(page);
+
 next_page:
                mark_page_accessed(page);
-               if (release_page)
-                       page_cache_release(page);
 
-               if (ret) {
-                       ret = -EFAULT;
+               if (ret)
                        goto out;
-               }
 
                remain -= page_length;
                user_data += page_length;
@@ -501,6 +501,8 @@ 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)
@@ -606,7 +608,7 @@ i915_gem_gtt_pwrite_fast(struct drm_device *dev,
        char __user *user_data;
        int page_offset, page_length, ret;
 
-       ret = i915_gem_object_pin(obj, 0, true);
+       ret = i915_gem_object_pin(obj, 0, true, true);
        if (ret)
                goto out;
 
@@ -686,7 +688,7 @@ shmem_pwrite_fast(struct page *page, int shmem_page_offset, int page_length,
                                       page_length);
        kunmap_atomic(vaddr);
 
-       return ret;
+       return ret ? -EFAULT : 0;
 }
 
 /* Only difference to the fast-path function is that this can handle bit17
@@ -720,7 +722,7 @@ shmem_pwrite_slow(struct page *page, int shmem_page_offset, int page_length,
                                             page_do_bit17_swizzling);
        kunmap(page);
 
-       return ret;
+       return ret ? -EFAULT : 0;
 }
 
 static int
@@ -729,7 +731,6 @@ i915_gem_shmem_pwrite(struct drm_device *dev,
                      struct drm_i915_gem_pwrite *args,
                      struct drm_file *file)
 {
-       struct address_space *mapping = obj->base.filp->f_path.dentry->d_inode->i_mapping;
        ssize_t remain;
        loff_t offset;
        char __user *user_data;
@@ -738,7 +739,8 @@ i915_gem_shmem_pwrite(struct drm_device *dev,
        int hit_slowpath = 0;
        int needs_clflush_after = 0;
        int needs_clflush_before = 0;
-       int release_page;
+       int i;
+       struct scatterlist *sg;
 
        user_data = (char __user *) (uintptr_t) args->data_ptr;
        remain = args->size;
@@ -752,9 +754,11 @@ i915_gem_shmem_pwrite(struct drm_device *dev,
                 * right away and we therefore have to clflush anyway. */
                if (obj->cache_level == I915_CACHE_NONE)
                        needs_clflush_after = 1;
-               ret = i915_gem_object_set_to_gtt_domain(obj, true);
-               if (ret)
-                       return ret;
+               if (obj->gtt_space) {
+                       ret = i915_gem_object_set_to_gtt_domain(obj, true);
+                       if (ret)
+                               return ret;
+               }
        }
        /* Same trick applies for invalidate partially written cachelines before
         * writing.  */
@@ -762,13 +766,25 @@ i915_gem_shmem_pwrite(struct drm_device *dev,
            && obj->cache_level == I915_CACHE_NONE)
                needs_clflush_before = 1;
 
+       ret = i915_gem_object_get_pages(obj);
+       if (ret)
+               return ret;
+
+       i915_gem_object_pin_pages(obj);
+
        offset = args->offset;
        obj->dirty = 1;
 
-       while (remain > 0) {
+       for_each_sg(obj->pages->sgl, sg, obj->pages->nents, i) {
                struct page *page;
                int partial_cacheline_write;
 
+               if (i < offset >> PAGE_SHIFT)
+                       continue;
+
+               if (remain <= 0)
+                       break;
+
                /* Operation in this page
                 *
                 * shmem_page_offset = offset within page in shmem file
@@ -787,18 +803,7 @@ i915_gem_shmem_pwrite(struct drm_device *dev,
                        ((shmem_page_offset | page_length)
                                & (boot_cpu_data.x86_clflush_size - 1));
 
-               if (obj->pages) {
-                       page = obj->pages[offset >> PAGE_SHIFT];
-                       release_page = 0;
-               } else {
-                       page = shmem_read_mapping_page(mapping, offset >> PAGE_SHIFT);
-                       if (IS_ERR(page)) {
-                               ret = PTR_ERR(page);
-                               goto out;
-                       }
-                       release_page = 1;
-               }
-
+               page = sg_page(sg);
                page_do_bit17_swizzling = obj_do_bit17_swizzling &&
                        (page_to_phys(page) & (1 << 17)) != 0;
 
@@ -810,26 +815,20 @@ i915_gem_shmem_pwrite(struct drm_device *dev,
                        goto next_page;
 
                hit_slowpath = 1;
-               page_cache_get(page);
                mutex_unlock(&dev->struct_mutex);
-
                ret = shmem_pwrite_slow(page, shmem_page_offset, page_length,
                                        user_data, page_do_bit17_swizzling,
                                        partial_cacheline_write,
                                        needs_clflush_after);
 
                mutex_lock(&dev->struct_mutex);
-               page_cache_release(page);
+
 next_page:
                set_page_dirty(page);
                mark_page_accessed(page);
-               if (release_page)
-                       page_cache_release(page);
 
-               if (ret) {
-                       ret = -EFAULT;
+               if (ret)
                        goto out;
-               }
 
                remain -= page_length;
                user_data += page_length;
@@ -837,6 +836,8 @@ 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)
@@ -920,10 +921,8 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
                goto out;
        }
 
-       if (obj->gtt_space &&
-           obj->cache_level == I915_CACHE_NONE &&
+       if (obj->cache_level == I915_CACHE_NONE &&
            obj->tiling_mode == I915_TILING_NONE &&
-           obj->map_and_fenceable &&
            obj->base.write_domain != I915_GEM_DOMAIN_CPU) {
                ret = i915_gem_gtt_pwrite_fast(dev, obj, args, file);
                /* Note that the gtt paths might fail with non-page-backed user
@@ -931,7 +930,7 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
                 * textures). Fallback to the shmem path in that case. */
        }
 
-       if (ret == -EFAULT)
+       if (ret == -EFAULT || ret == -ENOSPC)
                ret = i915_gem_shmem_pwrite(dev, obj, args, file);
 
 out:
@@ -941,6 +940,240 @@ unlock:
        return ret;
 }
 
+int
+i915_gem_check_wedge(struct drm_i915_private *dev_priv,
+                    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);
+
+               /* 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)
+                       return -EIO;
+
+               return -EAGAIN;
+       }
+
+       return 0;
+}
+
+/*
+ * Compare seqno against outstanding lazy request. Emit a request if they are
+ * equal.
+ */
+static int
+i915_gem_check_olr(struct intel_ring_buffer *ring, u32 seqno)
+{
+       int ret;
+
+       BUG_ON(!mutex_is_locked(&ring->dev->struct_mutex));
+
+       ret = 0;
+       if (seqno == ring->outstanding_lazy_request)
+               ret = i915_add_request(ring, NULL, NULL);
+
+       return ret;
+}
+
+/**
+ * __wait_seqno - wait until execution of seqno has finished
+ * @ring: the ring expected to report seqno
+ * @seqno: duh!
+ * @interruptible: do an interruptible wait (normally yes)
+ * @timeout: in - how long to wait (NULL forever); out - how much time remaining
+ *
+ * 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,
+                       bool interruptible, struct timespec *timeout)
+{
+       drm_i915_private_t *dev_priv = ring->dev->dev_private;
+       struct timespec before, now, wait_time={1,0};
+       unsigned long timeout_jiffies;
+       long end;
+       bool wait_forever = true;
+       int ret;
+
+       if (i915_seqno_passed(ring->get_seqno(ring, true), seqno))
+               return 0;
+
+       trace_i915_gem_request_wait_begin(ring, seqno);
+
+       if (timeout != NULL) {
+               wait_time = *timeout;
+               wait_forever = false;
+       }
+
+       timeout_jiffies = timespec_to_jiffies(&wait_time);
+
+       if (WARN_ON(!ring->irq_get(ring)))
+               return -ENODEV;
+
+       /* Record current time in case interrupted by signal, or wedged * */
+       getrawmonotonic(&before);
+
+#define EXIT_COND \
+       (i915_seqno_passed(ring->get_seqno(ring, false), seqno) || \
+       atomic_read(&dev_priv->mm.wedged))
+       do {
+               if (interruptible)
+                       end = wait_event_interruptible_timeout(ring->irq_queue,
+                                                              EXIT_COND,
+                                                              timeout_jiffies);
+               else
+                       end = wait_event_timeout(ring->irq_queue, EXIT_COND,
+                                                timeout_jiffies);
+
+               ret = i915_gem_check_wedge(dev_priv, interruptible);
+               if (ret)
+                       end = ret;
+       } while (end == 0 && wait_forever);
+
+       getrawmonotonic(&now);
+
+       ring->irq_put(ring);
+       trace_i915_gem_request_wait_end(ring, seqno);
+#undef EXIT_COND
+
+       if (timeout) {
+               struct timespec sleep_time = timespec_sub(now, before);
+               *timeout = timespec_sub(*timeout, sleep_time);
+       }
+
+       switch (end) {
+       case -EIO:
+       case -EAGAIN: /* Wedged */
+       case -ERESTARTSYS: /* Signal */
+               return (int)end;
+       case 0: /* Timeout */
+               if (timeout)
+                       set_normalized_timespec(timeout, 0, 0);
+               return -ETIME;
+       default: /* Completed */
+               WARN_ON(end < 0); /* We're not aware of other errors */
+               return 0;
+       }
+}
+
+/**
+ * Waits for a sequence number to be signaled, and cleans up the
+ * request and object lists appropriately for that event.
+ */
+int
+i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno)
+{
+       struct drm_device *dev = ring->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       bool interruptible = dev_priv->mm.interruptible;
+       int ret;
+
+       BUG_ON(!mutex_is_locked(&dev->struct_mutex));
+       BUG_ON(seqno == 0);
+
+       ret = i915_gem_check_wedge(dev_priv, interruptible);
+       if (ret)
+               return ret;
+
+       ret = i915_gem_check_olr(ring, seqno);
+       if (ret)
+               return ret;
+
+       return __wait_seqno(ring, seqno, interruptible, NULL);
+}
+
+/**
+ * Ensures that all rendering to the object has completed and the object is
+ * safe to unbind from the GTT or access from the CPU.
+ */
+static __must_check int
+i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj,
+                              bool readonly)
+{
+       struct intel_ring_buffer *ring = obj->ring;
+       u32 seqno;
+       int ret;
+
+       seqno = readonly ? obj->last_write_seqno : obj->last_read_seqno;
+       if (seqno == 0)
+               return 0;
+
+       ret = i915_wait_seqno(ring, seqno);
+       if (ret)
+               return ret;
+
+       i915_gem_retire_requests_ring(ring);
+
+       /* Manually manage the write flush as we may have not yet
+        * retired the buffer.
+        */
+       if (obj->last_write_seqno &&
+           i915_seqno_passed(seqno, obj->last_write_seqno)) {
+               obj->last_write_seqno = 0;
+               obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS;
+       }
+
+       return 0;
+}
+
+/* A nonblocking variant of the above wait. This is a highly dangerous routine
+ * as the object state may change during this call.
+ */
+static __must_check int
+i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj,
+                                           bool readonly)
+{
+       struct drm_device *dev = obj->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_ring_buffer *ring = obj->ring;
+       u32 seqno;
+       int ret;
+
+       BUG_ON(!mutex_is_locked(&dev->struct_mutex));
+       BUG_ON(!dev_priv->mm.interruptible);
+
+       seqno = readonly ? obj->last_write_seqno : obj->last_read_seqno;
+       if (seqno == 0)
+               return 0;
+
+       ret = i915_gem_check_wedge(dev_priv, true);
+       if (ret)
+               return ret;
+
+       ret = i915_gem_check_olr(ring, seqno);
+       if (ret)
+               return ret;
+
+       mutex_unlock(&dev->struct_mutex);
+       ret = __wait_seqno(ring, seqno, true, NULL);
+       mutex_lock(&dev->struct_mutex);
+
+       i915_gem_retire_requests_ring(ring);
+
+       /* Manually manage the write flush as we may have not yet
+        * retired the buffer.
+        */
+       if (obj->last_write_seqno &&
+           i915_seqno_passed(seqno, obj->last_write_seqno)) {
+               obj->last_write_seqno = 0;
+               obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS;
+       }
+
+       return ret;
+}
+
 /**
  * Called when user space prepares to use an object with the CPU, either
  * through the mmap ioctl's mapping or a GTT mapping.
@@ -978,6 +1211,14 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
                goto unlock;
        }
 
+       /* Try to flush the object off the GPU without holding the lock.
+        * We will repeat the flush holding the lock in the normal manner
+        * to catch cases where we are gazumped.
+        */
+       ret = i915_gem_object_wait_rendering__nonblocking(obj, !write_domain);
+       if (ret)
+               goto unref;
+
        if (read_domains & I915_GEM_DOMAIN_GTT) {
                ret = i915_gem_object_set_to_gtt_domain(obj, write_domain != 0);
 
@@ -991,6 +1232,7 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
                ret = i915_gem_object_set_to_cpu_domain(obj, write_domain != 0);
        }
 
+unref:
        drm_gem_object_unreference(&obj->base);
 unlock:
        mutex_unlock(&dev->struct_mutex);
@@ -1110,7 +1352,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
                        goto unlock;
        }
        if (!obj->gtt_space) {
-               ret = i915_gem_object_bind_to_gtt(obj, 0, true);
+               ret = i915_gem_object_bind_to_gtt(obj, 0, true, false);
                if (ret)
                        goto unlock;
 
@@ -1271,8 +1513,44 @@ i915_gem_get_unfenced_gtt_alignment(struct drm_device *dev,
        return i915_gem_get_gtt_size(dev, size, tiling_mode);
 }
 
-int
-i915_gem_mmap_gtt(struct drm_file *file,
+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;
+       int ret;
+
+       if (obj->base.map_list.map)
+               return 0;
+
+       ret = drm_gem_create_mmap_offset(&obj->base);
+       if (ret != -ENOSPC)
+               return ret;
+
+       /* Badly fragmented mmap space? The only way we can recover
+        * space is by destroying unwanted objects. We can't randomly release
+        * mmap_offsets as userspace expects them to be persistent for the
+        * lifetime of the objects. The closest we can is to release the
+        * offsets on purgeable objects by truncating it and marking it purged,
+        * which prevents userspace from ever using that object again.
+        */
+       i915_gem_purge(dev_priv, obj->base.size >> PAGE_SHIFT);
+       ret = drm_gem_create_mmap_offset(&obj->base);
+       if (ret != -ENOSPC)
+               return ret;
+
+       i915_gem_shrink_all(dev_priv);
+       return drm_gem_create_mmap_offset(&obj->base);
+}
+
+static void i915_gem_object_free_mmap_offset(struct drm_i915_gem_object *obj)
+{
+       if (!obj->base.map_list.map)
+               return;
+
+       drm_gem_free_mmap_offset(&obj->base);
+}
+
+int
+i915_gem_mmap_gtt(struct drm_file *file,
                  struct drm_device *dev,
                  uint32_t handle,
                  uint64_t *offset)
@@ -1302,11 +1580,9 @@ i915_gem_mmap_gtt(struct drm_file *file,
                goto out;
        }
 
-       if (!obj->base.map_list.map) {
-               ret = drm_gem_create_mmap_offset(&obj->base);
-               if (ret)
-                       goto out;
-       }
+       ret = i915_gem_object_create_mmap_offset(obj);
+       if (ret)
+               goto out;
 
        *offset = (u64)obj->base.map_list.hash.key << PAGE_SHIFT;
 
@@ -1341,83 +1617,245 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
        return i915_gem_mmap_gtt(file, dev, args->handle, &args->offset);
 }
 
-int
-i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj,
-                             gfp_t gfpmask)
+/* Immediately discard the backing storage */
+static void
+i915_gem_object_truncate(struct drm_i915_gem_object *obj)
 {
-       int page_count, i;
-       struct address_space *mapping;
        struct inode *inode;
-       struct page *page;
 
-       if (obj->pages || obj->sg_table)
-               return 0;
+       i915_gem_object_free_mmap_offset(obj);
 
-       /* Get the list of pages out of our struct file.  They'll be pinned
-        * at this point until we release them.
-        */
-       page_count = obj->base.size / PAGE_SIZE;
-       BUG_ON(obj->pages != NULL);
-       obj->pages = drm_malloc_ab(page_count, sizeof(struct page *));
-       if (obj->pages == NULL)
-               return -ENOMEM;
+       if (obj->base.filp == NULL)
+               return;
 
+       /* Our goal here is to return as much of the memory as
+        * is possible back to the system as we are called from OOM.
+        * To do this we must instruct the shmfs to drop all of its
+        * backing pages, *now*.
+        */
        inode = obj->base.filp->f_path.dentry->d_inode;
-       mapping = inode->i_mapping;
-       gfpmask |= mapping_gfp_mask(mapping);
-
-       for (i = 0; i < page_count; i++) {
-               page = shmem_read_mapping_page_gfp(mapping, i, gfpmask);
-               if (IS_ERR(page))
-                       goto err_pages;
-
-               obj->pages[i] = page;
-       }
-
-       if (i915_gem_object_needs_bit17_swizzle(obj))
-               i915_gem_object_do_bit_17_swizzle(obj);
-
-       return 0;
+       shmem_truncate_range(inode, 0, (loff_t)-1);
 
-err_pages:
-       while (i--)
-               page_cache_release(obj->pages[i]);
+       obj->madv = __I915_MADV_PURGED;
+}
 
-       drm_free_large(obj->pages);
-       obj->pages = NULL;
-       return PTR_ERR(page);
+static inline int
+i915_gem_object_is_purgeable(struct drm_i915_gem_object *obj)
+{
+       return obj->madv == I915_MADV_DONTNEED;
 }
 
 static void
 i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj)
 {
        int page_count = obj->base.size / PAGE_SIZE;
-       int i;
-
-       if (!obj->pages)
-               return;
+       struct scatterlist *sg;
+       int ret, i;
 
        BUG_ON(obj->madv == __I915_MADV_PURGED);
 
+       ret = i915_gem_object_set_to_cpu_domain(obj, true);
+       if (ret) {
+               /* In the event of a disaster, abandon all caches and
+                * hope for the best.
+                */
+               WARN_ON(ret != -EIO);
+               i915_gem_clflush_object(obj);
+               obj->base.read_domains = obj->base.write_domain = I915_GEM_DOMAIN_CPU;
+       }
+
        if (i915_gem_object_needs_bit17_swizzle(obj))
                i915_gem_object_save_bit_17_swizzle(obj);
 
        if (obj->madv == I915_MADV_DONTNEED)
                obj->dirty = 0;
 
-       for (i = 0; i < page_count; i++) {
+       for_each_sg(obj->pages->sgl, sg, page_count, i) {
+               struct page *page = sg_page(sg);
+
                if (obj->dirty)
-                       set_page_dirty(obj->pages[i]);
+                       set_page_dirty(page);
 
                if (obj->madv == I915_MADV_WILLNEED)
-                       mark_page_accessed(obj->pages[i]);
+                       mark_page_accessed(page);
 
-               page_cache_release(obj->pages[i]);
+               page_cache_release(page);
        }
        obj->dirty = 0;
 
-       drm_free_large(obj->pages);
+       sg_free_table(obj->pages);
+       kfree(obj->pages);
+}
+
+static int
+i915_gem_object_put_pages(struct drm_i915_gem_object *obj)
+{
+       const struct drm_i915_gem_object_ops *ops = obj->ops;
+
+       if (obj->pages == NULL)
+               return 0;
+
+       BUG_ON(obj->gtt_space);
+
+       if (obj->pages_pin_count)
+               return -EBUSY;
+
+       ops->put_pages(obj);
        obj->pages = NULL;
+
+       list_del(&obj->gtt_list);
+       if (i915_gem_object_is_purgeable(obj))
+               i915_gem_object_truncate(obj);
+
+       return 0;
+}
+
+static long
+i915_gem_purge(struct drm_i915_private *dev_priv, long target)
+{
+       struct drm_i915_gem_object *obj, *next;
+       long count = 0;
+
+       list_for_each_entry_safe(obj, next,
+                                &dev_priv->mm.unbound_list,
+                                gtt_list) {
+               if (i915_gem_object_is_purgeable(obj) &&
+                   i915_gem_object_put_pages(obj) == 0) {
+                       count += obj->base.size >> PAGE_SHIFT;
+                       if (count >= target)
+                               return count;
+               }
+       }
+
+       list_for_each_entry_safe(obj, next,
+                                &dev_priv->mm.inactive_list,
+                                mm_list) {
+               if (i915_gem_object_is_purgeable(obj) &&
+                   i915_gem_object_unbind(obj) == 0 &&
+                   i915_gem_object_put_pages(obj) == 0) {
+                       count += obj->base.size >> PAGE_SHIFT;
+                       if (count >= target)
+                               return count;
+               }
+       }
+
+       return count;
+}
+
+static void
+i915_gem_shrink_all(struct drm_i915_private *dev_priv)
+{
+       struct drm_i915_gem_object *obj, *next;
+
+       i915_gem_evict_everything(dev_priv->dev);
+
+       list_for_each_entry_safe(obj, next, &dev_priv->mm.unbound_list, gtt_list)
+               i915_gem_object_put_pages(obj);
+}
+
+static int
+i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj)
+{
+       struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
+       int page_count, i;
+       struct address_space *mapping;
+       struct sg_table *st;
+       struct scatterlist *sg;
+       struct page *page;
+       gfp_t gfp;
+
+       /* Assert that the object is not currently in any GPU domain. As it
+        * wasn't in the GTT, there shouldn't be any way it could have been in
+        * a GPU cache
+        */
+       BUG_ON(obj->base.read_domains & I915_GEM_GPU_DOMAINS);
+       BUG_ON(obj->base.write_domain & I915_GEM_GPU_DOMAINS);
+
+       st = kmalloc(sizeof(*st), GFP_KERNEL);
+       if (st == NULL)
+               return -ENOMEM;
+
+       page_count = obj->base.size / PAGE_SIZE;
+       if (sg_alloc_table(st, page_count, GFP_KERNEL)) {
+               sg_free_table(st);
+               kfree(st);
+               return -ENOMEM;
+       }
+
+       /* Get the list of pages out of our struct file.  They'll be pinned
+        * at this point until we release them.
+        *
+        * Fail silently without starting the shrinker
+        */
+       mapping = obj->base.filp->f_path.dentry->d_inode->i_mapping;
+       gfp = mapping_gfp_mask(mapping);
+       gfp |= __GFP_NORETRY | __GFP_NOWARN;
+       gfp &= ~(__GFP_IO | __GFP_WAIT);
+       for_each_sg(st->sgl, sg, page_count, i) {
+               page = shmem_read_mapping_page_gfp(mapping, i, gfp);
+               if (IS_ERR(page)) {
+                       i915_gem_purge(dev_priv, page_count);
+                       page = shmem_read_mapping_page_gfp(mapping, i, gfp);
+               }
+               if (IS_ERR(page)) {
+                       /* We've tried hard to allocate the memory by reaping
+                        * our own buffer, now let the real VM do its job and
+                        * go down in flames if truly OOM.
+                        */
+                       gfp &= ~(__GFP_NORETRY | __GFP_NOWARN);
+                       gfp |= __GFP_IO | __GFP_WAIT;
+
+                       i915_gem_shrink_all(dev_priv);
+                       page = shmem_read_mapping_page_gfp(mapping, i, gfp);
+                       if (IS_ERR(page))
+                               goto err_pages;
+
+                       gfp |= __GFP_NORETRY | __GFP_NOWARN;
+                       gfp &= ~(__GFP_IO | __GFP_WAIT);
+               }
+
+               sg_set_page(sg, page, PAGE_SIZE, 0);
+       }
+
+       if (i915_gem_object_needs_bit17_swizzle(obj))
+               i915_gem_object_do_bit_17_swizzle(obj);
+
+       obj->pages = st;
+       return 0;
+
+err_pages:
+       for_each_sg(st->sgl, sg, i, page_count)
+               page_cache_release(sg_page(sg));
+       sg_free_table(st);
+       kfree(st);
+       return PTR_ERR(page);
+}
+
+/* Ensure that the associated pages are gathered from the backing storage
+ * and pinned into our object. i915_gem_object_get_pages() may be called
+ * multiple times before they are released by a single call to
+ * i915_gem_object_put_pages() - once the pages are no longer referenced
+ * either as a result of memory pressure (reaping pages under the shrinker)
+ * or as the object is itself released.
+ */
+int
+i915_gem_object_get_pages(struct drm_i915_gem_object *obj)
+{
+       struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
+       const struct drm_i915_gem_object_ops *ops = obj->ops;
+       int ret;
+
+       if (obj->pages)
+               return 0;
+
+       BUG_ON(obj->pages_pin_count);
+
+       ret = ops->get_pages(obj);
+       if (ret)
+               return ret;
+
+       list_add_tail(&obj->gtt_list, &dev_priv->mm.unbound_list);
+       return 0;
 }
 
 void
@@ -1441,7 +1879,7 @@ i915_gem_object_move_to_active(struct drm_i915_gem_object *obj,
        list_move_tail(&obj->mm_list, &dev_priv->mm.active_list);
        list_move_tail(&obj->ring_list, &ring->active_list);
 
-       obj->last_rendering_seqno = seqno;
+       obj->last_read_seqno = seqno;
 
        if (obj->fenced_gpu_access) {
                obj->last_fenced_seqno = seqno;
@@ -1458,97 +1896,35 @@ i915_gem_object_move_to_active(struct drm_i915_gem_object *obj,
 }
 
 static void
-i915_gem_object_move_off_active(struct drm_i915_gem_object *obj)
-{
-       list_del_init(&obj->ring_list);
-       obj->last_rendering_seqno = 0;
-       obj->last_fenced_seqno = 0;
-}
-
-static void
-i915_gem_object_move_to_flushing(struct drm_i915_gem_object *obj)
+i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj)
 {
        struct drm_device *dev = obj->base.dev;
-       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct drm_i915_private *dev_priv = dev->dev_private;
 
+       BUG_ON(obj->base.write_domain & ~I915_GEM_GPU_DOMAINS);
        BUG_ON(!obj->active);
-       list_move_tail(&obj->mm_list, &dev_priv->mm.flushing_list);
 
-       i915_gem_object_move_off_active(obj);
-}
-
-static void
-i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj)
-{
-       struct drm_device *dev = obj->base.dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
+       if (obj->pin_count) /* are we a framebuffer? */
+               intel_mark_fb_idle(obj);
 
        list_move_tail(&obj->mm_list, &dev_priv->mm.inactive_list);
 
-       BUG_ON(!list_empty(&obj->gpu_write_list));
-       BUG_ON(!obj->active);
+       list_del_init(&obj->ring_list);
        obj->ring = NULL;
 
-       i915_gem_object_move_off_active(obj);
+       obj->last_read_seqno = 0;
+       obj->last_write_seqno = 0;
+       obj->base.write_domain = 0;
+
+       obj->last_fenced_seqno = 0;
        obj->fenced_gpu_access = false;
 
        obj->active = 0;
-       obj->pending_gpu_write = false;
        drm_gem_object_unreference(&obj->base);
 
        WARN_ON(i915_verify_lists(dev));
 }
 
-/* Immediately discard the backing storage */
-static void
-i915_gem_object_truncate(struct drm_i915_gem_object *obj)
-{
-       struct inode *inode;
-
-       /* Our goal here is to return as much of the memory as
-        * is possible back to the system as we are called from OOM.
-        * To do this we must instruct the shmfs to drop all of its
-        * backing pages, *now*.
-        */
-       inode = obj->base.filp->f_path.dentry->d_inode;
-       shmem_truncate_range(inode, 0, (loff_t)-1);
-
-       if (obj->base.map_list.map)
-               drm_gem_free_mmap_offset(&obj->base);
-
-       obj->madv = __I915_MADV_PURGED;
-}
-
-static inline int
-i915_gem_object_is_purgeable(struct drm_i915_gem_object *obj)
-{
-       return obj->madv == I915_MADV_DONTNEED;
-}
-
-static void
-i915_gem_process_flushing_list(struct intel_ring_buffer *ring,
-                              uint32_t flush_domains)
-{
-       struct drm_i915_gem_object *obj, *next;
-
-       list_for_each_entry_safe(obj, next,
-                                &ring->gpu_write_list,
-                                gpu_write_list) {
-               if (obj->base.write_domain & flush_domains) {
-                       uint32_t old_write_domain = obj->base.write_domain;
-
-                       obj->base.write_domain = 0;
-                       list_del_init(&obj->gpu_write_list);
-                       i915_gem_object_move_to_active(obj, ring,
-                                                      i915_gem_next_request_seqno(ring));
-
-                       trace_i915_gem_object_change_domain(obj,
-                                                           obj->base.read_domains,
-                                                           old_write_domain);
-               }
-       }
-}
-
 static u32
 i915_gem_get_seqno(struct drm_device *dev)
 {
@@ -1589,15 +1965,16 @@ i915_add_request(struct intel_ring_buffer *ring,
         * is that the flush _must_ happen before the next request, no matter
         * what.
         */
-       if (ring->gpu_caches_dirty) {
-               ret = i915_gem_flush_ring(ring, 0, I915_GEM_GPU_DOMAINS);
-               if (ret)
-                       return ret;
+       ret = intel_ring_flush_all_caches(ring);
+       if (ret)
+               return ret;
 
-               ring->gpu_caches_dirty = false;
+       if (request == NULL) {
+               request = kmalloc(sizeof(*request), GFP_KERNEL);
+               if (request == NULL)
+                       return -ENOMEM;
        }
 
-       BUG_ON(request == NULL);
        seqno = i915_gem_next_request_seqno(ring);
 
        /* Record the position of the start of the request so that
@@ -1608,8 +1985,10 @@ i915_add_request(struct intel_ring_buffer *ring,
        request_ring_position = intel_ring_get_tail(ring);
 
        ret = ring->add_request(ring, &seqno);
-       if (ret)
-           return ret;
+       if (ret) {
+               kfree(request);
+               return ret;
+       }
 
        trace_i915_gem_request_add(ring, seqno);
 
@@ -1619,6 +1998,7 @@ i915_add_request(struct intel_ring_buffer *ring,
        request->emitted_jiffies = jiffies;
        was_empty = list_empty(&ring->request_list);
        list_add_tail(&request->list, &ring->request_list);
+       request->file_priv = NULL;
 
        if (file) {
                struct drm_i915_file_private *file_priv = file->driver_priv;
@@ -1638,13 +2018,13 @@ i915_add_request(struct intel_ring_buffer *ring,
                                  jiffies +
                                  msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD));
                }
-               if (was_empty)
+               if (was_empty) {
                        queue_delayed_work(dev_priv->wq,
                                           &dev_priv->mm.retire_work, HZ);
+                       intel_mark_busy(dev_priv->dev);
+               }
        }
 
-       WARN_ON(!list_empty(&ring->gpu_write_list));
-
        return 0;
 }
 
@@ -1686,8 +2066,6 @@ static void i915_gem_reset_ring_lists(struct drm_i915_private *dev_priv,
                                       struct drm_i915_gem_object,
                                       ring_list);
 
-               obj->base.write_domain = 0;
-               list_del_init(&obj->gpu_write_list);
                i915_gem_object_move_to_inactive(obj);
        }
 }
@@ -1723,20 +2101,6 @@ void i915_gem_reset(struct drm_device *dev)
        for_each_ring(ring, dev_priv, i)
                i915_gem_reset_ring_lists(dev_priv, ring);
 
-       /* Remove anything from the flushing lists. The GPU cache is likely
-        * to be lost on reset along with the data, so simply move the
-        * lost bo to the inactive list.
-        */
-       while (!list_empty(&dev_priv->mm.flushing_list)) {
-               obj = list_first_entry(&dev_priv->mm.flushing_list,
-                                     struct drm_i915_gem_object,
-                                     mm_list);
-
-               obj->base.write_domain = 0;
-               list_del_init(&obj->gpu_write_list);
-               i915_gem_object_move_to_inactive(obj);
-       }
-
        /* Move everything out of the GPU domains to ensure we do any
         * necessary invalidation upon reuse.
         */
@@ -1765,7 +2129,7 @@ i915_gem_retire_requests_ring(struct intel_ring_buffer *ring)
 
        WARN_ON(i915_verify_lists(ring->dev));
 
-       seqno = ring->get_seqno(ring);
+       seqno = ring->get_seqno(ring, true);
 
        for (i = 0; i < ARRAY_SIZE(ring->sync_seqno); i++)
                if (seqno >= ring->sync_seqno[i])
@@ -1804,13 +2168,10 @@ i915_gem_retire_requests_ring(struct intel_ring_buffer *ring)
                                      struct drm_i915_gem_object,
                                      ring_list);
 
-               if (!i915_seqno_passed(seqno, obj->last_rendering_seqno))
+               if (!i915_seqno_passed(seqno, obj->last_read_seqno))
                        break;
 
-               if (obj->base.write_domain != 0)
-                       i915_gem_object_move_to_flushing(obj);
-               else
-                       i915_gem_object_move_to_inactive(obj);
+               i915_gem_object_move_to_inactive(obj);
        }
 
        if (unlikely(ring->trace_irq_seqno &&
@@ -1848,225 +2209,29 @@ i915_gem_retire_work_handler(struct work_struct *work)
 
        /* Come back later if the device is busy... */
        if (!mutex_trylock(&dev->struct_mutex)) {
-               queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ);
-               return;
-       }
-
-       i915_gem_retire_requests(dev);
-
-       /* Send a periodic flush down the ring so we don't hold onto GEM
-        * objects indefinitely.
-        */
-       idle = true;
-       for_each_ring(ring, dev_priv, i) {
-               if (ring->gpu_caches_dirty) {
-                       struct drm_i915_gem_request *request;
-
-                       request = kzalloc(sizeof(*request), GFP_KERNEL);
-                       if (request == NULL ||
-                           i915_add_request(ring, NULL, request))
-                           kfree(request);
-               }
-
-               idle &= list_empty(&ring->request_list);
-       }
-
-       if (!dev_priv->mm.suspended && !idle)
-               queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ);
-
-       mutex_unlock(&dev->struct_mutex);
-}
-
-int
-i915_gem_check_wedge(struct drm_i915_private *dev_priv,
-                    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);
-
-               /* 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)
-                       return -EIO;
-
-               return -EAGAIN;
-       }
-
-       return 0;
-}
-
-/*
- * Compare seqno against outstanding lazy request. Emit a request if they are
- * equal.
- */
-static int
-i915_gem_check_olr(struct intel_ring_buffer *ring, u32 seqno)
-{
-       int ret = 0;
-
-       BUG_ON(!mutex_is_locked(&ring->dev->struct_mutex));
-
-       if (seqno == ring->outstanding_lazy_request) {
-               struct drm_i915_gem_request *request;
-
-               request = kzalloc(sizeof(*request), GFP_KERNEL);
-               if (request == NULL)
-                       return -ENOMEM;
-
-               ret = i915_add_request(ring, NULL, request);
-               if (ret) {
-                       kfree(request);
-                       return ret;
-               }
-
-               BUG_ON(seqno != request->seqno);
-       }
-
-       return ret;
-}
-
-/**
- * __wait_seqno - wait until execution of seqno has finished
- * @ring: the ring expected to report seqno
- * @seqno: duh!
- * @interruptible: do an interruptible wait (normally yes)
- * @timeout: in - how long to wait (NULL forever); out - how much time remaining
- *
- * 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,
-                       bool interruptible, struct timespec *timeout)
-{
-       drm_i915_private_t *dev_priv = ring->dev->dev_private;
-       struct timespec before, now, wait_time={1,0};
-       unsigned long timeout_jiffies;
-       long end;
-       bool wait_forever = true;
-       int ret;
-
-       if (i915_seqno_passed(ring->get_seqno(ring), seqno))
-               return 0;
-
-       trace_i915_gem_request_wait_begin(ring, seqno);
-
-       if (timeout != NULL) {
-               wait_time = *timeout;
-               wait_forever = false;
-       }
-
-       timeout_jiffies = timespec_to_jiffies(&wait_time);
-
-       if (WARN_ON(!ring->irq_get(ring)))
-               return -ENODEV;
-
-       /* Record current time in case interrupted by signal, or wedged * */
-       getrawmonotonic(&before);
-
-#define EXIT_COND \
-       (i915_seqno_passed(ring->get_seqno(ring), seqno) || \
-       atomic_read(&dev_priv->mm.wedged))
-       do {
-               if (interruptible)
-                       end = wait_event_interruptible_timeout(ring->irq_queue,
-                                                              EXIT_COND,
-                                                              timeout_jiffies);
-               else
-                       end = wait_event_timeout(ring->irq_queue, EXIT_COND,
-                                                timeout_jiffies);
-
-               ret = i915_gem_check_wedge(dev_priv, interruptible);
-               if (ret)
-                       end = ret;
-       } while (end == 0 && wait_forever);
-
-       getrawmonotonic(&now);
-
-       ring->irq_put(ring);
-       trace_i915_gem_request_wait_end(ring, seqno);
-#undef EXIT_COND
-
-       if (timeout) {
-               struct timespec sleep_time = timespec_sub(now, before);
-               *timeout = timespec_sub(*timeout, sleep_time);
-       }
-
-       switch (end) {
-       case -EIO:
-       case -EAGAIN: /* Wedged */
-       case -ERESTARTSYS: /* Signal */
-               return (int)end;
-       case 0: /* Timeout */
-               if (timeout)
-                       set_normalized_timespec(timeout, 0, 0);
-               return -ETIME;
-       default: /* Completed */
-               WARN_ON(end < 0); /* We're not aware of other errors */
-               return 0;
-       }
-}
-
-/**
- * Waits for a sequence number to be signaled, and cleans up the
- * request and object lists appropriately for that event.
- */
-int
-i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno)
-{
-       drm_i915_private_t *dev_priv = ring->dev->dev_private;
-       int ret = 0;
-
-       BUG_ON(seqno == 0);
-
-       ret = i915_gem_check_wedge(dev_priv, dev_priv->mm.interruptible);
-       if (ret)
-               return ret;
-
-       ret = i915_gem_check_olr(ring, seqno);
-       if (ret)
-               return ret;
-
-       ret = __wait_seqno(ring, seqno, dev_priv->mm.interruptible, NULL);
-
-       return ret;
-}
-
-/**
- * Ensures that all rendering to the object has completed and the object is
- * safe to unbind from the GTT or access from the CPU.
- */
-int
-i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj)
-{
-       int ret;
+               queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ);
+               return;
+       }
 
-       /* This function only exists to support waiting for existing rendering,
-        * not for emitting required flushes.
-        */
-       BUG_ON((obj->base.write_domain & I915_GEM_GPU_DOMAINS) != 0);
+       i915_gem_retire_requests(dev);
 
-       /* If there is rendering queued on the buffer being evicted, wait for
-        * it.
+       /* Send a periodic flush down the ring so we don't hold onto GEM
+        * objects indefinitely.
         */
-       if (obj->active) {
-               ret = i915_wait_seqno(obj->ring, obj->last_rendering_seqno);
-               if (ret)
-                       return ret;
-               i915_gem_retire_requests_ring(obj->ring);
+       idle = true;
+       for_each_ring(ring, dev_priv, i) {
+               if (ring->gpu_caches_dirty)
+                       i915_add_request(ring, NULL, NULL);
+
+               idle &= list_empty(&ring->request_list);
        }
 
-       return 0;
+       if (!dev_priv->mm.suspended && !idle)
+               queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ);
+       if (idle)
+               intel_mark_idle(dev);
+
+       mutex_unlock(&dev->struct_mutex);
 }
 
 /**
@@ -2080,14 +2245,10 @@ i915_gem_object_flush_active(struct drm_i915_gem_object *obj)
        int ret;
 
        if (obj->active) {
-               ret = i915_gem_object_flush_gpu_write_domain(obj);
+               ret = i915_gem_check_olr(obj->ring, obj->last_read_seqno);
                if (ret)
                        return ret;
 
-               ret = i915_gem_check_olr(obj->ring,
-                                        obj->last_rendering_seqno);
-               if (ret)
-                       return ret;
                i915_gem_retire_requests_ring(obj->ring);
        }
 
@@ -2147,7 +2308,7 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
                goto out;
 
        if (obj->active) {
-               seqno = obj->last_rendering_seqno;
+               seqno = obj->last_read_seqno;
                ring = obj->ring;
        }
 
@@ -2202,11 +2363,11 @@ i915_gem_object_sync(struct drm_i915_gem_object *obj,
                return 0;
 
        if (to == NULL || !i915_semaphore_is_enabled(obj->base.dev))
-               return i915_gem_object_wait_rendering(obj);
+               return i915_gem_object_wait_rendering(obj, false);
 
        idx = intel_ring_sync_index(from, to);
 
-       seqno = obj->last_rendering_seqno;
+       seqno = obj->last_read_seqno;
        if (seqno <= from->sync_seqno[idx])
                return 0;
 
@@ -2260,6 +2421,8 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj)
        if (obj->pin_count)
                return -EBUSY;
 
+       BUG_ON(obj->pages == NULL);
+
        ret = i915_gem_object_finish_gpu(obj);
        if (ret)
                return ret;
@@ -2270,22 +2433,6 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj)
 
        i915_gem_object_finish_gtt(obj);
 
-       /* Move the object to the CPU domain to ensure that
-        * any possible CPU writes while it's not in the GTT
-        * are flushed when we go to remap it.
-        */
-       if (ret == 0)
-               ret = i915_gem_object_set_to_cpu_domain(obj, 1);
-       if (ret == -ERESTARTSYS)
-               return ret;
-       if (ret) {
-               /* In the event of a disaster, abandon all caches and
-                * hope for the best.
-                */
-               i915_gem_clflush_object(obj);
-               obj->base.read_domains = obj->base.write_domain = I915_GEM_DOMAIN_CPU;
-       }
-
        /* release the fence reg _after_ flushing */
        ret = i915_gem_object_put_fence(obj);
        if (ret)
@@ -2301,10 +2448,8 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj)
        }
        i915_gem_gtt_finish_object(obj);
 
-       i915_gem_object_put_pages_gtt(obj);
-
-       list_del_init(&obj->gtt_list);
-       list_del_init(&obj->mm_list);
+       list_del(&obj->mm_list);
+       list_move_tail(&obj->gtt_list, &dev_priv->mm.unbound_list);
        /* Avoid an unnecessary call to unbind on rebind. */
        obj->map_and_fenceable = true;
 
@@ -2312,48 +2457,14 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj)
        obj->gtt_space = NULL;
        obj->gtt_offset = 0;
 
-       if (i915_gem_object_is_purgeable(obj))
-               i915_gem_object_truncate(obj);
-
-       return ret;
-}
-
-int
-i915_gem_flush_ring(struct intel_ring_buffer *ring,
-                   uint32_t invalidate_domains,
-                   uint32_t flush_domains)
-{
-       int ret;
-
-       if (((invalidate_domains | flush_domains) & I915_GEM_GPU_DOMAINS) == 0)
-               return 0;
-
-       trace_i915_gem_ring_flush(ring, invalidate_domains, flush_domains);
-
-       ret = ring->flush(ring, invalidate_domains, flush_domains);
-       if (ret)
-               return ret;
-
-       if (flush_domains & I915_GEM_GPU_DOMAINS)
-               i915_gem_process_flushing_list(ring, flush_domains);
-
        return 0;
 }
 
 static int i915_ring_idle(struct intel_ring_buffer *ring)
 {
-       int ret;
-
-       if (list_empty(&ring->gpu_write_list) && list_empty(&ring->active_list))
+       if (list_empty(&ring->active_list))
                return 0;
 
-       if (!list_empty(&ring->gpu_write_list)) {
-               ret = i915_gem_flush_ring(ring,
-                                   I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
-               if (ret)
-                       return ret;
-       }
-
        return i915_wait_seqno(ring, i915_gem_next_request_seqno(ring));
 }
 
@@ -2372,10 +2483,6 @@ int i915_gpu_idle(struct drm_device *dev)
                ret = i915_ring_idle(ring);
                if (ret)
                        return ret;
-
-               /* Is the device fubar? */
-               if (WARN_ON(!list_empty(&ring->gpu_write_list)))
-                       return -EBUSY;
        }
 
        return 0;
@@ -2548,21 +2655,8 @@ 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)
 {
-       int ret;
-
-       if (obj->fenced_gpu_access) {
-               if (obj->base.write_domain & I915_GEM_GPU_DOMAINS) {
-                       ret = i915_gem_flush_ring(obj->ring,
-                                                 0, obj->base.write_domain);
-                       if (ret)
-                               return ret;
-               }
-
-               obj->fenced_gpu_access = false;
-       }
-
        if (obj->last_fenced_seqno) {
-               ret = i915_wait_seqno(obj->ring, obj->last_fenced_seqno);
+               int ret = i915_wait_seqno(obj->ring, obj->last_fenced_seqno);
                if (ret)
                        return ret;
 
@@ -2575,6 +2669,7 @@ i915_gem_object_flush_fence(struct drm_i915_gem_object *obj)
        if (obj->base.read_domains & I915_GEM_DOMAIN_GTT)
                mb();
 
+       obj->fenced_gpu_access = false;
        return 0;
 }
 
@@ -2694,18 +2789,88 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj)
        return 0;
 }
 
+static bool i915_gem_valid_gtt_space(struct drm_device *dev,
+                                    struct drm_mm_node *gtt_space,
+                                    unsigned long cache_level)
+{
+       struct drm_mm_node *other;
+
+       /* 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.
+        */
+       if (HAS_LLC(dev))
+               return true;
+
+       if (gtt_space == NULL)
+               return true;
+
+       if (list_empty(&gtt_space->node_list))
+               return true;
+
+       other = list_entry(gtt_space->node_list.prev, struct drm_mm_node, node_list);
+       if (other->allocated && !other->hole_follows && other->color != cache_level)
+               return false;
+
+       other = list_entry(gtt_space->node_list.next, struct drm_mm_node, node_list);
+       if (other->allocated && !gtt_space->hole_follows && other->color != cache_level)
+               return false;
+
+       return true;
+}
+
+static void i915_gem_verify_gtt(struct drm_device *dev)
+{
+#if WATCH_GTT
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_i915_gem_object *obj;
+       int err = 0;
+
+       list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) {
+               if (obj->gtt_space == NULL) {
+                       printk(KERN_ERR "object found on GTT list with no space reserved\n");
+                       err++;
+                       continue;
+               }
+
+               if (obj->cache_level != obj->gtt_space->color) {
+                       printk(KERN_ERR "object reserved space [%08lx, %08lx] with wrong color, cache_level=%x, color=%lx\n",
+                              obj->gtt_space->start,
+                              obj->gtt_space->start + obj->gtt_space->size,
+                              obj->cache_level,
+                              obj->gtt_space->color);
+                       err++;
+                       continue;
+               }
+
+               if (!i915_gem_valid_gtt_space(dev,
+                                             obj->gtt_space,
+                                             obj->cache_level)) {
+                       printk(KERN_ERR "invalid GTT space found at [%08lx, %08lx] - color=%x\n",
+                              obj->gtt_space->start,
+                              obj->gtt_space->start + obj->gtt_space->size,
+                              obj->cache_level);
+                       err++;
+                       continue;
+               }
+       }
+
+       WARN_ON(err);
+#endif
+}
+
 /**
  * Finds free space in the GTT aperture and binds the object there.
  */
 static int
 i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
                            unsigned alignment,
-                           bool map_and_fenceable)
+                           bool map_and_fenceable,
+                           bool nonblocking)
 {
        struct drm_device *dev = obj->base.dev;
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct drm_mm_node *free_space;
-       gfp_t gfpmask = __GFP_NORETRY | __GFP_NOWARN;
        u32 size, fence_size, fence_alignment, unfenced_alignment;
        bool mappable, fenceable;
        int ret;
@@ -2745,89 +2910,67 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
                return -E2BIG;
        }
 
+       ret = i915_gem_object_get_pages(obj);
+       if (ret)
+               return ret;
+
  search_free:
        if (map_and_fenceable)
                free_space =
-                       drm_mm_search_free_in_range(&dev_priv->mm.gtt_space,
-                                                   size, alignment,
-                                                   0, dev_priv->mm.gtt_mappable_end,
-                                                   0);
+                       drm_mm_search_free_in_range_color(&dev_priv->mm.gtt_space,
+                                                         size, alignment, obj->cache_level,
+                                                         0, dev_priv->mm.gtt_mappable_end,
+                                                         false);
        else
-               free_space = drm_mm_search_free(&dev_priv->mm.gtt_space,
-                                               size, alignment, 0);
+               free_space = drm_mm_search_free_color(&dev_priv->mm.gtt_space,
+                                                     size, alignment, obj->cache_level,
+                                                     false);
 
        if (free_space != NULL) {
                if (map_and_fenceable)
                        obj->gtt_space =
                                drm_mm_get_block_range_generic(free_space,
-                                                              size, alignment, 0,
+                                                              size, alignment, obj->cache_level,
                                                               0, dev_priv->mm.gtt_mappable_end,
-                                                              0);
+                                                              false);
                else
                        obj->gtt_space =
-                               drm_mm_get_block(free_space, size, alignment);
+                               drm_mm_get_block_generic(free_space,
+                                                        size, alignment, obj->cache_level,
+                                                        false);
        }
        if (obj->gtt_space == NULL) {
-               /* If the gtt is empty and we're still having trouble
-                * fitting our object in, we're out of memory.
-                */
                ret = i915_gem_evict_something(dev, size, alignment,
-                                              map_and_fenceable);
+                                              obj->cache_level,
+                                              map_and_fenceable,
+                                              nonblocking);
                if (ret)
                        return ret;
 
                goto search_free;
        }
-
-       ret = i915_gem_object_get_pages_gtt(obj, gfpmask);
-       if (ret) {
+       if (WARN_ON(!i915_gem_valid_gtt_space(dev,
+                                             obj->gtt_space,
+                                             obj->cache_level))) {
                drm_mm_put_block(obj->gtt_space);
                obj->gtt_space = NULL;
-
-               if (ret == -ENOMEM) {
-                       /* first try to reclaim some memory by clearing the GTT */
-                       ret = i915_gem_evict_everything(dev, false);
-                       if (ret) {
-                               /* now try to shrink everyone else */
-                               if (gfpmask) {
-                                       gfpmask = 0;
-                                       goto search_free;
-                               }
-
-                               return -ENOMEM;
-                       }
-
-                       goto search_free;
-               }
-
-               return ret;
+               return -EINVAL;
        }
 
+
        ret = i915_gem_gtt_prepare_object(obj);
        if (ret) {
-               i915_gem_object_put_pages_gtt(obj);
                drm_mm_put_block(obj->gtt_space);
                obj->gtt_space = NULL;
-
-               if (i915_gem_evict_everything(dev, false))
-                       return ret;
-
-               goto search_free;
+               return ret;
        }
 
        if (!dev_priv->mm.aliasing_ppgtt)
                i915_gem_gtt_bind_object(obj, obj->cache_level);
 
-       list_add_tail(&obj->gtt_list, &dev_priv->mm.gtt_list);
+       list_move_tail(&obj->gtt_list, &dev_priv->mm.bound_list);
        list_add_tail(&obj->mm_list, &dev_priv->mm.inactive_list);
 
-       /* Assert that the object is not currently in any GPU domain. As it
-        * wasn't in the GTT, there shouldn't be any way it could have been in
-        * a GPU cache
-        */
-       BUG_ON(obj->base.read_domains & I915_GEM_GPU_DOMAINS);
-       BUG_ON(obj->base.write_domain & I915_GEM_GPU_DOMAINS);
-
        obj->gtt_offset = obj->gtt_space->start;
 
        fenceable =
@@ -2840,6 +2983,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
        obj->map_and_fenceable = mappable && fenceable;
 
        trace_i915_gem_object_bind(obj, map_and_fenceable);
+       i915_gem_verify_gtt(dev);
        return 0;
 }
 
@@ -2866,18 +3010,7 @@ i915_gem_clflush_object(struct drm_i915_gem_object *obj)
 
        trace_i915_gem_object_clflush(obj);
 
-       drm_clflush_pages(obj->pages, obj->base.size / PAGE_SIZE);
-}
-
-/** Flushes any GPU write domain for the object if it's dirty. */
-static int
-i915_gem_object_flush_gpu_write_domain(struct drm_i915_gem_object *obj)
-{
-       if ((obj->base.write_domain & I915_GEM_GPU_DOMAINS) == 0)
-               return 0;
-
-       /* Queue the GPU write cache flushing we need. */
-       return i915_gem_flush_ring(obj->ring, 0, obj->base.write_domain);
+       drm_clflush_sg(obj->pages);
 }
 
 /** Flushes the GTT write domain for the object if it's dirty. */
@@ -2946,16 +3079,10 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write)
        if (obj->base.write_domain == I915_GEM_DOMAIN_GTT)
                return 0;
 
-       ret = i915_gem_object_flush_gpu_write_domain(obj);
+       ret = i915_gem_object_wait_rendering(obj, !write);
        if (ret)
                return ret;
 
-       if (obj->pending_gpu_write || write) {
-               ret = i915_gem_object_wait_rendering(obj);
-               if (ret)
-                       return ret;
-       }
-
        i915_gem_object_flush_cpu_write_domain(obj);
 
        old_write_domain = obj->base.write_domain;
@@ -2998,6 +3125,12 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
                return -EBUSY;
        }
 
+       if (!i915_gem_valid_gtt_space(dev, obj->gtt_space, cache_level)) {
+               ret = i915_gem_object_unbind(obj);
+               if (ret)
+                       return ret;
+       }
+
        if (obj->gtt_space) {
                ret = i915_gem_object_finish_gpu(obj);
                if (ret)
@@ -3009,7 +3142,7 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
                 * registers with snooped memory, so relinquish any fences
                 * currently pointing to our region in the aperture.
                 */
-               if (INTEL_INFO(obj->base.dev)->gen < 6) {
+               if (INTEL_INFO(dev)->gen < 6) {
                        ret = i915_gem_object_put_fence(obj);
                        if (ret)
                                return ret;
@@ -3020,6 +3153,8 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
                if (obj->has_aliasing_ppgtt_mapping)
                        i915_ppgtt_bind_object(dev_priv->mm.aliasing_ppgtt,
                                               obj, cache_level);
+
+               obj->gtt_space->color = cache_level;
        }
 
        if (cache_level == I915_CACHE_NONE) {
@@ -3046,9 +3181,72 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
        }
 
        obj->cache_level = cache_level;
+       i915_gem_verify_gtt(dev);
        return 0;
 }
 
+int i915_gem_get_caching_ioctl(struct drm_device *dev, void *data,
+                              struct drm_file *file)
+{
+       struct drm_i915_gem_caching *args = data;
+       struct drm_i915_gem_object *obj;
+       int ret;
+
+       ret = i915_mutex_lock_interruptible(dev);
+       if (ret)
+               return ret;
+
+       obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle));
+       if (&obj->base == NULL) {
+               ret = -ENOENT;
+               goto unlock;
+       }
+
+       args->caching = obj->cache_level != I915_CACHE_NONE;
+
+       drm_gem_object_unreference(&obj->base);
+unlock:
+       mutex_unlock(&dev->struct_mutex);
+       return ret;
+}
+
+int i915_gem_set_caching_ioctl(struct drm_device *dev, void *data,
+                              struct drm_file *file)
+{
+       struct drm_i915_gem_caching *args = data;
+       struct drm_i915_gem_object *obj;
+       enum i915_cache_level level;
+       int ret;
+
+       ret = i915_mutex_lock_interruptible(dev);
+       if (ret)
+               return ret;
+
+       switch (args->caching) {
+       case I915_CACHING_NONE:
+               level = I915_CACHE_NONE;
+               break;
+       case I915_CACHING_CACHED:
+               level = I915_CACHE_LLC;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle));
+       if (&obj->base == NULL) {
+               ret = -ENOENT;
+               goto unlock;
+       }
+
+       ret = i915_gem_object_set_cache_level(obj, level);
+
+       drm_gem_object_unreference(&obj->base);
+unlock:
+       mutex_unlock(&dev->struct_mutex);
+       return ret;
+}
+
 /*
  * Prepare buffer for display plane (scanout, cursors, etc).
  * Can be called from an uninterruptible phase (modesetting) and allows
@@ -3062,10 +3260,6 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
        u32 old_read_domains, old_write_domain;
        int ret;
 
-       ret = i915_gem_object_flush_gpu_write_domain(obj);
-       if (ret)
-               return ret;
-
        if (pipelined != obj->ring) {
                ret = i915_gem_object_sync(obj, pipelined);
                if (ret)
@@ -3089,7 +3283,7 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
         * (e.g. libkms for the bootup splash), we have to ensure that we
         * always use map_and_fenceable for all scanout buffers.
         */
-       ret = i915_gem_object_pin(obj, alignment, true);
+       ret = i915_gem_object_pin(obj, alignment, true, false);
        if (ret)
                return ret;
 
@@ -3101,7 +3295,7 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
        /* It should now be out of any other write domains, and we can update
         * the domain values for our changes.
         */
-       BUG_ON((obj->base.write_domain & ~I915_GEM_DOMAIN_GTT) != 0);
+       obj->base.write_domain = 0;
        obj->base.read_domains |= I915_GEM_DOMAIN_GTT;
 
        trace_i915_gem_object_change_domain(obj,
@@ -3119,13 +3313,7 @@ i915_gem_object_finish_gpu(struct drm_i915_gem_object *obj)
        if ((obj->base.read_domains & I915_GEM_GPU_DOMAINS) == 0)
                return 0;
 
-       if (obj->base.write_domain & I915_GEM_GPU_DOMAINS) {
-               ret = i915_gem_flush_ring(obj->ring, 0, obj->base.write_domain);
-               if (ret)
-                       return ret;
-       }
-
-       ret = i915_gem_object_wait_rendering(obj);
+       ret = i915_gem_object_wait_rendering(obj, false);
        if (ret)
                return ret;
 
@@ -3149,16 +3337,10 @@ i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj, bool write)
        if (obj->base.write_domain == I915_GEM_DOMAIN_CPU)
                return 0;
 
-       ret = i915_gem_object_flush_gpu_write_domain(obj);
+       ret = i915_gem_object_wait_rendering(obj, !write);
        if (ret)
                return ret;
 
-       if (write || obj->pending_gpu_write) {
-               ret = i915_gem_object_wait_rendering(obj);
-               if (ret)
-                       return ret;
-       }
-
        i915_gem_object_flush_gtt_write_domain(obj);
 
        old_write_domain = obj->base.write_domain;
@@ -3238,11 +3420,13 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file)
 int
 i915_gem_object_pin(struct drm_i915_gem_object *obj,
                    uint32_t alignment,
-                   bool map_and_fenceable)
+                   bool map_and_fenceable,
+                   bool nonblocking)
 {
        int ret;
 
-       BUG_ON(obj->pin_count == DRM_I915_GEM_OBJECT_MAX_PIN_COUNT);
+       if (WARN_ON(obj->pin_count == DRM_I915_GEM_OBJECT_MAX_PIN_COUNT))
+               return -EBUSY;
 
        if (obj->gtt_space != NULL) {
                if ((alignment && obj->gtt_offset & (alignment - 1)) ||
@@ -3262,7 +3446,8 @@ i915_gem_object_pin(struct drm_i915_gem_object *obj,
 
        if (obj->gtt_space == NULL) {
                ret = i915_gem_object_bind_to_gtt(obj, alignment,
-                                                 map_and_fenceable);
+                                                 map_and_fenceable,
+                                                 nonblocking);
                if (ret)
                        return ret;
        }
@@ -3320,7 +3505,7 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data,
        obj->user_pin_count++;
        obj->pin_filp = file;
        if (obj->user_pin_count == 1) {
-               ret = i915_gem_object_pin(obj, args->alignment, true);
+               ret = i915_gem_object_pin(obj, args->alignment, true, false);
                if (ret)
                        goto out;
        }
@@ -3400,6 +3585,10 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
        ret = i915_gem_object_flush_active(obj);
 
        args->busy = obj->active;
+       if (obj->ring) {
+               BUILD_BUG_ON(I915_NUM_RINGS > 16);
+               args->busy |= intel_ring_flag(obj->ring) << 16;
+       }
 
        drm_gem_object_unreference(&obj->base);
 unlock:
@@ -3448,9 +3637,8 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data,
        if (obj->madv != __I915_MADV_PURGED)
                obj->madv = args->madv;
 
-       /* if the object is no longer bound, discard its backing storage */
-       if (i915_gem_object_is_purgeable(obj) &&
-           obj->gtt_space == NULL)
+       /* if the object is no longer attached, discard its backing storage */
+       if (i915_gem_object_is_purgeable(obj) && obj->pages == NULL)
                i915_gem_object_truncate(obj);
 
        args->retained = obj->madv != __I915_MADV_PURGED;
@@ -3462,10 +3650,32 @@ unlock:
        return ret;
 }
 
+void i915_gem_object_init(struct drm_i915_gem_object *obj,
+                         const struct drm_i915_gem_object_ops *ops)
+{
+       INIT_LIST_HEAD(&obj->mm_list);
+       INIT_LIST_HEAD(&obj->gtt_list);
+       INIT_LIST_HEAD(&obj->ring_list);
+       INIT_LIST_HEAD(&obj->exec_list);
+
+       obj->ops = ops;
+
+       obj->fence_reg = I915_FENCE_REG_NONE;
+       obj->madv = I915_MADV_WILLNEED;
+       /* Avoid an unnecessary call to unbind on the first bind. */
+       obj->map_and_fenceable = true;
+
+       i915_gem_info_add_obj(obj->base.dev->dev_private, obj->base.size);
+}
+
+static const struct drm_i915_gem_object_ops i915_gem_object_ops = {
+       .get_pages = i915_gem_object_get_pages_gtt,
+       .put_pages = i915_gem_object_put_pages_gtt,
+};
+
 struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
                                                  size_t size)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_i915_gem_object *obj;
        struct address_space *mapping;
        u32 mask;
@@ -3489,7 +3699,7 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
        mapping = obj->base.filp->f_path.dentry->d_inode->i_mapping;
        mapping_set_gfp_mask(mapping, mask);
 
-       i915_gem_info_add_obj(dev_priv, size);
+       i915_gem_object_init(obj, &i915_gem_object_ops);
 
        obj->base.write_domain = I915_GEM_DOMAIN_CPU;
        obj->base.read_domains = I915_GEM_DOMAIN_CPU;
@@ -3511,17 +3721,6 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
        } else
                obj->cache_level = I915_CACHE_NONE;
 
-       obj->base.driver_private = NULL;
-       obj->fence_reg = I915_FENCE_REG_NONE;
-       INIT_LIST_HEAD(&obj->mm_list);
-       INIT_LIST_HEAD(&obj->gtt_list);
-       INIT_LIST_HEAD(&obj->ring_list);
-       INIT_LIST_HEAD(&obj->exec_list);
-       INIT_LIST_HEAD(&obj->gpu_write_list);
-       obj->madv = I915_MADV_WILLNEED;
-       /* Avoid an unnecessary call to unbind on the first bind. */
-       obj->map_and_fenceable = true;
-
        return obj;
 }
 
@@ -3540,9 +3739,6 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj)
 
        trace_i915_gem_object_destroy(obj);
 
-       if (gem_obj->import_attach)
-               drm_prime_gem_destroy(gem_obj, obj->sg_table);
-
        if (obj->phys_obj)
                i915_gem_detach_phys_object(dev, obj);
 
@@ -3558,8 +3754,14 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj)
                dev_priv->mm.interruptible = was_interruptible;
        }
 
-       if (obj->base.map_list.map)
-               drm_gem_free_mmap_offset(&obj->base);
+       obj->pages_pin_count = 0;
+       i915_gem_object_put_pages(obj);
+       i915_gem_object_free_mmap_offset(obj);
+
+       BUG_ON(obj->pages);
+
+       if (obj->base.import_attach)
+               drm_prime_gem_destroy(&obj->base, NULL);
 
        drm_gem_object_release(&obj->base);
        i915_gem_info_remove_obj(dev_priv, obj->base.size);
@@ -3590,7 +3792,7 @@ i915_gem_idle(struct drm_device *dev)
 
        /* Under UMS, be paranoid and evict. */
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               i915_gem_evict_everything(dev, false);
+               i915_gem_evict_everything(dev);
 
        i915_gem_reset_fences(dev);
 
@@ -3891,7 +4093,6 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data,
        }
 
        BUG_ON(!list_empty(&dev_priv->mm.active_list));
-       BUG_ON(!list_empty(&dev_priv->mm.flushing_list));
        BUG_ON(!list_empty(&dev_priv->mm.inactive_list));
        mutex_unlock(&dev->struct_mutex);
 
@@ -3939,7 +4140,6 @@ init_ring_lists(struct intel_ring_buffer *ring)
 {
        INIT_LIST_HEAD(&ring->active_list);
        INIT_LIST_HEAD(&ring->request_list);
-       INIT_LIST_HEAD(&ring->gpu_write_list);
 }
 
 void
@@ -3949,10 +4149,10 @@ i915_gem_load(struct drm_device *dev)
        drm_i915_private_t *dev_priv = dev->dev_private;
 
        INIT_LIST_HEAD(&dev_priv->mm.active_list);
-       INIT_LIST_HEAD(&dev_priv->mm.flushing_list);
        INIT_LIST_HEAD(&dev_priv->mm.inactive_list);
+       INIT_LIST_HEAD(&dev_priv->mm.unbound_list);
+       INIT_LIST_HEAD(&dev_priv->mm.bound_list);
        INIT_LIST_HEAD(&dev_priv->mm.fence_list);
-       INIT_LIST_HEAD(&dev_priv->mm.gtt_list);
        for (i = 0; i < I915_NUM_RINGS; i++)
                init_ring_lists(&dev_priv->ring[i]);
        for (i = 0; i < I915_MAX_NUM_FENCES; i++)
@@ -4196,18 +4396,6 @@ void i915_gem_release(struct drm_device *dev, struct drm_file *file)
        spin_unlock(&file_priv->mm.lock);
 }
 
-static int
-i915_gpu_is_active(struct drm_device *dev)
-{
-       drm_i915_private_t *dev_priv = dev->dev_private;
-       int lists_empty;
-
-       lists_empty = list_empty(&dev_priv->mm.flushing_list) &&
-                     list_empty(&dev_priv->mm.active_list);
-
-       return !lists_empty;
-}
-
 static int
 i915_gem_inactive_shrink(struct shrinker *shrinker, struct shrink_control *sc)
 {
@@ -4216,60 +4404,27 @@ i915_gem_inactive_shrink(struct shrinker *shrinker, struct shrink_control *sc)
                             struct drm_i915_private,
                             mm.inactive_shrinker);
        struct drm_device *dev = dev_priv->dev;
-       struct drm_i915_gem_object *obj, *next;
+       struct drm_i915_gem_object *obj;
        int nr_to_scan = sc->nr_to_scan;
        int cnt;
 
        if (!mutex_trylock(&dev->struct_mutex))
                return 0;
 
-       /* "fast-path" to count number of available objects */
-       if (nr_to_scan == 0) {
-               cnt = 0;
-               list_for_each_entry(obj,
-                                   &dev_priv->mm.inactive_list,
-                                   mm_list)
-                       cnt++;
-               mutex_unlock(&dev->struct_mutex);
-               return cnt / 100 * sysctl_vfs_cache_pressure;
-       }
-
-rescan:
-       /* first scan for clean buffers */
-       i915_gem_retire_requests(dev);
-
-       list_for_each_entry_safe(obj, next,
-                                &dev_priv->mm.inactive_list,
-                                mm_list) {
-               if (i915_gem_object_is_purgeable(obj)) {
-                       if (i915_gem_object_unbind(obj) == 0 &&
-                           --nr_to_scan == 0)
-                               break;
-               }
+       if (nr_to_scan) {
+               nr_to_scan -= i915_gem_purge(dev_priv, nr_to_scan);
+               if (nr_to_scan > 0)
+                       i915_gem_shrink_all(dev_priv);
        }
 
-       /* second pass, evict/count anything still on the inactive list */
        cnt = 0;
-       list_for_each_entry_safe(obj, next,
-                                &dev_priv->mm.inactive_list,
-                                mm_list) {
-               if (nr_to_scan &&
-                   i915_gem_object_unbind(obj) == 0)
-                       nr_to_scan--;
-               else
-                       cnt++;
-       }
+       list_for_each_entry(obj, &dev_priv->mm.unbound_list, gtt_list)
+               if (obj->pages_pin_count == 0)
+                       cnt += obj->base.size >> PAGE_SHIFT;
+       list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list)
+               if (obj->pin_count == 0 && obj->pages_pin_count == 0)
+                       cnt += obj->base.size >> PAGE_SHIFT;
 
-       if (nr_to_scan && i915_gpu_is_active(dev)) {
-               /*
-                * We are desperate for pages, so as a last resort, wait
-                * for the GPU to finish and discard whatever we can.
-                * This has a dramatic impact to reduce the number of
-                * OOM-killer events whilst running the GPU aggressively.
-                */
-               if (i915_gpu_idle(dev) == 0)
-                       goto rescan;
-       }
        mutex_unlock(&dev->struct_mutex);
-       return cnt / 100 * sysctl_vfs_cache_pressure;
+       return cnt;
 }
index a9d58d72bb4da2215592bb8ba6554e9835fb48cf..4aa7ecf77ede168e9e829391bcb766b1ba2821da 100644 (file)
@@ -97,8 +97,7 @@
 
 static struct i915_hw_context *
 i915_gem_context_get(struct drm_i915_file_private *file_priv, u32 id);
-static int do_switch(struct drm_i915_gem_object *from_obj,
-                    struct i915_hw_context *to, u32 seqno);
+static int do_switch(struct i915_hw_context *to);
 
 static int get_context_size(struct drm_device *dev)
 {
@@ -113,7 +112,10 @@ static int get_context_size(struct drm_device *dev)
                break;
        case 7:
                reg = I915_READ(GEN7_CXT_SIZE);
-               ret = GEN7_CXT_TOTAL_SIZE(reg) * 64;
+               if (IS_HASWELL(dev))
+                       ret = HSW_CXT_TOTAL_SIZE(reg) * 64;
+               else
+                       ret = GEN7_CXT_TOTAL_SIZE(reg) * 64;
                break;
        default:
                BUG();
@@ -219,20 +221,21 @@ static int create_default_context(struct drm_i915_private *dev_priv)
         * default context.
         */
        dev_priv->ring[RCS].default_context = ctx;
-       ret = i915_gem_object_pin(ctx->obj, CONTEXT_ALIGN, false);
-       if (ret) {
-               do_destroy(ctx);
-               return ret;
-       }
+       ret = i915_gem_object_pin(ctx->obj, CONTEXT_ALIGN, false, false);
+       if (ret)
+               goto err_destroy;
 
-       ret = do_switch(NULL, ctx, 0);
-       if (ret) {
-               i915_gem_object_unpin(ctx->obj);
-               do_destroy(ctx);
-       } else {
-               DRM_DEBUG_DRIVER("Default HW context loaded\n");
-       }
+       ret = do_switch(ctx);
+       if (ret)
+               goto err_unpin;
 
+       DRM_DEBUG_DRIVER("Default HW context loaded\n");
+       return 0;
+
+err_unpin:
+       i915_gem_object_unpin(ctx->obj);
+err_destroy:
+       do_destroy(ctx);
        return ret;
 }
 
@@ -359,18 +362,19 @@ mi_set_context(struct intel_ring_buffer *ring,
        return ret;
 }
 
-static int do_switch(struct drm_i915_gem_object *from_obj,
-                    struct i915_hw_context *to,
-                    u32 seqno)
+static int do_switch(struct i915_hw_context *to)
 {
-       struct intel_ring_buffer *ring = NULL;
+       struct intel_ring_buffer *ring = to->ring;
+       struct drm_i915_gem_object *from_obj = ring->last_context_obj;
        u32 hw_flags = 0;
        int ret;
 
-       BUG_ON(to == NULL);
        BUG_ON(from_obj != NULL && from_obj->pin_count == 0);
 
-       ret = i915_gem_object_pin(to->obj, CONTEXT_ALIGN, false);
+       if (from_obj == to->obj)
+               return 0;
+
+       ret = i915_gem_object_pin(to->obj, CONTEXT_ALIGN, false, false);
        if (ret)
                return ret;
 
@@ -393,7 +397,6 @@ static int do_switch(struct drm_i915_gem_object *from_obj,
        else if (WARN_ON_ONCE(from_obj == to->obj)) /* not yet expected */
                hw_flags |= MI_FORCE_RESTORE;
 
-       ring = to->ring;
        ret = mi_set_context(ring, to, hw_flags);
        if (ret) {
                i915_gem_object_unpin(to->obj);
@@ -407,6 +410,7 @@ static int do_switch(struct drm_i915_gem_object *from_obj,
         * MI_SET_CONTEXT instead of when the next seqno has completed.
         */
        if (from_obj != NULL) {
+               u32 seqno = i915_gem_next_request_seqno(ring);
                from_obj->base.read_domains = I915_GEM_DOMAIN_INSTRUCTION;
                i915_gem_object_move_to_active(from_obj, ring, seqno);
                /* As long as MI_SET_CONTEXT is serializing, ie. it flushes the
@@ -417,7 +421,7 @@ static int do_switch(struct drm_i915_gem_object *from_obj,
                 * swapped, but there is no way to do that yet.
                 */
                from_obj->dirty = 1;
-               BUG_ON(from_obj->ring != to->ring);
+               BUG_ON(from_obj->ring != ring);
                i915_gem_object_unpin(from_obj);
 
                drm_gem_object_unreference(&from_obj->base);
@@ -448,9 +452,7 @@ int i915_switch_context(struct intel_ring_buffer *ring,
                        int to_id)
 {
        struct drm_i915_private *dev_priv = ring->dev->dev_private;
-       struct drm_i915_file_private *file_priv = NULL;
        struct i915_hw_context *to;
-       struct drm_i915_gem_object *from_obj = ring->last_context_obj;
 
        if (dev_priv->hw_contexts_disabled)
                return 0;
@@ -458,21 +460,18 @@ int i915_switch_context(struct intel_ring_buffer *ring,
        if (ring != &dev_priv->ring[RCS])
                return 0;
 
-       if (file)
-               file_priv = file->driver_priv;
-
        if (to_id == DEFAULT_CONTEXT_ID) {
                to = ring->default_context;
        } else {
-               to = i915_gem_context_get(file_priv, to_id);
+               if (file == NULL)
+                       return -EINVAL;
+
+               to = i915_gem_context_get(file->driver_priv, to_id);
                if (to == NULL)
                        return -ENOENT;
        }
 
-       if (from_obj == to->obj)
-               return 0;
-
-       return do_switch(from_obj, to, i915_gem_next_request_seqno(to->ring));
+       return do_switch(to);
 }
 
 int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
index aa308e1337db7c8bacbf73d00dc80badecb617a2..ca3497e1108cd5e63c8f5655c5ec23bdd688ef69 100644 (file)
 #include <linux/dma-buf.h>
 
 static struct sg_table *i915_gem_map_dma_buf(struct dma_buf_attachment *attachment,
-                                     enum dma_data_direction dir)
+                                            enum dma_data_direction dir)
 {
        struct drm_i915_gem_object *obj = attachment->dmabuf->priv;
-       struct drm_device *dev = obj->base.dev;
-       int npages = obj->base.size / PAGE_SIZE;
-       struct sg_table *sg = NULL;
-       int ret;
-       int nents;
+       struct sg_table *st;
+       struct scatterlist *src, *dst;
+       int ret, i;
 
-       ret = i915_mutex_lock_interruptible(dev);
+       ret = i915_mutex_lock_interruptible(obj->base.dev);
        if (ret)
                return ERR_PTR(ret);
 
-       if (!obj->pages) {
-               ret = i915_gem_object_get_pages_gtt(obj, __GFP_NORETRY | __GFP_NOWARN);
-               if (ret)
-                       goto out;
+       ret = i915_gem_object_get_pages(obj);
+       if (ret) {
+               st = ERR_PTR(ret);
+               goto out;
+       }
+
+       /* Copy sg so that we make an independent mapping */
+       st = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
+       if (st == NULL) {
+               st = ERR_PTR(-ENOMEM);
+               goto out;
        }
 
-       /* link the pages into an SG then map the sg */
-       sg = drm_prime_pages_to_sg(obj->pages, npages);
-       nents = dma_map_sg(attachment->dev, sg->sgl, sg->nents, dir);
+       ret = sg_alloc_table(st, obj->pages->nents, GFP_KERNEL);
+       if (ret) {
+               kfree(st);
+               st = ERR_PTR(ret);
+               goto out;
+       }
+
+       src = obj->pages->sgl;
+       dst = st->sgl;
+       for (i = 0; i < obj->pages->nents; i++) {
+               sg_set_page(dst, sg_page(src), PAGE_SIZE, 0);
+               dst = sg_next(dst);
+               src = sg_next(src);
+       }
+
+       if (!dma_map_sg(attachment->dev, st->sgl, st->nents, dir)) {
+               sg_free_table(st);
+               kfree(st);
+               st = ERR_PTR(-ENOMEM);
+               goto out;
+       }
+
+       i915_gem_object_pin_pages(obj);
+
 out:
-       mutex_unlock(&dev->struct_mutex);
-       return sg;
+       mutex_unlock(&obj->base.dev->struct_mutex);
+       return st;
 }
 
 static void i915_gem_unmap_dma_buf(struct dma_buf_attachment *attachment,
-                           struct sg_table *sg, enum dma_data_direction dir)
+                                  struct sg_table *sg,
+                                  enum dma_data_direction dir)
 {
        dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, dir);
        sg_free_table(sg);
@@ -78,7 +105,9 @@ static void *i915_gem_dmabuf_vmap(struct dma_buf *dma_buf)
 {
        struct drm_i915_gem_object *obj = dma_buf->priv;
        struct drm_device *dev = obj->base.dev;
-       int ret;
+       struct scatterlist *sg;
+       struct page **pages;
+       int ret, i;
 
        ret = i915_mutex_lock_interruptible(dev);
        if (ret)
@@ -89,24 +118,34 @@ static void *i915_gem_dmabuf_vmap(struct dma_buf *dma_buf)
                goto out_unlock;
        }
 
-       if (!obj->pages) {
-               ret = i915_gem_object_get_pages_gtt(obj, __GFP_NORETRY | __GFP_NOWARN);
-               if (ret) {
-                       mutex_unlock(&dev->struct_mutex);
-                       return ERR_PTR(ret);
-               }
-       }
+       ret = i915_gem_object_get_pages(obj);
+       if (ret)
+               goto error;
 
-       obj->dma_buf_vmapping = vmap(obj->pages, obj->base.size / PAGE_SIZE, 0, PAGE_KERNEL);
-       if (!obj->dma_buf_vmapping) {
-               DRM_ERROR("failed to vmap object\n");
-               goto out_unlock;
-       }
+       ret = -ENOMEM;
+
+       pages = drm_malloc_ab(obj->pages->nents, sizeof(struct page *));
+       if (pages == NULL)
+               goto error;
+
+       for_each_sg(obj->pages->sgl, sg, obj->pages->nents, i)
+               pages[i] = sg_page(sg);
+
+       obj->dma_buf_vmapping = vmap(pages, obj->pages->nents, 0, PAGE_KERNEL);
+       drm_free_large(pages);
+
+       if (!obj->dma_buf_vmapping)
+               goto error;
 
        obj->vmapping_count = 1;
+       i915_gem_object_pin_pages(obj);
 out_unlock:
        mutex_unlock(&dev->struct_mutex);
        return obj->dma_buf_vmapping;
+
+error:
+       mutex_unlock(&dev->struct_mutex);
+       return ERR_PTR(ret);
 }
 
 static void i915_gem_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr)
@@ -119,10 +158,11 @@ static void i915_gem_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr)
        if (ret)
                return;
 
-       --obj->vmapping_count;
-       if (obj->vmapping_count == 0) {
+       if (--obj->vmapping_count == 0) {
                vunmap(obj->dma_buf_vmapping);
                obj->dma_buf_vmapping = NULL;
+
+               i915_gem_object_unpin_pages(obj);
        }
        mutex_unlock(&dev->struct_mutex);
 }
@@ -151,6 +191,22 @@ static int i915_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *
        return -EINVAL;
 }
 
+static int i915_gem_begin_cpu_access(struct dma_buf *dma_buf, size_t start, size_t length, enum dma_data_direction direction)
+{
+       struct drm_i915_gem_object *obj = dma_buf->priv;
+       struct drm_device *dev = obj->base.dev;
+       int ret;
+       bool write = (direction == DMA_BIDIRECTIONAL || direction == DMA_TO_DEVICE);
+
+       ret = i915_mutex_lock_interruptible(dev);
+       if (ret)
+               return ret;
+
+       ret = i915_gem_object_set_to_cpu_domain(obj, write);
+       mutex_unlock(&dev->struct_mutex);
+       return ret;
+}
+
 static const struct dma_buf_ops i915_dmabuf_ops =  {
        .map_dma_buf = i915_gem_map_dma_buf,
        .unmap_dma_buf = i915_gem_unmap_dma_buf,
@@ -162,25 +218,47 @@ static const struct dma_buf_ops i915_dmabuf_ops =  {
        .mmap = i915_gem_dmabuf_mmap,
        .vmap = i915_gem_dmabuf_vmap,
        .vunmap = i915_gem_dmabuf_vunmap,
+       .begin_cpu_access = i915_gem_begin_cpu_access,
 };
 
 struct dma_buf *i915_gem_prime_export(struct drm_device *dev,
-                               struct drm_gem_object *gem_obj, int flags)
+                                     struct drm_gem_object *gem_obj, int flags)
 {
        struct drm_i915_gem_object *obj = to_intel_bo(gem_obj);
 
-       return dma_buf_export(obj, &i915_dmabuf_ops,
-                                                 obj->base.size, 0600);
+       return dma_buf_export(obj, &i915_dmabuf_ops, obj->base.size, 0600);
+}
+
+static int i915_gem_object_get_pages_dmabuf(struct drm_i915_gem_object *obj)
+{
+       struct sg_table *sg;
+
+       sg = dma_buf_map_attachment(obj->base.import_attach, DMA_BIDIRECTIONAL);
+       if (IS_ERR(sg))
+               return PTR_ERR(sg);
+
+       obj->pages = sg;
+       obj->has_dma_mapping = true;
+       return 0;
 }
 
+static void i915_gem_object_put_pages_dmabuf(struct drm_i915_gem_object *obj)
+{
+       dma_buf_unmap_attachment(obj->base.import_attach,
+                                obj->pages, DMA_BIDIRECTIONAL);
+       obj->has_dma_mapping = false;
+}
+
+static const struct drm_i915_gem_object_ops i915_gem_object_dmabuf_ops = {
+       .get_pages = i915_gem_object_get_pages_dmabuf,
+       .put_pages = i915_gem_object_put_pages_dmabuf,
+};
+
 struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev,
-                               struct dma_buf *dma_buf)
+                                            struct dma_buf *dma_buf)
 {
        struct dma_buf_attachment *attach;
-       struct sg_table *sg;
        struct drm_i915_gem_object *obj;
-       int npages;
-       int size;
        int ret;
 
        /* is this one of own objects? */
@@ -198,34 +276,24 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *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;
-       }
-
-       size = dma_buf->size;
-       npages = size / PAGE_SIZE;
 
        obj = kzalloc(sizeof(*obj), GFP_KERNEL);
        if (obj == NULL) {
                ret = -ENOMEM;
-               goto fail_unmap;
+               goto fail_detach;
        }
 
-       ret = drm_gem_private_object_init(dev, &obj->base, size);
+       ret = drm_gem_private_object_init(dev, &obj->base, dma_buf->size);
        if (ret) {
                kfree(obj);
-               goto fail_unmap;
+               goto fail_detach;
        }
 
-       obj->sg_table = sg;
+       i915_gem_object_init(obj, &i915_gem_object_dmabuf_ops);
        obj->base.import_attach = attach;
 
        return &obj->base;
 
-fail_unmap:
-       dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL);
 fail_detach:
        dma_buf_detach(dma_buf, attach);
        return ERR_PTR(ret);
index eba0308f10e3d5cdad1c8c9b71333bfb46c57896..a2d8acde85508e2aa11ab05073cb0db8b7701339 100644 (file)
@@ -44,7 +44,8 @@ mark_free(struct drm_i915_gem_object *obj, struct list_head *unwind)
 
 int
 i915_gem_evict_something(struct drm_device *dev, int min_size,
-                        unsigned alignment, bool mappable)
+                        unsigned alignment, unsigned cache_level,
+                        bool mappable, bool nonblocking)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct list_head eviction_list, unwind_list;
@@ -79,11 +80,11 @@ i915_gem_evict_something(struct drm_device *dev, int min_size,
        INIT_LIST_HEAD(&unwind_list);
        if (mappable)
                drm_mm_init_scan_with_range(&dev_priv->mm.gtt_space,
-                                           min_size, alignment, 0,
+                                           min_size, alignment, cache_level,
                                            0, dev_priv->mm.gtt_mappable_end);
        else
                drm_mm_init_scan(&dev_priv->mm.gtt_space,
-                                min_size, alignment, 0);
+                                min_size, alignment, cache_level);
 
        /* First see if there is a large enough contiguous idle region... */
        list_for_each_entry(obj, &dev_priv->mm.inactive_list, mm_list) {
@@ -91,29 +92,16 @@ i915_gem_evict_something(struct drm_device *dev, int min_size,
                        goto found;
        }
 
-       /* Now merge in the soon-to-be-expired objects... */
-       list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) {
-               /* Does the object require an outstanding flush? */
-               if (obj->base.write_domain)
-                       continue;
-
-               if (mark_free(obj, &unwind_list))
-                       goto found;
-       }
+       if (nonblocking)
+               goto none;
 
-       /* Finally add anything with a pending flush (in order of retirement) */
-       list_for_each_entry(obj, &dev_priv->mm.flushing_list, mm_list) {
-               if (mark_free(obj, &unwind_list))
-                       goto found;
-       }
+       /* Now merge in the soon-to-be-expired objects... */
        list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) {
-               if (!obj->base.write_domain)
-                       continue;
-
                if (mark_free(obj, &unwind_list))
                        goto found;
        }
 
+none:
        /* Nothing found, clean up and bail out! */
        while (!list_empty(&unwind_list)) {
                obj = list_first_entry(&unwind_list,
@@ -164,7 +152,7 @@ found:
 }
 
 int
-i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only)
+i915_gem_evict_everything(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct drm_i915_gem_object *obj, *next;
@@ -172,12 +160,11 @@ i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only)
        int ret;
 
        lists_empty = (list_empty(&dev_priv->mm.inactive_list) &&
-                      list_empty(&dev_priv->mm.flushing_list) &&
                       list_empty(&dev_priv->mm.active_list));
        if (lists_empty)
                return -ENOSPC;
 
-       trace_i915_gem_evict_everything(dev, purgeable_only);
+       trace_i915_gem_evict_everything(dev);
 
        /* The gpu_idle will flush everything in the write domain to the
         * active list. Then we must move everything off the active list
@@ -189,16 +176,11 @@ i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only)
 
        i915_gem_retire_requests(dev);
 
-       BUG_ON(!list_empty(&dev_priv->mm.flushing_list));
-
        /* Having flushed everything, unbind() should never raise an error */
        list_for_each_entry_safe(obj, next,
-                                &dev_priv->mm.inactive_list, mm_list) {
-               if (!purgeable_only || obj->madv != I915_MADV_WILLNEED) {
-                       if (obj->pin_count == 0)
-                               WARN_ON(i915_gem_object_unbind(obj));
-               }
-       }
+                                &dev_priv->mm.inactive_list, mm_list)
+               if (obj->pin_count == 0)
+                       WARN_ON(i915_gem_object_unbind(obj));
 
        return 0;
 }
index ff2819ea08130fb98da5f67550a18d9ca3c5974d..6a2f3e50c7149f863fa5d6d496b275bb7b45f73a 100644 (file)
 #include "intel_drv.h"
 #include <linux/dma_remapping.h>
 
-struct change_domains {
-       uint32_t invalidate_domains;
-       uint32_t flush_domains;
-       uint32_t flush_rings;
-       uint32_t flips;
-};
-
-/*
- * Set the next domain for the specified object. This
- * may not actually perform the necessary flushing/invaliding though,
- * as that may want to be batched with other set_domain operations
- *
- * This is (we hope) the only really tricky part of gem. The goal
- * is fairly simple -- track which caches hold bits of the object
- * and make sure they remain coherent. A few concrete examples may
- * help to explain how it works. For shorthand, we use the notation
- * (read_domains, write_domain), e.g. (CPU, CPU) to indicate the
- * a pair of read and write domain masks.
- *
- * Case 1: the batch buffer
- *
- *     1. Allocated
- *     2. Written by CPU
- *     3. Mapped to GTT
- *     4. Read by GPU
- *     5. Unmapped from GTT
- *     6. Freed
- *
- *     Let's take these a step at a time
- *
- *     1. Allocated
- *             Pages allocated from the kernel may still have
- *             cache contents, so we set them to (CPU, CPU) always.
- *     2. Written by CPU (using pwrite)
- *             The pwrite function calls set_domain (CPU, CPU) and
- *             this function does nothing (as nothing changes)
- *     3. Mapped by GTT
- *             This function asserts that the object is not
- *             currently in any GPU-based read or write domains
- *     4. Read by GPU
- *             i915_gem_execbuffer calls set_domain (COMMAND, 0).
- *             As write_domain is zero, this function adds in the
- *             current read domains (CPU+COMMAND, 0).
- *             flush_domains is set to CPU.
- *             invalidate_domains is set to COMMAND
- *             clflush is run to get data out of the CPU caches
- *             then i915_dev_set_domain calls i915_gem_flush to
- *             emit an MI_FLUSH and drm_agp_chipset_flush
- *     5. Unmapped from GTT
- *             i915_gem_object_unbind calls set_domain (CPU, CPU)
- *             flush_domains and invalidate_domains end up both zero
- *             so no flushing/invalidating happens
- *     6. Freed
- *             yay, done
- *
- * Case 2: The shared render buffer
- *
- *     1. Allocated
- *     2. Mapped to GTT
- *     3. Read/written by GPU
- *     4. set_domain to (CPU,CPU)
- *     5. Read/written by CPU
- *     6. Read/written by GPU
- *
- *     1. Allocated
- *             Same as last example, (CPU, CPU)
- *     2. Mapped to GTT
- *             Nothing changes (assertions find that it is not in the GPU)
- *     3. Read/written by GPU
- *             execbuffer calls set_domain (RENDER, RENDER)
- *             flush_domains gets CPU
- *             invalidate_domains gets GPU
- *             clflush (obj)
- *             MI_FLUSH and drm_agp_chipset_flush
- *     4. set_domain (CPU, CPU)
- *             flush_domains gets GPU
- *             invalidate_domains gets CPU
- *             wait_rendering (obj) to make sure all drawing is complete.
- *             This will include an MI_FLUSH to get the data from GPU
- *             to memory
- *             clflush (obj) to invalidate the CPU cache
- *             Another MI_FLUSH in i915_gem_flush (eliminate this somehow?)
- *     5. Read/written by CPU
- *             cache lines are loaded and dirtied
- *     6. Read written by GPU
- *             Same as last GPU access
- *
- * Case 3: The constant buffer
- *
- *     1. Allocated
- *     2. Written by CPU
- *     3. Read by GPU
- *     4. Updated (written) by CPU again
- *     5. Read by GPU
- *
- *     1. Allocated
- *             (CPU, CPU)
- *     2. Written by CPU
- *             (CPU, CPU)
- *     3. Read by GPU
- *             (CPU+RENDER, 0)
- *             flush_domains = CPU
- *             invalidate_domains = RENDER
- *             clflush (obj)
- *             MI_FLUSH
- *             drm_agp_chipset_flush
- *     4. Updated (written) by CPU again
- *             (CPU, CPU)
- *             flush_domains = 0 (no previous write domain)
- *             invalidate_domains = 0 (no new read domains)
- *     5. Read by GPU
- *             (CPU+RENDER, 0)
- *             flush_domains = CPU
- *             invalidate_domains = RENDER
- *             clflush (obj)
- *             MI_FLUSH
- *             drm_agp_chipset_flush
- */
-static void
-i915_gem_object_set_to_gpu_domain(struct drm_i915_gem_object *obj,
-                                 struct intel_ring_buffer *ring,
-                                 struct change_domains *cd)
-{
-       uint32_t invalidate_domains = 0, flush_domains = 0;
-
-       /*
-        * If the object isn't moving to a new write domain,
-        * let the object stay in multiple read domains
-        */
-       if (obj->base.pending_write_domain == 0)
-               obj->base.pending_read_domains |= obj->base.read_domains;
-
-       /*
-        * Flush the current write domain if
-        * the new read domains don't match. Invalidate
-        * any read domains which differ from the old
-        * write domain
-        */
-       if (obj->base.write_domain &&
-           (((obj->base.write_domain != obj->base.pending_read_domains ||
-              obj->ring != ring)) ||
-            (obj->fenced_gpu_access && !obj->pending_fenced_gpu_access))) {
-               flush_domains |= obj->base.write_domain;
-               invalidate_domains |=
-                       obj->base.pending_read_domains & ~obj->base.write_domain;
-       }
-       /*
-        * Invalidate any read caches which may have
-        * stale data. That is, any new read domains.
-        */
-       invalidate_domains |= obj->base.pending_read_domains & ~obj->base.read_domains;
-       if ((flush_domains | invalidate_domains) & I915_GEM_DOMAIN_CPU)
-               i915_gem_clflush_object(obj);
-
-       if (obj->base.pending_write_domain)
-               cd->flips |= atomic_read(&obj->pending_flip);
-
-       /* The actual obj->write_domain will be updated with
-        * pending_write_domain after we emit the accumulated flush for all
-        * of our domain changes in execbuffers (which clears objects'
-        * write_domains).  So if we have a current write domain that we
-        * aren't changing, set pending_write_domain to that.
-        */
-       if (flush_domains == 0 && obj->base.pending_write_domain == 0)
-               obj->base.pending_write_domain = obj->base.write_domain;
-
-       cd->invalidate_domains |= invalidate_domains;
-       cd->flush_domains |= flush_domains;
-       if (flush_domains & I915_GEM_GPU_DOMAINS)
-               cd->flush_rings |= intel_ring_flag(obj->ring);
-       if (invalidate_domains & I915_GEM_GPU_DOMAINS)
-               cd->flush_rings |= intel_ring_flag(ring);
-}
-
 struct eb_objects {
        int and;
        struct hlist_head buckets[0];
@@ -218,6 +44,7 @@ eb_create(int size)
 {
        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) +
@@ -269,6 +96,7 @@ eb_destroy(struct eb_objects *eb)
 static inline int use_cpu_reloc(struct drm_i915_gem_object *obj)
 {
        return (obj->base.write_domain == I915_GEM_DOMAIN_CPU ||
+               !obj->map_and_fenceable ||
                obj->cache_level != I915_CACHE_NONE);
 }
 
@@ -383,7 +211,8 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
                if (ret)
                        return ret;
 
-               vaddr = kmap_atomic(obj->pages[reloc->offset >> PAGE_SHIFT]);
+               vaddr = kmap_atomic(i915_gem_object_get_page(obj,
+                                                            reloc->offset >> PAGE_SHIFT));
                *(uint32_t *)(vaddr + page_offset) = reloc->delta;
                kunmap_atomic(vaddr);
        } else {
@@ -504,7 +333,8 @@ i915_gem_execbuffer_relocate(struct drm_device *dev,
        return ret;
 }
 
-#define  __EXEC_OBJECT_HAS_FENCE (1<<31)
+#define  __EXEC_OBJECT_HAS_PIN (1<<31)
+#define  __EXEC_OBJECT_HAS_FENCE (1<<30)
 
 static int
 need_reloc_mappable(struct drm_i915_gem_object *obj)
@@ -514,9 +344,10 @@ need_reloc_mappable(struct drm_i915_gem_object *obj)
 }
 
 static int
-pin_and_fence_object(struct drm_i915_gem_object *obj,
-                    struct intel_ring_buffer *ring)
+i915_gem_execbuffer_reserve_object(struct drm_i915_gem_object *obj,
+                                  struct intel_ring_buffer *ring)
 {
+       struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
        struct drm_i915_gem_exec_object2 *entry = obj->exec_entry;
        bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4;
        bool need_fence, need_mappable;
@@ -528,15 +359,17 @@ pin_and_fence_object(struct drm_i915_gem_object *obj,
                obj->tiling_mode != I915_TILING_NONE;
        need_mappable = need_fence || need_reloc_mappable(obj);
 
-       ret = i915_gem_object_pin(obj, entry->alignment, need_mappable);
+       ret = i915_gem_object_pin(obj, entry->alignment, need_mappable, false);
        if (ret)
                return ret;
 
+       entry->flags |= __EXEC_OBJECT_HAS_PIN;
+
        if (has_fenced_gpu_access) {
                if (entry->flags & EXEC_OBJECT_NEEDS_FENCE) {
                        ret = i915_gem_object_get_fence(obj);
                        if (ret)
-                               goto err_unpin;
+                               return ret;
 
                        if (i915_gem_object_pin_fence(obj))
                                entry->flags |= __EXEC_OBJECT_HAS_FENCE;
@@ -545,12 +378,35 @@ pin_and_fence_object(struct drm_i915_gem_object *obj,
                }
        }
 
+       /* Ensure ppgtt mapping exists if needed */
+       if (dev_priv->mm.aliasing_ppgtt && !obj->has_aliasing_ppgtt_mapping) {
+               i915_ppgtt_bind_object(dev_priv->mm.aliasing_ppgtt,
+                                      obj, obj->cache_level);
+
+               obj->has_aliasing_ppgtt_mapping = 1;
+       }
+
        entry->offset = obj->gtt_offset;
        return 0;
+}
 
-err_unpin:
-       i915_gem_object_unpin(obj);
-       return ret;
+static void
+i915_gem_execbuffer_unreserve_object(struct drm_i915_gem_object *obj)
+{
+       struct drm_i915_gem_exec_object2 *entry;
+
+       if (!obj->gtt_space)
+               return;
+
+       entry = obj->exec_entry;
+
+       if (entry->flags & __EXEC_OBJECT_HAS_FENCE)
+               i915_gem_object_unpin_fence(obj);
+
+       if (entry->flags & __EXEC_OBJECT_HAS_PIN)
+               i915_gem_object_unpin(obj);
+
+       entry->flags &= ~(__EXEC_OBJECT_HAS_FENCE | __EXEC_OBJECT_HAS_PIN);
 }
 
 static int
@@ -558,11 +414,10 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
                            struct drm_file *file,
                            struct list_head *objects)
 {
-       drm_i915_private_t *dev_priv = ring->dev->dev_private;
        struct drm_i915_gem_object *obj;
-       int ret, retry;
-       bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4;
        struct list_head ordered_objects;
+       bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4;
+       int retry;
 
        INIT_LIST_HEAD(&ordered_objects);
        while (!list_empty(objects)) {
@@ -587,6 +442,7 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
 
                obj->base.pending_read_domains = 0;
                obj->base.pending_write_domain = 0;
+               obj->pending_fenced_gpu_access = false;
        }
        list_splice(&ordered_objects, objects);
 
@@ -599,12 +455,12 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
         * 2.  Bind new objects.
         * 3.  Decrement pin count.
         *
-        * This avoid unnecessary unbinding of later objects in order to makr
+        * This avoid unnecessary unbinding of later objects in order to make
         * room for the earlier objects *unless* we need to defragment.
         */
        retry = 0;
        do {
-               ret = 0;
+               int ret = 0;
 
                /* Unbind any ill-fitting objects or pin. */
                list_for_each_entry(obj, objects, exec_list) {
@@ -624,7 +480,7 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
                            (need_mappable && !obj->map_and_fenceable))
                                ret = i915_gem_object_unbind(obj);
                        else
-                               ret = pin_and_fence_object(obj, ring);
+                               ret = i915_gem_execbuffer_reserve_object(obj, ring);
                        if (ret)
                                goto err;
                }
@@ -634,77 +490,22 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
                        if (obj->gtt_space)
                                continue;
 
-                       ret = pin_and_fence_object(obj, ring);
-                       if (ret) {
-                               int ret_ignore;
-
-                               /* This can potentially raise a harmless
-                                * -EINVAL if we failed to bind in the above
-                                * call. It cannot raise -EINTR since we know
-                                * that the bo is freshly bound and so will
-                                * not need to be flushed or waited upon.
-                                */
-                               ret_ignore = i915_gem_object_unbind(obj);
-                               (void)ret_ignore;
-                               WARN_ON(obj->gtt_space);
-                               break;
-                       }
+                       ret = i915_gem_execbuffer_reserve_object(obj, ring);
+                       if (ret)
+                               goto err;
                }
 
-               /* Decrement pin count for bound objects */
-               list_for_each_entry(obj, objects, exec_list) {
-                       struct drm_i915_gem_exec_object2 *entry;
-
-                       if (!obj->gtt_space)
-                               continue;
-
-                       entry = obj->exec_entry;
-                       if (entry->flags & __EXEC_OBJECT_HAS_FENCE) {
-                               i915_gem_object_unpin_fence(obj);
-                               entry->flags &= ~__EXEC_OBJECT_HAS_FENCE;
-                       }
-
-                       i915_gem_object_unpin(obj);
-
-                       /* ... and ensure ppgtt mapping exist if needed. */
-                       if (dev_priv->mm.aliasing_ppgtt && !obj->has_aliasing_ppgtt_mapping) {
-                               i915_ppgtt_bind_object(dev_priv->mm.aliasing_ppgtt,
-                                                      obj, obj->cache_level);
+err:           /* Decrement pin count for bound objects */
+               list_for_each_entry(obj, objects, exec_list)
+                       i915_gem_execbuffer_unreserve_object(obj);
 
-                               obj->has_aliasing_ppgtt_mapping = 1;
-                       }
-               }
-
-               if (ret != -ENOSPC || retry > 1)
+               if (ret != -ENOSPC || retry++)
                        return ret;
 
-               /* First attempt, just clear anything that is purgeable.
-                * Second attempt, clear the entire GTT.
-                */
-               ret = i915_gem_evict_everything(ring->dev, retry == 0);
+               ret = i915_gem_evict_everything(ring->dev);
                if (ret)
                        return ret;
-
-               retry++;
        } while (1);
-
-err:
-       list_for_each_entry_continue_reverse(obj, objects, exec_list) {
-               struct drm_i915_gem_exec_object2 *entry;
-
-               if (!obj->gtt_space)
-                       continue;
-
-               entry = obj->exec_entry;
-               if (entry->flags & __EXEC_OBJECT_HAS_FENCE) {
-                       i915_gem_object_unpin_fence(obj);
-                       entry->flags &= ~__EXEC_OBJECT_HAS_FENCE;
-               }
-
-               i915_gem_object_unpin(obj);
-       }
-
-       return ret;
 }
 
 static int
@@ -810,18 +611,6 @@ err:
        return ret;
 }
 
-static void
-i915_gem_execbuffer_flush(struct drm_device *dev,
-                         uint32_t invalidate_domains,
-                         uint32_t flush_domains)
-{
-       if (flush_domains & I915_GEM_DOMAIN_CPU)
-               intel_gtt_chipset_flush();
-
-       if (flush_domains & I915_GEM_DOMAIN_GTT)
-               wmb();
-}
-
 static int
 i915_gem_execbuffer_wait_for_flips(struct intel_ring_buffer *ring, u32 flips)
 {
@@ -854,48 +643,45 @@ i915_gem_execbuffer_wait_for_flips(struct intel_ring_buffer *ring, u32 flips)
        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;
-       struct change_domains cd;
+       uint32_t flush_domains = 0;
+       uint32_t flips = 0;
        int ret;
 
-       memset(&cd, 0, sizeof(cd));
-       list_for_each_entry(obj, objects, exec_list)
-               i915_gem_object_set_to_gpu_domain(obj, ring, &cd);
-
-       if (cd.invalidate_domains | cd.flush_domains) {
-               i915_gem_execbuffer_flush(ring->dev,
-                                         cd.invalidate_domains,
-                                         cd.flush_domains);
-       }
-
-       if (cd.flips) {
-               ret = i915_gem_execbuffer_wait_for_flips(ring, cd.flips);
+       list_for_each_entry(obj, objects, exec_list) {
+               ret = i915_gem_object_sync(obj, ring);
                if (ret)
                        return ret;
+
+               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;
        }
 
-       list_for_each_entry(obj, objects, exec_list) {
-               ret = i915_gem_object_sync(obj, ring);
+       if (flips) {
+               ret = i915_gem_execbuffer_wait_for_flips(ring, flips);
                if (ret)
                        return ret;
        }
 
+       if (flush_domains & I915_GEM_DOMAIN_CPU)
+               intel_gtt_chipset_flush();
+
+       if (flush_domains & I915_GEM_DOMAIN_GTT)
+               wmb();
+
        /* Unconditionally invalidate gpu caches and ensure that we do flush
         * any residual writes from the previous batch.
         */
-       ret = i915_gem_flush_ring(ring,
-                                 I915_GEM_GPU_DOMAINS,
-                                 ring->gpu_caches_dirty ? I915_GEM_GPU_DOMAINS : 0);
-       if (ret)
-               return ret;
-
-       ring->gpu_caches_dirty = false;
-       return 0;
+       return intel_ring_invalidate_all_caches(ring);
 }
 
 static bool
@@ -943,9 +729,8 @@ i915_gem_execbuffer_move_to_active(struct list_head *objects,
        struct drm_i915_gem_object *obj;
 
        list_for_each_entry(obj, objects, exec_list) {
-                 u32 old_read = obj->base.read_domains;
-                 u32 old_write = obj->base.write_domain;
-
+               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;
@@ -954,17 +739,13 @@ i915_gem_execbuffer_move_to_active(struct list_head *objects,
                i915_gem_object_move_to_active(obj, ring, seqno);
                if (obj->base.write_domain) {
                        obj->dirty = 1;
-                       obj->pending_gpu_write = true;
-                       list_move_tail(&obj->gpu_write_list,
-                                      &ring->gpu_write_list);
+                       obj->last_write_seqno = seqno;
                        if (obj->pin_count) /* check for potential scanout */
-                               intel_mark_busy(ring->dev, obj);
+                               intel_mark_fb_busy(obj);
                }
 
                trace_i915_gem_object_change_domain(obj, old_read, old_write);
        }
-
-       intel_mark_busy(ring->dev, NULL);
 }
 
 static void
@@ -972,16 +753,11 @@ i915_gem_execbuffer_retire_commands(struct drm_device *dev,
                                    struct drm_file *file,
                                    struct intel_ring_buffer *ring)
 {
-       struct drm_i915_gem_request *request;
-
        /* Unconditionally force add_request to emit a full flush. */
        ring->gpu_caches_dirty = true;
 
        /* Add a breadcrumb for the completion of the batch buffer */
-       request = kzalloc(sizeof(*request), GFP_KERNEL);
-       if (request == NULL || i915_add_request(ring, file, request)) {
-               kfree(request);
-       }
+       (void)i915_add_request(ring, file, NULL);
 }
 
 static int
@@ -1327,8 +1103,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
                return -ENOMEM;
        }
        ret = copy_from_user(exec_list,
-                            (struct drm_i915_relocation_entry __user *)
-                            (uintptr_t) args->buffers_ptr,
+                            (void __user *)(uintptr_t)args->buffers_ptr,
                             sizeof(*exec_list) * args->buffer_count);
        if (ret != 0) {
                DRM_DEBUG("copy %d exec entries failed %d\n",
@@ -1367,8 +1142,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
                for (i = 0; i < args->buffer_count; i++)
                        exec_list[i].offset = exec2_list[i].offset;
                /* ... and back out to userspace */
-               ret = copy_to_user((struct drm_i915_relocation_entry __user *)
-                                  (uintptr_t) args->buffers_ptr,
+               ret = copy_to_user((void __user *)(uintptr_t)args->buffers_ptr,
                                   exec_list,
                                   sizeof(*exec_list) * args->buffer_count);
                if (ret) {
@@ -1422,8 +1196,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,
        ret = i915_gem_do_execbuffer(dev, data, file, args, exec2_list);
        if (!ret) {
                /* Copy the new buffer offsets back to the user's exec list. */
-               ret = copy_to_user((struct drm_i915_relocation_entry __user *)
-                                  (uintptr_t) args->buffers_ptr,
+               ret = copy_to_user((void __user *)(uintptr_t)args->buffers_ptr,
                                   exec2_list,
                                   sizeof(*exec2_list) * args->buffer_count);
                if (ret) {
index 60815b861ec2143dc1fdad526232d03bea1158fd..47e427e4bc67d997b2f0a071bd1fe30a37543738 100644 (file)
@@ -167,8 +167,7 @@ void i915_gem_cleanup_aliasing_ppgtt(struct drm_device *dev)
 }
 
 static void i915_ppgtt_insert_sg_entries(struct i915_hw_ppgtt *ppgtt,
-                                        struct scatterlist *sg_list,
-                                        unsigned sg_len,
+                                        const struct sg_table *pages,
                                         unsigned first_entry,
                                         uint32_t pte_flags)
 {
@@ -180,12 +179,12 @@ static void i915_ppgtt_insert_sg_entries(struct i915_hw_ppgtt *ppgtt,
        struct scatterlist *sg;
 
        /* init sg walking */
-       sg = sg_list;
+       sg = pages->sgl;
        i = 0;
        segment_len = sg_dma_len(sg) >> PAGE_SHIFT;
        m = 0;
 
-       while (i < sg_len) {
+       while (i < pages->nents) {
                pt_vaddr = kmap_atomic(ppgtt->pt_pages[act_pd]);
 
                for (j = first_pte; j < I915_PPGTT_PT_ENTRIES; j++) {
@@ -194,13 +193,11 @@ static void i915_ppgtt_insert_sg_entries(struct i915_hw_ppgtt *ppgtt,
                        pt_vaddr[j] = pte | pte_flags;
 
                        /* grab the next page */
-                       m++;
-                       if (m == segment_len) {
-                               sg = sg_next(sg);
-                               i++;
-                               if (i == sg_len)
+                       if (++m == segment_len) {
+                               if (++i == pages->nents)
                                        break;
 
+                               sg = sg_next(sg);
                                segment_len = sg_dma_len(sg) >> PAGE_SHIFT;
                                m = 0;
                        }
@@ -213,44 +210,10 @@ static void i915_ppgtt_insert_sg_entries(struct i915_hw_ppgtt *ppgtt,
        }
 }
 
-static void i915_ppgtt_insert_pages(struct i915_hw_ppgtt *ppgtt,
-                                   unsigned first_entry, unsigned num_entries,
-                                   struct page **pages, uint32_t pte_flags)
-{
-       uint32_t *pt_vaddr, pte;
-       unsigned act_pd = first_entry / I915_PPGTT_PT_ENTRIES;
-       unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES;
-       unsigned last_pte, i;
-       dma_addr_t page_addr;
-
-       while (num_entries) {
-               last_pte = first_pte + num_entries;
-               last_pte = min_t(unsigned, last_pte, I915_PPGTT_PT_ENTRIES);
-
-               pt_vaddr = kmap_atomic(ppgtt->pt_pages[act_pd]);
-
-               for (i = first_pte; i < last_pte; i++) {
-                       page_addr = page_to_phys(*pages);
-                       pte = GEN6_PTE_ADDR_ENCODE(page_addr);
-                       pt_vaddr[i] = pte | pte_flags;
-
-                       pages++;
-               }
-
-               kunmap_atomic(pt_vaddr);
-
-               num_entries -= last_pte - first_pte;
-               first_pte = 0;
-               act_pd++;
-       }
-}
-
 void i915_ppgtt_bind_object(struct i915_hw_ppgtt *ppgtt,
                            struct drm_i915_gem_object *obj,
                            enum i915_cache_level cache_level)
 {
-       struct drm_device *dev = obj->base.dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
        uint32_t pte_flags = GEN6_PTE_VALID;
 
        switch (cache_level) {
@@ -261,7 +224,7 @@ void i915_ppgtt_bind_object(struct i915_hw_ppgtt *ppgtt,
                pte_flags |= GEN6_PTE_CACHE_LLC;
                break;
        case I915_CACHE_NONE:
-               if (IS_HASWELL(dev))
+               if (IS_HASWELL(obj->base.dev))
                        pte_flags |= HSW_PTE_UNCACHED;
                else
                        pte_flags |= GEN6_PTE_UNCACHED;
@@ -270,26 +233,10 @@ void i915_ppgtt_bind_object(struct i915_hw_ppgtt *ppgtt,
                BUG();
        }
 
-       if (obj->sg_table) {
-               i915_ppgtt_insert_sg_entries(ppgtt,
-                                            obj->sg_table->sgl,
-                                            obj->sg_table->nents,
-                                            obj->gtt_space->start >> PAGE_SHIFT,
-                                            pte_flags);
-       } else if (dev_priv->mm.gtt->needs_dmar) {
-               BUG_ON(!obj->sg_list);
-
-               i915_ppgtt_insert_sg_entries(ppgtt,
-                                            obj->sg_list,
-                                            obj->num_sg,
-                                            obj->gtt_space->start >> PAGE_SHIFT,
-                                            pte_flags);
-       } else
-               i915_ppgtt_insert_pages(ppgtt,
-                                       obj->gtt_space->start >> PAGE_SHIFT,
-                                       obj->base.size >> PAGE_SHIFT,
-                                       obj->pages,
-                                       pte_flags);
+       i915_ppgtt_insert_sg_entries(ppgtt,
+                                    obj->pages,
+                                    obj->gtt_space->start >> PAGE_SHIFT,
+                                    pte_flags);
 }
 
 void i915_ppgtt_unbind_object(struct i915_hw_ppgtt *ppgtt,
@@ -351,7 +298,7 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev)
        intel_gtt_clear_range(dev_priv->mm.gtt_start / PAGE_SIZE,
                              (dev_priv->mm.gtt_end - dev_priv->mm.gtt_start) / PAGE_SIZE);
 
-       list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) {
+       list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) {
                i915_gem_clflush_object(obj);
                i915_gem_gtt_bind_object(obj, obj->cache_level);
        }
@@ -361,44 +308,26 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev)
 
 int i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj)
 {
-       struct drm_device *dev = obj->base.dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-
-       /* don't map imported dma buf objects */
-       if (dev_priv->mm.gtt->needs_dmar && !obj->sg_table)
-               return intel_gtt_map_memory(obj->pages,
-                                           obj->base.size >> PAGE_SHIFT,
-                                           &obj->sg_list,
-                                           &obj->num_sg);
-       else
+       if (obj->has_dma_mapping)
                return 0;
+
+       if (!dma_map_sg(&obj->base.dev->pdev->dev,
+                       obj->pages->sgl, obj->pages->nents,
+                       PCI_DMA_BIDIRECTIONAL))
+               return -ENOSPC;
+
+       return 0;
 }
 
 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;
-       struct drm_i915_private *dev_priv = dev->dev_private;
        unsigned int agp_type = cache_level_to_agp_type(dev, cache_level);
 
-       if (obj->sg_table) {
-               intel_gtt_insert_sg_entries(obj->sg_table->sgl,
-                                           obj->sg_table->nents,
-                                           obj->gtt_space->start >> PAGE_SHIFT,
-                                           agp_type);
-       } else if (dev_priv->mm.gtt->needs_dmar) {
-               BUG_ON(!obj->sg_list);
-
-               intel_gtt_insert_sg_entries(obj->sg_list,
-                                           obj->num_sg,
-                                           obj->gtt_space->start >> PAGE_SHIFT,
-                                           agp_type);
-       } else
-               intel_gtt_insert_pages(obj->gtt_space->start >> PAGE_SHIFT,
-                                      obj->base.size >> PAGE_SHIFT,
-                                      obj->pages,
-                                      agp_type);
-
+       intel_gtt_insert_sg_entries(obj->pages,
+                                   obj->gtt_space->start >> PAGE_SHIFT,
+                                   agp_type);
        obj->has_global_gtt_mapping = 1;
 }
 
@@ -418,14 +347,31 @@ void i915_gem_gtt_finish_object(struct drm_i915_gem_object *obj)
 
        interruptible = do_idling(dev_priv);
 
-       if (obj->sg_list) {
-               intel_gtt_unmap_memory(obj->sg_list, obj->num_sg);
-               obj->sg_list = NULL;
-       }
+       if (!obj->has_dma_mapping)
+               dma_unmap_sg(&dev->pdev->dev,
+                            obj->pages->sgl, obj->pages->nents,
+                            PCI_DMA_BIDIRECTIONAL);
 
        undo_idling(dev_priv, interruptible);
 }
 
+static void i915_gtt_color_adjust(struct drm_mm_node *node,
+                                 unsigned long color,
+                                 unsigned long *start,
+                                 unsigned long *end)
+{
+       if (node->color != color)
+               *start += 4096;
+
+       if (!list_empty(&node->node_list)) {
+               node = list_entry(node->node_list.next,
+                                 struct drm_mm_node,
+                                 node_list);
+               if (node->allocated && node->color != color)
+                       *end -= 4096;
+       }
+}
+
 void i915_gem_init_global_gtt(struct drm_device *dev,
                              unsigned long start,
                              unsigned long mappable_end,
@@ -435,6 +381,8 @@ void i915_gem_init_global_gtt(struct drm_device *dev,
 
        /* Substract 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;
index b964df51cec7c6d906a2865313734b0c69f6fd4c..8093ecd2ea31906793be3d29f6909fc5fbc12f74 100644 (file)
@@ -470,18 +470,20 @@ i915_gem_swizzle_page(struct page *page)
 void
 i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj)
 {
+       struct scatterlist *sg;
        int page_count = obj->base.size >> PAGE_SHIFT;
        int i;
 
        if (obj->bit_17 == NULL)
                return;
 
-       for (i = 0; i < page_count; i++) {
-               char new_bit_17 = page_to_phys(obj->pages[i]) >> 17;
+       for_each_sg(obj->pages->sgl, sg, page_count, i) {
+               struct page *page = sg_page(sg);
+               char new_bit_17 = page_to_phys(page) >> 17;
                if ((new_bit_17 & 0x1) !=
                    (test_bit(i, obj->bit_17) != 0)) {
-                       i915_gem_swizzle_page(obj->pages[i]);
-                       set_page_dirty(obj->pages[i]);
+                       i915_gem_swizzle_page(page);
+                       set_page_dirty(page);
                }
        }
 }
@@ -489,6 +491,7 @@ 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)
 {
+       struct scatterlist *sg;
        int page_count = obj->base.size >> PAGE_SHIFT;
        int i;
 
@@ -502,8 +505,9 @@ i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj)
                }
        }
 
-       for (i = 0; i < page_count; i++) {
-               if (page_to_phys(obj->pages[i]) & (1 << 17))
+       for_each_sg(obj->pages->sgl, sg, page_count, i) {
+               struct page *page = sg_page(sg);
+               if (page_to_phys(page) & (1 << 17))
                        __set_bit(i, obj->bit_17);
                else
                        __clear_bit(i, obj->bit_17);
index 5249640cce1381c912c76ec73c6658cde12ae95f..d7f0066538a9f4229bc70aaba2cc97e245b30080 100644 (file)
@@ -296,11 +296,21 @@ static void i915_hotplug_work_func(struct work_struct *work)
        drm_helper_hpd_irq_event(dev);
 }
 
-static void i915_handle_rps_change(struct drm_device *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;
        u32 busy_up, busy_down, max_avg, min_avg;
-       u8 new_delay = dev_priv->cur_delay;
+       u8 new_delay;
+       unsigned long flags;
+
+       spin_lock_irqsave(&mchdev_lock, flags);
+
+       I915_WRITE16(MEMINTRSTS, I915_READ(MEMINTRSTS));
+
+       new_delay = dev_priv->ips.cur_delay;
 
        I915_WRITE16(MEMINTRSTS, MEMINT_EVAL_CHG);
        busy_up = I915_READ(RCPREVBSYTUPAVG);
@@ -310,19 +320,21 @@ static void i915_handle_rps_change(struct drm_device *dev)
 
        /* Handle RCS change request from hw */
        if (busy_up > max_avg) {
-               if (dev_priv->cur_delay != dev_priv->max_delay)
-                       new_delay = dev_priv->cur_delay - 1;
-               if (new_delay < dev_priv->max_delay)
-                       new_delay = dev_priv->max_delay;
+               if (dev_priv->ips.cur_delay != dev_priv->ips.max_delay)
+                       new_delay = dev_priv->ips.cur_delay - 1;
+               if (new_delay < dev_priv->ips.max_delay)
+                       new_delay = dev_priv->ips.max_delay;
        } else if (busy_down < min_avg) {
-               if (dev_priv->cur_delay != dev_priv->min_delay)
-                       new_delay = dev_priv->cur_delay + 1;
-               if (new_delay > dev_priv->min_delay)
-                       new_delay = dev_priv->min_delay;
+               if (dev_priv->ips.cur_delay != dev_priv->ips.min_delay)
+                       new_delay = dev_priv->ips.cur_delay + 1;
+               if (new_delay > dev_priv->ips.min_delay)
+                       new_delay = dev_priv->ips.min_delay;
        }
 
        if (ironlake_set_drps(dev, new_delay))
-               dev_priv->cur_delay = new_delay;
+               dev_priv->ips.cur_delay = new_delay;
+
+       spin_unlock_irqrestore(&mchdev_lock, flags);
 
        return;
 }
@@ -335,7 +347,7 @@ static void notify_ring(struct drm_device *dev,
        if (ring->obj == NULL)
                return;
 
-       trace_i915_gem_request_complete(ring, ring->get_seqno(ring));
+       trace_i915_gem_request_complete(ring, ring->get_seqno(ring, false));
 
        wake_up_all(&ring->irq_queue);
        if (i915_enable_hangcheck) {
@@ -349,16 +361,16 @@ static void notify_ring(struct drm_device *dev,
 static void gen6_pm_rps_work(struct work_struct *work)
 {
        drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
-                                                   rps_work);
+                                                   rps.work);
        u32 pm_iir, pm_imr;
        u8 new_delay;
 
-       spin_lock_irq(&dev_priv->rps_lock);
-       pm_iir = dev_priv->pm_iir;
-       dev_priv->pm_iir = 0;
+       spin_lock_irq(&dev_priv->rps.lock);
+       pm_iir = dev_priv->rps.pm_iir;
+       dev_priv->rps.pm_iir = 0;
        pm_imr = I915_READ(GEN6_PMIMR);
        I915_WRITE(GEN6_PMIMR, 0);
-       spin_unlock_irq(&dev_priv->rps_lock);
+       spin_unlock_irq(&dev_priv->rps.lock);
 
        if ((pm_iir & GEN6_PM_DEFERRED_EVENTS) == 0)
                return;
@@ -366,11 +378,17 @@ static void gen6_pm_rps_work(struct work_struct *work)
        mutex_lock(&dev_priv->dev->struct_mutex);
 
        if (pm_iir & GEN6_PM_RP_UP_THRESHOLD)
-               new_delay = dev_priv->cur_delay + 1;
+               new_delay = dev_priv->rps.cur_delay + 1;
        else
-               new_delay = dev_priv->cur_delay - 1;
+               new_delay = dev_priv->rps.cur_delay - 1;
 
-       gen6_set_rps(dev_priv->dev, new_delay);
+       /* sysfs frequency interfaces may have snuck in while servicing the
+        * interrupt
+        */
+       if (!(new_delay > dev_priv->rps.max_delay ||
+             new_delay < dev_priv->rps.min_delay)) {
+               gen6_set_rps(dev_priv->dev, new_delay);
+       }
 
        mutex_unlock(&dev_priv->dev->struct_mutex);
 }
@@ -444,7 +462,7 @@ static void ivybridge_handle_parity_error(struct drm_device *dev)
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        unsigned long flags;
 
-       if (!IS_IVYBRIDGE(dev))
+       if (!HAS_L3_GPU_CACHE(dev))
                return;
 
        spin_lock_irqsave(&dev_priv->irq_lock, flags);
@@ -488,19 +506,19 @@ static void gen6_queue_rps_work(struct drm_i915_private *dev_priv,
         * IIR bits should never already be set because IMR should
         * prevent an interrupt from being shown in IIR. The warning
         * displays a case where we've unsafely cleared
-        * dev_priv->pm_iir. Although missing an interrupt of the same
+        * dev_priv->rps.pm_iir. Although missing an interrupt of the same
         * type is not a problem, it displays a problem in the logic.
         *
-        * The mask bit in IMR is cleared by rps_work.
+        * The mask bit in IMR is cleared by dev_priv->rps.work.
         */
 
-       spin_lock_irqsave(&dev_priv->rps_lock, flags);
-       dev_priv->pm_iir |= pm_iir;
-       I915_WRITE(GEN6_PMIMR, dev_priv->pm_iir);
+       spin_lock_irqsave(&dev_priv->rps.lock, flags);
+       dev_priv->rps.pm_iir |= pm_iir;
+       I915_WRITE(GEN6_PMIMR, dev_priv->rps.pm_iir);
        POSTING_READ(GEN6_PMIMR);
-       spin_unlock_irqrestore(&dev_priv->rps_lock, flags);
+       spin_unlock_irqrestore(&dev_priv->rps.lock, flags);
 
-       queue_work(dev_priv->wq, &dev_priv->rps_work);
+       queue_work(dev_priv->wq, &dev_priv->rps.work);
 }
 
 static irqreturn_t valleyview_irq_handler(DRM_IRQ_ARGS)
@@ -793,10 +811,8 @@ static irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS)
                        ibx_irq_handler(dev, pch_iir);
        }
 
-       if (de_iir & DE_PCU_EVENT) {
-               I915_WRITE16(MEMINTRSTS, I915_READ(MEMINTRSTS));
-               i915_handle_rps_change(dev);
-       }
+       if (IS_GEN5(dev) &&  de_iir & DE_PCU_EVENT)
+               ironlake_handle_rps_change(dev);
 
        if (IS_GEN6(dev) && pm_iir & GEN6_PM_DEFERRED_EVENTS)
                gen6_queue_rps_work(dev_priv, pm_iir);
@@ -843,26 +859,55 @@ static void i915_error_work_func(struct work_struct *work)
        }
 }
 
+/* NB: please notice the memset */
+static void i915_get_extra_instdone(struct drm_device *dev,
+                                   uint32_t *instdone)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       memset(instdone, 0, sizeof(*instdone) * I915_NUM_INSTDONE_REG);
+
+       switch(INTEL_INFO(dev)->gen) {
+       case 2:
+       case 3:
+               instdone[0] = I915_READ(INSTDONE);
+               break;
+       case 4:
+       case 5:
+       case 6:
+               instdone[0] = I915_READ(INSTDONE_I965);
+               instdone[1] = I915_READ(INSTDONE1);
+               break;
+       default:
+               WARN_ONCE(1, "Unsupported platform\n");
+       case 7:
+               instdone[0] = I915_READ(GEN7_INSTDONE_1);
+               instdone[1] = I915_READ(GEN7_SC_INSTDONE);
+               instdone[2] = I915_READ(GEN7_SAMPLER_INSTDONE);
+               instdone[3] = I915_READ(GEN7_ROW_INSTDONE);
+               break;
+       }
+}
+
 #ifdef CONFIG_DEBUG_FS
 static struct drm_i915_error_object *
 i915_error_object_create(struct drm_i915_private *dev_priv,
                         struct drm_i915_gem_object *src)
 {
        struct drm_i915_error_object *dst;
-       int page, page_count;
+       int i, count;
        u32 reloc_offset;
 
        if (src == NULL || src->pages == NULL)
                return NULL;
 
-       page_count = src->base.size / PAGE_SIZE;
+       count = src->base.size / PAGE_SIZE;
 
-       dst = kmalloc(sizeof(*dst) + page_count * sizeof(u32 *), GFP_ATOMIC);
+       dst = kmalloc(sizeof(*dst) + count * sizeof(u32 *), GFP_ATOMIC);
        if (dst == NULL)
                return NULL;
 
        reloc_offset = src->gtt_offset;
-       for (page = 0; page < page_count; page++) {
+       for (i = 0; i < count; i++) {
                unsigned long flags;
                void *d;
 
@@ -885,30 +930,33 @@ i915_error_object_create(struct drm_i915_private *dev_priv,
                        memcpy_fromio(d, s, PAGE_SIZE);
                        io_mapping_unmap_atomic(s);
                } else {
+                       struct page *page;
                        void *s;
 
-                       drm_clflush_pages(&src->pages[page], 1);
+                       page = i915_gem_object_get_page(src, i);
+
+                       drm_clflush_pages(&page, 1);
 
-                       s = kmap_atomic(src->pages[page]);
+                       s = kmap_atomic(page);
                        memcpy(d, s, PAGE_SIZE);
                        kunmap_atomic(s);
 
-                       drm_clflush_pages(&src->pages[page], 1);
+                       drm_clflush_pages(&page, 1);
                }
                local_irq_restore(flags);
 
-               dst->pages[page] = d;
+               dst->pages[i] = d;
 
                reloc_offset += PAGE_SIZE;
        }
-       dst->page_count = page_count;
+       dst->page_count = count;
        dst->gtt_offset = src->gtt_offset;
 
        return dst;
 
 unwind:
-       while (page--)
-               kfree(dst->pages[page]);
+       while (i--)
+               kfree(dst->pages[i]);
        kfree(dst);
        return NULL;
 }
@@ -949,7 +997,8 @@ static void capture_bo(struct drm_i915_error_buffer *err,
 {
        err->size = obj->base.size;
        err->name = obj->base.name;
-       err->seqno = obj->last_rendering_seqno;
+       err->rseqno = obj->last_read_seqno;
+       err->wseqno = obj->last_write_seqno;
        err->gtt_offset = obj->gtt_offset;
        err->read_domains = obj->base.read_domains;
        err->write_domain = obj->base.write_domain;
@@ -1039,12 +1088,12 @@ i915_error_first_batchbuffer(struct drm_i915_private *dev_priv,
        if (!ring->get_seqno)
                return NULL;
 
-       seqno = ring->get_seqno(ring);
+       seqno = ring->get_seqno(ring, false);
        list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) {
                if (obj->ring != ring)
                        continue;
 
-               if (i915_seqno_passed(seqno, obj->last_rendering_seqno))
+               if (i915_seqno_passed(seqno, obj->last_read_seqno))
                        continue;
 
                if ((obj->base.read_domains & I915_GEM_DOMAIN_COMMAND) == 0)
@@ -1080,10 +1129,8 @@ static void i915_record_ring_state(struct drm_device *dev,
                error->ipehr[ring->id] = I915_READ(RING_IPEHR(ring->mmio_base));
                error->instdone[ring->id] = I915_READ(RING_INSTDONE(ring->mmio_base));
                error->instps[ring->id] = I915_READ(RING_INSTPS(ring->mmio_base));
-               if (ring->id == RCS) {
-                       error->instdone1 = I915_READ(INSTDONE1);
+               if (ring->id == RCS)
                        error->bbaddr = I915_READ64(BB_ADDR);
-               }
        } else {
                error->faddr[ring->id] = I915_READ(DMA_FADD_I8XX);
                error->ipeir[ring->id] = I915_READ(IPEIR);
@@ -1093,7 +1140,7 @@ static void i915_record_ring_state(struct drm_device *dev,
 
        error->waiting[ring->id] = waitqueue_active(&ring->irq_queue);
        error->instpm[ring->id] = I915_READ(RING_INSTPM(ring->mmio_base));
-       error->seqno[ring->id] = ring->get_seqno(ring);
+       error->seqno[ring->id] = ring->get_seqno(ring, false);
        error->acthd[ring->id] = intel_ring_get_active_head(ring);
        error->head[ring->id] = I915_READ_HEAD(ring);
        error->tail[ring->id] = I915_READ_TAIL(ring);
@@ -1199,6 +1246,11 @@ static void i915_capture_error_state(struct drm_device *dev)
                error->done_reg = I915_READ(DONE_REG);
        }
 
+       if (INTEL_INFO(dev)->gen == 7)
+               error->err_int = I915_READ(GEN7_ERR_INT);
+
+       i915_get_extra_instdone(dev, error->extra_instdone);
+
        i915_gem_record_fences(dev, error);
        i915_gem_record_rings(dev, error);
 
@@ -1210,7 +1262,7 @@ static void i915_capture_error_state(struct drm_device *dev)
        list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list)
                i++;
        error->active_bo_count = i;
-       list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list)
+       list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list)
                if (obj->pin_count)
                        i++;
        error->pinned_bo_count = i - error->active_bo_count;
@@ -1235,7 +1287,7 @@ static void i915_capture_error_state(struct drm_device *dev)
                error->pinned_bo_count =
                        capture_pinned_bo(error->pinned_bo,
                                          error->pinned_bo_count,
-                                         &dev_priv->mm.gtt_list);
+                                         &dev_priv->mm.bound_list);
 
        do_gettimeofday(&error->time);
 
@@ -1274,24 +1326,26 @@ void i915_destroy_error_state(struct drm_device *dev)
 static void i915_report_and_clear_eir(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t instdone[I915_NUM_INSTDONE_REG];
        u32 eir = I915_READ(EIR);
-       int pipe;
+       int pipe, i;
 
        if (!eir)
                return;
 
        pr_err("render error detected, EIR: 0x%08x\n", eir);
 
+       i915_get_extra_instdone(dev, instdone);
+
        if (IS_G4X(dev)) {
                if (eir & (GM45_ERROR_MEM_PRIV | GM45_ERROR_CP_PRIV)) {
                        u32 ipeir = I915_READ(IPEIR_I965);
 
                        pr_err("  IPEIR: 0x%08x\n", I915_READ(IPEIR_I965));
                        pr_err("  IPEHR: 0x%08x\n", I915_READ(IPEHR_I965));
-                       pr_err("  INSTDONE: 0x%08x\n",
-                              I915_READ(INSTDONE_I965));
+                       for (i = 0; i < ARRAY_SIZE(instdone); i++)
+                               pr_err("  INSTDONE_%d: 0x%08x\n", i, instdone[i]);
                        pr_err("  INSTPS: 0x%08x\n", I915_READ(INSTPS));
-                       pr_err("  INSTDONE1: 0x%08x\n", I915_READ(INSTDONE1));
                        pr_err("  ACTHD: 0x%08x\n", I915_READ(ACTHD_I965));
                        I915_WRITE(IPEIR_I965, ipeir);
                        POSTING_READ(IPEIR_I965);
@@ -1325,12 +1379,13 @@ static void i915_report_and_clear_eir(struct drm_device *dev)
        if (eir & I915_ERROR_INSTRUCTION) {
                pr_err("instruction error\n");
                pr_err("  INSTPM: 0x%08x\n", I915_READ(INSTPM));
+               for (i = 0; i < ARRAY_SIZE(instdone); i++)
+                       pr_err("  INSTDONE_%d: 0x%08x\n", i, instdone[i]);
                if (INTEL_INFO(dev)->gen < 4) {
                        u32 ipeir = I915_READ(IPEIR);
 
                        pr_err("  IPEIR: 0x%08x\n", I915_READ(IPEIR));
                        pr_err("  IPEHR: 0x%08x\n", I915_READ(IPEHR));
-                       pr_err("  INSTDONE: 0x%08x\n", I915_READ(INSTDONE));
                        pr_err("  ACTHD: 0x%08x\n", I915_READ(ACTHD));
                        I915_WRITE(IPEIR, ipeir);
                        POSTING_READ(IPEIR);
@@ -1339,10 +1394,7 @@ static void i915_report_and_clear_eir(struct drm_device *dev)
 
                        pr_err("  IPEIR: 0x%08x\n", I915_READ(IPEIR_I965));
                        pr_err("  IPEHR: 0x%08x\n", I915_READ(IPEHR_I965));
-                       pr_err("  INSTDONE: 0x%08x\n",
-                              I915_READ(INSTDONE_I965));
                        pr_err("  INSTPS: 0x%08x\n", I915_READ(INSTPS));
-                       pr_err("  INSTDONE1: 0x%08x\n", I915_READ(INSTDONE1));
                        pr_err("  ACTHD: 0x%08x\n", I915_READ(ACTHD_I965));
                        I915_WRITE(IPEIR_I965, ipeir);
                        POSTING_READ(IPEIR_I965);
@@ -1590,7 +1642,8 @@ ring_last_seqno(struct intel_ring_buffer *ring)
 static bool i915_hangcheck_ring_idle(struct intel_ring_buffer *ring, bool *err)
 {
        if (list_empty(&ring->request_list) ||
-           i915_seqno_passed(ring->get_seqno(ring), ring_last_seqno(ring))) {
+           i915_seqno_passed(ring->get_seqno(ring, false),
+                             ring_last_seqno(ring))) {
                /* Issue a wake-up to catch stuck h/w. */
                if (waitqueue_active(&ring->irq_queue)) {
                        DRM_ERROR("Hangcheck timer elapsed... %s idle\n",
@@ -1656,7 +1709,7 @@ void i915_hangcheck_elapsed(unsigned long data)
 {
        struct drm_device *dev = (struct drm_device *)data;
        drm_i915_private_t *dev_priv = dev->dev_private;
-       uint32_t acthd[I915_NUM_RINGS], instdone, instdone1;
+       uint32_t acthd[I915_NUM_RINGS], instdone[I915_NUM_INSTDONE_REG];
        struct intel_ring_buffer *ring;
        bool err = false, idle;
        int i;
@@ -1684,25 +1737,16 @@ void i915_hangcheck_elapsed(unsigned long data)
                return;
        }
 
-       if (INTEL_INFO(dev)->gen < 4) {
-               instdone = I915_READ(INSTDONE);
-               instdone1 = 0;
-       } else {
-               instdone = I915_READ(INSTDONE_I965);
-               instdone1 = I915_READ(INSTDONE1);
-       }
-
+       i915_get_extra_instdone(dev, instdone);
        if (memcmp(dev_priv->last_acthd, acthd, sizeof(acthd)) == 0 &&
-           dev_priv->last_instdone == instdone &&
-           dev_priv->last_instdone1 == instdone1) {
+           memcmp(dev_priv->prev_instdone, instdone, sizeof(instdone)) == 0) {
                if (i915_hangcheck_hung(dev))
                        return;
        } else {
                dev_priv->hangcheck_count = 0;
 
                memcpy(dev_priv->last_acthd, acthd, sizeof(acthd));
-               dev_priv->last_instdone = instdone;
-               dev_priv->last_instdone1 = instdone1;
+               memcpy(dev_priv->prev_instdone, instdone, sizeof(instdone));
        }
 
 repeat:
@@ -2647,7 +2691,7 @@ void intel_irq_init(struct drm_device *dev)
 
        INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
        INIT_WORK(&dev_priv->error_work, i915_error_work_func);
-       INIT_WORK(&dev_priv->rps_work, gen6_pm_rps_work);
+       INIT_WORK(&dev_priv->rps.work, gen6_pm_rps_work);
        INIT_WORK(&dev_priv->parity_error_work, ivybridge_parity_work);
 
        dev->driver->get_vblank_counter = i915_get_vblank_counter;
index 28725ce5b82cc052a4469828166a7f9c8d987236..7637824c6a7d5f8975ebeac1ccfaff4ed24c383e 100644 (file)
 #define RING_ACTHD(base)       ((base)+0x74)
 #define RING_NOPID(base)       ((base)+0x94)
 #define RING_IMR(base)         ((base)+0xa8)
+#define RING_TIMESTAMP(base)   ((base)+0x358)
 #define   TAIL_ADDR            0x001FFFF8
 #define   HEAD_WRAP_COUNT      0xFFE00000
 #define   HEAD_WRAP_ONE                0x00200000
 #define IPEIR_I965     0x02064
 #define IPEHR_I965     0x02068
 #define INSTDONE_I965  0x0206c
+#define GEN7_INSTDONE_1                0x0206c
+#define GEN7_SC_INSTDONE       0x07100
+#define GEN7_SAMPLER_INSTDONE  0x0e160
+#define GEN7_ROW_INSTDONE      0x0e164
+#define I915_NUM_INSTDONE_REG  4
 #define RING_IPEIR(base)       ((base)+0x64)
 #define RING_IPEHR(base)       ((base)+0x68)
 #define RING_INSTDONE(base)    ((base)+0x6c)
 #define DMA_FADD_I8XX  0x020d0
 
 #define ERROR_GEN6     0x040a0
+#define GEN7_ERR_INT   0x44040
+#define   ERR_INT_MMIO_UNCLAIMED (1<<13)
 
 /* GM45+ chicken bits -- debug workaround bits that may be required
  * for various sorts of correct behavior.  The top 16 bits of each are
 #define   GFX_PSMI_GRANULARITY         (1<<10)
 #define   GFX_PPGTT_ENABLE             (1<<9)
 
+#define VLV_DISPLAY_BASE 0x180000
+
 #define SCPD0          0x0209c /* 915+ only */
 #define IER            0x020a0
 #define IIR            0x020a4
                                         GEN7_CXT_EXTENDED_SIZE(ctx_reg) + \
                                         GEN7_CXT_GT1_SIZE(ctx_reg) + \
                                         GEN7_CXT_VFSTATE_SIZE(ctx_reg))
+#define HSW_CXT_POWER_SIZE(ctx_reg)    ((ctx_reg >> 26) & 0x3f)
+#define HSW_CXT_RING_SIZE(ctx_reg)     ((ctx_reg >> 23) & 0x7)
+#define HSW_CXT_RENDER_SIZE(ctx_reg)   ((ctx_reg >> 15) & 0xff)
+#define HSW_CXT_TOTAL_SIZE(ctx_reg)    (HSW_CXT_POWER_SIZE(ctx_reg) + \
+                                        HSW_CXT_RING_SIZE(ctx_reg) + \
+                                        HSW_CXT_RENDER_SIZE(ctx_reg) + \
+                                        GEN7_CXT_VFSTATE_SIZE(ctx_reg))
+
 
 /*
  * Overlay regs
 
 /* VGA port control */
 #define ADPA                   0x61100
+#define PCH_ADPA                0xe1100
+#define VLV_ADPA               (VLV_DISPLAY_BASE + ADPA)
+
 #define   ADPA_DAC_ENABLE      (1<<31)
 #define   ADPA_DAC_DISABLE     0
 #define   ADPA_PIPE_SELECT_MASK        (1<<30)
 #define   ADPA_PIPE_A_SELECT   0
 #define   ADPA_PIPE_B_SELECT   (1<<30)
 #define   ADPA_PIPE_SELECT(pipe) ((pipe) << 30)
+/* CPT uses bits 29:30 for pch transcoder select */
+#define   ADPA_CRT_HOTPLUG_MASK  0x03ff0000 /* bit 25-16 */
+#define   ADPA_CRT_HOTPLUG_MONITOR_NONE  (0<<24)
+#define   ADPA_CRT_HOTPLUG_MONITOR_MASK  (3<<24)
+#define   ADPA_CRT_HOTPLUG_MONITOR_COLOR (3<<24)
+#define   ADPA_CRT_HOTPLUG_MONITOR_MONO  (2<<24)
+#define   ADPA_CRT_HOTPLUG_ENABLE        (1<<23)
+#define   ADPA_CRT_HOTPLUG_PERIOD_64     (0<<22)
+#define   ADPA_CRT_HOTPLUG_PERIOD_128    (1<<22)
+#define   ADPA_CRT_HOTPLUG_WARMUP_5MS    (0<<21)
+#define   ADPA_CRT_HOTPLUG_WARMUP_10MS   (1<<21)
+#define   ADPA_CRT_HOTPLUG_SAMPLE_2S     (0<<20)
+#define   ADPA_CRT_HOTPLUG_SAMPLE_4S     (1<<20)
+#define   ADPA_CRT_HOTPLUG_VOLTAGE_40    (0<<18)
+#define   ADPA_CRT_HOTPLUG_VOLTAGE_50    (1<<18)
+#define   ADPA_CRT_HOTPLUG_VOLTAGE_60    (2<<18)
+#define   ADPA_CRT_HOTPLUG_VOLTAGE_70    (3<<18)
+#define   ADPA_CRT_HOTPLUG_VOLREF_325MV  (0<<17)
+#define   ADPA_CRT_HOTPLUG_VOLREF_475MV  (1<<17)
+#define   ADPA_CRT_HOTPLUG_FORCE_TRIGGER (1<<16)
 #define   ADPA_USE_VGA_HVPOLARITY (1<<15)
 #define   ADPA_SETS_HVPOLARITY 0
 #define   ADPA_VSYNC_CNTL_DISABLE (1<<11)
 
 /* Video Data Island Packet control */
 #define VIDEO_DIP_DATA         0x61178
+/* Read the description of VIDEO_DIP_DATA (before Haswel) or VIDEO_DIP_ECC
+ * (Haswell and newer) to see which VIDEO_DIP_DATA byte corresponds to each byte
+ * of the infoframe structure specified by CEA-861. */
+#define   VIDEO_DIP_DATA_SIZE  32
 #define VIDEO_DIP_CTL          0x61170
 /* Pre HSW: */
 #define   VIDEO_DIP_ENABLE             (1 << 31)
 #define FDI_PLL_CTL_1           0xfe000
 #define FDI_PLL_CTL_2           0xfe004
 
-/* CRT */
-#define PCH_ADPA                0xe1100
-#define  ADPA_TRANS_SELECT_MASK (1<<30)
-#define  ADPA_TRANS_A_SELECT    0
-#define  ADPA_TRANS_B_SELECT    (1<<30)
-#define  ADPA_CRT_HOTPLUG_MASK  0x03ff0000 /* bit 25-16 */
-#define  ADPA_CRT_HOTPLUG_MONITOR_NONE  (0<<24)
-#define  ADPA_CRT_HOTPLUG_MONITOR_MASK  (3<<24)
-#define  ADPA_CRT_HOTPLUG_MONITOR_COLOR (3<<24)
-#define  ADPA_CRT_HOTPLUG_MONITOR_MONO  (2<<24)
-#define  ADPA_CRT_HOTPLUG_ENABLE        (1<<23)
-#define  ADPA_CRT_HOTPLUG_PERIOD_64     (0<<22)
-#define  ADPA_CRT_HOTPLUG_PERIOD_128    (1<<22)
-#define  ADPA_CRT_HOTPLUG_WARMUP_5MS    (0<<21)
-#define  ADPA_CRT_HOTPLUG_WARMUP_10MS   (1<<21)
-#define  ADPA_CRT_HOTPLUG_SAMPLE_2S     (0<<20)
-#define  ADPA_CRT_HOTPLUG_SAMPLE_4S     (1<<20)
-#define  ADPA_CRT_HOTPLUG_VOLTAGE_40    (0<<18)
-#define  ADPA_CRT_HOTPLUG_VOLTAGE_50    (1<<18)
-#define  ADPA_CRT_HOTPLUG_VOLTAGE_60    (2<<18)
-#define  ADPA_CRT_HOTPLUG_VOLTAGE_70    (3<<18)
-#define  ADPA_CRT_HOTPLUG_VOLREF_325MV  (0<<17)
-#define  ADPA_CRT_HOTPLUG_VOLREF_475MV  (1<<17)
-#define  ADPA_CRT_HOTPLUG_FORCE_TRIGGER (1<<16)
-
 /* or SDVOB */
 #define HDMIB   0xe1140
 #define  PORT_ENABLE    (1 << 31)
 #define  PORT_TRANS_C_SEL_CPT  (2<<29)
 #define  PORT_TRANS_SEL_MASK   (3<<29)
 #define  PORT_TRANS_SEL_CPT(pipe)      ((pipe) << 29)
+#define  PORT_TO_PIPE(val)     (((val) & (1<<30)) >> 30)
+#define  PORT_TO_PIPE_CPT(val) (((val) & PORT_TRANS_SEL_MASK) >> 29)
 
 #define TRANS_DP_CTL_A         0xe0300
 #define TRANS_DP_CTL_B         0xe1300
 #define G4X_HDMIW_HDMIEDID             0x6210C
 
 #define IBX_HDMIW_HDMIEDID_A           0xE2050
+#define IBX_HDMIW_HDMIEDID_B           0xE2150
+#define IBX_HDMIW_HDMIEDID(pipe) _PIPE(pipe, \
+                                       IBX_HDMIW_HDMIEDID_A, \
+                                       IBX_HDMIW_HDMIEDID_B)
 #define IBX_AUD_CNTL_ST_A              0xE20B4
+#define IBX_AUD_CNTL_ST_B              0xE21B4
+#define IBX_AUD_CNTL_ST(pipe) _PIPE(pipe, \
+                                       IBX_AUD_CNTL_ST_A, \
+                                       IBX_AUD_CNTL_ST_B)
 #define IBX_ELD_BUFFER_SIZE            (0x1f << 10)
 #define IBX_ELD_ADDRESS                        (0x1f << 5)
 #define IBX_ELD_ACK                    (1 << 4)
 #define IBX_CP_READYB                  (1 << 1)
 
 #define CPT_HDMIW_HDMIEDID_A           0xE5050
+#define CPT_HDMIW_HDMIEDID_B           0xE5150
+#define CPT_HDMIW_HDMIEDID(pipe) _PIPE(pipe, \
+                                       CPT_HDMIW_HDMIEDID_A, \
+                                       CPT_HDMIW_HDMIEDID_B)
 #define CPT_AUD_CNTL_ST_A              0xE50B4
+#define CPT_AUD_CNTL_ST_B              0xE51B4
+#define CPT_AUD_CNTL_ST(pipe) _PIPE(pipe, \
+                                       CPT_AUD_CNTL_ST_A, \
+                                       CPT_AUD_CNTL_ST_B)
 #define CPT_AUD_CNTRL_ST2              0xE50C0
 
 /* These are the 4 32-bit write offset registers for each stream
 #define GEN7_SO_WRITE_OFFSET(n)                (0x5280 + (n) * 4)
 
 #define IBX_AUD_CONFIG_A                       0xe2000
+#define IBX_AUD_CONFIG_B                       0xe2100
+#define IBX_AUD_CFG(pipe) _PIPE(pipe, \
+                                       IBX_AUD_CONFIG_A, \
+                                       IBX_AUD_CONFIG_B)
 #define CPT_AUD_CONFIG_A                       0xe5000
+#define CPT_AUD_CONFIG_B                       0xe5100
+#define CPT_AUD_CFG(pipe) _PIPE(pipe, \
+                                       CPT_AUD_CONFIG_A, \
+                                       CPT_AUD_CONFIG_B)
 #define   AUD_CONFIG_N_VALUE_INDEX             (1 << 29)
 #define   AUD_CONFIG_N_PROG_ENABLE             (1 << 28)
 #define   AUD_CONFIG_UPPER_N_SHIFT             20
 #define   AUD_CONFIG_PIXEL_CLOCK_HDMI          (0xf << 16)
 #define   AUD_CONFIG_DISABLE_NCTS              (1 << 3)
 
+/* HSW Audio */
+#define   HSW_AUD_CONFIG_A             0x65000 /* Audio Configuration Transcoder A */
+#define   HSW_AUD_CONFIG_B             0x65100 /* Audio Configuration Transcoder B */
+#define   HSW_AUD_CFG(pipe) _PIPE(pipe, \
+                                       HSW_AUD_CONFIG_A, \
+                                       HSW_AUD_CONFIG_B)
+
+#define   HSW_AUD_MISC_CTRL_A          0x65010 /* Audio Misc Control Convert 1 */
+#define   HSW_AUD_MISC_CTRL_B          0x65110 /* Audio Misc Control Convert 2 */
+#define   HSW_AUD_MISC_CTRL(pipe) _PIPE(pipe, \
+                                       HSW_AUD_MISC_CTRL_A, \
+                                       HSW_AUD_MISC_CTRL_B)
+
+#define   HSW_AUD_DIP_ELD_CTRL_ST_A    0x650b4 /* Audio DIP and ELD Control State Transcoder A */
+#define   HSW_AUD_DIP_ELD_CTRL_ST_B    0x651b4 /* Audio DIP and ELD Control State Transcoder B */
+#define   HSW_AUD_DIP_ELD_CTRL(pipe) _PIPE(pipe, \
+                                       HSW_AUD_DIP_ELD_CTRL_ST_A, \
+                                       HSW_AUD_DIP_ELD_CTRL_ST_B)
+
+/* Audio Digital Converter */
+#define   HSW_AUD_DIG_CNVT_1           0x65080 /* Audio Converter 1 */
+#define   HSW_AUD_DIG_CNVT_2           0x65180 /* Audio Converter 1 */
+#define   AUD_DIG_CNVT(pipe) _PIPE(pipe, \
+                                       HSW_AUD_DIG_CNVT_1, \
+                                       HSW_AUD_DIG_CNVT_2)
+#define   DIP_PORT_SEL_MASK            0x3
+
+#define   HSW_AUD_EDID_DATA_A          0x65050
+#define   HSW_AUD_EDID_DATA_B          0x65150
+#define   HSW_AUD_EDID_DATA(pipe) _PIPE(pipe, \
+                                       HSW_AUD_EDID_DATA_A, \
+                                       HSW_AUD_EDID_DATA_B)
+
+#define   HSW_AUD_PIPE_CONV_CFG                0x6507c /* Audio pipe and converter configs */
+#define   HSW_AUD_PIN_ELD_CP_VLD       0x650c0 /* Audio ELD and CP Ready Status */
+#define   AUDIO_INACTIVE_C             (1<<11)
+#define   AUDIO_INACTIVE_B             (1<<7)
+#define   AUDIO_INACTIVE_A             (1<<3)
+#define   AUDIO_OUTPUT_ENABLE_A                (1<<2)
+#define   AUDIO_OUTPUT_ENABLE_B                (1<<6)
+#define   AUDIO_OUTPUT_ENABLE_C                (1<<10)
+#define   AUDIO_ELD_VALID_A            (1<<0)
+#define   AUDIO_ELD_VALID_B            (1<<4)
+#define   AUDIO_ELD_VALID_C            (1<<8)
+#define   AUDIO_CP_READY_A             (1<<1)
+#define   AUDIO_CP_READY_B             (1<<5)
+#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_ENABLE                          (1<<31)
-#define   HSW_PWR_WELL_STATE                           (1<<30)
-#define HSW_PWR_WELL_CTL5              0x45410
+#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_ENABLE                  (1<<31)
+#define   HSW_PWR_WELL_STATE                   (1<<30)
+#define HSW_PWR_WELL_CTL5                      0x45410
 #define   HSW_PWR_WELL_ENABLE_SINGLE_STEP      (1<<31)
 #define   HSW_PWR_WELL_PWR_GATE_OVERRIDE       (1<<20)
-#define   HSW_PWR_WELL_FORCE_ON                                (1<<19)
-#define HSW_PWR_WELL_CTL6              0x45414
+#define   HSW_PWR_WELL_FORCE_ON                        (1<<19)
+#define HSW_PWR_WELL_CTL6                      0x45414
 
 /* Per-pipe DDI Function Control */
-#define PIPE_DDI_FUNC_CTL_A                    0x60400
-#define PIPE_DDI_FUNC_CTL_B                    0x61400
-#define PIPE_DDI_FUNC_CTL_C                    0x62400
+#define PIPE_DDI_FUNC_CTL_A            0x60400
+#define PIPE_DDI_FUNC_CTL_B            0x61400
+#define PIPE_DDI_FUNC_CTL_C            0x62400
 #define PIPE_DDI_FUNC_CTL_EDP          0x6F400
-#define DDI_FUNC_CTL(pipe) _PIPE(pipe, \
-                                       PIPE_DDI_FUNC_CTL_A, \
-                                       PIPE_DDI_FUNC_CTL_B)
+#define DDI_FUNC_CTL(pipe) _PIPE(pipe, PIPE_DDI_FUNC_CTL_A, \
+                                      PIPE_DDI_FUNC_CTL_B)
 #define  PIPE_DDI_FUNC_ENABLE          (1<<31)
 /* Those bits are ignored by pipe EDP since it can only connect to DDI A */
-#define  PIPE_DDI_PORT_MASK                    (7<<28)
-#define  PIPE_DDI_SELECT_PORT(x)               ((x)<<28)
-#define  PIPE_DDI_MODE_SELECT_HDMI             (0<<24)
-#define  PIPE_DDI_MODE_SELECT_DVI              (1<<24)
+#define  PIPE_DDI_PORT_MASK            (7<<28)
+#define  PIPE_DDI_SELECT_PORT(x)       ((x)<<28)
+#define  PIPE_DDI_MODE_SELECT_MASK     (7<<24)
+#define  PIPE_DDI_MODE_SELECT_HDMI     (0<<24)
+#define  PIPE_DDI_MODE_SELECT_DVI      (1<<24)
 #define  PIPE_DDI_MODE_SELECT_DP_SST   (2<<24)
 #define  PIPE_DDI_MODE_SELECT_DP_MST   (3<<24)
-#define  PIPE_DDI_MODE_SELECT_FDI              (4<<24)
-#define  PIPE_DDI_BPC_8                                        (0<<20)
-#define  PIPE_DDI_BPC_10                               (1<<20)
-#define  PIPE_DDI_BPC_6                                        (2<<20)
-#define  PIPE_DDI_BPC_12                               (3<<20)
-#define  PIPE_DDI_BFI_ENABLE                   (1<<4)
-#define  PIPE_DDI_PORT_WIDTH_X1                        (0<<1)
-#define  PIPE_DDI_PORT_WIDTH_X2                        (1<<1)
-#define  PIPE_DDI_PORT_WIDTH_X4                        (3<<1)
+#define  PIPE_DDI_MODE_SELECT_FDI      (4<<24)
+#define  PIPE_DDI_BPC_MASK             (7<<20)
+#define  PIPE_DDI_BPC_8                        (0<<20)
+#define  PIPE_DDI_BPC_10               (1<<20)
+#define  PIPE_DDI_BPC_6                        (2<<20)
+#define  PIPE_DDI_BPC_12               (3<<20)
+#define  PIPE_DDI_PVSYNC               (1<<17)
+#define  PIPE_DDI_PHSYNC               (1<<16)
+#define  PIPE_DDI_BFI_ENABLE           (1<<4)
+#define  PIPE_DDI_PORT_WIDTH_X1                (0<<1)
+#define  PIPE_DDI_PORT_WIDTH_X2                (1<<1)
+#define  PIPE_DDI_PORT_WIDTH_X4                (3<<1)
 
 /* DisplayPort Transport Control */
 #define DP_TP_CTL_A                    0x64040
 #define DP_TP_CTL_B                    0x64140
-#define DP_TP_CTL(port) _PORT(port, \
-                                       DP_TP_CTL_A, \
-                                       DP_TP_CTL_B)
-#define  DP_TP_CTL_ENABLE              (1<<31)
-#define  DP_TP_CTL_MODE_SST    (0<<27)
-#define  DP_TP_CTL_MODE_MST    (1<<27)
+#define DP_TP_CTL(port) _PORT(port, DP_TP_CTL_A, DP_TP_CTL_B)
+#define  DP_TP_CTL_ENABLE                      (1<<31)
+#define  DP_TP_CTL_MODE_SST                    (0<<27)
+#define  DP_TP_CTL_MODE_MST                    (1<<27)
 #define  DP_TP_CTL_ENHANCED_FRAME_ENABLE       (1<<18)
-#define  DP_TP_CTL_FDI_AUTOTRAIN       (1<<15)
+#define  DP_TP_CTL_FDI_AUTOTRAIN               (1<<15)
 #define  DP_TP_CTL_LINK_TRAIN_MASK             (7<<8)
 #define  DP_TP_CTL_LINK_TRAIN_PAT1             (0<<8)
 #define  DP_TP_CTL_LINK_TRAIN_PAT2             (1<<8)
-#define  DP_TP_CTL_LINK_TRAIN_NORMAL   (3<<8)
+#define  DP_TP_CTL_LINK_TRAIN_NORMAL           (3<<8)
 
 /* DisplayPort Transport Status */
 #define DP_TP_STATUS_A                 0x64044
 #define DP_TP_STATUS_B                 0x64144
-#define DP_TP_STATUS(port) _PORT(port, \
-                                       DP_TP_STATUS_A, \
-                                       DP_TP_STATUS_B)
+#define DP_TP_STATUS(port) _PORT(port, DP_TP_STATUS_A, DP_TP_STATUS_B)
 #define  DP_TP_STATUS_AUTOTRAIN_DONE   (1<<12)
 
 /* DDI Buffer Control */
 #define DDI_BUF_CTL_A                          0x64000
 #define DDI_BUF_CTL_B                          0x64100
-#define DDI_BUF_CTL(port) _PORT(port, \
-                                       DDI_BUF_CTL_A, \
-                                       DDI_BUF_CTL_B)
-#define  DDI_BUF_CTL_ENABLE                            (1<<31)
+#define DDI_BUF_CTL(port) _PORT(port, DDI_BUF_CTL_A, DDI_BUF_CTL_B)
+#define  DDI_BUF_CTL_ENABLE                    (1<<31)
 #define  DDI_BUF_EMP_400MV_0DB_HSW             (0<<24)   /* Sel0 */
-#define  DDI_BUF_EMP_400MV_3_5DB_HSW   (1<<24)   /* Sel1 */
+#define  DDI_BUF_EMP_400MV_3_5DB_HSW           (1<<24)   /* Sel1 */
 #define  DDI_BUF_EMP_400MV_6DB_HSW             (2<<24)   /* Sel2 */
-#define  DDI_BUF_EMP_400MV_9_5DB_HSW   (3<<24)   /* Sel3 */
+#define  DDI_BUF_EMP_400MV_9_5DB_HSW           (3<<24)   /* Sel3 */
 #define  DDI_BUF_EMP_600MV_0DB_HSW             (4<<24)   /* Sel4 */
-#define  DDI_BUF_EMP_600MV_3_5DB_HSW   (5<<24)   /* Sel5 */
+#define  DDI_BUF_EMP_600MV_3_5DB_HSW           (5<<24)   /* Sel5 */
 #define  DDI_BUF_EMP_600MV_6DB_HSW             (6<<24)   /* Sel6 */
 #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_IS_IDLE                               (1<<7)
-#define  DDI_PORT_WIDTH_X1                             (0<<1)
-#define  DDI_PORT_WIDTH_X2                             (1<<1)
-#define  DDI_PORT_WIDTH_X4                             (3<<1)
+#define  DDI_BUF_EMP_800MV_3_5DB_HSW           (8<<24)   /* Sel8 */
+#define  DDI_BUF_EMP_MASK                      (0xf<<24)
+#define  DDI_BUF_IS_IDLE                       (1<<7)
+#define  DDI_PORT_WIDTH_X1                     (0<<1)
+#define  DDI_PORT_WIDTH_X2                     (1<<1)
+#define  DDI_PORT_WIDTH_X4                     (3<<1)
 #define  DDI_INIT_DISPLAY_DETECTED             (1<<0)
 
 /* DDI Buffer Translations */
 #define DDI_BUF_TRANS_A                                0x64E00
 #define DDI_BUF_TRANS_B                                0x64E60
-#define DDI_BUF_TRANS(port) _PORT(port, \
-                                       DDI_BUF_TRANS_A, \
-                                       DDI_BUF_TRANS_B)
+#define DDI_BUF_TRANS(port) _PORT(port, DDI_BUF_TRANS_A, DDI_BUF_TRANS_B)
 
 /* Sideband Interface (SBI) is programmed indirectly, via
  * SBI_ADDR, which contains the register offset; and SBI_DATA,
  * which contains the payload */
-#define SBI_ADDR                               0xC6000
-#define SBI_DATA                               0xC6004
+#define SBI_ADDR                       0xC6000
+#define SBI_DATA                       0xC6004
 #define SBI_CTL_STAT                   0xC6008
 #define  SBI_CTL_OP_CRRD               (0x6<<8)
 #define  SBI_CTL_OP_CRWR               (0x7<<8)
 #define  SBI_RESPONSE_FAIL             (0x1<<1)
-#define  SBI_RESPONSE_SUCCESS  (0x0<<1)
-#define  SBI_BUSY                              (0x1<<0)
-#define  SBI_READY                             (0x0<<0)
+#define  SBI_RESPONSE_SUCCESS          (0x0<<1)
+#define  SBI_BUSY                      (0x1<<0)
+#define  SBI_READY                     (0x0<<0)
 
 /* SBI offsets */
-#define  SBI_SSCDIVINTPHASE6           0x0600
+#define  SBI_SSCDIVINTPHASE6                   0x0600
 #define   SBI_SSCDIVINTPHASE_DIVSEL_MASK       ((0x7f)<<1)
 #define   SBI_SSCDIVINTPHASE_DIVSEL(x)         ((x)<<1)
 #define   SBI_SSCDIVINTPHASE_INCVAL_MASK       ((0x7f)<<8)
 #define   SBI_SSCDIVINTPHASE_INCVAL(x)         ((x)<<8)
-#define   SBI_SSCDIVINTPHASE_DIR(x)                    ((x)<<15)
+#define   SBI_SSCDIVINTPHASE_DIR(x)            ((x)<<15)
 #define   SBI_SSCDIVINTPHASE_PROPAGATE         (1<<0)
-#define  SBI_SSCCTL                                    0x020c
+#define  SBI_SSCCTL                            0x020c
 #define  SBI_SSCCTL6                           0x060C
-#define   SBI_SSCCTL_DISABLE           (1<<0)
+#define   SBI_SSCCTL_DISABLE                   (1<<0)
 #define  SBI_SSCAUXDIV6                                0x0610
 #define   SBI_SSCAUXDIV_FINALDIV2SEL(x)                ((x)<<4)
-#define  SBI_DBUFF0                                    0x2a00
+#define  SBI_DBUFF0                            0x2a00
 
 /* LPT PIXCLK_GATE */
-#define PIXCLK_GATE                            0xC6020
-#define  PIXCLK_GATE_UNGATE            1<<0
-#define  PIXCLK_GATE_GATE              0<<0
+#define PIXCLK_GATE                    0xC6020
+#define  PIXCLK_GATE_UNGATE            (1<<0)
+#define  PIXCLK_GATE_GATE              (0<<0)
 
 /* SPLL */
-#define SPLL_CTL                               0x46020
+#define SPLL_CTL                       0x46020
 #define  SPLL_PLL_ENABLE               (1<<31)
 #define  SPLL_PLL_SCC                  (1<<28)
 #define  SPLL_PLL_NON_SCC              (2<<28)
-#define  SPLL_PLL_FREQ_810MHz  (0<<26)
-#define  SPLL_PLL_FREQ_1350MHz (1<<26)
+#define  SPLL_PLL_FREQ_810MHz          (0<<26)
+#define  SPLL_PLL_FREQ_1350MHz         (1<<26)
 
 /* WRPLL */
-#define WRPLL_CTL1                             0x46040
-#define WRPLL_CTL2                             0x46060
-#define  WRPLL_PLL_ENABLE                              (1<<31)
-#define  WRPLL_PLL_SELECT_SSC                  (0x01<<28)
-#define  WRPLL_PLL_SELECT_NON_SCC              (0x02<<28)
+#define WRPLL_CTL1                     0x46040
+#define WRPLL_CTL2                     0x46060
+#define  WRPLL_PLL_ENABLE              (1<<31)
+#define  WRPLL_PLL_SELECT_SSC          (0x01<<28)
+#define  WRPLL_PLL_SELECT_NON_SCC      (0x02<<28)
 #define  WRPLL_PLL_SELECT_LCPLL_2700   (0x03<<28)
 /* WRPLL divider programming */
-#define  WRPLL_DIVIDER_REFERENCE(x)            ((x)<<0)
-#define  WRPLL_DIVIDER_POST(x)                 ((x)<<8)
-#define  WRPLL_DIVIDER_FEEDBACK(x)             ((x)<<16)
+#define  WRPLL_DIVIDER_REFERENCE(x)    ((x)<<0)
+#define  WRPLL_DIVIDER_POST(x)         ((x)<<8)
+#define  WRPLL_DIVIDER_FEEDBACK(x)     ((x)<<16)
 
 /* Port clock selection */
 #define PORT_CLK_SEL_A                 0x46100
 #define PORT_CLK_SEL_B                 0x46104
-#define PORT_CLK_SEL(port) _PORT(port, \
-                                       PORT_CLK_SEL_A, \
-                                       PORT_CLK_SEL_B)
+#define PORT_CLK_SEL(port) _PORT(port, PORT_CLK_SEL_A, PORT_CLK_SEL_B)
 #define  PORT_CLK_SEL_LCPLL_2700       (0<<29)
 #define  PORT_CLK_SEL_LCPLL_1350       (1<<29)
 #define  PORT_CLK_SEL_LCPLL_810                (2<<29)
-#define  PORT_CLK_SEL_SPLL                     (3<<29)
+#define  PORT_CLK_SEL_SPLL             (3<<29)
 #define  PORT_CLK_SEL_WRPLL1           (4<<29)
 #define  PORT_CLK_SEL_WRPLL2           (5<<29)
 
 /* Pipe clock selection */
 #define PIPE_CLK_SEL_A                 0x46140
 #define PIPE_CLK_SEL_B                 0x46144
-#define PIPE_CLK_SEL(pipe) _PIPE(pipe, \
-                                       PIPE_CLK_SEL_A, \
-                                       PIPE_CLK_SEL_B)
+#define PIPE_CLK_SEL(pipe) _PIPE(pipe, PIPE_CLK_SEL_A, PIPE_CLK_SEL_B)
 /* For each pipe, we need to select the corresponding port clock */
-#define  PIPE_CLK_SEL_DISABLED (0x0<<29)
-#define  PIPE_CLK_SEL_PORT(x)  ((x+1)<<29)
+#define  PIPE_CLK_SEL_DISABLED         (0x0<<29)
+#define  PIPE_CLK_SEL_PORT(x)          ((x+1)<<29)
 
 /* LCPLL Control */
-#define LCPLL_CTL                              0x130040
+#define LCPLL_CTL                      0x130040
 #define  LCPLL_PLL_DISABLE             (1<<31)
 #define  LCPLL_PLL_LOCK                        (1<<30)
-#define  LCPLL_CD_CLOCK_DISABLE        (1<<25)
+#define  LCPLL_CD_CLOCK_DISABLE                (1<<25)
 #define  LCPLL_CD2X_CLOCK_DISABLE      (1<<23)
 
 /* Pipe WM_LINETIME - watermark line time */
 #define PIPE_WM_LINETIME_A             0x45270
 #define PIPE_WM_LINETIME_B             0x45274
-#define PIPE_WM_LINETIME(pipe) _PIPE(pipe, \
-                                       PIPE_WM_LINETIME_A, \
-                                       PIPE_WM_LINETIME_B)
-#define   PIPE_WM_LINETIME_MASK                (0x1ff)
-#define   PIPE_WM_LINETIME_TIME(x)                     ((x))
+#define PIPE_WM_LINETIME(pipe) _PIPE(pipe, PIPE_WM_LINETIME_A, \
+                                          PIPE_WM_LINETIME_B)
+#define   PIPE_WM_LINETIME_MASK                        (0x1ff)
+#define   PIPE_WM_LINETIME_TIME(x)             ((x))
 #define   PIPE_WM_LINETIME_IPS_LINETIME_MASK   (0x1ff<<16)
-#define   PIPE_WM_LINETIME_IPS_LINETIME(x)             ((x)<<16)
+#define   PIPE_WM_LINETIME_IPS_LINETIME(x)     ((x)<<16)
 
 /* SFUSE_STRAP */
-#define SFUSE_STRAP                            0xc2014
+#define SFUSE_STRAP                    0xc2014
 #define  SFUSE_STRAP_DDIB_DETECTED     (1<<2)
 #define  SFUSE_STRAP_DDIC_DETECTED     (1<<1)
 #define  SFUSE_STRAP_DDID_DETECTED     (1<<0)
index 7631807a27886e2483d77de1046461350f9c7d15..903eebd2117a41437837131adb46811ce858fbdd 100644 (file)
@@ -46,32 +46,32 @@ static u32 calc_residency(struct drm_device *dev, const u32 reg)
 }
 
 static ssize_t
-show_rc6_mask(struct device *dev, struct device_attribute *attr, char *buf)
+show_rc6_mask(struct device *kdev, struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev);
+       struct drm_minor *dminor = container_of(kdev, struct drm_minor, kdev);
        return snprintf(buf, PAGE_SIZE, "%x", intel_enable_rc6(dminor->dev));
 }
 
 static ssize_t
-show_rc6_ms(struct device *dev, struct device_attribute *attr, char *buf)
+show_rc6_ms(struct device *kdev, struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev);
+       struct drm_minor *dminor = container_of(kdev, struct drm_minor, kdev);
        u32 rc6_residency = calc_residency(dminor->dev, GEN6_GT_GFX_RC6);
        return snprintf(buf, PAGE_SIZE, "%u", rc6_residency);
 }
 
 static ssize_t
-show_rc6p_ms(struct device *dev, struct device_attribute *attr, char *buf)
+show_rc6p_ms(struct device *kdev, struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev);
+       struct drm_minor *dminor = container_of(kdev, struct drm_minor, kdev);
        u32 rc6p_residency = calc_residency(dminor->dev, GEN6_GT_GFX_RC6p);
        return snprintf(buf, PAGE_SIZE, "%u", rc6p_residency);
 }
 
 static ssize_t
-show_rc6pp_ms(struct device *dev, struct device_attribute *attr, char *buf)
+show_rc6pp_ms(struct device *kdev, struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev);
+       struct drm_minor *dminor = container_of(kdev, struct drm_minor, kdev);
        u32 rc6pp_residency = calc_residency(dminor->dev, GEN6_GT_GFX_RC6pp);
        return snprintf(buf, PAGE_SIZE, "%u", rc6pp_residency);
 }
@@ -93,6 +93,7 @@ static struct attribute_group rc6_attr_group = {
        .name = power_group_name,
        .attrs =  rc6_attrs
 };
+#endif
 
 static int l3_access_valid(struct drm_device *dev, loff_t offset)
 {
@@ -202,37 +203,214 @@ static struct bin_attribute dpf_attrs = {
        .mmap = NULL
 };
 
+static ssize_t gt_cur_freq_mhz_show(struct device *kdev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct drm_minor *minor = container_of(kdev, struct drm_minor, kdev);
+       struct drm_device *dev = minor->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int ret;
+
+       ret = i915_mutex_lock_interruptible(dev);
+       if (ret)
+               return ret;
+
+       ret = dev_priv->rps.cur_delay * GT_FREQUENCY_MULTIPLIER;
+       mutex_unlock(&dev->struct_mutex);
+
+       return snprintf(buf, PAGE_SIZE, "%d", ret);
+}
+
+static ssize_t gt_max_freq_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf)
+{
+       struct drm_minor *minor = container_of(kdev, struct drm_minor, kdev);
+       struct drm_device *dev = minor->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int ret;
+
+       ret = i915_mutex_lock_interruptible(dev);
+       if (ret)
+               return ret;
+
+       ret = dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER;
+       mutex_unlock(&dev->struct_mutex);
+
+       return snprintf(buf, PAGE_SIZE, "%d", ret);
+}
+
+static ssize_t gt_max_freq_mhz_store(struct device *kdev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       struct drm_minor *minor = container_of(kdev, struct drm_minor, kdev);
+       struct drm_device *dev = minor->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 val, rp_state_cap, hw_max, hw_min;
+       ssize_t ret;
+
+       ret = kstrtou32(buf, 0, &val);
+       if (ret)
+               return ret;
+
+       val /= GT_FREQUENCY_MULTIPLIER;
+
+       ret = mutex_lock_interruptible(&dev->struct_mutex);
+       if (ret)
+               return ret;
+
+       rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
+       hw_max = (rp_state_cap & 0xff);
+       hw_min = ((rp_state_cap & 0xff0000) >> 16);
+
+       if (val < hw_min || val > hw_max || val < dev_priv->rps.min_delay) {
+               mutex_unlock(&dev->struct_mutex);
+               return -EINVAL;
+       }
+
+       if (dev_priv->rps.cur_delay > val)
+               gen6_set_rps(dev_priv->dev, val);
+
+       dev_priv->rps.max_delay = val;
+
+       mutex_unlock(&dev->struct_mutex);
+
+       return count;
+}
+
+static ssize_t gt_min_freq_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf)
+{
+       struct drm_minor *minor = container_of(kdev, struct drm_minor, kdev);
+       struct drm_device *dev = minor->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int ret;
+
+       ret = i915_mutex_lock_interruptible(dev);
+       if (ret)
+               return ret;
+
+       ret = dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER;
+       mutex_unlock(&dev->struct_mutex);
+
+       return snprintf(buf, PAGE_SIZE, "%d", ret);
+}
+
+static ssize_t gt_min_freq_mhz_store(struct device *kdev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       struct drm_minor *minor = container_of(kdev, struct drm_minor, kdev);
+       struct drm_device *dev = minor->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 val, rp_state_cap, hw_max, hw_min;
+       ssize_t ret;
+
+       ret = kstrtou32(buf, 0, &val);
+       if (ret)
+               return ret;
+
+       val /= GT_FREQUENCY_MULTIPLIER;
+
+       ret = mutex_lock_interruptible(&dev->struct_mutex);
+       if (ret)
+               return ret;
+
+       rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
+       hw_max = (rp_state_cap & 0xff);
+       hw_min = ((rp_state_cap & 0xff0000) >> 16);
+
+       if (val < hw_min || val > hw_max || val > dev_priv->rps.max_delay) {
+               mutex_unlock(&dev->struct_mutex);
+               return -EINVAL;
+       }
+
+       if (dev_priv->rps.cur_delay < val)
+               gen6_set_rps(dev_priv->dev, val);
+
+       dev_priv->rps.min_delay = val;
+
+       mutex_unlock(&dev->struct_mutex);
+
+       return count;
+
+}
+
+static DEVICE_ATTR(gt_cur_freq_mhz, S_IRUGO, gt_cur_freq_mhz_show, NULL);
+static DEVICE_ATTR(gt_max_freq_mhz, S_IRUGO | S_IWUSR, gt_max_freq_mhz_show, gt_max_freq_mhz_store);
+static DEVICE_ATTR(gt_min_freq_mhz, S_IRUGO | S_IWUSR, gt_min_freq_mhz_show, gt_min_freq_mhz_store);
+
+
+static ssize_t gt_rp_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(gt_RP0_freq_mhz, S_IRUGO, gt_rp_mhz_show, NULL);
+static DEVICE_ATTR(gt_RP1_freq_mhz, S_IRUGO, gt_rp_mhz_show, NULL);
+static DEVICE_ATTR(gt_RPn_freq_mhz, S_IRUGO, gt_rp_mhz_show, NULL);
+
+/* For now we have a static number of RP states */
+static ssize_t gt_rp_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf)
+{
+       struct drm_minor *minor = container_of(kdev, struct drm_minor, kdev);
+       struct drm_device *dev = minor->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 val, rp_state_cap;
+       ssize_t ret;
+
+       ret = mutex_lock_interruptible(&dev->struct_mutex);
+       if (ret)
+               return ret;
+       rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
+       mutex_unlock(&dev->struct_mutex);
+
+       if (attr == &dev_attr_gt_RP0_freq_mhz) {
+               val = ((rp_state_cap & 0x0000ff) >> 0) * GT_FREQUENCY_MULTIPLIER;
+       } else if (attr == &dev_attr_gt_RP1_freq_mhz) {
+               val = ((rp_state_cap & 0x00ff00) >> 8) * GT_FREQUENCY_MULTIPLIER;
+       } else if (attr == &dev_attr_gt_RPn_freq_mhz) {
+               val = ((rp_state_cap & 0xff0000) >> 16) * GT_FREQUENCY_MULTIPLIER;
+       } else {
+               BUG();
+       }
+       return snprintf(buf, PAGE_SIZE, "%d", val);
+}
+
+static const struct attribute *gen6_attrs[] = {
+       &dev_attr_gt_cur_freq_mhz.attr,
+       &dev_attr_gt_max_freq_mhz.attr,
+       &dev_attr_gt_min_freq_mhz.attr,
+       &dev_attr_gt_RP0_freq_mhz.attr,
+       &dev_attr_gt_RP1_freq_mhz.attr,
+       &dev_attr_gt_RPn_freq_mhz.attr,
+       NULL,
+};
+
 void i915_setup_sysfs(struct drm_device *dev)
 {
        int ret;
 
+#ifdef CONFIG_PM
        if (INTEL_INFO(dev)->gen >= 6) {
                ret = sysfs_merge_group(&dev->primary->kdev.kobj,
                                        &rc6_attr_group);
                if (ret)
                        DRM_ERROR("RC6 residency sysfs setup failed\n");
        }
-
-       if (IS_IVYBRIDGE(dev)) {
+#endif
+       if (HAS_L3_GPU_CACHE(dev)) {
                ret = device_create_bin_file(&dev->primary->kdev, &dpf_attrs);
                if (ret)
                        DRM_ERROR("l3 parity sysfs setup failed\n");
        }
+
+       if (INTEL_INFO(dev)->gen >= 6) {
+               ret = sysfs_create_files(&dev->primary->kdev.kobj, gen6_attrs);
+               if (ret)
+                       DRM_ERROR("gen6 sysfs setup failed\n");
+       }
 }
 
 void i915_teardown_sysfs(struct drm_device *dev)
 {
+       sysfs_remove_files(&dev->primary->kdev.kobj, gen6_attrs);
        device_remove_bin_file(&dev->primary->kdev,  &dpf_attrs);
+#ifdef CONFIG_PM
        sysfs_unmerge_group(&dev->primary->kdev.kobj, &rc6_attr_group);
+#endif
 }
-#else
-void i915_setup_sysfs(struct drm_device *dev)
-{
-       return;
-}
-
-void i915_teardown_sysfs(struct drm_device *dev)
-{
-       return;
-}
-#endif /* CONFIG_PM */
index fe90b3a84a6d9db3b7cac327173749fe0fc0b0a9..8134421b89a6c582b9862a252e246fb9f69ae12f 100644 (file)
@@ -214,22 +214,18 @@ TRACE_EVENT(i915_gem_evict,
 );
 
 TRACE_EVENT(i915_gem_evict_everything,
-           TP_PROTO(struct drm_device *dev, bool purgeable),
-           TP_ARGS(dev, purgeable),
+           TP_PROTO(struct drm_device *dev),
+           TP_ARGS(dev),
 
            TP_STRUCT__entry(
                             __field(u32, dev)
-                            __field(bool, purgeable)
                            ),
 
            TP_fast_assign(
                           __entry->dev = dev->primary->index;
-                          __entry->purgeable = purgeable;
                          ),
 
-           TP_printk("dev=%d%s",
-                     __entry->dev,
-                     __entry->purgeable ? ", purgeable only" : "")
+           TP_printk("dev=%d", __entry->dev)
 );
 
 TRACE_EVENT(i915_gem_ring_dispatch,
@@ -434,6 +430,21 @@ TRACE_EVENT(i915_reg_rw,
                (u32)(__entry->val >> 32))
 );
 
+TRACE_EVENT(intel_gpu_freq_change,
+           TP_PROTO(u32 freq),
+           TP_ARGS(freq),
+
+           TP_STRUCT__entry(
+                            __field(u32, freq)
+                            ),
+
+           TP_fast_assign(
+                          __entry->freq = freq;
+                          ),
+
+           TP_printk("new_freq=%u", __entry->freq)
+);
+
 #endif /* _I915_TRACE_H_ */
 
 /* This part must be outside protection */
index 23bdc8cd145864f8346ce8d8ce4a255e436b0b6f..c42b9809f86de5c21fefd55cb12e5c458fbc1d1b 100644 (file)
@@ -47,6 +47,7 @@
 struct intel_crt {
        struct intel_encoder base;
        bool force_hotplug_required;
+       u32 adpa_reg;
 };
 
 static struct intel_crt *intel_attached_crt(struct drm_connector *connector)
@@ -55,42 +56,68 @@ static struct intel_crt *intel_attached_crt(struct drm_connector *connector)
                            struct intel_crt, base);
 }
 
-static void pch_crt_dpms(struct drm_encoder *encoder, int mode)
+static struct intel_crt *intel_encoder_to_crt(struct intel_encoder *encoder)
 {
-       struct drm_device *dev = encoder->dev;
+       return container_of(encoder, struct intel_crt, base);
+}
+
+static bool intel_crt_get_hw_state(struct intel_encoder *encoder,
+                                  enum pipe *pipe)
+{
+       struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crt *crt = intel_encoder_to_crt(encoder);
+       u32 tmp;
+
+       tmp = I915_READ(crt->adpa_reg);
+
+       if (!(tmp & ADPA_DAC_ENABLE))
+               return false;
+
+       if (HAS_PCH_CPT(dev))
+               *pipe = PORT_TO_PIPE_CPT(tmp);
+       else
+               *pipe = PORT_TO_PIPE(tmp);
+
+       return true;
+}
+
+static void intel_disable_crt(struct intel_encoder *encoder)
+{
+       struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+       struct intel_crt *crt = intel_encoder_to_crt(encoder);
        u32 temp;
 
-       temp = I915_READ(PCH_ADPA);
+       temp = I915_READ(crt->adpa_reg);
+       temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
        temp &= ~ADPA_DAC_ENABLE;
+       I915_WRITE(crt->adpa_reg, temp);
+}
 
-       switch (mode) {
-       case DRM_MODE_DPMS_ON:
-               temp |= ADPA_DAC_ENABLE;
-               break;
-       case DRM_MODE_DPMS_STANDBY:
-       case DRM_MODE_DPMS_SUSPEND:
-       case DRM_MODE_DPMS_OFF:
-               /* Just leave port enable cleared */
-               break;
-       }
+static void intel_enable_crt(struct intel_encoder *encoder)
+{
+       struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+       struct intel_crt *crt = intel_encoder_to_crt(encoder);
+       u32 temp;
 
-       I915_WRITE(PCH_ADPA, temp);
+       temp = I915_READ(crt->adpa_reg);
+       temp |= ADPA_DAC_ENABLE;
+       I915_WRITE(crt->adpa_reg, temp);
 }
 
-static void gmch_crt_dpms(struct drm_encoder *encoder, int mode)
+/* Note: The caller is required to filter out dpms modes not supported by the
+ * platform. */
+static void intel_crt_set_dpms(struct intel_encoder *encoder, int mode)
 {
-       struct drm_device *dev = encoder->dev;
+       struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crt *crt = intel_encoder_to_crt(encoder);
        u32 temp;
 
-       temp = I915_READ(ADPA);
+       temp = I915_READ(crt->adpa_reg);
        temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
        temp &= ~ADPA_DAC_ENABLE;
 
-       if (IS_VALLEYVIEW(dev) && mode != DRM_MODE_DPMS_ON)
-               mode = DRM_MODE_DPMS_OFF;
-
        switch (mode) {
        case DRM_MODE_DPMS_ON:
                temp |= ADPA_DAC_ENABLE;
@@ -106,7 +133,51 @@ static void gmch_crt_dpms(struct drm_encoder *encoder, int mode)
                break;
        }
 
-       I915_WRITE(ADPA, temp);
+       I915_WRITE(crt->adpa_reg, temp);
+}
+
+static void intel_crt_dpms(struct drm_connector *connector, int mode)
+{
+       struct drm_device *dev = connector->dev;
+       struct intel_encoder *encoder = intel_attached_encoder(connector);
+       struct drm_crtc *crtc;
+       int old_dpms;
+
+       /* PCH platforms and VLV only support on/off. */
+       if (INTEL_INFO(dev)->gen < 5 && mode != DRM_MODE_DPMS_ON)
+               mode = DRM_MODE_DPMS_OFF;
+
+       if (mode == connector->dpms)
+               return;
+
+       old_dpms = connector->dpms;
+       connector->dpms = mode;
+
+       /* Only need to change hw state when actually enabled */
+       crtc = encoder->base.crtc;
+       if (!crtc) {
+               encoder->connectors_active = false;
+               return;
+       }
+
+       /* We need the pipe to run for anything but OFF. */
+       if (mode == DRM_MODE_DPMS_OFF)
+               encoder->connectors_active = false;
+       else
+               encoder->connectors_active = true;
+
+       if (mode < old_dpms) {
+               /* From off to on, enable the pipe first. */
+               intel_crtc_update_dpms(crtc);
+
+               intel_crt_set_dpms(encoder, mode);
+       } else {
+               intel_crt_set_dpms(encoder, mode);
+
+               intel_crtc_update_dpms(crtc);
+       }
+
+       intel_modeset_check_state(connector->dev);
 }
 
 static int intel_crt_mode_valid(struct drm_connector *connector,
@@ -145,19 +216,15 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
 
        struct drm_device *dev = encoder->dev;
        struct drm_crtc *crtc = encoder->crtc;
+       struct intel_crt *crt =
+               intel_encoder_to_crt(to_intel_encoder(encoder));
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct drm_i915_private *dev_priv = dev->dev_private;
        int dpll_md_reg;
        u32 adpa, dpll_md;
-       u32 adpa_reg;
 
        dpll_md_reg = DPLL_MD(intel_crtc->pipe);
 
-       if (HAS_PCH_SPLIT(dev))
-               adpa_reg = PCH_ADPA;
-       else
-               adpa_reg = ADPA;
-
        /*
         * Disable separate mode multiplier used when cloning SDVO to CRT
         * XXX this needs to be adjusted when we really are cloning
@@ -185,7 +252,7 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
        if (!HAS_PCH_SPLIT(dev))
                I915_WRITE(BCLRPAT(intel_crtc->pipe), 0);
 
-       I915_WRITE(adpa_reg, adpa);
+       I915_WRITE(crt->adpa_reg, adpa);
 }
 
 static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector)
@@ -545,14 +612,12 @@ intel_crt_detect(struct drm_connector *connector, bool force)
                return connector->status;
 
        /* for pre-945g platforms use load detect */
-       if (intel_get_load_detect_pipe(&crt->base, connector, NULL,
-                                      &tmp)) {
+       if (intel_get_load_detect_pipe(connector, NULL, &tmp)) {
                if (intel_crt_detect_ddc(connector))
                        status = connector_status_connected;
                else
                        status = intel_crt_load_detect(crt);
-               intel_release_load_detect_pipe(&crt->base, connector,
-                                              &tmp);
+               intel_release_load_detect_pipe(connector, &tmp);
        } else
                status = connector_status_unknown;
 
@@ -603,25 +668,15 @@ static void intel_crt_reset(struct drm_connector *connector)
  * Routines for controlling stuff on the analog port
  */
 
-static const struct drm_encoder_helper_funcs pch_encoder_funcs = {
+static const struct drm_encoder_helper_funcs crt_encoder_funcs = {
        .mode_fixup = intel_crt_mode_fixup,
-       .prepare = intel_encoder_prepare,
-       .commit = intel_encoder_commit,
        .mode_set = intel_crt_mode_set,
-       .dpms = pch_crt_dpms,
-};
-
-static const struct drm_encoder_helper_funcs gmch_encoder_funcs = {
-       .mode_fixup = intel_crt_mode_fixup,
-       .prepare = intel_encoder_prepare,
-       .commit = intel_encoder_commit,
-       .mode_set = intel_crt_mode_set,
-       .dpms = gmch_crt_dpms,
+       .disable = intel_encoder_noop,
 };
 
 static const struct drm_connector_funcs intel_crt_connector_funcs = {
        .reset = intel_crt_reset,
-       .dpms = drm_helper_connector_dpms,
+       .dpms = intel_crt_dpms,
        .detect = intel_crt_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .destroy = intel_crt_destroy,
@@ -662,7 +717,6 @@ void intel_crt_init(struct drm_device *dev)
        struct intel_crt *crt;
        struct intel_connector *intel_connector;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       const struct drm_encoder_helper_funcs *encoder_helper_funcs;
 
        /* Skip machines without VGA that falsely report hotplug events */
        if (dmi_check_system(intel_no_crt))
@@ -688,13 +742,11 @@ void intel_crt_init(struct drm_device *dev)
        intel_connector_attach_encoder(intel_connector, &crt->base);
 
        crt->base.type = INTEL_OUTPUT_ANALOG;
-       crt->base.clone_mask = (1 << INTEL_SDVO_NON_TV_CLONE_BIT |
-                               1 << INTEL_ANALOG_CLONE_BIT |
-                               1 << INTEL_SDVO_LVDS_CLONE_BIT);
+       crt->base.cloneable = true;
        if (IS_HASWELL(dev))
                crt->base.crtc_mask = (1 << 0);
        else
-               crt->base.crtc_mask = (1 << 0) | (1 << 1);
+               crt->base.crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
 
        if (IS_GEN2(dev))
                connector->interlace_allowed = 0;
@@ -703,11 +755,18 @@ void intel_crt_init(struct drm_device *dev)
        connector->doublescan_allowed = 0;
 
        if (HAS_PCH_SPLIT(dev))
-               encoder_helper_funcs = &pch_encoder_funcs;
+               crt->adpa_reg = PCH_ADPA;
+       else if (IS_VALLEYVIEW(dev))
+               crt->adpa_reg = VLV_ADPA;
        else
-               encoder_helper_funcs = &gmch_encoder_funcs;
+               crt->adpa_reg = ADPA;
+
+       crt->base.disable = intel_disable_crt;
+       crt->base.enable = intel_enable_crt;
+       crt->base.get_hw_state = intel_crt_get_hw_state;
+       intel_connector->get_hw_state = intel_connector_get_hw_state;
 
-       drm_encoder_helper_add(&crt->base.base, encoder_helper_funcs);
+       drm_encoder_helper_add(&crt->base.base, &crt_encoder_funcs);
        drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
 
        drm_sysfs_connector_add(connector);
index 933c7485917264d2eb07b6c42a8cd336876b070b..bfe375466a0e03b953022248e65cfafdf3a17874 100644 (file)
@@ -250,7 +250,7 @@ void intel_ddi_init(struct drm_device *dev, enum port port)
        case PORT_B:
        case PORT_C:
        case PORT_D:
-               intel_hdmi_init(dev, DDI_BUF_CTL(port));
+               intel_hdmi_init(dev, DDI_BUF_CTL(port), port);
                break;
        default:
                DRM_DEBUG_DRIVER("No handlers defined for port %d, skipping DDI initialization\n",
@@ -267,7 +267,8 @@ struct wrpll_tmds_clock {
        u16 r2;         /* Reference divider */
 };
 
-/* Table of matching values for WRPLL clocks programming for each frequency */
+/* Table of matching values for WRPLL clocks programming for each frequency.
+ * The code assumes this table is sorted. */
 static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = {
        {19750, 38,     25,     18},
        {20000, 48,     32,     18},
@@ -277,7 +278,6 @@ static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = {
        {23000, 36,     23,     15},
        {23500, 40,     40,     23},
        {23750, 26,     16,     14},
-       {23750, 26,     16,     14},
        {24000, 36,     24,     15},
        {25000, 36,     25,     15},
        {25175, 26,     40,     33},
@@ -437,7 +437,6 @@ static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = {
        {108000,        8,      24,     15},
        {108108,        8,      173,    108},
        {109000,        6,      23,     19},
-       {109000,        6,      23,     19},
        {110000,        6,      22,     18},
        {110013,        6,      22,     18},
        {110250,        8,      49,     30},
@@ -614,7 +613,6 @@ static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = {
        {218250,        4,      42,     26},
        {218750,        4,      34,     21},
        {219000,        4,      47,     29},
-       {219000,        4,      47,     29},
        {220000,        4,      44,     27},
        {220640,        4,      49,     30},
        {220750,        4,      36,     22},
@@ -658,7 +656,7 @@ void intel_ddi_mode_set(struct drm_encoder *encoder,
        struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
        int port = intel_hdmi->ddi_port;
        int pipe = intel_crtc->pipe;
-       int p, n2, r2, valid=0;
+       int p, n2, r2;
        u32 temp, i;
 
        /* On Haswell, we need to enable the clocks and prepare DDI function to
@@ -666,26 +664,23 @@ void intel_ddi_mode_set(struct drm_encoder *encoder,
         */
        DRM_DEBUG_KMS("Preparing HDMI DDI mode for Haswell on port %c, pipe %c\n", port_name(port), pipe_name(pipe));
 
-       for (i=0; i < ARRAY_SIZE(wrpll_tmds_clock_table); i++) {
-               if (crtc->mode.clock == wrpll_tmds_clock_table[i].clock) {
-                       p = wrpll_tmds_clock_table[i].p;
-                       n2 = wrpll_tmds_clock_table[i].n2;
-                       r2 = wrpll_tmds_clock_table[i].r2;
+       for (i = 0; i < ARRAY_SIZE(wrpll_tmds_clock_table); i++)
+               if (crtc->mode.clock <= wrpll_tmds_clock_table[i].clock)
+                       break;
 
-                       DRM_DEBUG_KMS("WR PLL clock: found settings for %dKHz refresh rate: p=%d, n2=%d, r2=%d\n",
-                                       crtc->mode.clock,
-                                       p, n2, r2);
+       if (i == ARRAY_SIZE(wrpll_tmds_clock_table))
+               i--;
 
-                       valid = 1;
-                       break;
-               }
-       }
+       p = wrpll_tmds_clock_table[i].p;
+       n2 = wrpll_tmds_clock_table[i].n2;
+       r2 = wrpll_tmds_clock_table[i].r2;
 
-       if (!valid) {
-               DRM_ERROR("Unable to find WR PLL clock settings for %dKHz refresh rate\n",
-                               crtc->mode.clock);
-               return;
-       }
+       if (wrpll_tmds_clock_table[i].clock != crtc->mode.clock)
+               DRM_INFO("WR PLL: using settings for %dKHz on %dKHz mode\n",
+                        wrpll_tmds_clock_table[i].clock, crtc->mode.clock);
+
+       DRM_DEBUG_KMS("WR PLL: %dKHz refresh rate with p=%d, n2=%d r2=%d\n",
+                     crtc->mode.clock, p, n2, r2);
 
        /* Enable LCPLL if disabled */
        temp = I915_READ(LCPLL_CTL);
@@ -718,46 +713,107 @@ void intel_ddi_mode_set(struct drm_encoder *encoder,
                /* Proper support for digital audio needs a new logic and a new set
                 * of registers, so we leave it for future patch bombing.
                 */
-               DRM_DEBUG_DRIVER("HDMI audio on pipe %c not yet supported on DDI\n",
+               DRM_DEBUG_DRIVER("HDMI audio on pipe %c on DDI\n",
                                 pipe_name(intel_crtc->pipe));
+
+               /* write eld */
+               DRM_DEBUG_DRIVER("HDMI audio: write eld information\n");
+               intel_write_eld(encoder, adjusted_mode);
        }
 
        /* Enable PIPE_DDI_FUNC_CTL for the pipe to work in HDMI mode */
-       temp = I915_READ(DDI_FUNC_CTL(pipe));
-       temp &= ~PIPE_DDI_PORT_MASK;
-       temp &= ~PIPE_DDI_BPC_12;
-       temp |= PIPE_DDI_SELECT_PORT(port) |
-                       PIPE_DDI_MODE_SELECT_HDMI |
-                       ((intel_crtc->bpp > 24) ?
-                               PIPE_DDI_BPC_12 :
-                               PIPE_DDI_BPC_8) |
-                       PIPE_DDI_FUNC_ENABLE;
+       temp = PIPE_DDI_FUNC_ENABLE | PIPE_DDI_SELECT_PORT(port);
+
+       switch (intel_crtc->bpp) {
+       case 18:
+               temp |= PIPE_DDI_BPC_6;
+               break;
+       case 24:
+               temp |= PIPE_DDI_BPC_8;
+               break;
+       case 30:
+               temp |= PIPE_DDI_BPC_10;
+               break;
+       case 36:
+               temp |= PIPE_DDI_BPC_12;
+               break;
+       default:
+               WARN(1, "%d bpp unsupported by pipe DDI function\n",
+                    intel_crtc->bpp);
+       }
+
+       if (intel_hdmi->has_hdmi_sink)
+               temp |= PIPE_DDI_MODE_SELECT_HDMI;
+       else
+               temp |= PIPE_DDI_MODE_SELECT_DVI;
+
+       if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
+               temp |= PIPE_DDI_PVSYNC;
+       if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
+               temp |= PIPE_DDI_PHSYNC;
 
        I915_WRITE(DDI_FUNC_CTL(pipe), temp);
 
        intel_hdmi->set_infoframes(encoder, adjusted_mode);
 }
 
-void intel_ddi_dpms(struct drm_encoder *encoder, int mode)
+bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
+                           enum pipe *pipe)
 {
-       struct drm_device *dev = encoder->dev;
+       struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
+       u32 tmp;
+       int i;
+
+       tmp = I915_READ(DDI_BUF_CTL(intel_hdmi->ddi_port));
+
+       if (!(tmp & DDI_BUF_CTL_ENABLE))
+               return false;
+
+       for_each_pipe(i) {
+               tmp = I915_READ(DDI_FUNC_CTL(i));
+
+               if ((tmp & PIPE_DDI_PORT_MASK)
+                   == PIPE_DDI_SELECT_PORT(intel_hdmi->ddi_port)) {
+                       *pipe = i;
+                       return true;
+               }
+       }
+
+       DRM_DEBUG_KMS("No pipe for ddi port %i found\n", intel_hdmi->ddi_port);
+
+       return true;
+}
+
+void intel_enable_ddi(struct intel_encoder *encoder)
+{
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
        int port = intel_hdmi->ddi_port;
        u32 temp;
 
        temp = I915_READ(DDI_BUF_CTL(port));
-
-       if (mode != DRM_MODE_DPMS_ON) {
-               temp &= ~DDI_BUF_CTL_ENABLE;
-       } else {
-               temp |= DDI_BUF_CTL_ENABLE;
-       }
+       temp |= DDI_BUF_CTL_ENABLE;
 
        /* Enable DDI_BUF_CTL. 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),
-                       temp);
+       I915_WRITE(DDI_BUF_CTL(port), temp);
+}
+
+void intel_disable_ddi(struct intel_encoder *encoder)
+{
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
+       int port = intel_hdmi->ddi_port;
+       u32 temp;
+
+       temp = I915_READ(DDI_BUF_CTL(port));
+       temp &= ~DDI_BUF_CTL_ENABLE;
+
+       I915_WRITE(DDI_BUF_CTL(port), temp);
 }
index bc2ad348e5d8ce9e13c51b1c6dba019e0e2acc4e..6f5aafa1b633c2f9f13a28ba141a07a96f2adc28 100644 (file)
@@ -1006,7 +1006,7 @@ void intel_wait_for_pipe_off(struct drm_device *dev, int pipe)
                /* Wait for the Pipe State to go off */
                if (wait_for((I915_READ(reg) & I965_PIPECONF_ACTIVE) == 0,
                             100))
-                       DRM_DEBUG_KMS("pipe_off wait timed out\n");
+                       WARN(1, "pipe_off wait timed out\n");
        } else {
                u32 last_line, line_mask;
                int reg = PIPEDSL(pipe);
@@ -1024,7 +1024,7 @@ void intel_wait_for_pipe_off(struct drm_device *dev, int pipe)
                } while (((I915_READ(reg) & line_mask) != last_line) &&
                         time_after(timeout, jiffies));
                if (time_after(jiffies, timeout))
-                       DRM_DEBUG_KMS("pipe_off wait timed out\n");
+                       WARN(1, "pipe_off wait timed out\n");
        }
 }
 
@@ -1431,6 +1431,8 @@ static void assert_pch_ports_disabled(struct drm_i915_private *dev_priv,
  * protect mechanism may be enabled.
  *
  * Note!  This is for pre-ILK only.
+ *
+ * Unfortunately needed by dvo_ns2501 since the dvo depends on it running.
  */
 static void intel_enable_pll(struct drm_i915_private *dev_priv, enum pipe pipe)
 {
@@ -1860,59 +1862,6 @@ static void intel_disable_plane(struct drm_i915_private *dev_priv,
        intel_wait_for_vblank(dev_priv->dev, pipe);
 }
 
-static void disable_pch_dp(struct drm_i915_private *dev_priv,
-                          enum pipe pipe, int reg, u32 port_sel)
-{
-       u32 val = I915_READ(reg);
-       if (dp_pipe_enabled(dev_priv, pipe, port_sel, val)) {
-               DRM_DEBUG_KMS("Disabling pch dp %x on pipe %d\n", reg, pipe);
-               I915_WRITE(reg, val & ~DP_PORT_EN);
-       }
-}
-
-static void disable_pch_hdmi(struct drm_i915_private *dev_priv,
-                            enum pipe pipe, int reg)
-{
-       u32 val = I915_READ(reg);
-       if (hdmi_pipe_enabled(dev_priv, pipe, val)) {
-               DRM_DEBUG_KMS("Disabling pch HDMI %x on pipe %d\n",
-                             reg, pipe);
-               I915_WRITE(reg, val & ~PORT_ENABLE);
-       }
-}
-
-/* Disable any ports connected to this transcoder */
-static void intel_disable_pch_ports(struct drm_i915_private *dev_priv,
-                                   enum pipe pipe)
-{
-       u32 reg, val;
-
-       val = I915_READ(PCH_PP_CONTROL);
-       I915_WRITE(PCH_PP_CONTROL, val | PANEL_UNLOCK_REGS);
-
-       disable_pch_dp(dev_priv, pipe, PCH_DP_B, TRANS_DP_PORT_SEL_B);
-       disable_pch_dp(dev_priv, pipe, PCH_DP_C, TRANS_DP_PORT_SEL_C);
-       disable_pch_dp(dev_priv, pipe, PCH_DP_D, TRANS_DP_PORT_SEL_D);
-
-       reg = PCH_ADPA;
-       val = I915_READ(reg);
-       if (adpa_pipe_enabled(dev_priv, pipe, val))
-               I915_WRITE(reg, val & ~ADPA_DAC_ENABLE);
-
-       reg = PCH_LVDS;
-       val = I915_READ(reg);
-       if (lvds_pipe_enabled(dev_priv, pipe, val)) {
-               DRM_DEBUG_KMS("disable lvds on pipe %d val 0x%08x\n", pipe, val);
-               I915_WRITE(reg, val & ~LVDS_PORT_EN);
-               POSTING_READ(reg);
-               udelay(100);
-       }
-
-       disable_pch_hdmi(dev_priv, pipe, HDMIB);
-       disable_pch_hdmi(dev_priv, pipe, HDMIC);
-       disable_pch_hdmi(dev_priv, pipe, HDMID);
-}
-
 int
 intel_pin_and_fence_fb_obj(struct drm_device *dev,
                           struct drm_i915_gem_object *obj,
@@ -2201,16 +2150,17 @@ intel_finish_fb(struct drm_framebuffer *old_fb)
 
 static int
 intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
-                   struct drm_framebuffer *old_fb)
+                   struct drm_framebuffer *fb)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_i915_master_private *master_priv;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       struct drm_framebuffer *old_fb;
        int ret;
 
        /* no fb bound */
-       if (!crtc->fb) {
+       if (!fb) {
                DRM_ERROR("No FB bound\n");
                return 0;
        }
@@ -2224,7 +2174,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
 
        mutex_lock(&dev->struct_mutex);
        ret = intel_pin_and_fence_fb_obj(dev,
-                                        to_intel_framebuffer(crtc->fb)->obj,
+                                        to_intel_framebuffer(fb)->obj,
                                         NULL);
        if (ret != 0) {
                mutex_unlock(&dev->struct_mutex);
@@ -2232,17 +2182,22 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                return ret;
        }
 
-       if (old_fb)
-               intel_finish_fb(old_fb);
+       if (crtc->fb)
+               intel_finish_fb(crtc->fb);
 
-       ret = dev_priv->display.update_plane(crtc, crtc->fb, x, y);
+       ret = dev_priv->display.update_plane(crtc, fb, x, y);
        if (ret) {
-               intel_unpin_fb_obj(to_intel_framebuffer(crtc->fb)->obj);
+               intel_unpin_fb_obj(to_intel_framebuffer(fb)->obj);
                mutex_unlock(&dev->struct_mutex);
                DRM_ERROR("failed to update base address\n");
                return ret;
        }
 
+       old_fb = crtc->fb;
+       crtc->fb = fb;
+       crtc->x = x;
+       crtc->y = y;
+
        if (old_fb) {
                intel_wait_for_vblank(dev, intel_crtc->pipe);
                intel_unpin_fb_obj(to_intel_framebuffer(old_fb)->obj);
@@ -2709,11 +2664,10 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc)
        DRM_DEBUG_KMS("FDI train done.\n");
 }
 
-static void ironlake_fdi_pll_enable(struct drm_crtc *crtc)
+static void ironlake_fdi_pll_enable(struct intel_crtc *intel_crtc)
 {
-       struct drm_device *dev = crtc->dev;
+       struct drm_device *dev = intel_crtc->base.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 reg, temp;
 
@@ -2754,6 +2708,35 @@ static void ironlake_fdi_pll_enable(struct drm_crtc *crtc)
        }
 }
 
+static void ironlake_fdi_pll_disable(struct intel_crtc *intel_crtc)
+{
+       struct drm_device *dev = intel_crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int pipe = intel_crtc->pipe;
+       u32 reg, temp;
+
+       /* Switch from PCDclk to Rawclk */
+       reg = FDI_RX_CTL(pipe);
+       temp = I915_READ(reg);
+       I915_WRITE(reg, temp & ~FDI_PCDCLK);
+
+       /* Disable CPU FDI TX PLL */
+       reg = FDI_TX_CTL(pipe);
+       temp = I915_READ(reg);
+       I915_WRITE(reg, temp & ~FDI_TX_PLL_ENABLE);
+
+       POSTING_READ(reg);
+       udelay(100);
+
+       reg = FDI_RX_CTL(pipe);
+       temp = I915_READ(reg);
+       I915_WRITE(reg, temp & ~FDI_RX_PLL_ENABLE);
+
+       /* Wait for the clocks to turn off. */
+       POSTING_READ(reg);
+       udelay(100);
+}
+
 static void cpt_phase_pointer_disable(struct drm_device *dev, int pipe)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2838,13 +2821,13 @@ static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc)
 static bool intel_crtc_driving_pch(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
-       struct intel_encoder *encoder;
+       struct intel_encoder *intel_encoder;
 
        /*
         * If there's a non-PCH eDP on this crtc, it must be DP_A, and that
         * must be driven by its own crtc; no sharing is possible.
         */
-       for_each_encoder_on_crtc(dev, crtc, encoder) {
+       for_each_encoder_on_crtc(dev, crtc, intel_encoder) {
 
                /* On Haswell, LPT PCH handles the VGA connection via FDI, and Haswell
                 * CPU handles all others */
@@ -2852,19 +2835,19 @@ static bool intel_crtc_driving_pch(struct drm_crtc *crtc)
                        /* It is still unclear how this will work on PPT, so throw up a warning */
                        WARN_ON(!HAS_PCH_LPT(dev));
 
-                       if (encoder->type == DRM_MODE_ENCODER_DAC) {
+                       if (intel_encoder->type == INTEL_OUTPUT_ANALOG) {
                                DRM_DEBUG_KMS("Haswell detected DAC encoder, assuming is PCH\n");
                                return true;
                        } else {
                                DRM_DEBUG_KMS("Haswell detected encoder %d, assuming is CPU\n",
-                                               encoder->type);
+                                             intel_encoder->type);
                                return false;
                        }
                }
 
-               switch (encoder->type) {
+               switch (intel_encoder->type) {
                case INTEL_OUTPUT_EDP:
-                       if (!intel_encoder_is_pch_edp(&encoder->base))
+                       if (!intel_encoder_is_pch_edp(&intel_encoder->base))
                                return false;
                        continue;
                }
@@ -3181,11 +3164,14 @@ static void ironlake_crtc_enable(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;
        int plane = intel_crtc->plane;
        u32 temp;
        bool is_pch_port;
 
+       WARN_ON(!crtc->enabled);
+
        if (intel_crtc->active)
                return;
 
@@ -3200,10 +3186,16 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
 
        is_pch_port = intel_crtc_driving_pch(crtc);
 
-       if (is_pch_port)
-               ironlake_fdi_pll_enable(crtc);
-       else
-               ironlake_fdi_disable(crtc);
+       if (is_pch_port) {
+               ironlake_fdi_pll_enable(intel_crtc);
+       } else {
+               assert_fdi_tx_disabled(dev_priv, pipe);
+               assert_fdi_rx_disabled(dev_priv, pipe);
+       }
+
+       for_each_encoder_on_crtc(dev, crtc, encoder)
+               if (encoder->pre_enable)
+                       encoder->pre_enable(encoder);
 
        /* Enable panel fitting for LVDS */
        if (dev_priv->pch_pf_size &&
@@ -3234,6 +3226,12 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
        mutex_unlock(&dev->struct_mutex);
 
        intel_crtc_update_cursor(crtc, true);
+
+       for_each_encoder_on_crtc(dev, crtc, encoder)
+               encoder->enable(encoder);
+
+       if (HAS_PCH_CPT(dev))
+               intel_cpt_verify_modeset(dev, intel_crtc->pipe);
 }
 
 static void ironlake_crtc_disable(struct drm_crtc *crtc)
@@ -3241,13 +3239,18 @@ static void ironlake_crtc_disable(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;
        int plane = intel_crtc->plane;
        u32 reg, temp;
 
+
        if (!intel_crtc->active)
                return;
 
+       for_each_encoder_on_crtc(dev, crtc, encoder)
+               encoder->disable(encoder);
+
        intel_crtc_wait_for_pending_flips(crtc);
        drm_vblank_off(dev, pipe);
        intel_crtc_update_cursor(crtc, false);
@@ -3263,14 +3266,11 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
        I915_WRITE(PF_CTL(pipe), 0);
        I915_WRITE(PF_WIN_SZ(pipe), 0);
 
-       ironlake_fdi_disable(crtc);
+       for_each_encoder_on_crtc(dev, crtc, encoder)
+               if (encoder->post_disable)
+                       encoder->post_disable(encoder);
 
-       /* This is a horrible layering violation; we should be doing this in
-        * the connector/encoder ->prepare instead, but we don't always have
-        * enough information there about the config to know whether it will
-        * actually be necessary or just cause undesired flicker.
-        */
-       intel_disable_pch_ports(dev_priv, pipe);
+       ironlake_fdi_disable(crtc);
 
        intel_disable_transcoder(dev_priv, pipe);
 
@@ -3304,26 +3304,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
        /* disable PCH DPLL */
        intel_disable_pch_pll(intel_crtc);
 
-       /* Switch from PCDclk to Rawclk */
-       reg = FDI_RX_CTL(pipe);
-       temp = I915_READ(reg);
-       I915_WRITE(reg, temp & ~FDI_PCDCLK);
-
-       /* Disable CPU FDI TX PLL */
-       reg = FDI_TX_CTL(pipe);
-       temp = I915_READ(reg);
-       I915_WRITE(reg, temp & ~FDI_TX_PLL_ENABLE);
-
-       POSTING_READ(reg);
-       udelay(100);
-
-       reg = FDI_RX_CTL(pipe);
-       temp = I915_READ(reg);
-       I915_WRITE(reg, temp & ~FDI_RX_PLL_ENABLE);
-
-       /* Wait for the clocks to turn off. */
-       POSTING_READ(reg);
-       udelay(100);
+       ironlake_fdi_pll_disable(intel_crtc);
 
        intel_crtc->active = false;
        intel_update_watermarks(dev);
@@ -3333,30 +3314,6 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
        mutex_unlock(&dev->struct_mutex);
 }
 
-static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode)
-{
-       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       int pipe = intel_crtc->pipe;
-       int plane = intel_crtc->plane;
-
-       /* XXX: When our outputs are all unaware of DPMS modes other than off
-        * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
-        */
-       switch (mode) {
-       case DRM_MODE_DPMS_ON:
-       case DRM_MODE_DPMS_STANDBY:
-       case DRM_MODE_DPMS_SUSPEND:
-               DRM_DEBUG_KMS("crtc %d/%d dpms on\n", pipe, plane);
-               ironlake_crtc_enable(crtc);
-               break;
-
-       case DRM_MODE_DPMS_OFF:
-               DRM_DEBUG_KMS("crtc %d/%d dpms off\n", pipe, plane);
-               ironlake_crtc_disable(crtc);
-               break;
-       }
-}
-
 static void ironlake_crtc_off(struct drm_crtc *crtc)
 {
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
@@ -3386,9 +3343,12 @@ static void i9xx_crtc_enable(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;
        int plane = intel_crtc->plane;
 
+       WARN_ON(!crtc->enabled);
+
        if (intel_crtc->active)
                return;
 
@@ -3405,6 +3365,9 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
        /* Give the overlay scaler a chance to enable if it's on this pipe */
        intel_crtc_dpms_overlay(intel_crtc, true);
        intel_crtc_update_cursor(crtc, true);
+
+       for_each_encoder_on_crtc(dev, crtc, encoder)
+               encoder->enable(encoder);
 }
 
 static void i9xx_crtc_disable(struct drm_crtc *crtc)
@@ -3412,12 +3375,17 @@ static void i9xx_crtc_disable(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;
        int plane = intel_crtc->plane;
 
+
        if (!intel_crtc->active)
                return;
 
+       for_each_encoder_on_crtc(dev, crtc, encoder)
+               encoder->disable(encoder);
+
        /* Give the overlay scaler a chance to disable if it's on this pipe */
        intel_crtc_wait_for_pending_flips(crtc);
        drm_vblank_off(dev, pipe);
@@ -3436,45 +3404,17 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
        intel_update_watermarks(dev);
 }
 
-static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
-{
-       /* XXX: When our outputs are all unaware of DPMS modes other than off
-        * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
-        */
-       switch (mode) {
-       case DRM_MODE_DPMS_ON:
-       case DRM_MODE_DPMS_STANDBY:
-       case DRM_MODE_DPMS_SUSPEND:
-               i9xx_crtc_enable(crtc);
-               break;
-       case DRM_MODE_DPMS_OFF:
-               i9xx_crtc_disable(crtc);
-               break;
-       }
-}
-
 static void i9xx_crtc_off(struct drm_crtc *crtc)
 {
 }
 
-/**
- * Sets the power management mode of the pipe and plane.
- */
-static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
+static void intel_crtc_update_sarea(struct drm_crtc *crtc,
+                                   bool enabled)
 {
        struct drm_device *dev = crtc->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_i915_master_private *master_priv;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int pipe = intel_crtc->pipe;
-       bool enabled;
-
-       if (intel_crtc->dpms_mode == mode)
-               return;
-
-       intel_crtc->dpms_mode = mode;
-
-       dev_priv->display.dpms(crtc, mode);
 
        if (!dev->primary->master)
                return;
@@ -3483,8 +3423,6 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
        if (!master_priv->sarea_priv)
                return;
 
-       enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF;
-
        switch (pipe) {
        case 0:
                master_priv->sarea_priv->pipeA_w = enabled ? crtc->mode.hdisplay : 0;
@@ -3500,13 +3438,42 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
        }
 }
 
+/**
+ * Sets the power management mode of the pipe and plane.
+ */
+void intel_crtc_update_dpms(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_encoder *intel_encoder;
+       bool enable = false;
+
+       for_each_encoder_on_crtc(dev, crtc, intel_encoder)
+               enable |= intel_encoder->connectors_active;
+
+       if (enable)
+               dev_priv->display.crtc_enable(crtc);
+       else
+               dev_priv->display.crtc_disable(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_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
        struct drm_device *dev = crtc->dev;
+       struct drm_connector *connector;
        struct drm_i915_private *dev_priv = dev->dev_private;
 
-       crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
+       /* crtc should still be enabled when we disable it. */
+       WARN_ON(!crtc->enabled);
+
+       dev_priv->display.crtc_disable(crtc);
+       intel_crtc_update_sarea(crtc, false);
        dev_priv->display.off(crtc);
 
        assert_plane_disabled(dev->dev_private, to_intel_crtc(crtc)->plane);
@@ -3516,63 +3483,128 @@ static void intel_crtc_disable(struct drm_crtc *crtc)
                mutex_lock(&dev->struct_mutex);
                intel_unpin_fb_obj(to_intel_framebuffer(crtc->fb)->obj);
                mutex_unlock(&dev->struct_mutex);
+               crtc->fb = NULL;
+       }
+
+       /* Update computed state. */
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               if (!connector->encoder || !connector->encoder->crtc)
+                       continue;
+
+               if (connector->encoder->crtc != crtc)
+                       continue;
+
+               connector->dpms = DRM_MODE_DPMS_OFF;
+               to_intel_encoder(connector->encoder)->connectors_active = false;
        }
 }
 
-/* Prepare for a mode set.
- *
- * Note we could be a lot smarter here.  We need to figure out which outputs
- * will be enabled, which disabled (in short, how the config will changes)
- * and perform the minimum necessary steps to accomplish that, e.g. updating
- * watermarks, FBC configuration, making sure PLLs are programmed correctly,
- * panel fitting is in the proper state, etc.
- */
-static void i9xx_crtc_prepare(struct drm_crtc *crtc)
+void intel_modeset_disable(struct drm_device *dev)
 {
-       i9xx_crtc_disable(crtc);
+       struct drm_crtc *crtc;
+
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+               if (crtc->enabled)
+                       intel_crtc_disable(crtc);
+       }
 }
 
-static void i9xx_crtc_commit(struct drm_crtc *crtc)
+void intel_encoder_noop(struct drm_encoder *encoder)
 {
-       i9xx_crtc_enable(crtc);
 }
 
-static void ironlake_crtc_prepare(struct drm_crtc *crtc)
+void intel_encoder_destroy(struct drm_encoder *encoder)
 {
-       ironlake_crtc_disable(crtc);
+       struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
+
+       drm_encoder_cleanup(encoder);
+       kfree(intel_encoder);
 }
 
-static void ironlake_crtc_commit(struct drm_crtc *crtc)
+/* Simple dpms helper for encodres with just one connector, no cloning and only
+ * one kind of off state. It clamps all !ON modes to fully OFF and changes the
+ * state of the entire output pipe. */
+void intel_encoder_dpms(struct intel_encoder *encoder, int mode)
 {
-       ironlake_crtc_enable(crtc);
+       if (mode == DRM_MODE_DPMS_ON) {
+               encoder->connectors_active = true;
+
+               intel_crtc_update_dpms(encoder->base.crtc);
+       } else {
+               encoder->connectors_active = false;
+
+               intel_crtc_update_dpms(encoder->base.crtc);
+       }
 }
 
-void intel_encoder_prepare(struct drm_encoder *encoder)
+/* Cross check the actual hw state with our own modeset state tracking (and it's
+ * internal consistency). */
+static void intel_connector_check_state(struct intel_connector *connector)
 {
-       struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
-       /* lvds has its own version of prepare see intel_lvds_prepare */
-       encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
+       if (connector->get_hw_state(connector)) {
+               struct intel_encoder *encoder = connector->encoder;
+               struct drm_crtc *crtc;
+               bool encoder_enabled;
+               enum pipe pipe;
+
+               DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
+                             connector->base.base.id,
+                             drm_get_connector_name(&connector->base));
+
+               WARN(connector->base.dpms == DRM_MODE_DPMS_OFF,
+                    "wrong connector dpms state\n");
+               WARN(connector->base.encoder != &encoder->base,
+                    "active connector not linked to encoder\n");
+               WARN(!encoder->connectors_active,
+                    "encoder->connectors_active not set\n");
+
+               encoder_enabled = encoder->get_hw_state(encoder, &pipe);
+               WARN(!encoder_enabled, "encoder not enabled\n");
+               if (WARN_ON(!encoder->base.crtc))
+                       return;
+
+               crtc = encoder->base.crtc;
+
+               WARN(!crtc->enabled, "crtc not enabled\n");
+               WARN(!to_intel_crtc(crtc)->active, "crtc not active\n");
+               WARN(pipe != to_intel_crtc(crtc)->pipe,
+                    "encoder active on the wrong pipe\n");
+       }
 }
 
-void intel_encoder_commit(struct drm_encoder *encoder)
+/* Even simpler default implementation, if there's really no special case to
+ * consider. */
+void intel_connector_dpms(struct drm_connector *connector, int mode)
 {
-       struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
-       struct drm_device *dev = encoder->dev;
-       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       struct intel_encoder *encoder = intel_attached_encoder(connector);
 
-       /* lvds has its own version of commit see intel_lvds_commit */
-       encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
+       /* All the simple cases only support two dpms states. */
+       if (mode != DRM_MODE_DPMS_ON)
+               mode = DRM_MODE_DPMS_OFF;
 
-       if (HAS_PCH_CPT(dev))
-               intel_cpt_verify_modeset(dev, intel_crtc->pipe);
+       if (mode == connector->dpms)
+               return;
+
+       connector->dpms = mode;
+
+       /* Only need to change hw state when actually enabled */
+       if (encoder->base.crtc)
+               intel_encoder_dpms(encoder, mode);
+       else
+               WARN_ON(encoder->connectors_active != false);
+
+       intel_modeset_check_state(connector->dev);
 }
 
-void intel_encoder_destroy(struct drm_encoder *encoder)
+/* Simple connector->get_hw_state implementation for encoders that support only
+ * one connector and no cloning and hence the encoder state determines the state
+ * of the connector. */
+bool intel_connector_get_hw_state(struct intel_connector *connector)
 {
-       struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
+       enum pipe pipe = 0;
+       struct intel_encoder *encoder = connector->encoder;
 
-       drm_encoder_cleanup(encoder);
-       kfree(intel_encoder);
+       return encoder->get_hw_state(encoder, &pipe);
 }
 
 static bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
@@ -3593,6 +3625,13 @@ static bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
        if (!(adjusted_mode->private_flags & INTEL_MODE_CRTC_TIMINGS_SET))
                drm_mode_set_crtcinfo(adjusted_mode, 0);
 
+       /* WaPruneModeWithIncorrectHsyncOffset: Cantiga+ cannot handle modes
+        * with a hsync front porch of 0.
+        */
+       if ((INTEL_INFO(dev)->gen > 4 || IS_G4X(dev)) &&
+               adjusted_mode->hsync_start == adjusted_mode->hdisplay)
+               return false;
+
        return true;
 }
 
@@ -3728,6 +3767,7 @@ static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv)
  * true if they don't match).
  */
 static bool intel_choose_pipe_bpp_dither(struct drm_crtc *crtc,
+                                        struct drm_framebuffer *fb,
                                         unsigned int *pipe_bpp,
                                         struct drm_display_mode *mode)
 {
@@ -3797,7 +3837,7 @@ static bool intel_choose_pipe_bpp_dither(struct drm_crtc *crtc,
         * also stays within the max display bpc discovered above.
         */
 
-       switch (crtc->fb->depth) {
+       switch (fb->depth) {
        case 8:
                bpc = 8; /* since we go through a colormap */
                break;
@@ -4191,12 +4231,6 @@ static void i8xx_update_pll(struct drm_crtc *crtc,
        POSTING_READ(DPLL(pipe));
        udelay(150);
 
-       I915_WRITE(DPLL(pipe), dpll);
-
-       /* Wait for the clocks to stabilize. */
-       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.
@@ -4204,6 +4238,12 @@ static void i8xx_update_pll(struct drm_crtc *crtc,
        if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
                intel_update_lvds(crtc, clock, adjusted_mode);
 
+       I915_WRITE(DPLL(pipe), dpll);
+
+       /* Wait for the clocks to stabilize. */
+       POSTING_READ(DPLL(pipe));
+       udelay(150);
+
        /* The pixel multiplier can only be updated once the
         * DPLL is enabled and the clocks are stable.
         *
@@ -4216,7 +4256,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
                              struct drm_display_mode *mode,
                              struct drm_display_mode *adjusted_mode,
                              int x, int y,
-                             struct drm_framebuffer *old_fb)
+                             struct drm_framebuffer *fb)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -4406,7 +4446,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
        I915_WRITE(DSPCNTR(plane), dspcntr);
        POSTING_READ(DSPCNTR(plane));
 
-       ret = intel_pipe_set_base(crtc, x, y, old_fb);
+       ret = intel_pipe_set_base(crtc, x, y, fb);
 
        intel_update_watermarks(dev);
 
@@ -4560,63 +4600,78 @@ static int ironlake_get_refclk(struct drm_crtc *crtc)
        return 120000;
 }
 
-static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
-                                 struct drm_display_mode *mode,
+static void ironlake_set_pipeconf(struct drm_crtc *crtc,
                                  struct drm_display_mode *adjusted_mode,
-                                 int x, int y,
-                                 struct drm_framebuffer *old_fb)
+                                 bool dither)
 {
-       struct drm_device *dev = crtc->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_i915_private *dev_priv = crtc->dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int pipe = intel_crtc->pipe;
-       int plane = intel_crtc->plane;
-       int refclk, num_connectors = 0;
-       intel_clock_t clock, reduced_clock;
-       u32 dpll, fp = 0, fp2 = 0, dspcntr, pipeconf;
-       bool ok, has_reduced_clock = false, is_sdvo = false;
-       bool is_crt = false, is_lvds = false, is_tv = false, is_dp = false;
-       struct intel_encoder *encoder, *edp_encoder = NULL;
-       const intel_limit_t *limit;
-       int ret;
-       struct fdi_m_n m_n = {0};
-       u32 temp;
-       int target_clock, pixel_multiplier, lane, link_bw, factor;
-       unsigned int pipe_bpp;
-       bool dither;
-       bool is_cpu_edp = false, is_pch_edp = false;
+       uint32_t val;
 
-       for_each_encoder_on_crtc(dev, crtc, encoder) {
-               switch (encoder->type) {
-               case INTEL_OUTPUT_LVDS:
-                       is_lvds = true;
-                       break;
-               case INTEL_OUTPUT_SDVO:
-               case INTEL_OUTPUT_HDMI:
-                       is_sdvo = true;
-                       if (encoder->needs_tv_clock)
-                               is_tv = true;
-                       break;
-               case INTEL_OUTPUT_TVOUT:
-                       is_tv = true;
-                       break;
-               case INTEL_OUTPUT_ANALOG:
-                       is_crt = true;
-                       break;
-               case INTEL_OUTPUT_DISPLAYPORT:
-                       is_dp = true;
-                       break;
-               case INTEL_OUTPUT_EDP:
-                       is_dp = true;
-                       if (intel_encoder_is_pch_edp(&encoder->base))
-                               is_pch_edp = true;
-                       else
-                               is_cpu_edp = true;
-                       edp_encoder = encoder;
+       val = I915_READ(PIPECONF(pipe));
+
+       val &= ~PIPE_BPC_MASK;
+       switch (intel_crtc->bpp) {
+       case 18:
+               val |= PIPE_6BPC;
+               break;
+       case 24:
+               val |= PIPE_8BPC;
+               break;
+       case 30:
+               val |= PIPE_10BPC;
+               break;
+       case 36:
+               val |= PIPE_12BPC;
+               break;
+       default:
+               val |= PIPE_8BPC;
+               break;
+       }
+
+       val &= ~(PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_MASK);
+       if (dither)
+               val |= (PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_SP);
+
+       val &= ~PIPECONF_INTERLACE_MASK;
+       if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE)
+               val |= PIPECONF_INTERLACED_ILK;
+       else
+               val |= PIPECONF_PROGRESSIVE;
+
+       I915_WRITE(PIPECONF(pipe), val);
+       POSTING_READ(PIPECONF(pipe));
+}
+
+static bool ironlake_compute_clocks(struct drm_crtc *crtc,
+                                   struct drm_display_mode *adjusted_mode,
+                                   intel_clock_t *clock,
+                                   bool *has_reduced_clock,
+                                   intel_clock_t *reduced_clock)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_encoder *intel_encoder;
+       int refclk;
+       const intel_limit_t *limit;
+       bool ret, is_sdvo = false, is_tv = false, is_lvds = false;
+
+       for_each_encoder_on_crtc(dev, crtc, intel_encoder) {
+               switch (intel_encoder->type) {
+               case INTEL_OUTPUT_LVDS:
+                       is_lvds = true;
+                       break;
+               case INTEL_OUTPUT_SDVO:
+               case INTEL_OUTPUT_HDMI:
+                       is_sdvo = true;
+                       if (intel_encoder->needs_tv_clock)
+                               is_tv = true;
+                       break;
+               case INTEL_OUTPUT_TVOUT:
+                       is_tv = true;
                        break;
                }
-
-               num_connectors++;
        }
 
        refclk = ironlake_get_refclk(crtc);
@@ -4627,15 +4682,10 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
         * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
         */
        limit = intel_limit(crtc, refclk);
-       ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, NULL,
-                            &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);
+       ret = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, NULL,
+                             clock);
+       if (!ret)
+               return false;
 
        if (is_lvds && dev_priv->lvds_downclock_avail) {
                /*
@@ -4644,16 +4694,86 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
                 * by using the FP0/FP1. In such case we will disable the LVDS
                 * downclock feature.
                */
-               has_reduced_clock = limit->find_pll(limit, crtc,
-                                                   dev_priv->lvds_downclock,
-                                                   refclk,
-                                                   &clock,
-                                                   &reduced_clock);
+               *has_reduced_clock = limit->find_pll(limit, crtc,
+                                                    dev_priv->lvds_downclock,
+                                                    refclk,
+                                                    clock,
+                                                    reduced_clock);
        }
 
        if (is_sdvo && is_tv)
-               i9xx_adjust_sdvo_tv_clock(adjusted_mode, &clock);
+               i9xx_adjust_sdvo_tv_clock(adjusted_mode, clock);
+
+       return true;
+}
+
+static int ironlake_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 *fb)
+{
+       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;
+       int plane = intel_crtc->plane;
+       int num_connectors = 0;
+       intel_clock_t clock, reduced_clock;
+       u32 dpll, fp = 0, fp2 = 0;
+       bool ok, has_reduced_clock = false, is_sdvo = false;
+       bool is_crt = false, is_lvds = false, is_tv = false, is_dp = false;
+       struct intel_encoder *encoder, *edp_encoder = NULL;
+       int ret;
+       struct fdi_m_n m_n = {0};
+       u32 temp;
+       int target_clock, pixel_multiplier, lane, link_bw, factor;
+       unsigned int pipe_bpp;
+       bool dither;
+       bool is_cpu_edp = false, is_pch_edp = false;
+
+       for_each_encoder_on_crtc(dev, crtc, encoder) {
+               switch (encoder->type) {
+               case INTEL_OUTPUT_LVDS:
+                       is_lvds = true;
+                       break;
+               case INTEL_OUTPUT_SDVO:
+               case INTEL_OUTPUT_HDMI:
+                       is_sdvo = true;
+                       if (encoder->needs_tv_clock)
+                               is_tv = true;
+                       break;
+               case INTEL_OUTPUT_TVOUT:
+                       is_tv = true;
+                       break;
+               case INTEL_OUTPUT_ANALOG:
+                       is_crt = true;
+                       break;
+               case INTEL_OUTPUT_DISPLAYPORT:
+                       is_dp = true;
+                       break;
+               case INTEL_OUTPUT_EDP:
+                       is_dp = true;
+                       if (intel_encoder_is_pch_edp(&encoder->base))
+                               is_pch_edp = true;
+                       else
+                               is_cpu_edp = true;
+                       edp_encoder = encoder;
+                       break;
+               }
+
+               num_connectors++;
+       }
+
+       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);
 
        /* FDI link */
        pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode);
@@ -4682,32 +4802,17 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
                target_clock = adjusted_mode->clock;
 
        /* determine panel color depth */
-       temp = I915_READ(PIPECONF(pipe));
-       temp &= ~PIPE_BPC_MASK;
-       dither = intel_choose_pipe_bpp_dither(crtc, &pipe_bpp, mode);
-       switch (pipe_bpp) {
-       case 18:
-               temp |= PIPE_6BPC;
-               break;
-       case 24:
-               temp |= PIPE_8BPC;
-               break;
-       case 30:
-               temp |= PIPE_10BPC;
-               break;
-       case 36:
-               temp |= PIPE_12BPC;
-               break;
-       default:
+       dither = intel_choose_pipe_bpp_dither(crtc, fb, &pipe_bpp, mode);
+       if (is_lvds && dev_priv->lvds_dither)
+               dither = true;
+
+       if (pipe_bpp != 18 && pipe_bpp != 24 && pipe_bpp != 30 &&
+           pipe_bpp != 36) {
                WARN(1, "intel_choose_pipe_bpp returned invalid value %d\n",
-                       pipe_bpp);
-               temp |= PIPE_8BPC;
+                    pipe_bpp);
                pipe_bpp = 24;
-               break;
        }
-
        intel_crtc->bpp = pipe_bpp;
-       I915_WRITE(PIPECONF(pipe), temp);
 
        if (!lane) {
                /*
@@ -4791,12 +4896,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
        else
                dpll |= PLL_REF_INPUT_DREFCLK;
 
-       /* setup pipeconf */
-       pipeconf = I915_READ(PIPECONF(pipe));
-
-       /* Set up the display plane register */
-       dspcntr = DISPPLANE_GAMMA_ENABLE;
-
        DRM_DEBUG_KMS("Mode for pipe %d:\n", pipe);
        drm_mode_debug_printmodeline(mode);
 
@@ -4856,12 +4955,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
                I915_WRITE(PCH_LVDS, temp);
        }
 
-       pipeconf &= ~PIPECONF_DITHER_EN;
-       pipeconf &= ~PIPECONF_DITHER_TYPE_MASK;
-       if ((is_lvds && dev_priv->lvds_dither) || dither) {
-               pipeconf |= PIPECONF_DITHER_EN;
-               pipeconf |= PIPECONF_DITHER_TYPE_SP;
-       }
        if (is_dp && !is_cpu_edp) {
                intel_dp_set_m_n(crtc, mode, adjusted_mode);
        } else {
@@ -4897,9 +4990,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
                }
        }
 
-       pipeconf &= ~PIPECONF_INTERLACE_MASK;
        if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) {
-               pipeconf |= PIPECONF_INTERLACED_ILK;
                /* the chip adds 2 halflines automatically */
                adjusted_mode->crtc_vtotal -= 1;
                adjusted_mode->crtc_vblank_end -= 1;
@@ -4907,7 +4998,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
                           adjusted_mode->crtc_hsync_start
                           - adjusted_mode->crtc_htotal/2);
        } else {
-               pipeconf |= PIPECONF_PROGRESSIVE;
                I915_WRITE(VSYNCSHIFT(pipe), 0);
        }
 
@@ -4945,15 +5035,15 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
        if (is_cpu_edp)
                ironlake_set_pll_edp(crtc, adjusted_mode->clock);
 
-       I915_WRITE(PIPECONF(pipe), pipeconf);
-       POSTING_READ(PIPECONF(pipe));
+       ironlake_set_pipeconf(crtc, adjusted_mode, dither);
 
        intel_wait_for_vblank(dev, pipe);
 
-       I915_WRITE(DSPCNTR(plane), dspcntr);
+       /* Set up the display plane register */
+       I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE);
        POSTING_READ(DSPCNTR(plane));
 
-       ret = intel_pipe_set_base(crtc, x, y, old_fb);
+       ret = intel_pipe_set_base(crtc, x, y, fb);
 
        intel_update_watermarks(dev);
 
@@ -4966,7 +5056,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                               struct drm_display_mode *mode,
                               struct drm_display_mode *adjusted_mode,
                               int x, int y,
-                              struct drm_framebuffer *old_fb)
+                              struct drm_framebuffer *fb)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -4977,14 +5067,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        drm_vblank_pre_modeset(dev, pipe);
 
        ret = dev_priv->display.crtc_mode_set(crtc, mode, adjusted_mode,
-                                             x, y, old_fb);
+                                             x, y, fb);
        drm_vblank_post_modeset(dev, pipe);
 
-       if (ret)
-               intel_crtc->dpms_mode = DRM_MODE_DPMS_OFF;
-       else
-               intel_crtc->dpms_mode = DRM_MODE_DPMS_ON;
-
        return ret;
 }
 
@@ -5057,6 +5142,91 @@ static void g4x_write_eld(struct drm_connector *connector,
        I915_WRITE(G4X_AUD_CNTL_ST, i);
 }
 
+static void haswell_write_eld(struct drm_connector *connector,
+                                    struct drm_crtc *crtc)
+{
+       struct drm_i915_private *dev_priv = connector->dev->dev_private;
+       uint8_t *eld = connector->eld;
+       struct drm_device *dev = crtc->dev;
+       uint32_t eldv;
+       uint32_t i;
+       int len;
+       int pipe = to_intel_crtc(crtc)->pipe;
+       int tmp;
+
+       int hdmiw_hdmiedid = HSW_AUD_EDID_DATA(pipe);
+       int aud_cntl_st = HSW_AUD_DIP_ELD_CTRL(pipe);
+       int aud_config = HSW_AUD_CFG(pipe);
+       int aud_cntrl_st2 = HSW_AUD_PIN_ELD_CP_VLD;
+
+
+       DRM_DEBUG_DRIVER("HDMI: Haswell Audio initialize....\n");
+
+       /* Audio output enable */
+       DRM_DEBUG_DRIVER("HDMI audio: enable codec\n");
+       tmp = I915_READ(aud_cntrl_st2);
+       tmp |= (AUDIO_OUTPUT_ENABLE_A << (pipe * 4));
+       I915_WRITE(aud_cntrl_st2, tmp);
+
+       /* Wait for 1 vertical blank */
+       intel_wait_for_vblank(dev, pipe);
+
+       /* Set ELD valid state */
+       tmp = I915_READ(aud_cntrl_st2);
+       DRM_DEBUG_DRIVER("HDMI audio: pin eld vld status=0x%8x\n", tmp);
+       tmp |= (AUDIO_ELD_VALID_A << (pipe * 4));
+       I915_WRITE(aud_cntrl_st2, tmp);
+       tmp = I915_READ(aud_cntrl_st2);
+       DRM_DEBUG_DRIVER("HDMI audio: eld vld status=0x%8x\n", tmp);
+
+       /* Enable HDMI mode */
+       tmp = I915_READ(aud_config);
+       DRM_DEBUG_DRIVER("HDMI audio: audio conf: 0x%8x\n", tmp);
+       /* clear N_programing_enable and N_value_index */
+       tmp &= ~(AUD_CONFIG_N_VALUE_INDEX | AUD_CONFIG_N_PROG_ENABLE);
+       I915_WRITE(aud_config, tmp);
+
+       DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(pipe));
+
+       eldv = AUDIO_ELD_VALID_A << (pipe * 4);
+
+       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
+               DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n");
+               eld[5] |= (1 << 2);     /* Conn_Type, 0x1 = DisplayPort */
+               I915_WRITE(aud_config, AUD_CONFIG_N_VALUE_INDEX); /* 0x1 = DP */
+       } else
+               I915_WRITE(aud_config, 0);
+
+       if (intel_eld_uptodate(connector,
+                              aud_cntrl_st2, eldv,
+                              aud_cntl_st, IBX_ELD_ADDRESS,
+                              hdmiw_hdmiedid))
+               return;
+
+       i = I915_READ(aud_cntrl_st2);
+       i &= ~eldv;
+       I915_WRITE(aud_cntrl_st2, i);
+
+       if (!eld[0])
+               return;
+
+       i = I915_READ(aud_cntl_st);
+       i &= ~IBX_ELD_ADDRESS;
+       I915_WRITE(aud_cntl_st, i);
+       i = (i >> 29) & DIP_PORT_SEL_MASK;              /* DIP_Port_Select, 0x1 = PortB */
+       DRM_DEBUG_DRIVER("port num:%d\n", i);
+
+       len = min_t(uint8_t, eld[2], 21);       /* 84 bytes of hw ELD buffer */
+       DRM_DEBUG_DRIVER("ELD size %d\n", len);
+       for (i = 0; i < len; i++)
+               I915_WRITE(hdmiw_hdmiedid, *((uint32_t *)eld + i));
+
+       i = I915_READ(aud_cntrl_st2);
+       i |= eldv;
+       I915_WRITE(aud_cntrl_st2, i);
+
+}
+
 static void ironlake_write_eld(struct drm_connector *connector,
                                     struct drm_crtc *crtc)
 {
@@ -5069,28 +5239,24 @@ static void ironlake_write_eld(struct drm_connector *connector,
        int aud_config;
        int aud_cntl_st;
        int aud_cntrl_st2;
+       int pipe = to_intel_crtc(crtc)->pipe;
 
        if (HAS_PCH_IBX(connector->dev)) {
-               hdmiw_hdmiedid = IBX_HDMIW_HDMIEDID_A;
-               aud_config = IBX_AUD_CONFIG_A;
-               aud_cntl_st = IBX_AUD_CNTL_ST_A;
+               hdmiw_hdmiedid = IBX_HDMIW_HDMIEDID(pipe);
+               aud_config = IBX_AUD_CFG(pipe);
+               aud_cntl_st = IBX_AUD_CNTL_ST(pipe);
                aud_cntrl_st2 = IBX_AUD_CNTL_ST2;
        } else {
-               hdmiw_hdmiedid = CPT_HDMIW_HDMIEDID_A;
-               aud_config = CPT_AUD_CONFIG_A;
-               aud_cntl_st = CPT_AUD_CNTL_ST_A;
+               hdmiw_hdmiedid = CPT_HDMIW_HDMIEDID(pipe);
+               aud_config = CPT_AUD_CFG(pipe);
+               aud_cntl_st = CPT_AUD_CNTL_ST(pipe);
                aud_cntrl_st2 = CPT_AUD_CNTRL_ST2;
        }
 
-       i = to_intel_crtc(crtc)->pipe;
-       hdmiw_hdmiedid += i * 0x100;
-       aud_cntl_st += i * 0x100;
-       aud_config += i * 0x100;
-
-       DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(i));
+       DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(pipe));
 
        i = I915_READ(aud_cntl_st);
-       i = (i >> 29) & 0x3;            /* DIP_Port_Select, 0x1 = PortB */
+       i = (i >> 29) & DIP_PORT_SEL_MASK;              /* DIP_Port_Select, 0x1 = PortB */
        if (!i) {
                DRM_DEBUG_DRIVER("Audio directed to unknown port\n");
                /* operate blindly on all ports */
@@ -5337,8 +5503,6 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
        uint32_t addr;
        int ret;
 
-       DRM_DEBUG_KMS("\n");
-
        /* if we want to turn off the cursor ignore width and height */
        if (!handle) {
                DRM_DEBUG_KMS("cursor off\n");
@@ -5584,17 +5748,18 @@ mode_fits_in_fbdev(struct drm_device *dev,
        return fb;
 }
 
-bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
-                               struct drm_connector *connector,
+bool intel_get_load_detect_pipe(struct drm_connector *connector,
                                struct drm_display_mode *mode,
                                struct intel_load_detect_pipe *old)
 {
        struct intel_crtc *intel_crtc;
+       struct intel_encoder *intel_encoder =
+               intel_attached_encoder(connector);
        struct drm_crtc *possible_crtc;
        struct drm_encoder *encoder = &intel_encoder->base;
        struct drm_crtc *crtc = NULL;
        struct drm_device *dev = encoder->dev;
-       struct drm_framebuffer *old_fb;
+       struct drm_framebuffer *fb;
        int i = -1;
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
@@ -5615,21 +5780,12 @@ bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
        if (encoder->crtc) {
                crtc = encoder->crtc;
 
-               intel_crtc = to_intel_crtc(crtc);
-               old->dpms_mode = intel_crtc->dpms_mode;
+               old->dpms_mode = connector->dpms;
                old->load_detect_temp = false;
 
                /* Make sure the crtc and connector are running */
-               if (intel_crtc->dpms_mode != DRM_MODE_DPMS_ON) {
-                       struct drm_encoder_helper_funcs *encoder_funcs;
-                       struct drm_crtc_helper_funcs *crtc_funcs;
-
-                       crtc_funcs = crtc->helper_private;
-                       crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
-
-                       encoder_funcs = encoder->helper_private;
-                       encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
-               }
+               if (connector->dpms != DRM_MODE_DPMS_ON)
+                       connector->funcs->dpms(connector, DRM_MODE_DPMS_ON);
 
                return true;
        }
@@ -5653,19 +5809,17 @@ bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
                return false;
        }
 
-       encoder->crtc = crtc;
-       connector->encoder = encoder;
+       intel_encoder->new_crtc = to_intel_crtc(crtc);
+       to_intel_connector(connector)->new_encoder = intel_encoder;
 
        intel_crtc = to_intel_crtc(crtc);
-       old->dpms_mode = intel_crtc->dpms_mode;
+       old->dpms_mode = connector->dpms;
        old->load_detect_temp = true;
        old->release_fb = NULL;
 
        if (!mode)
                mode = &load_detect_mode;
 
-       old_fb = crtc->fb;
-
        /* We need a framebuffer large enough to accommodate all accesses
         * that the plane may generate whilst we perform load detection.
         * We can not rely on the fbcon either being present (we get called
@@ -5673,50 +5827,52 @@ bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
         * not even exist) or that it is large enough to satisfy the
         * requested mode.
         */
-       crtc->fb = mode_fits_in_fbdev(dev, mode);
-       if (crtc->fb == NULL) {
+       fb = mode_fits_in_fbdev(dev, mode);
+       if (fb == NULL) {
                DRM_DEBUG_KMS("creating tmp fb for load-detection\n");
-               crtc->fb = intel_framebuffer_create_for_mode(dev, mode, 24, 32);
-               old->release_fb = crtc->fb;
+               fb = intel_framebuffer_create_for_mode(dev, mode, 24, 32);
+               old->release_fb = fb;
        } else
                DRM_DEBUG_KMS("reusing fbdev for load-detection framebuffer\n");
-       if (IS_ERR(crtc->fb)) {
+       if (IS_ERR(fb)) {
                DRM_DEBUG_KMS("failed to allocate framebuffer for load-detection\n");
-               crtc->fb = old_fb;
-               return false;
+               goto fail;
        }
 
-       if (!drm_crtc_helper_set_mode(crtc, mode, 0, 0, old_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);
-               crtc->fb = old_fb;
-               return false;
+               goto fail;
        }
 
        /* let the connector get through one full cycle before testing */
        intel_wait_for_vblank(dev, intel_crtc->pipe);
 
        return true;
+fail:
+       connector->encoder = NULL;
+       encoder->crtc = NULL;
+       return false;
 }
 
-void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder,
-                                   struct drm_connector *connector,
+void intel_release_load_detect_pipe(struct drm_connector *connector,
                                    struct intel_load_detect_pipe *old)
 {
+       struct intel_encoder *intel_encoder =
+               intel_attached_encoder(connector);
        struct drm_encoder *encoder = &intel_encoder->base;
-       struct drm_device *dev = encoder->dev;
-       struct drm_crtc *crtc = encoder->crtc;
-       struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
-       struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
 
        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) {
-               connector->encoder = NULL;
-               drm_helper_disable_unused_functions(dev);
+               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);
@@ -5725,10 +5881,8 @@ void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder,
        }
 
        /* Switch crtc and encoder back off if necessary */
-       if (old->dpms_mode != DRM_MODE_DPMS_ON) {
-               encoder_funcs->dpms(encoder, old->dpms_mode);
-               crtc_funcs->dpms(crtc, old->dpms_mode);
-       }
+       if (old->dpms_mode != DRM_MODE_DPMS_ON)
+               connector->funcs->dpms(connector, old->dpms_mode);
 }
 
 /* Returns the clock of the currently programmed mode of the given pipe. */
@@ -5850,46 +6004,6 @@ struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
        return mode;
 }
 
-#define GPU_IDLE_TIMEOUT 500 /* ms */
-
-/* When this timer fires, we've been idle for awhile */
-static void intel_gpu_idle_timer(unsigned long arg)
-{
-       struct drm_device *dev = (struct drm_device *)arg;
-       drm_i915_private_t *dev_priv = dev->dev_private;
-
-       if (!list_empty(&dev_priv->mm.active_list)) {
-               /* Still processing requests, so just re-arm the timer. */
-               mod_timer(&dev_priv->idle_timer, jiffies +
-                         msecs_to_jiffies(GPU_IDLE_TIMEOUT));
-               return;
-       }
-
-       dev_priv->busy = false;
-       queue_work(dev_priv->wq, &dev_priv->idle_work);
-}
-
-#define CRTC_IDLE_TIMEOUT 1000 /* ms */
-
-static void intel_crtc_idle_timer(unsigned long arg)
-{
-       struct intel_crtc *intel_crtc = (struct intel_crtc *)arg;
-       struct drm_crtc *crtc = &intel_crtc->base;
-       drm_i915_private_t *dev_priv = crtc->dev->dev_private;
-       struct intel_framebuffer *intel_fb;
-
-       intel_fb = to_intel_framebuffer(crtc->fb);
-       if (intel_fb && intel_fb->obj->active) {
-               /* The framebuffer is still being accessed by the GPU. */
-               mod_timer(&intel_crtc->idle_timer, jiffies +
-                         msecs_to_jiffies(CRTC_IDLE_TIMEOUT));
-               return;
-       }
-
-       intel_crtc->busy = false;
-       queue_work(dev_priv->wq, &dev_priv->idle_work);
-}
-
 static void intel_increase_pllclock(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
@@ -5919,10 +6033,6 @@ static void intel_increase_pllclock(struct drm_crtc *crtc)
                if (dpll & DISPLAY_RATE_SELECT_FPA1)
                        DRM_DEBUG_DRIVER("failed to upclock LVDS!\n");
        }
-
-       /* Schedule downclock */
-       mod_timer(&intel_crtc->idle_timer, jiffies +
-                 msecs_to_jiffies(CRTC_IDLE_TIMEOUT));
 }
 
 static void intel_decrease_pllclock(struct drm_crtc *crtc)
@@ -5961,89 +6071,46 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc)
 
 }
 
-/**
- * intel_idle_update - adjust clocks for idleness
- * @work: work struct
- *
- * Either the GPU or display (or both) went idle.  Check the busy status
- * here and adjust the CRTC and GPU clocks as necessary.
- */
-static void intel_idle_update(struct work_struct *work)
+void intel_mark_busy(struct drm_device *dev)
 {
-       drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
-                                                   idle_work);
-       struct drm_device *dev = dev_priv->dev;
-       struct drm_crtc *crtc;
-       struct intel_crtc *intel_crtc;
+       i915_update_gfx_val(dev->dev_private);
+}
 
-       if (!i915_powersave)
-               return;
+void intel_mark_idle(struct drm_device *dev)
+{
+}
 
-       mutex_lock(&dev->struct_mutex);
+void intel_mark_fb_busy(struct drm_i915_gem_object *obj)
+{
+       struct drm_device *dev = obj->base.dev;
+       struct drm_crtc *crtc;
 
-       i915_update_gfx_val(dev_priv);
+       if (!i915_powersave)
+               return;
 
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-               /* Skip inactive CRTCs */
                if (!crtc->fb)
                        continue;
 
-               intel_crtc = to_intel_crtc(crtc);
-               if (!intel_crtc->busy)
-                       intel_decrease_pllclock(crtc);
+               if (to_intel_framebuffer(crtc->fb)->obj == obj)
+                       intel_increase_pllclock(crtc);
        }
-
-
-       mutex_unlock(&dev->struct_mutex);
 }
 
-/**
- * intel_mark_busy - mark the GPU and possibly the display busy
- * @dev: drm device
- * @obj: object we're operating on
- *
- * Callers can use this function to indicate that the GPU is busy processing
- * commands.  If @obj matches one of the CRTC objects (i.e. it's a scanout
- * buffer), we'll also mark the display as busy, so we know to increase its
- * clock frequency.
- */
-void intel_mark_busy(struct drm_device *dev, struct drm_i915_gem_object *obj)
+void intel_mark_fb_idle(struct drm_i915_gem_object *obj)
 {
-       drm_i915_private_t *dev_priv = dev->dev_private;
-       struct drm_crtc *crtc = NULL;
-       struct intel_framebuffer *intel_fb;
-       struct intel_crtc *intel_crtc;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return;
-
-       if (!dev_priv->busy) {
-               intel_sanitize_pm(dev);
-               dev_priv->busy = true;
-       } else
-               mod_timer(&dev_priv->idle_timer, jiffies +
-                         msecs_to_jiffies(GPU_IDLE_TIMEOUT));
+       struct drm_device *dev = obj->base.dev;
+       struct drm_crtc *crtc;
 
-       if (obj == NULL)
+       if (!i915_powersave)
                return;
 
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
                if (!crtc->fb)
                        continue;
 
-               intel_crtc = to_intel_crtc(crtc);
-               intel_fb = to_intel_framebuffer(crtc->fb);
-               if (intel_fb->obj == obj) {
-                       if (!intel_crtc->busy) {
-                               /* Non-busy -> busy, upclock */
-                               intel_increase_pllclock(crtc);
-                               intel_crtc->busy = true;
-                       } else {
-                               /* Busy -> busy, put off timer */
-                               mod_timer(&intel_crtc->idle_timer, jiffies +
-                                         msecs_to_jiffies(CRTC_IDLE_TIMEOUT));
-                       }
-               }
+               if (to_intel_framebuffer(crtc->fb)->obj == obj)
+                       intel_decrease_pllclock(crtc);
        }
 }
 
@@ -6394,7 +6461,7 @@ static int intel_gen7_queue_flip(struct drm_device *dev,
        default:
                WARN_ONCE(1, "unknown plane in flip command\n");
                ret = -ENODEV;
-               goto err;
+               goto err_unpin;
        }
 
        ret = intel_ring_begin(ring, 4);
@@ -6502,7 +6569,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
                goto cleanup_pending;
 
        intel_disable_fbc(dev);
-       intel_mark_busy(dev, obj);
+       intel_mark_fb_busy(obj);
        mutex_unlock(&dev->struct_mutex);
 
        trace_i915_flip_request(intel_crtc->plane, obj);
@@ -6527,81 +6594,807 @@ free_work:
        return ret;
 }
 
-static void intel_sanitize_modesetting(struct drm_device *dev,
-                                      int pipe, int plane)
+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)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 reg, val;
-       int i;
+       struct intel_encoder *other_encoder;
+       struct drm_crtc *crtc = &encoder->new_crtc->base;
 
-       /* Clear any frame start delays used for debugging left by the BIOS */
-       for_each_pipe(i) {
-               reg = PIPECONF(i);
-               I915_WRITE(reg, I915_READ(reg) & ~PIPECONF_FRAME_START_DELAY_MASK);
+       if (WARN_ON(!crtc))
+               return false;
+
+       list_for_each_entry(other_encoder,
+                           &crtc->dev->mode_config.encoder_list,
+                           base.head) {
+
+               if (&other_encoder->new_crtc->base != crtc ||
+                   encoder == other_encoder)
+                       continue;
+               else
+                       return true;
        }
 
-       if (HAS_PCH_SPLIT(dev))
-               return;
+       return false;
+}
 
-       /* Who knows what state these registers were left in by the BIOS or
-        * grub?
-        *
-        * If we leave the registers in a conflicting state (e.g. with the
-        * display plane reading from the other pipe than the one we intend
-        * to use) then when we attempt to teardown the active mode, we will
-        * not disable the pipes and planes in the correct order -- leaving
-        * a plane reading from a disabled pipe and possibly leading to
-        * undefined behaviour.
+static bool intel_encoder_crtc_ok(struct drm_encoder *encoder,
+                                 struct drm_crtc *crtc)
+{
+       struct drm_device *dev;
+       struct drm_crtc *tmp;
+       int crtc_mask = 1;
+
+       WARN(!crtc, "checking null crtc?\n");
+
+       dev = crtc->dev;
+
+       list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) {
+               if (tmp == crtc)
+                       break;
+               crtc_mask <<= 1;
+       }
+
+       if (encoder->possible_crtcs & crtc_mask)
+               return true;
+       return false;
+}
+
+/**
+ * intel_modeset_update_staged_output_state
+ *
+ * Updates the staged output configuration state, e.g. after we've read out the
+ * current hw state.
+ */
+static void intel_modeset_update_staged_output_state(struct drm_device *dev)
+{
+       struct intel_encoder *encoder;
+       struct intel_connector *connector;
+
+       list_for_each_entry(connector, &dev->mode_config.connector_list,
+                           base.head) {
+               connector->new_encoder =
+                       to_intel_encoder(connector->base.encoder);
+       }
+
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list,
+                           base.head) {
+               encoder->new_crtc =
+                       to_intel_crtc(encoder->base.crtc);
+       }
+}
+
+/**
+ * intel_modeset_commit_output_state
+ *
+ * This function copies the stage display pipe configuration to the real one.
+ */
+static void intel_modeset_commit_output_state(struct drm_device *dev)
+{
+       struct intel_encoder *encoder;
+       struct intel_connector *connector;
+
+       list_for_each_entry(connector, &dev->mode_config.connector_list,
+                           base.head) {
+               connector->base.encoder = &connector->new_encoder->base;
+       }
+
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list,
+                           base.head) {
+               encoder->base.crtc = &encoder->new_crtc->base;
+       }
+}
+
+static struct drm_display_mode *
+intel_modeset_adjusted_mode(struct drm_crtc *crtc,
+                           struct drm_display_mode *mode)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_display_mode *adjusted_mode;
+       struct drm_encoder_helper_funcs *encoder_funcs;
+       struct intel_encoder *encoder;
+
+       adjusted_mode = drm_mode_duplicate(dev, mode);
+       if (!adjusted_mode)
+               return ERR_PTR(-ENOMEM);
+
+       /* Pass our mode to the connectors and the CRTC to give them a chance to
+        * adjust it according to limitations or connector properties, and also
+        * a chance to reject the mode entirely.
         */
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list,
+                           base.head) {
 
-       reg = DSPCNTR(plane);
-       val = I915_READ(reg);
+               if (&encoder->new_crtc->base != crtc)
+                       continue;
+               encoder_funcs = encoder->base.helper_private;
+               if (!(encoder_funcs->mode_fixup(&encoder->base, mode,
+                                               adjusted_mode))) {
+                       DRM_DEBUG_KMS("Encoder fixup failed\n");
+                       goto fail;
+               }
+       }
 
-       if ((val & DISPLAY_PLANE_ENABLE) == 0)
-               return;
-       if (!!(val & DISPPLANE_SEL_PIPE_MASK) == pipe)
-               return;
+       if (!(intel_crtc_mode_fixup(crtc, mode, adjusted_mode))) {
+               DRM_DEBUG_KMS("CRTC fixup failed\n");
+               goto fail;
+       }
+       DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
 
-       /* This display plane is active and attached to the other CPU pipe. */
-       pipe = !pipe;
+       return adjusted_mode;
+fail:
+       drm_mode_destroy(dev, adjusted_mode);
+       return ERR_PTR(-EINVAL);
+}
 
-       /* Disable the plane and wait for it to stop reading from the pipe. */
-       intel_disable_plane(dev_priv, plane, pipe);
-       intel_disable_pipe(dev_priv, pipe);
+/* Computes which crtcs are affected and sets the relevant bits in the mask. For
+ * simplicity we use the crtc's pipe number (because it's easier to obtain). */
+static void
+intel_modeset_affected_pipes(struct drm_crtc *crtc, unsigned *modeset_pipes,
+                            unsigned *prepare_pipes, unsigned *disable_pipes)
+{
+       struct intel_crtc *intel_crtc;
+       struct drm_device *dev = crtc->dev;
+       struct intel_encoder *encoder;
+       struct intel_connector *connector;
+       struct drm_crtc *tmp_crtc;
+
+       *disable_pipes = *modeset_pipes = *prepare_pipes = 0;
+
+       /* Check which crtcs have changed outputs connected to them, these need
+        * to be part of the prepare_pipes mask. We don't (yet) support global
+        * modeset across multiple crtcs, so modeset_pipes will only have one
+        * bit set at most. */
+       list_for_each_entry(connector, &dev->mode_config.connector_list,
+                           base.head) {
+               if (connector->base.encoder == &connector->new_encoder->base)
+                       continue;
+
+               if (connector->base.encoder) {
+                       tmp_crtc = connector->base.encoder->crtc;
+
+                       *prepare_pipes |= 1 << to_intel_crtc(tmp_crtc)->pipe;
+               }
+
+               if (connector->new_encoder)
+                       *prepare_pipes |=
+                               1 << connector->new_encoder->new_crtc->pipe;
+       }
+
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list,
+                           base.head) {
+               if (encoder->base.crtc == &encoder->new_crtc->base)
+                       continue;
+
+               if (encoder->base.crtc) {
+                       tmp_crtc = encoder->base.crtc;
+
+                       *prepare_pipes |= 1 << to_intel_crtc(tmp_crtc)->pipe;
+               }
+
+               if (encoder->new_crtc)
+                       *prepare_pipes |= 1 << encoder->new_crtc->pipe;
+       }
+
+       /* Check for any pipes that will be fully disabled ... */
+       list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list,
+                           base.head) {
+               bool used = false;
+
+               /* Don't try to disable disabled crtcs. */
+               if (!intel_crtc->base.enabled)
+                       continue;
+
+               list_for_each_entry(encoder, &dev->mode_config.encoder_list,
+                                   base.head) {
+                       if (encoder->new_crtc == intel_crtc)
+                               used = true;
+               }
+
+               if (!used)
+                       *disable_pipes |= 1 << intel_crtc->pipe;
+       }
+
+
+       /* set_mode is also used to update properties on life display pipes. */
+       intel_crtc = to_intel_crtc(crtc);
+       if (crtc->enabled)
+               *prepare_pipes |= 1 << intel_crtc->pipe;
+
+       /* We only support modeset on one single crtc, hence we need to do that
+        * only for the passed in crtc iff we change anything else than just
+        * disable crtcs.
+        *
+        * This is actually not true, to be fully compatible with the old crtc
+        * helper we automatically disable _any_ output (i.e. doesn't need to be
+        * connected to the crtc we're modesetting on) if it's disconnected.
+        * Which is a rather nutty api (since changed the output configuration
+        * without userspace's explicit request can lead to confusion), but
+        * alas. Hence we currently need to modeset on all pipes we prepare. */
+       if (*prepare_pipes)
+               *modeset_pipes = *prepare_pipes;
+
+       /* ... and mask these out. */
+       *modeset_pipes &= ~(*disable_pipes);
+       *prepare_pipes &= ~(*disable_pipes);
 }
 
-static void intel_crtc_reset(struct drm_crtc *crtc)
+static bool intel_crtc_in_use(struct drm_crtc *crtc)
 {
+       struct drm_encoder *encoder;
        struct drm_device *dev = crtc->dev;
-       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 
-       /* Reset flags back to the 'unknown' status so that they
-        * will be correctly set on the initial modeset.
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+               if (encoder->crtc == crtc)
+                       return true;
+
+       return false;
+}
+
+static void
+intel_modeset_update_state(struct drm_device *dev, unsigned prepare_pipes)
+{
+       struct intel_encoder *intel_encoder;
+       struct intel_crtc *intel_crtc;
+       struct drm_connector *connector;
+
+       list_for_each_entry(intel_encoder, &dev->mode_config.encoder_list,
+                           base.head) {
+               if (!intel_encoder->base.crtc)
+                       continue;
+
+               intel_crtc = to_intel_crtc(intel_encoder->base.crtc);
+
+               if (prepare_pipes & (1 << intel_crtc->pipe))
+                       intel_encoder->connectors_active = false;
+       }
+
+       intel_modeset_commit_output_state(dev);
+
+       /* Update computed state. */
+       list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list,
+                           base.head) {
+               intel_crtc->base.enabled = intel_crtc_in_use(&intel_crtc->base);
+       }
+
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               if (!connector->encoder || !connector->encoder->crtc)
+                       continue;
+
+               intel_crtc = to_intel_crtc(connector->encoder->crtc);
+
+               if (prepare_pipes & (1 << intel_crtc->pipe)) {
+                       struct drm_property *dpms_property =
+                               dev->mode_config.dpms_property;
+
+                       connector->dpms = DRM_MODE_DPMS_ON;
+                       drm_connector_property_set_value(connector,
+                                                        dpms_property,
+                                                        DRM_MODE_DPMS_ON);
+
+                       intel_encoder = to_intel_encoder(connector->encoder);
+                       intel_encoder->connectors_active = true;
+               }
+       }
+
+}
+
+#define for_each_intel_crtc_masked(dev, mask, intel_crtc) \
+       list_for_each_entry((intel_crtc), \
+                           &(dev)->mode_config.crtc_list, \
+                           base.head) \
+               if (mask & (1 <<(intel_crtc)->pipe)) \
+
+void
+intel_modeset_check_state(struct drm_device *dev)
+{
+       struct intel_crtc *crtc;
+       struct intel_encoder *encoder;
+       struct intel_connector *connector;
+
+       list_for_each_entry(connector, &dev->mode_config.connector_list,
+                           base.head) {
+               /* This also checks the encoder/connector hw state with the
+                * ->get_hw_state callbacks. */
+               intel_connector_check_state(connector);
+
+               WARN(&connector->new_encoder->base != connector->base.encoder,
+                    "connector's staged encoder doesn't match current encoder\n");
+       }
+
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list,
+                           base.head) {
+               bool enabled = false;
+               bool active = false;
+               enum pipe pipe, tracked_pipe;
+
+               DRM_DEBUG_KMS("[ENCODER:%d:%s]\n",
+                             encoder->base.base.id,
+                             drm_get_encoder_name(&encoder->base));
+
+               WARN(&encoder->new_crtc->base != encoder->base.crtc,
+                    "encoder's stage crtc doesn't match current crtc\n");
+               WARN(encoder->connectors_active && !encoder->base.crtc,
+                    "encoder's active_connectors set, but no crtc\n");
+
+               list_for_each_entry(connector, &dev->mode_config.connector_list,
+                                   base.head) {
+                       if (connector->base.encoder != &encoder->base)
+                               continue;
+                       enabled = true;
+                       if (connector->base.dpms != DRM_MODE_DPMS_OFF)
+                               active = true;
+               }
+               WARN(!!encoder->base.crtc != enabled,
+                    "encoder's enabled state mismatch "
+                    "(expected %i, found %i)\n",
+                    !!encoder->base.crtc, enabled);
+               WARN(active && !encoder->base.crtc,
+                    "active encoder with no crtc\n");
+
+               WARN(encoder->connectors_active != active,
+                    "encoder's computed active state doesn't match tracked active state "
+                    "(expected %i, found %i)\n", active, encoder->connectors_active);
+
+               active = encoder->get_hw_state(encoder, &pipe);
+               WARN(active != encoder->connectors_active,
+                    "encoder's hw state doesn't match sw tracking "
+                    "(expected %i, found %i)\n",
+                    encoder->connectors_active, active);
+
+               if (!encoder->base.crtc)
+                       continue;
+
+               tracked_pipe = to_intel_crtc(encoder->base.crtc)->pipe;
+               WARN(active && pipe != tracked_pipe,
+                    "active encoder's pipe doesn't match"
+                    "(expected %i, found %i)\n",
+                    tracked_pipe, pipe);
+
+       }
+
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list,
+                           base.head) {
+               bool enabled = false;
+               bool active = false;
+
+               DRM_DEBUG_KMS("[CRTC:%d]\n",
+                             crtc->base.base.id);
+
+               WARN(crtc->active && !crtc->base.enabled,
+                    "active crtc, but not enabled in sw tracking\n");
+
+               list_for_each_entry(encoder, &dev->mode_config.encoder_list,
+                                   base.head) {
+                       if (encoder->base.crtc != &crtc->base)
+                               continue;
+                       enabled = true;
+                       if (encoder->connectors_active)
+                               active = true;
+               }
+               WARN(active != crtc->active,
+                    "crtc's computed active state doesn't match tracked active state "
+                    "(expected %i, found %i)\n", active, crtc->active);
+               WARN(enabled != crtc->base.enabled,
+                    "crtc's computed enabled state doesn't match tracked enabled state "
+                    "(expected %i, found %i)\n", enabled, crtc->base.enabled);
+
+               assert_pipe(dev->dev_private, crtc->pipe, crtc->active);
+       }
+}
+
+bool 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_encoder_helper_funcs *encoder_funcs;
+       struct drm_encoder *encoder;
+       struct intel_crtc *intel_crtc;
+       unsigned disable_pipes, prepare_pipes, modeset_pipes;
+       bool ret = true;
+
+       intel_modeset_affected_pipes(crtc, &modeset_pipes,
+                                    &prepare_pipes, &disable_pipes);
+
+       DRM_DEBUG_KMS("set mode pipe masks: modeset: %x, prepare: %x, disable: %x\n",
+                     modeset_pipes, prepare_pipes, disable_pipes);
+
+       for_each_intel_crtc_masked(dev, disable_pipes, intel_crtc)
+               intel_crtc_disable(&intel_crtc->base);
+
+       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.
+        * Hence simply check whether any bit is set in modeset_pipes in all the
+        * pieces of code that are not yet converted to deal with mutliple crtcs
+        * changing their mode at the same time. */
+       adjusted_mode = NULL;
+       if (modeset_pipes) {
+               adjusted_mode = intel_modeset_adjusted_mode(crtc, mode);
+               if (IS_ERR(adjusted_mode)) {
+                       return false;
+               }
+       }
+
+       for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc) {
+               if (intel_crtc->base.enabled)
+                       dev_priv->display.crtc_disable(&intel_crtc->base);
+       }
+
+       /* crtc->mode is already used by the ->mode_set callbacks, hence we need
+        * to set it here already despite that we pass it down the callchain.
         */
-       intel_crtc->dpms_mode = -1;
+       if (modeset_pipes)
+               crtc->mode = *mode;
+
+       /* Only after disabling all output pipelines that will be changed can we
+        * update the the output configuration. */
+       intel_modeset_update_state(dev, prepare_pipes);
 
-       /* We need to fix up any BIOS configuration that conflicts with
-        * our expectations.
+       /* Set up the DPLL and any encoders state that needs to adjust or depend
+        * on the DPLL.
         */
-       intel_sanitize_modesetting(dev, intel_crtc->pipe, intel_crtc->plane);
+       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;
+
+               list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+
+                       if (encoder->crtc != &intel_crtc->base)
+                               continue;
+
+                       DRM_DEBUG_KMS("[ENCODER:%d:%s] set [MODE:%d:%s]\n",
+                               encoder->base.id, drm_get_encoder_name(encoder),
+                               mode->base.id, mode->name);
+                       encoder_funcs = encoder->helper_private;
+                       encoder_funcs->mode_set(encoder, mode, adjusted_mode);
+               }
+       }
+
+       /* Now enable the clocks, plane, pipe, and connectors that we set up. */
+       for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc)
+               dev_priv->display.crtc_enable(&intel_crtc->base);
+
+       if (modeset_pipes) {
+               /* Store real post-adjustment hardware mode. */
+               crtc->hwmode = *adjusted_mode;
+
+               /* Calculate and store various constants which
+                * are later needed by vblank and swap-completion
+                * timestamping. They are derived from true hwmode.
+                */
+               drm_calc_timestamping_constants(crtc);
+       }
+
+       /* FIXME: add subpixel order */
+done:
+       drm_mode_destroy(dev, adjusted_mode);
+       if (!ret && crtc->enabled) {
+               crtc->hwmode = saved_hwmode;
+               crtc->mode = saved_mode;
+       } else {
+               intel_modeset_check_state(dev);
+       }
+
+       return ret;
 }
 
-static struct drm_crtc_helper_funcs intel_helper_funcs = {
-       .dpms = intel_crtc_dpms,
-       .mode_fixup = intel_crtc_mode_fixup,
-       .mode_set = intel_crtc_mode_set,
-       .mode_set_base = intel_pipe_set_base,
-       .mode_set_base_atomic = intel_pipe_set_base_atomic,
-       .load_lut = intel_crtc_load_lut,
-       .disable = intel_crtc_disable,
-};
+#undef for_each_intel_crtc_masked
+
+static void intel_set_config_free(struct intel_set_config *config)
+{
+       if (!config)
+               return;
+
+       kfree(config->save_connector_encoders);
+       kfree(config->save_encoder_crtcs);
+       kfree(config);
+}
+
+static int intel_set_config_save_state(struct drm_device *dev,
+                                      struct intel_set_config *config)
+{
+       struct drm_encoder *encoder;
+       struct drm_connector *connector;
+       int count;
+
+       config->save_encoder_crtcs =
+               kcalloc(dev->mode_config.num_encoder,
+                       sizeof(struct drm_crtc *), GFP_KERNEL);
+       if (!config->save_encoder_crtcs)
+               return -ENOMEM;
+
+       config->save_connector_encoders =
+               kcalloc(dev->mode_config.num_connector,
+                       sizeof(struct drm_encoder *), GFP_KERNEL);
+       if (!config->save_connector_encoders)
+               return -ENOMEM;
+
+       /* Copy data. Note that driver private data is not affected.
+        * Should anything bad happen only the expected state is
+        * restored, not the drivers personal bookkeeping.
+        */
+       count = 0;
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               config->save_encoder_crtcs[count++] = encoder->crtc;
+       }
+
+       count = 0;
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               config->save_connector_encoders[count++] = connector->encoder;
+       }
+
+       return 0;
+}
+
+static void intel_set_config_restore_state(struct drm_device *dev,
+                                          struct intel_set_config *config)
+{
+       struct intel_encoder *encoder;
+       struct intel_connector *connector;
+       int count;
+
+       count = 0;
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
+               encoder->new_crtc =
+                       to_intel_crtc(config->save_encoder_crtcs[count++]);
+       }
+
+       count = 0;
+       list_for_each_entry(connector, &dev->mode_config.connector_list, base.head) {
+               connector->new_encoder =
+                       to_intel_encoder(config->save_connector_encoders[count++]);
+       }
+}
+
+static void
+intel_set_config_compute_mode_changes(struct drm_mode_set *set,
+                                     struct intel_set_config *config)
+{
+
+       /* We should be able to check here if the fb has the same properties
+        * and then just flip_or_move it */
+       if (set->crtc->fb != set->fb) {
+               /* If we have no fb then treat it as a full mode set */
+               if (set->crtc->fb == NULL) {
+                       DRM_DEBUG_KMS("crtc has no fb, full mode set\n");
+                       config->mode_changed = true;
+               } else if (set->fb == NULL) {
+                       config->mode_changed = true;
+               } else if (set->fb->depth != set->crtc->fb->depth) {
+                       config->mode_changed = true;
+               } else if (set->fb->bits_per_pixel !=
+                          set->crtc->fb->bits_per_pixel) {
+                       config->mode_changed = true;
+               } else
+                       config->fb_changed = true;
+       }
+
+       if (set->fb && (set->x != set->crtc->x || set->y != set->crtc->y))
+               config->fb_changed = true;
+
+       if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) {
+               DRM_DEBUG_KMS("modes are different, full mode set\n");
+               drm_mode_debug_printmodeline(&set->crtc->mode);
+               drm_mode_debug_printmodeline(set->mode);
+               config->mode_changed = true;
+       }
+}
+
+static int
+intel_modeset_stage_output_state(struct drm_device *dev,
+                                struct drm_mode_set *set,
+                                struct intel_set_config *config)
+{
+       struct drm_crtc *new_crtc;
+       struct intel_connector *connector;
+       struct intel_encoder *encoder;
+       int count, ro;
+
+       /* The upper layers ensure that we either disabl 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));
+
+       count = 0;
+       list_for_each_entry(connector, &dev->mode_config.connector_list,
+                           base.head) {
+               /* Otherwise traverse passed in connector list and get encoders
+                * for them. */
+               for (ro = 0; ro < set->num_connectors; ro++) {
+                       if (set->connectors[ro] == &connector->base) {
+                               connector->new_encoder = connector->encoder;
+                               break;
+                       }
+               }
+
+               /* If we disable the crtc, disable all its connectors. Also, if
+                * the connector is on the changing crtc but not on the new
+                * connector list, disable it. */
+               if ((!set->fb || ro == set->num_connectors) &&
+                   connector->base.encoder &&
+                   connector->base.encoder->crtc == set->crtc) {
+                       connector->new_encoder = NULL;
+
+                       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n",
+                               connector->base.base.id,
+                               drm_get_connector_name(&connector->base));
+               }
+
+
+               if (&connector->new_encoder->base != connector->base.encoder) {
+                       DRM_DEBUG_KMS("encoder changed, full mode switch\n");
+                       config->mode_changed = true;
+               }
+
+               /* Disable all disconnected encoders. */
+               if (connector->base.status == connector_status_disconnected)
+                       connector->new_encoder = NULL;
+       }
+       /* connector->new_encoder is now updated for all connectors. */
+
+       /* Update crtc of enabled connectors. */
+       count = 0;
+       list_for_each_entry(connector, &dev->mode_config.connector_list,
+                           base.head) {
+               if (!connector->new_encoder)
+                       continue;
+
+               new_crtc = connector->new_encoder->base.crtc;
+
+               for (ro = 0; ro < set->num_connectors; ro++) {
+                       if (set->connectors[ro] == &connector->base)
+                               new_crtc = set->crtc;
+               }
+
+               /* Make sure the new CRTC will work with the encoder */
+               if (!intel_encoder_crtc_ok(&connector->new_encoder->base,
+                                          new_crtc)) {
+                       return -EINVAL;
+               }
+               connector->encoder->new_crtc = to_intel_crtc(new_crtc);
+
+               DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n",
+                       connector->base.base.id,
+                       drm_get_connector_name(&connector->base),
+                       new_crtc->base.id);
+       }
+
+       /* Check for any encoders that needs to be disabled. */
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list,
+                           base.head) {
+               list_for_each_entry(connector,
+                                   &dev->mode_config.connector_list,
+                                   base.head) {
+                       if (connector->new_encoder == encoder) {
+                               WARN_ON(!connector->new_encoder->new_crtc);
+
+                               goto next_encoder;
+                       }
+               }
+               encoder->new_crtc = NULL;
+next_encoder:
+               /* Only now check for crtc changes so we don't miss encoders
+                * that will be disabled. */
+               if (&encoder->new_crtc->base != encoder->base.crtc) {
+                       DRM_DEBUG_KMS("crtc changed, full mode switch\n");
+                       config->mode_changed = true;
+               }
+       }
+       /* Now we've also updated encoder->new_crtc for all encoders. */
+
+       return 0;
+}
+
+static int intel_crtc_set_config(struct drm_mode_set *set)
+{
+       struct drm_device *dev;
+       struct drm_mode_set save_set;
+       struct intel_set_config *config;
+       int ret;
+
+       BUG_ON(!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;
+
+       if (set->fb) {
+               DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n",
+                               set->crtc->base.id, set->fb->base.id,
+                               (int)set->num_connectors, set->x, set->y);
+       } else {
+               DRM_DEBUG_KMS("[CRTC:%d] [NOFB]\n", set->crtc->base.id);
+       }
+
+       dev = set->crtc->dev;
+
+       ret = -ENOMEM;
+       config = kzalloc(sizeof(*config), GFP_KERNEL);
+       if (!config)
+               goto out_config;
+
+       ret = intel_set_config_save_state(dev, config);
+       if (ret)
+               goto out_config;
+
+       save_set.crtc = set->crtc;
+       save_set.mode = &set->crtc->mode;
+       save_set.x = set->crtc->x;
+       save_set.y = set->crtc->y;
+       save_set.fb = set->crtc->fb;
+
+       /* Compute whether we need a full modeset, only an fb base update or no
+        * change at all. In the future we might also check whether only the
+        * mode changed, e.g. for LVDS where we only change the panel fitter in
+        * such cases. */
+       intel_set_config_compute_mode_changes(set, config);
+
+       ret = intel_modeset_stage_output_state(dev, set, config);
+       if (ret)
+               goto fail;
+
+       if (config->mode_changed) {
+               if (set->mode) {
+                       DRM_DEBUG_KMS("attempting to set mode from"
+                                       " userspace\n");
+                       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;
+                       goto fail;
+               }
+       } else if (config->fb_changed) {
+               ret = intel_pipe_set_base(set->crtc,
+                                         set->x, set->y, set->fb);
+       }
+
+       intel_set_config_free(config);
+
+       return 0;
+
+fail:
+       intel_set_config_restore_state(dev, config);
+
+       /* 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))
+               DRM_ERROR("failed to restore config after modeset failure\n");
+
+out_config:
+       intel_set_config_free(config);
+       return ret;
+}
 
 static const struct drm_crtc_funcs intel_crtc_funcs = {
-       .reset = intel_crtc_reset,
        .cursor_set = intel_crtc_cursor_set,
        .cursor_move = intel_crtc_cursor_move,
        .gamma_set = intel_crtc_gamma_set,
-       .set_config = drm_crtc_helper_set_config,
+       .set_config = intel_crtc_set_config,
        .destroy = intel_crtc_destroy,
        .page_flip = intel_crtc_page_flip,
 };
@@ -6655,24 +7448,9 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
        dev_priv->plane_to_crtc_mapping[intel_crtc->plane] = &intel_crtc->base;
        dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = &intel_crtc->base;
 
-       intel_crtc_reset(&intel_crtc->base);
-       intel_crtc->active = true; /* force the pipe off on setup_init_config */
        intel_crtc->bpp = 24; /* default for pre-Ironlake */
 
-       if (HAS_PCH_SPLIT(dev)) {
-               intel_helper_funcs.prepare = ironlake_crtc_prepare;
-               intel_helper_funcs.commit = ironlake_crtc_commit;
-       } else {
-               intel_helper_funcs.prepare = i9xx_crtc_prepare;
-               intel_helper_funcs.commit = i9xx_crtc_commit;
-       }
-
        drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
-
-       intel_crtc->busy = false;
-
-       setup_timer(&intel_crtc->idle_timer, intel_crtc_idle_timer,
-                   (unsigned long)intel_crtc);
 }
 
 int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
@@ -6699,15 +7477,23 @@ int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
        return 0;
 }
 
-static int intel_encoder_clones(struct drm_device *dev, int type_mask)
+static int intel_encoder_clones(struct intel_encoder *encoder)
 {
-       struct intel_encoder *encoder;
+       struct drm_device *dev = encoder->base.dev;
+       struct intel_encoder *source_encoder;
        int index_mask = 0;
        int entry = 0;
 
-       list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
-               if (type_mask & encoder->clone_mask)
+       list_for_each_entry(source_encoder,
+                           &dev->mode_config.encoder_list, base.head) {
+
+               if (encoder == source_encoder)
+                       index_mask |= (1 << entry);
+
+               /* Intel hw has only one MUX where enocoders could be cloned. */
+               if (encoder->cloneable && source_encoder->cloneable)
                        index_mask |= (1 << entry);
+
                entry++;
        }
 
@@ -6748,10 +7534,10 @@ static void intel_setup_outputs(struct drm_device *dev)
                dpd_is_edp = intel_dpd_is_edp(dev);
 
                if (has_edp_a(dev))
-                       intel_dp_init(dev, DP_A);
+                       intel_dp_init(dev, DP_A, PORT_A);
 
                if (dpd_is_edp && (I915_READ(PCH_DP_D) & DP_DETECTED))
-                       intel_dp_init(dev, PCH_DP_D);
+                       intel_dp_init(dev, PCH_DP_D, PORT_D);
        }
 
        intel_crt_init(dev);
@@ -6782,22 +7568,22 @@ static void intel_setup_outputs(struct drm_device *dev)
                        /* PCH SDVOB multiplex with HDMIB */
                        found = intel_sdvo_init(dev, PCH_SDVOB, true);
                        if (!found)
-                               intel_hdmi_init(dev, HDMIB);
+                               intel_hdmi_init(dev, HDMIB, PORT_B);
                        if (!found && (I915_READ(PCH_DP_B) & DP_DETECTED))
-                               intel_dp_init(dev, PCH_DP_B);
+                               intel_dp_init(dev, PCH_DP_B, PORT_B);
                }
 
                if (I915_READ(HDMIC) & PORT_DETECTED)
-                       intel_hdmi_init(dev, HDMIC);
+                       intel_hdmi_init(dev, HDMIC, PORT_C);
 
                if (!dpd_is_edp && I915_READ(HDMID) & PORT_DETECTED)
-                       intel_hdmi_init(dev, HDMID);
+                       intel_hdmi_init(dev, HDMID, PORT_D);
 
                if (I915_READ(PCH_DP_C) & DP_DETECTED)
-                       intel_dp_init(dev, PCH_DP_C);
+                       intel_dp_init(dev, PCH_DP_C, PORT_C);
 
                if (!dpd_is_edp && (I915_READ(PCH_DP_D) & DP_DETECTED))
-                       intel_dp_init(dev, PCH_DP_D);
+                       intel_dp_init(dev, PCH_DP_D, PORT_D);
        } else if (IS_VALLEYVIEW(dev)) {
                int found;
 
@@ -6805,17 +7591,17 @@ static void intel_setup_outputs(struct drm_device *dev)
                        /* SDVOB multiplex with HDMIB */
                        found = intel_sdvo_init(dev, SDVOB, true);
                        if (!found)
-                               intel_hdmi_init(dev, SDVOB);
+                               intel_hdmi_init(dev, SDVOB, PORT_B);
                        if (!found && (I915_READ(DP_B) & DP_DETECTED))
-                               intel_dp_init(dev, DP_B);
+                               intel_dp_init(dev, DP_B, PORT_B);
                }
 
                if (I915_READ(SDVOC) & PORT_DETECTED)
-                       intel_hdmi_init(dev, SDVOC);
+                       intel_hdmi_init(dev, SDVOC, PORT_C);
 
                /* Shares lanes with HDMI on SDVOC */
                if (I915_READ(DP_C) & DP_DETECTED)
-                       intel_dp_init(dev, DP_C);
+                       intel_dp_init(dev, DP_C, PORT_C);
        } else if (SUPPORTS_DIGITAL_OUTPUTS(dev)) {
                bool found = false;
 
@@ -6824,12 +7610,12 @@ static void intel_setup_outputs(struct drm_device *dev)
                        found = intel_sdvo_init(dev, SDVOB, true);
                        if (!found && SUPPORTS_INTEGRATED_HDMI(dev)) {
                                DRM_DEBUG_KMS("probing HDMI on SDVOB\n");
-                               intel_hdmi_init(dev, SDVOB);
+                               intel_hdmi_init(dev, SDVOB, PORT_B);
                        }
 
                        if (!found && SUPPORTS_INTEGRATED_DP(dev)) {
                                DRM_DEBUG_KMS("probing DP_B\n");
-                               intel_dp_init(dev, DP_B);
+                               intel_dp_init(dev, DP_B, PORT_B);
                        }
                }
 
@@ -6844,18 +7630,18 @@ static void intel_setup_outputs(struct drm_device *dev)
 
                        if (SUPPORTS_INTEGRATED_HDMI(dev)) {
                                DRM_DEBUG_KMS("probing HDMI on SDVOC\n");
-                               intel_hdmi_init(dev, SDVOC);
+                               intel_hdmi_init(dev, SDVOC, PORT_C);
                        }
                        if (SUPPORTS_INTEGRATED_DP(dev)) {
                                DRM_DEBUG_KMS("probing DP_C\n");
-                               intel_dp_init(dev, DP_C);
+                               intel_dp_init(dev, DP_C, PORT_C);
                        }
                }
 
                if (SUPPORTS_INTEGRATED_DP(dev) &&
                    (I915_READ(DP_D) & DP_DETECTED)) {
                        DRM_DEBUG_KMS("probing DP_D\n");
-                       intel_dp_init(dev, DP_D);
+                       intel_dp_init(dev, DP_D, PORT_D);
                }
        } else if (IS_GEN2(dev))
                intel_dvo_init(dev);
@@ -6866,12 +7652,9 @@ static void intel_setup_outputs(struct drm_device *dev)
        list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
                encoder->base.possible_crtcs = encoder->crtc_mask;
                encoder->base.possible_clones =
-                       intel_encoder_clones(dev, encoder->clone_mask);
+                       intel_encoder_clones(encoder);
        }
 
-       /* disable all the possible outputs/crtcs before entering KMS mode */
-       drm_helper_disable_unused_functions(dev);
-
        if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev))
                ironlake_init_pch_refclk(dev);
 }
@@ -6973,13 +7756,15 @@ static void intel_init_display(struct drm_device *dev)
 
        /* We always want a DPMS function */
        if (HAS_PCH_SPLIT(dev)) {
-               dev_priv->display.dpms = ironlake_crtc_dpms;
                dev_priv->display.crtc_mode_set = ironlake_crtc_mode_set;
+               dev_priv->display.crtc_enable = ironlake_crtc_enable;
+               dev_priv->display.crtc_disable = ironlake_crtc_disable;
                dev_priv->display.off = ironlake_crtc_off;
                dev_priv->display.update_plane = ironlake_update_plane;
        } else {
-               dev_priv->display.dpms = i9xx_crtc_dpms;
                dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set;
+               dev_priv->display.crtc_enable = i9xx_crtc_enable;
+               dev_priv->display.crtc_disable = i9xx_crtc_disable;
                dev_priv->display.off = i9xx_crtc_off;
                dev_priv->display.update_plane = i9xx_update_plane;
        }
@@ -7023,7 +7808,7 @@ static void intel_init_display(struct drm_device *dev)
                        dev_priv->display.write_eld = ironlake_write_eld;
                } else if (IS_HASWELL(dev)) {
                        dev_priv->display.fdi_link_train = hsw_fdi_link_train;
-                       dev_priv->display.write_eld = ironlake_write_eld;
+                       dev_priv->display.write_eld = haswell_write_eld;
                } else
                        dev_priv->display.update_wm = NULL;
        } else if (IS_G4X(dev)) {
@@ -7101,21 +7886,16 @@ static struct intel_quirk intel_quirks[] = {
        /* HP Mini needs pipe A force quirk (LP: #322104) */
        { 0x27ae, 0x103c, 0x361a, quirk_pipea_force },
 
-       /* Thinkpad R31 needs pipe A force quirk */
-       { 0x3577, 0x1014, 0x0505, quirk_pipea_force },
        /* Toshiba Protege R-205, S-209 needs pipe A force quirk */
        { 0x2592, 0x1179, 0x0001, quirk_pipea_force },
 
-       /* ThinkPad X30 needs pipe A force quirk (LP: #304614) */
-       { 0x3577,  0x1014, 0x0513, quirk_pipea_force },
-       /* ThinkPad X40 needs pipe A force quirk */
-
        /* ThinkPad T60 needs pipe A force quirk (bug #16494) */
        { 0x2782, 0x17aa, 0x201a, quirk_pipea_force },
 
        /* 855 & before need to leave pipe A & dpll A up */
        { 0x3582, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force },
        { 0x2562, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force },
+       { 0x3577, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force },
 
        /* Lenovo U160 cannot use SSC on LVDS */
        { 0x0046, 0x17aa, 0x3920, quirk_ssc_force_disable },
@@ -7231,10 +8011,251 @@ void intel_modeset_init(struct drm_device *dev)
        /* Just disable it once at startup */
        i915_disable_vga(dev);
        intel_setup_outputs(dev);
+}
+
+static void
+intel_connector_break_all_links(struct intel_connector *connector)
+{
+       connector->base.dpms = DRM_MODE_DPMS_OFF;
+       connector->base.encoder = NULL;
+       connector->encoder->connectors_active = false;
+       connector->encoder->base.crtc = NULL;
+}
+
+static void intel_enable_pipe_a(struct drm_device *dev)
+{
+       struct intel_connector *connector;
+       struct drm_connector *crt = NULL;
+       struct intel_load_detect_pipe load_detect_temp;
+
+       /* We can't just switch on the pipe A, we need to set things up with a
+        * proper mode and output configuration. As a gross hack, enable pipe A
+        * by enabling the load detect pipe once. */
+       list_for_each_entry(connector,
+                           &dev->mode_config.connector_list,
+                           base.head) {
+               if (connector->encoder->type == INTEL_OUTPUT_ANALOG) {
+                       crt = &connector->base;
+                       break;
+               }
+       }
+
+       if (!crt)
+               return;
+
+       if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp))
+               intel_release_load_detect_pipe(crt, &load_detect_temp);
+
+
+}
+
+static void intel_sanitize_crtc(struct intel_crtc *crtc)
+{
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 reg, val;
+
+       /* Clear any frame start delays used for debugging left by the BIOS */
+       reg = PIPECONF(crtc->pipe);
+       I915_WRITE(reg, I915_READ(reg) & ~PIPECONF_FRAME_START_DELAY_MASK);
+
+       /* We need to sanitize the plane -> pipe mapping first because this will
+        * disable the crtc (and hence change the state) if it is wrong. */
+       if (!HAS_PCH_SPLIT(dev)) {
+               struct intel_connector *connector;
+               bool plane;
+
+               reg = DSPCNTR(crtc->plane);
+               val = I915_READ(reg);
+
+               if ((val & DISPLAY_PLANE_ENABLE) == 0 &&
+                   (!!(val & DISPPLANE_SEL_PIPE_MASK) == crtc->pipe))
+                       goto ok;
+
+               DRM_DEBUG_KMS("[CRTC:%d] wrong plane connection detected!\n",
+                             crtc->base.base.id);
+
+               /* Pipe has the wrong plane attached and the plane is active.
+                * Temporarily change the plane mapping and disable everything
+                * ...  */
+               plane = crtc->plane;
+               crtc->plane = !plane;
+               dev_priv->display.crtc_disable(&crtc->base);
+               crtc->plane = plane;
+
+               /* ... and break all links. */
+               list_for_each_entry(connector, &dev->mode_config.connector_list,
+                                   base.head) {
+                       if (connector->encoder->base.crtc != &crtc->base)
+                               continue;
+
+                       intel_connector_break_all_links(connector);
+               }
+
+               WARN_ON(crtc->active);
+               crtc->base.enabled = false;
+       }
+ok:
+
+       if (dev_priv->quirks & QUIRK_PIPEA_FORCE &&
+           crtc->pipe == PIPE_A && !crtc->active) {
+               /* BIOS forgot to enable pipe A, this mostly happens after
+                * resume. Force-enable the pipe to fix this, the update_dpms
+                * call below we restore the pipe to the right state, but leave
+                * the required bits on. */
+               intel_enable_pipe_a(dev);
+       }
+
+       /* Adjust the state of the output pipe according to whether we
+        * have active connectors/encoders. */
+       intel_crtc_update_dpms(&crtc->base);
 
-       INIT_WORK(&dev_priv->idle_work, intel_idle_update);
-       setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer,
-                   (unsigned long)dev);
+       if (crtc->active != crtc->base.enabled) {
+               struct intel_encoder *encoder;
+
+               /* This can happen either due to bugs in the get_hw_state
+                * functions or because the pipe is force-enabled due to the
+                * pipe A quirk. */
+               DRM_DEBUG_KMS("[CRTC:%d] hw state adjusted, was %s, now %s\n",
+                             crtc->base.base.id,
+                             crtc->base.enabled ? "enabled" : "disabled",
+                             crtc->active ? "enabled" : "disabled");
+
+               crtc->base.enabled = crtc->active;
+
+               /* Because we only establish the connector -> encoder ->
+                * crtc links if something is active, this means the
+                * crtc is now deactivated. Break the links. connector
+                * -> encoder links are only establish when things are
+                *  actually up, hence no need to break them. */
+               WARN_ON(crtc->active);
+
+               for_each_encoder_on_crtc(dev, &crtc->base, encoder) {
+                       WARN_ON(encoder->connectors_active);
+                       encoder->base.crtc = NULL;
+               }
+       }
+}
+
+static void intel_sanitize_encoder(struct intel_encoder *encoder)
+{
+       struct intel_connector *connector;
+       struct drm_device *dev = encoder->base.dev;
+
+       /* We need to check both for a crtc link (meaning that the
+        * encoder is active and trying to read from a pipe) and the
+        * pipe itself being active. */
+       bool has_active_crtc = encoder->base.crtc &&
+               to_intel_crtc(encoder->base.crtc)->active;
+
+       if (encoder->connectors_active && !has_active_crtc) {
+               DRM_DEBUG_KMS("[ENCODER:%d:%s] has active connectors but no active pipe!\n",
+                             encoder->base.base.id,
+                             drm_get_encoder_name(&encoder->base));
+
+               /* Connector is active, but has no active pipe. This is
+                * fallout from our resume register restoring. Disable
+                * the encoder manually again. */
+               if (encoder->base.crtc) {
+                       DRM_DEBUG_KMS("[ENCODER:%d:%s] manually disabled\n",
+                                     encoder->base.base.id,
+                                     drm_get_encoder_name(&encoder->base));
+                       encoder->disable(encoder);
+               }
+
+               /* Inconsistent output/port/pipe state happens presumably due to
+                * a bug in one of the get_hw_state functions. Or someplace else
+                * in our code, like the register restore mess on resume. Clamp
+                * things to off as a safer default. */
+               list_for_each_entry(connector,
+                                   &dev->mode_config.connector_list,
+                                   base.head) {
+                       if (connector->encoder != encoder)
+                               continue;
+
+                       intel_connector_break_all_links(connector);
+               }
+       }
+       /* Enabled encoders without active connectors will be fixed in
+        * the crtc fixup. */
+}
+
+/* Scan out the current hw modeset state, sanitizes it and maps it into the drm
+ * and i915 state tracking structures. */
+void intel_modeset_setup_hw_state(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       enum pipe pipe;
+       u32 tmp;
+       struct intel_crtc *crtc;
+       struct intel_encoder *encoder;
+       struct intel_connector *connector;
+
+       for_each_pipe(pipe) {
+               crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
+
+               tmp = I915_READ(PIPECONF(pipe));
+               if (tmp & PIPECONF_ENABLE)
+                       crtc->active = true;
+               else
+                       crtc->active = false;
+
+               crtc->base.enabled = crtc->active;
+
+               DRM_DEBUG_KMS("[CRTC:%d] hw state readout: %s\n",
+                             crtc->base.base.id,
+                             crtc->active ? "enabled" : "disabled");
+       }
+
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list,
+                           base.head) {
+               pipe = 0;
+
+               if (encoder->get_hw_state(encoder, &pipe)) {
+                       encoder->base.crtc =
+                               dev_priv->pipe_to_crtc_mapping[pipe];
+               } else {
+                       encoder->base.crtc = NULL;
+               }
+
+               encoder->connectors_active = false;
+               DRM_DEBUG_KMS("[ENCODER:%d:%s] hw state readout: %s, pipe=%i\n",
+                             encoder->base.base.id,
+                             drm_get_encoder_name(&encoder->base),
+                             encoder->base.crtc ? "enabled" : "disabled",
+                             pipe);
+       }
+
+       list_for_each_entry(connector, &dev->mode_config.connector_list,
+                           base.head) {
+               if (connector->get_hw_state(connector)) {
+                       connector->base.dpms = DRM_MODE_DPMS_ON;
+                       connector->encoder->connectors_active = true;
+                       connector->base.encoder = &connector->encoder->base;
+               } else {
+                       connector->base.dpms = DRM_MODE_DPMS_OFF;
+                       connector->base.encoder = NULL;
+               }
+               DRM_DEBUG_KMS("[CONNECTOR:%d:%s] hw state readout: %s\n",
+                             connector->base.base.id,
+                             drm_get_connector_name(&connector->base),
+                             connector->base.encoder ? "enabled" : "disabled");
+       }
+
+       /* HW state is read out, now we need to sanitize this mess. */
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list,
+                           base.head) {
+               intel_sanitize_encoder(encoder);
+       }
+
+       for_each_pipe(pipe) {
+               crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
+               intel_sanitize_crtc(crtc);
+       }
+
+       intel_modeset_update_staged_output_state(dev);
+
+       intel_modeset_check_state(dev);
 }
 
 void intel_modeset_gem_init(struct drm_device *dev)
@@ -7242,6 +8263,8 @@ void intel_modeset_gem_init(struct drm_device *dev)
        intel_modeset_init_hw(dev);
 
        intel_setup_overlay(dev);
+
+       intel_modeset_setup_hw_state(dev);
 }
 
 void intel_modeset_cleanup(struct drm_device *dev)
@@ -7280,19 +8303,11 @@ void intel_modeset_cleanup(struct drm_device *dev)
         * enqueue unpin/hotplug work. */
        drm_irq_uninstall(dev);
        cancel_work_sync(&dev_priv->hotplug_work);
-       cancel_work_sync(&dev_priv->rps_work);
+       cancel_work_sync(&dev_priv->rps.work);
 
        /* flush any delayed tasks or pending work */
        flush_scheduled_work();
 
-       /* Shut off idle work before the crtcs get freed. */
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-               intel_crtc = to_intel_crtc(crtc);
-               del_timer_sync(&intel_crtc->idle_timer);
-       }
-       del_timer_sync(&dev_priv->idle_timer);
-       cancel_work_sync(&dev_priv->idle_work);
-
        drm_mode_config_cleanup(dev);
 }
 
@@ -7338,7 +8353,7 @@ struct intel_display_error_state {
                u32 position;
                u32 base;
                u32 size;
-       } cursor[2];
+       } cursor[I915_MAX_PIPES];
 
        struct intel_pipe_error_state {
                u32 conf;
@@ -7350,7 +8365,7 @@ struct intel_display_error_state {
                u32 vtotal;
                u32 vblank;
                u32 vsync;
-       } pipe[2];
+       } pipe[I915_MAX_PIPES];
 
        struct intel_plane_error_state {
                u32 control;
@@ -7360,7 +8375,7 @@ struct intel_display_error_state {
                u32 addr;
                u32 surface;
                u32 tile_offset;
-       } plane[2];
+       } plane[I915_MAX_PIPES];
 };
 
 struct intel_display_error_state *
@@ -7374,7 +8389,7 @@ intel_display_capture_error_state(struct drm_device *dev)
        if (error == NULL)
                return NULL;
 
-       for (i = 0; i < 2; i++) {
+       for_each_pipe(i) {
                error->cursor[i].control = I915_READ(CURCNTR(i));
                error->cursor[i].position = I915_READ(CURPOS(i));
                error->cursor[i].base = I915_READ(CURBASE(i));
@@ -7407,9 +8422,11 @@ intel_display_print_error_state(struct seq_file *m,
                                struct drm_device *dev,
                                struct intel_display_error_state *error)
 {
+       drm_i915_private_t *dev_priv = dev->dev_private;
        int i;
 
-       for (i = 0; i < 2; i++) {
+       seq_printf(m, "Num Pipes: %d\n", dev_priv->num_pipe);
+       for_each_pipe(i) {
                seq_printf(m, "Pipe [%d]:\n", i);
                seq_printf(m, "  CONF: %08x\n", error->pipe[i].conf);
                seq_printf(m, "  SRC: %08x\n", error->pipe[i].source);
index ace757af913366db3e7cff4e670fa9c804c84d08..1474f84fdbd058ca2d5bd54cbfe6ee5f5c9c865a 100644 (file)
 #include "intel_drv.h"
 #include "i915_drm.h"
 #include "i915_drv.h"
-#include "drm_dp_helper.h"
 
-#define DP_RECEIVER_CAP_SIZE   0xf
 #define DP_LINK_STATUS_SIZE    6
 #define DP_LINK_CHECK_TIMEOUT  (10 * 1000)
 
-#define DP_LINK_CONFIGURATION_SIZE     9
-
-struct intel_dp {
-       struct intel_encoder base;
-       uint32_t output_reg;
-       uint32_t DP;
-       uint8_t  link_configuration[DP_LINK_CONFIGURATION_SIZE];
-       bool has_audio;
-       enum hdmi_force_audio force_audio;
-       uint32_t color_range;
-       int dpms_mode;
-       uint8_t link_bw;
-       uint8_t lane_count;
-       uint8_t dpcd[DP_RECEIVER_CAP_SIZE];
-       struct i2c_adapter adapter;
-       struct i2c_algo_dp_aux_data algo;
-       bool is_pch_edp;
-       uint8_t train_set[4];
-       int panel_power_up_delay;
-       int panel_power_down_delay;
-       int panel_power_cycle_delay;
-       int backlight_on_delay;
-       int backlight_off_delay;
-       struct drm_display_mode *panel_fixed_mode;  /* for eDP */
-       struct delayed_work panel_vdd_work;
-       bool want_panel_vdd;
-       struct edid *edid; /* cached EDID for eDP */
-       int edid_mode_count;
-};
-
 /**
  * is_edp - is the given port attached to an eDP panel (either CPU or PCH)
  * @intel_dp: DP struct
@@ -840,9 +808,6 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
        }
 }
 
-static void ironlake_edp_pll_on(struct drm_encoder *encoder);
-static void ironlake_edp_pll_off(struct drm_encoder *encoder);
-
 static void
 intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
                  struct drm_display_mode *adjusted_mode)
@@ -853,14 +818,6 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
        struct drm_crtc *crtc = intel_dp->base.base.crtc;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 
-       /* Turn on the eDP PLL if needed */
-       if (is_edp(intel_dp)) {
-               if (!is_pch_edp(intel_dp))
-                       ironlake_edp_pll_on(encoder);
-               else
-                       ironlake_edp_pll_off(encoder);
-       }
-
        /*
         * There are four kinds of DP registers:
         *
@@ -882,10 +839,8 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
         * supposed to be read-only.
         */
        intel_dp->DP = I915_READ(intel_dp->output_reg) & DP_DETECTED;
-       intel_dp->DP |=  DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0;
 
        /* Handle DP bits in common between all three register formats */
-
        intel_dp->DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0;
 
        switch (intel_dp->lane_count) {
@@ -932,7 +887,6 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
                intel_dp->DP |= intel_crtc->pipe << 29;
 
                /* don't miss out required setting for eDP */
-               intel_dp->DP |= DP_PLL_ENABLE;
                if (adjusted_mode->clock < 200000)
                        intel_dp->DP |= DP_PLL_FREQ_160MHZ;
                else
@@ -954,7 +908,6 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
 
                if (is_cpu_edp(intel_dp)) {
                        /* don't miss out required setting for eDP */
-                       intel_dp->DP |= DP_PLL_ENABLE;
                        if (adjusted_mode->clock < 200000)
                                intel_dp->DP |= DP_PLL_FREQ_160MHZ;
                        else
@@ -1225,27 +1178,49 @@ static void ironlake_edp_backlight_off(struct intel_dp *intel_dp)
        msleep(intel_dp->backlight_off_delay);
 }
 
-static void ironlake_edp_pll_on(struct drm_encoder *encoder)
+static void ironlake_edp_pll_on(struct intel_dp *intel_dp)
 {
-       struct drm_device *dev = encoder->dev;
+       struct drm_device *dev = intel_dp->base.base.dev;
+       struct drm_crtc *crtc = intel_dp->base.base.crtc;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 dpa_ctl;
 
+       assert_pipe_disabled(dev_priv,
+                            to_intel_crtc(crtc)->pipe);
+
        DRM_DEBUG_KMS("\n");
        dpa_ctl = I915_READ(DP_A);
-       dpa_ctl |= DP_PLL_ENABLE;
-       I915_WRITE(DP_A, dpa_ctl);
+       WARN(dpa_ctl & DP_PLL_ENABLE, "dp pll on, should be off\n");
+       WARN(dpa_ctl & DP_PORT_EN, "dp port still on, should be off\n");
+
+       /* We don't adjust intel_dp->DP while tearing down the link, to
+        * facilitate link retraining (e.g. after hotplug). Hence clear all
+        * enable bits here to ensure that we don't enable too much. */
+       intel_dp->DP &= ~(DP_PORT_EN | DP_AUDIO_OUTPUT_ENABLE);
+       intel_dp->DP |= DP_PLL_ENABLE;
+       I915_WRITE(DP_A, intel_dp->DP);
        POSTING_READ(DP_A);
        udelay(200);
 }
 
-static void ironlake_edp_pll_off(struct drm_encoder *encoder)
+static void ironlake_edp_pll_off(struct intel_dp *intel_dp)
 {
-       struct drm_device *dev = encoder->dev;
+       struct drm_device *dev = intel_dp->base.base.dev;
+       struct drm_crtc *crtc = intel_dp->base.base.crtc;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 dpa_ctl;
 
+       assert_pipe_disabled(dev_priv,
+                            to_intel_crtc(crtc)->pipe);
+
        dpa_ctl = I915_READ(DP_A);
+       WARN((dpa_ctl & DP_PLL_ENABLE) == 0,
+            "dp pll off, should be on\n");
+       WARN(dpa_ctl & DP_PORT_EN, "dp port still on, should be off\n");
+
+       /* We can't rely on the value tracked for the DP register in
+        * intel_dp->DP because link_down must not change that (otherwise link
+        * re-training will fail. */
        dpa_ctl &= ~DP_PLL_ENABLE;
        I915_WRITE(DP_A, dpa_ctl);
        POSTING_READ(DP_A);
@@ -1282,10 +1257,57 @@ static void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode)
        }
 }
 
-static void intel_dp_prepare(struct drm_encoder *encoder)
+static bool intel_dp_get_hw_state(struct intel_encoder *encoder,
+                                 enum pipe *pipe)
 {
-       struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+       struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 tmp = I915_READ(intel_dp->output_reg);
 
+       if (!(tmp & DP_PORT_EN))
+               return false;
+
+       if (is_cpu_edp(intel_dp) && IS_GEN7(dev)) {
+               *pipe = PORT_TO_PIPE_CPT(tmp);
+       } else if (!HAS_PCH_CPT(dev) || is_cpu_edp(intel_dp)) {
+               *pipe = PORT_TO_PIPE(tmp);
+       } else {
+               u32 trans_sel;
+               u32 trans_dp;
+               int i;
+
+               switch (intel_dp->output_reg) {
+               case PCH_DP_B:
+                       trans_sel = TRANS_DP_PORT_SEL_B;
+                       break;
+               case PCH_DP_C:
+                       trans_sel = TRANS_DP_PORT_SEL_C;
+                       break;
+               case PCH_DP_D:
+                       trans_sel = TRANS_DP_PORT_SEL_D;
+                       break;
+               default:
+                       return true;
+               }
+
+               for_each_pipe(i) {
+                       trans_dp = I915_READ(TRANS_DP_CTL(i));
+                       if ((trans_dp & TRANS_DP_PORT_SEL_MASK) == trans_sel) {
+                               *pipe = i;
+                               return true;
+                       }
+               }
+       }
+
+       DRM_DEBUG_KMS("No pipe for dp port 0x%x found\n", intel_dp->output_reg);
+
+       return true;
+}
+
+static void intel_disable_dp(struct intel_encoder *encoder)
+{
+       struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
 
        /* Make sure the panel is off before trying to change the mode. But also
         * ensure that we have vdd while we switch off the panel. */
@@ -1293,14 +1315,31 @@ static void intel_dp_prepare(struct drm_encoder *encoder)
        ironlake_edp_backlight_off(intel_dp);
        intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
        ironlake_edp_panel_off(intel_dp);
-       intel_dp_link_down(intel_dp);
+
+       /* cpu edp my only be disable _after_ the cpu pipe/plane is disabled. */
+       if (!is_cpu_edp(intel_dp))
+               intel_dp_link_down(intel_dp);
 }
 
-static void intel_dp_commit(struct drm_encoder *encoder)
+static void intel_post_disable_dp(struct intel_encoder *encoder)
 {
-       struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
-       struct drm_device *dev = encoder->dev;
-       struct intel_crtc *intel_crtc = to_intel_crtc(intel_dp->base.base.crtc);
+       struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
+
+       if (is_cpu_edp(intel_dp)) {
+               intel_dp_link_down(intel_dp);
+               ironlake_edp_pll_off(intel_dp);
+       }
+}
+
+static void intel_enable_dp(struct intel_encoder *encoder)
+{
+       struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t dp_reg = I915_READ(intel_dp->output_reg);
+
+       if (WARN_ON(dp_reg & DP_PORT_EN))
+               return;
 
        ironlake_edp_panel_vdd_on(intel_dp);
        intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
@@ -1309,47 +1348,14 @@ static void intel_dp_commit(struct drm_encoder *encoder)
        ironlake_edp_panel_vdd_off(intel_dp, true);
        intel_dp_complete_link_train(intel_dp);
        ironlake_edp_backlight_on(intel_dp);
-
-       intel_dp->dpms_mode = DRM_MODE_DPMS_ON;
-
-       if (HAS_PCH_CPT(dev))
-               intel_cpt_verify_modeset(dev, intel_crtc->pipe);
 }
 
-static void
-intel_dp_dpms(struct drm_encoder *encoder, int mode)
+static void intel_pre_enable_dp(struct intel_encoder *encoder)
 {
-       struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
-       struct drm_device *dev = encoder->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       uint32_t dp_reg = I915_READ(intel_dp->output_reg);
+       struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
 
-       if (mode != DRM_MODE_DPMS_ON) {
-               /* Switching the panel off requires vdd. */
-               ironlake_edp_panel_vdd_on(intel_dp);
-               ironlake_edp_backlight_off(intel_dp);
-               intel_dp_sink_dpms(intel_dp, mode);
-               ironlake_edp_panel_off(intel_dp);
-               intel_dp_link_down(intel_dp);
-
-               if (is_cpu_edp(intel_dp))
-                       ironlake_edp_pll_off(encoder);
-       } else {
-               if (is_cpu_edp(intel_dp))
-                       ironlake_edp_pll_on(encoder);
-
-               ironlake_edp_panel_vdd_on(intel_dp);
-               intel_dp_sink_dpms(intel_dp, mode);
-               if (!(dp_reg & DP_PORT_EN)) {
-                       intel_dp_start_link_train(intel_dp);
-                       ironlake_edp_panel_on(intel_dp);
-                       ironlake_edp_panel_vdd_off(intel_dp, true);
-                       intel_dp_complete_link_train(intel_dp);
-               } else
-                       ironlake_edp_panel_vdd_off(intel_dp, false);
-               ironlake_edp_backlight_on(intel_dp);
-       }
-       intel_dp->dpms_mode = mode;
+       if (is_cpu_edp(intel_dp))
+               ironlake_edp_pll_on(intel_dp);
 }
 
 /*
@@ -1668,6 +1674,45 @@ intel_dp_set_link_train(struct intel_dp *intel_dp,
        struct drm_i915_private *dev_priv = dev->dev_private;
        int ret;
 
+       if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) {
+               dp_reg_value &= ~DP_LINK_TRAIN_MASK_CPT;
+
+               switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) {
+               case DP_TRAINING_PATTERN_DISABLE:
+                       dp_reg_value |= DP_LINK_TRAIN_OFF_CPT;
+                       break;
+               case DP_TRAINING_PATTERN_1:
+                       dp_reg_value |= DP_LINK_TRAIN_PAT_1_CPT;
+                       break;
+               case DP_TRAINING_PATTERN_2:
+                       dp_reg_value |= DP_LINK_TRAIN_PAT_2_CPT;
+                       break;
+               case DP_TRAINING_PATTERN_3:
+                       DRM_ERROR("DP training pattern 3 not supported\n");
+                       dp_reg_value |= DP_LINK_TRAIN_PAT_2_CPT;
+                       break;
+               }
+
+       } else {
+               dp_reg_value &= ~DP_LINK_TRAIN_MASK;
+
+               switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) {
+               case DP_TRAINING_PATTERN_DISABLE:
+                       dp_reg_value |= DP_LINK_TRAIN_OFF;
+                       break;
+               case DP_TRAINING_PATTERN_1:
+                       dp_reg_value |= DP_LINK_TRAIN_PAT_1;
+                       break;
+               case DP_TRAINING_PATTERN_2:
+                       dp_reg_value |= DP_LINK_TRAIN_PAT_2;
+                       break;
+               case DP_TRAINING_PATTERN_3:
+                       DRM_ERROR("DP training pattern 3 not supported\n");
+                       dp_reg_value |= DP_LINK_TRAIN_PAT_2;
+                       break;
+               }
+       }
+
        I915_WRITE(intel_dp->output_reg, dp_reg_value);
        POSTING_READ(intel_dp->output_reg);
 
@@ -1675,12 +1720,15 @@ intel_dp_set_link_train(struct intel_dp *intel_dp,
                                    DP_TRAINING_PATTERN_SET,
                                    dp_train_pat);
 
-       ret = intel_dp_aux_native_write(intel_dp,
-                                       DP_TRAINING_LANE0_SET,
-                                       intel_dp->train_set,
-                                       intel_dp->lane_count);
-       if (ret != intel_dp->lane_count)
-               return false;
+       if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) !=
+           DP_TRAINING_PATTERN_DISABLE) {
+               ret = intel_dp_aux_native_write(intel_dp,
+                                               DP_TRAINING_LANE0_SET,
+                                               intel_dp->train_set,
+                                               intel_dp->lane_count);
+               if (ret != intel_dp->lane_count)
+                       return false;
+       }
 
        return true;
 }
@@ -1690,26 +1738,12 @@ static void
 intel_dp_start_link_train(struct intel_dp *intel_dp)
 {
        struct drm_device *dev = intel_dp->base.base.dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_crtc *intel_crtc = to_intel_crtc(intel_dp->base.base.crtc);
        int i;
        uint8_t voltage;
        bool clock_recovery = false;
        int voltage_tries, loop_tries;
-       u32 reg;
        uint32_t DP = intel_dp->DP;
 
-       /*
-        * On CPT we have to enable the port in training pattern 1, which
-        * will happen below in intel_dp_set_link_train.  Otherwise, enable
-        * the port and wait for it to become active.
-        */
-       if (!HAS_PCH_CPT(dev)) {
-               I915_WRITE(intel_dp->output_reg, intel_dp->DP);
-               POSTING_READ(intel_dp->output_reg);
-               intel_wait_for_vblank(dev, intel_crtc->pipe);
-       }
-
        /* Write the link configuration data */
        intel_dp_aux_native_write(intel_dp, DP_LINK_BW_SET,
                                  intel_dp->link_configuration,
@@ -1717,10 +1751,6 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)
 
        DP |= DP_PORT_EN;
 
-       if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp)))
-               DP &= ~DP_LINK_TRAIN_MASK_CPT;
-       else
-               DP &= ~DP_LINK_TRAIN_MASK;
        memset(intel_dp->train_set, 0, 4);
        voltage = 0xff;
        voltage_tries = 0;
@@ -1744,12 +1774,7 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)
                        DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels;
                }
 
-               if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp)))
-                       reg = DP | DP_LINK_TRAIN_PAT_1_CPT;
-               else
-                       reg = DP | DP_LINK_TRAIN_PAT_1;
-
-               if (!intel_dp_set_link_train(intel_dp, reg,
+               if (!intel_dp_set_link_train(intel_dp, DP,
                                             DP_TRAINING_PATTERN_1 |
                                             DP_LINK_SCRAMBLING_DISABLE))
                        break;
@@ -1804,10 +1829,8 @@ static void
 intel_dp_complete_link_train(struct intel_dp *intel_dp)
 {
        struct drm_device *dev = intel_dp->base.base.dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
        bool channel_eq = false;
        int tries, cr_tries;
-       u32 reg;
        uint32_t DP = intel_dp->DP;
 
        /* channel equalization */
@@ -1836,13 +1859,8 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
                        DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels;
                }
 
-               if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp)))
-                       reg = DP | DP_LINK_TRAIN_PAT_2_CPT;
-               else
-                       reg = DP | DP_LINK_TRAIN_PAT_2;
-
                /* channel eq pattern */
-               if (!intel_dp_set_link_train(intel_dp, reg,
+               if (!intel_dp_set_link_train(intel_dp, DP,
                                             DP_TRAINING_PATTERN_2 |
                                             DP_LINK_SCRAMBLING_DISABLE))
                        break;
@@ -1877,15 +1895,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
                ++tries;
        }
 
-       if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp)))
-               reg = DP | DP_LINK_TRAIN_OFF_CPT;
-       else
-               reg = DP | DP_LINK_TRAIN_OFF;
-
-       I915_WRITE(intel_dp->output_reg, reg);
-       POSTING_READ(intel_dp->output_reg);
-       intel_dp_aux_native_write_1(intel_dp,
-                                   DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE);
+       intel_dp_set_link_train(intel_dp, DP, DP_TRAINING_PATTERN_DISABLE);
 }
 
 static void
@@ -1895,18 +1905,11 @@ intel_dp_link_down(struct intel_dp *intel_dp)
        struct drm_i915_private *dev_priv = dev->dev_private;
        uint32_t DP = intel_dp->DP;
 
-       if ((I915_READ(intel_dp->output_reg) & DP_PORT_EN) == 0)
+       if (WARN_ON((I915_READ(intel_dp->output_reg) & DP_PORT_EN) == 0))
                return;
 
        DRM_DEBUG_KMS("\n");
 
-       if (is_edp(intel_dp)) {
-               DP &= ~DP_PLL_ENABLE;
-               I915_WRITE(intel_dp->output_reg, DP);
-               POSTING_READ(intel_dp->output_reg);
-               udelay(100);
-       }
-
        if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) {
                DP &= ~DP_LINK_TRAIN_MASK_CPT;
                I915_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE_CPT);
@@ -1918,13 +1921,6 @@ intel_dp_link_down(struct intel_dp *intel_dp)
 
        msleep(17);
 
-       if (is_edp(intel_dp)) {
-               if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp)))
-                       DP |= DP_LINK_TRAIN_OFF_CPT;
-               else
-                       DP |= DP_LINK_TRAIN_OFF;
-       }
-
        if (HAS_PCH_IBX(dev) &&
            I915_READ(intel_dp->output_reg) & DP_PIPEB_SELECT) {
                struct drm_crtc *crtc = intel_dp->base.base.crtc;
@@ -2033,10 +2029,10 @@ intel_dp_check_link_status(struct intel_dp *intel_dp)
        u8 sink_irq_vector;
        u8 link_status[DP_LINK_STATUS_SIZE];
 
-       if (intel_dp->dpms_mode != DRM_MODE_DPMS_ON)
+       if (!intel_dp->base.connectors_active)
                return;
 
-       if (!intel_dp->base.base.crtc)
+       if (WARN_ON(!intel_dp->base.base.crtc))
                return;
 
        /* Try to read receiver status if the link appears to be up */
@@ -2160,7 +2156,6 @@ intel_dp_get_edid_modes(struct drm_connector *connector, struct i2c_adapter *ada
                ret = drm_add_edid_modes(connector, intel_dp->edid);
                drm_edid_to_eld(connector,
                                intel_dp->edid);
-               connector->display_info.raw_edid = NULL;
                return intel_dp->edid_mode_count;
        }
 
@@ -2206,7 +2201,6 @@ intel_dp_detect(struct drm_connector *connector, bool force)
                edid = intel_dp_get_edid(connector, &intel_dp->adapter);
                if (edid) {
                        intel_dp->has_audio = drm_detect_monitor_audio(edid);
-                       connector->display_info.raw_edid = NULL;
                        kfree(edid);
                }
        }
@@ -2271,8 +2265,6 @@ intel_dp_detect_audio(struct drm_connector *connector)
        edid = intel_dp_get_edid(connector, &intel_dp->adapter);
        if (edid) {
                has_audio = drm_detect_monitor_audio(edid);
-
-               connector->display_info.raw_edid = NULL;
                kfree(edid);
        }
 
@@ -2326,9 +2318,8 @@ intel_dp_set_property(struct drm_connector *connector,
 done:
        if (intel_dp->base.base.crtc) {
                struct drm_crtc *crtc = intel_dp->base.base.crtc;
-               drm_crtc_helper_set_mode(crtc, &crtc->mode,
-                                        crtc->x, crtc->y,
-                                        crtc->fb);
+               intel_set_mode(crtc, &crtc->mode,
+                              crtc->x, crtc->y, crtc->fb);
        }
 
        return 0;
@@ -2362,15 +2353,13 @@ static void intel_dp_encoder_destroy(struct drm_encoder *encoder)
 }
 
 static const struct drm_encoder_helper_funcs intel_dp_helper_funcs = {
-       .dpms = intel_dp_dpms,
        .mode_fixup = intel_dp_mode_fixup,
-       .prepare = intel_dp_prepare,
        .mode_set = intel_dp_mode_set,
-       .commit = intel_dp_commit,
+       .disable = intel_encoder_noop,
 };
 
 static const struct drm_connector_funcs intel_dp_connector_funcs = {
-       .dpms = drm_helper_connector_dpms,
+       .dpms = intel_connector_dpms,
        .detect = intel_dp_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .set_property = intel_dp_set_property,
@@ -2441,7 +2430,7 @@ intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connect
 }
 
 void
-intel_dp_init(struct drm_device *dev, int output_reg)
+intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_connector *connector;
@@ -2456,7 +2445,9 @@ intel_dp_init(struct drm_device *dev, int output_reg)
                return;
 
        intel_dp->output_reg = output_reg;
-       intel_dp->dpms_mode = -1;
+       intel_dp->port = port;
+       /* Preserve the current hw state. */
+       intel_dp->DP = I915_READ(intel_dp->output_reg);
 
        intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL);
        if (!intel_connector) {
@@ -2483,18 +2474,10 @@ intel_dp_init(struct drm_device *dev, int output_reg)
 
        connector->polled = DRM_CONNECTOR_POLL_HPD;
 
-       if (output_reg == DP_B || output_reg == PCH_DP_B)
-               intel_encoder->clone_mask = (1 << INTEL_DP_B_CLONE_BIT);
-       else if (output_reg == DP_C || output_reg == PCH_DP_C)
-               intel_encoder->clone_mask = (1 << INTEL_DP_C_CLONE_BIT);
-       else if (output_reg == DP_D || output_reg == PCH_DP_D)
-               intel_encoder->clone_mask = (1 << INTEL_DP_D_CLONE_BIT);
+       intel_encoder->cloneable = false;
 
-       if (is_edp(intel_dp)) {
-               intel_encoder->clone_mask = (1 << INTEL_EDP_CLONE_BIT);
-               INIT_DELAYED_WORK(&intel_dp->panel_vdd_work,
-                                 ironlake_panel_vdd_work);
-       }
+       INIT_DELAYED_WORK(&intel_dp->panel_vdd_work,
+                         ironlake_panel_vdd_work);
 
        intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
 
@@ -2508,29 +2491,33 @@ intel_dp_init(struct drm_device *dev, int output_reg)
        intel_connector_attach_encoder(intel_connector, intel_encoder);
        drm_sysfs_connector_add(connector);
 
+       intel_encoder->enable = intel_enable_dp;
+       intel_encoder->pre_enable = intel_pre_enable_dp;
+       intel_encoder->disable = intel_disable_dp;
+       intel_encoder->post_disable = intel_post_disable_dp;
+       intel_encoder->get_hw_state = intel_dp_get_hw_state;
+       intel_connector->get_hw_state = intel_connector_get_hw_state;
+
        /* Set up the DDC bus. */
-       switch (output_reg) {
-               case DP_A:
-                       name = "DPDDC-A";
-                       break;
-               case DP_B:
-               case PCH_DP_B:
-                       dev_priv->hotplug_supported_mask |=
-                               DPB_HOTPLUG_INT_STATUS;
-                       name = "DPDDC-B";
-                       break;
-               case DP_C:
-               case PCH_DP_C:
-                       dev_priv->hotplug_supported_mask |=
-                               DPC_HOTPLUG_INT_STATUS;
-                       name = "DPDDC-C";
-                       break;
-               case DP_D:
-               case PCH_DP_D:
-                       dev_priv->hotplug_supported_mask |=
-                               DPD_HOTPLUG_INT_STATUS;
-                       name = "DPDDC-D";
-                       break;
+       switch (port) {
+       case PORT_A:
+               name = "DPDDC-A";
+               break;
+       case PORT_B:
+               dev_priv->hotplug_supported_mask |= DPB_HOTPLUG_INT_STATUS;
+               name = "DPDDC-B";
+               break;
+       case PORT_C:
+               dev_priv->hotplug_supported_mask |= DPC_HOTPLUG_INT_STATUS;
+               name = "DPDDC-C";
+               break;
+       case PORT_D:
+               dev_priv->hotplug_supported_mask |= DPD_HOTPLUG_INT_STATUS;
+               name = "DPDDC-D";
+               break;
+       default:
+               WARN(1, "Invalid port %c\n", port_name(port));
+               break;
        }
 
        /* Cache some DPCD data in the eDP case */
index cd54cf88a28fe408519a6bd14e8f0b71659fd498..351fd7179cd723bdd261449e3d4965340722adea 100644 (file)
@@ -31,6 +31,7 @@
 #include "drm_crtc.h"
 #include "drm_crtc_helper.h"
 #include "drm_fb_helper.h"
+#include "drm_dp_helper.h"
 
 #define _wait_for(COND, MS, W) ({ \
        unsigned long timeout__ = jiffies + msecs_to_jiffies(MS);       \
                        ret__ = -ETIMEDOUT;                             \
                        break;                                          \
                }                                                       \
-               if (W && drm_can_sleep()) msleep(W);    \
+               if (W && drm_can_sleep())  {                            \
+                       msleep(W);                                      \
+               } else {                                                \
+                       cpu_relax();                                    \
+               }                                                       \
        }                                                               \
        ret__;                                                          \
 })
 #define INTEL_OUTPUT_DISPLAYPORT 7
 #define INTEL_OUTPUT_EDP 8
 
-/* Intel Pipe Clone Bit */
-#define INTEL_HDMIB_CLONE_BIT 1
-#define INTEL_HDMIC_CLONE_BIT 2
-#define INTEL_HDMID_CLONE_BIT 3
-#define INTEL_HDMIE_CLONE_BIT 4
-#define INTEL_HDMIF_CLONE_BIT 5
-#define INTEL_SDVO_NON_TV_CLONE_BIT 6
-#define INTEL_SDVO_TV_CLONE_BIT 7
-#define INTEL_SDVO_LVDS_CLONE_BIT 8
-#define INTEL_ANALOG_CLONE_BIT 9
-#define INTEL_TV_CLONE_BIT 10
-#define INTEL_DP_B_CLONE_BIT 11
-#define INTEL_DP_C_CLONE_BIT 12
-#define INTEL_DP_D_CLONE_BIT 13
-#define INTEL_LVDS_CLONE_BIT 14
-#define INTEL_DVO_TMDS_CLONE_BIT 15
-#define INTEL_DVO_LVDS_CLONE_BIT 16
-#define INTEL_EDP_CLONE_BIT 17
-
 #define INTEL_DVO_CHIP_NONE 0
 #define INTEL_DVO_CHIP_LVDS 1
 #define INTEL_DVO_CHIP_TMDS 2
@@ -151,16 +137,48 @@ struct intel_fbdev {
 
 struct intel_encoder {
        struct drm_encoder base;
+       /*
+        * The new crtc this encoder will be driven from. Only differs from
+        * base->crtc while a modeset is in progress.
+        */
+       struct intel_crtc *new_crtc;
+
        int type;
        bool needs_tv_clock;
+       /*
+        * Intel hw has only one MUX where encoders could be clone, hence a
+        * simple flag is enough to compute the possible_clones mask.
+        */
+       bool cloneable;
+       bool connectors_active;
        void (*hot_plug)(struct intel_encoder *);
+       void (*pre_enable)(struct intel_encoder *);
+       void (*enable)(struct intel_encoder *);
+       void (*disable)(struct intel_encoder *);
+       void (*post_disable)(struct intel_encoder *);
+       /* Read out the current hw state of this connector, returning true if
+        * the encoder is active. If the encoder is enabled it also set the pipe
+        * it is connected to in the pipe parameter. */
+       bool (*get_hw_state)(struct intel_encoder *, enum pipe *pipe);
        int crtc_mask;
-       int clone_mask;
 };
 
 struct intel_connector {
        struct drm_connector base;
+       /*
+        * The fixed encoder this connector is connected to.
+        */
        struct intel_encoder *encoder;
+
+       /*
+        * The new encoder this connector will be driven. Only differs from
+        * encoder while a modeset is in progress.
+        */
+       struct intel_encoder *new_encoder;
+
+       /* Reads out the current hw, returning true if the connector is enabled
+        * and active (i.e. dpms ON state). */
+       bool (*get_hw_state)(struct intel_connector *);
 };
 
 struct intel_crtc {
@@ -168,11 +186,13 @@ struct intel_crtc {
        enum pipe pipe;
        enum plane plane;
        u8 lut_r[256], lut_g[256], lut_b[256];
-       int dpms_mode;
-       bool active; /* is the crtc on? independent of the dpms mode */
+       /*
+        * Whether the crtc and the connected output pipeline is active. Implies
+        * that crtc->enabled is set, i.e. the current mode configuration has
+        * some outputs connected to this crtc.
+        */
+       bool active;
        bool primary_disabled; /* is the crtc obscured by a plane? */
-       bool busy; /* is scanout buffer being updated frequently? */
-       struct timer_list idle_timer;
        bool lowfreq_avail;
        struct intel_overlay *overlay;
        struct intel_unpin_work *unpin_work;
@@ -311,6 +331,37 @@ struct intel_hdmi {
                               struct drm_display_mode *adjusted_mode);
 };
 
+#define DP_RECEIVER_CAP_SIZE           0xf
+#define DP_LINK_CONFIGURATION_SIZE     9
+
+struct intel_dp {
+       struct intel_encoder base;
+       uint32_t output_reg;
+       uint32_t DP;
+       uint8_t  link_configuration[DP_LINK_CONFIGURATION_SIZE];
+       bool has_audio;
+       enum hdmi_force_audio force_audio;
+       enum port port;
+       uint32_t color_range;
+       uint8_t link_bw;
+       uint8_t lane_count;
+       uint8_t dpcd[DP_RECEIVER_CAP_SIZE];
+       struct i2c_adapter adapter;
+       struct i2c_algo_dp_aux_data algo;
+       bool is_pch_edp;
+       uint8_t train_set[4];
+       int panel_power_up_delay;
+       int panel_power_down_delay;
+       int panel_power_cycle_delay;
+       int backlight_on_delay;
+       int backlight_off_delay;
+       struct drm_display_mode *panel_fixed_mode;  /* for eDP */
+       struct delayed_work panel_vdd_work;
+       bool want_panel_vdd;
+       struct edid *edid; /* cached EDID for eDP */
+       int edid_mode_count;
+};
+
 static inline struct drm_crtc *
 intel_get_crtc_for_pipe(struct drm_device *dev, int pipe)
 {
@@ -350,17 +401,21 @@ extern void intel_attach_force_audio_property(struct drm_connector *connector);
 extern void intel_attach_broadcast_rgb_property(struct drm_connector *connector);
 
 extern void intel_crt_init(struct drm_device *dev);
-extern void intel_hdmi_init(struct drm_device *dev, int sdvox_reg);
+extern void intel_hdmi_init(struct drm_device *dev,
+                           int sdvox_reg, enum port port);
 extern struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder);
 extern void intel_dip_infoframe_csum(struct dip_infoframe *avi_if);
 extern bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg,
                            bool is_sdvob);
 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,
-                           struct drm_i915_gem_object *obj);
+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 bool intel_lvds_init(struct drm_device *dev);
-extern void intel_dp_init(struct drm_device *dev, int dp_reg);
+extern void intel_dp_init(struct drm_device *dev, int output_reg,
+                         enum port port);
 void
 intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
                 struct drm_display_mode *adjusted_mode);
@@ -373,8 +428,6 @@ extern int intel_plane_init(struct drm_device *dev, enum pipe pipe);
 extern void intel_flush_display_plane(struct drm_i915_private *dev_priv,
                                      enum plane plane);
 
-void intel_sanitize_pm(struct drm_device *dev);
-
 /* intel_panel.c */
 extern void intel_fixed_panel_mode(struct drm_display_mode *fixed_mode,
                                   struct drm_display_mode *adjusted_mode);
@@ -391,10 +444,27 @@ extern void intel_panel_disable_backlight(struct drm_device *dev);
 extern void intel_panel_destroy_backlight(struct drm_device *dev);
 extern enum drm_connector_status intel_panel_detect(struct drm_device *dev);
 
+struct intel_set_config {
+       struct drm_encoder **save_connector_encoders;
+       struct drm_crtc **save_encoder_crtcs;
+
+       bool fb_changed;
+       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 void intel_modeset_disable(struct drm_device *dev);
 extern void intel_crtc_load_lut(struct drm_crtc *crtc);
-extern void intel_encoder_prepare(struct drm_encoder *encoder);
-extern void intel_encoder_commit(struct drm_encoder *encoder);
+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);
+extern void intel_connector_dpms(struct drm_connector *, int mode);
+extern bool intel_connector_get_hw_state(struct intel_connector *connector);
+extern void intel_modeset_check_state(struct drm_device *dev);
+
 
 static inline struct intel_encoder *intel_attached_encoder(struct drm_connector *connector)
 {
@@ -417,12 +487,10 @@ struct intel_load_detect_pipe {
        bool load_detect_temp;
        int dpms_mode;
 };
-extern bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
-                                      struct drm_connector *connector,
+extern bool intel_get_load_detect_pipe(struct drm_connector *connector,
                                       struct drm_display_mode *mode,
                                       struct intel_load_detect_pipe *old);
-extern void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder,
-                                          struct drm_connector *connector,
+extern void intel_release_load_detect_pipe(struct drm_connector *connector,
                                           struct intel_load_detect_pipe *old);
 
 extern void intelfb_restore(void);
@@ -503,7 +571,10 @@ extern void intel_disable_gt_powersave(struct drm_device *dev);
 extern void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv);
 extern void ironlake_teardown_rc6(struct drm_device *dev);
 
-extern void intel_ddi_dpms(struct drm_encoder *encoder, int mode);
+extern void intel_enable_ddi(struct intel_encoder *encoder);
+extern void intel_disable_ddi(struct intel_encoder *encoder);
+extern bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
+                                  enum pipe *pipe);
 extern void intel_ddi_mode_set(struct drm_encoder *encoder,
                                struct drm_display_mode *mode,
                                struct drm_display_mode *adjusted_mode);
index 36c542e5036b915e40eb2444608835ade5458027..4f1fdcc4400588929b36e33f7cc4b8f4732b0031 100644 (file)
@@ -37,6 +37,7 @@
 #define SIL164_ADDR    0x38
 #define CH7xxx_ADDR    0x76
 #define TFP410_ADDR    0x38
+#define NS2501_ADDR     0x38
 
 static const struct intel_dvo_device intel_dvo_devices[] = {
        {
@@ -74,7 +75,14 @@ static const struct intel_dvo_device intel_dvo_devices[] = {
                .slave_addr = 0x75,
                .gpio = GMBUS_PORT_DPB,
                .dev_ops = &ch7017_ops,
-       }
+       },
+       {
+               .type = INTEL_DVO_CHIP_TMDS,
+               .name = "ns2501",
+               .dvo_reg = DVOC,
+               .slave_addr = NS2501_ADDR,
+               .dev_ops = &ns2501_ops,
+       }
 };
 
 struct intel_dvo {
@@ -97,22 +105,91 @@ static struct intel_dvo *intel_attached_dvo(struct drm_connector *connector)
                            struct intel_dvo, base);
 }
 
-static void intel_dvo_dpms(struct drm_encoder *encoder, int mode)
+static bool intel_dvo_connector_get_hw_state(struct intel_connector *connector)
 {
-       struct drm_i915_private *dev_priv = encoder->dev->dev_private;
-       struct intel_dvo *intel_dvo = enc_to_intel_dvo(encoder);
+       struct intel_dvo *intel_dvo = intel_attached_dvo(&connector->base);
+
+       return intel_dvo->dev.dev_ops->get_hw_state(&intel_dvo->dev);
+}
+
+static bool intel_dvo_get_hw_state(struct intel_encoder *encoder,
+                                  enum pipe *pipe)
+{
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_dvo *intel_dvo = enc_to_intel_dvo(&encoder->base);
+       u32 tmp;
+
+       tmp = I915_READ(intel_dvo->dev.dvo_reg);
+
+       if (!(tmp & DVO_ENABLE))
+               return false;
+
+       *pipe = PORT_TO_PIPE(tmp);
+
+       return true;
+}
+
+static void intel_disable_dvo(struct intel_encoder *encoder)
+{
+       struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+       struct intel_dvo *intel_dvo = enc_to_intel_dvo(&encoder->base);
+       u32 dvo_reg = intel_dvo->dev.dvo_reg;
+       u32 temp = I915_READ(dvo_reg);
+
+       intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, false);
+       I915_WRITE(dvo_reg, temp & ~DVO_ENABLE);
+       I915_READ(dvo_reg);
+}
+
+static void intel_enable_dvo(struct intel_encoder *encoder)
+{
+       struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+       struct intel_dvo *intel_dvo = enc_to_intel_dvo(&encoder->base);
        u32 dvo_reg = intel_dvo->dev.dvo_reg;
        u32 temp = I915_READ(dvo_reg);
 
+       I915_WRITE(dvo_reg, temp | DVO_ENABLE);
+       I915_READ(dvo_reg);
+       intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true);
+}
+
+static void intel_dvo_dpms(struct drm_connector *connector, int mode)
+{
+       struct intel_dvo *intel_dvo = intel_attached_dvo(connector);
+       struct drm_crtc *crtc;
+
+       /* dvo supports only 2 dpms states. */
+       if (mode != DRM_MODE_DPMS_ON)
+               mode = DRM_MODE_DPMS_OFF;
+
+       if (mode == connector->dpms)
+               return;
+
+       connector->dpms = mode;
+
+       /* Only need to change hw state when actually enabled */
+       crtc = intel_dvo->base.base.crtc;
+       if (!crtc) {
+               intel_dvo->base.connectors_active = false;
+               return;
+       }
+
        if (mode == DRM_MODE_DPMS_ON) {
-               I915_WRITE(dvo_reg, temp | DVO_ENABLE);
-               I915_READ(dvo_reg);
-               intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, mode);
+               intel_dvo->base.connectors_active = true;
+
+               intel_crtc_update_dpms(crtc);
+
+               intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true);
        } else {
-               intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, mode);
-               I915_WRITE(dvo_reg, temp & ~DVO_ENABLE);
-               I915_READ(dvo_reg);
+               intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, false);
+
+               intel_dvo->base.connectors_active = false;
+
+               intel_crtc_update_dpms(crtc);
        }
+
+       intel_modeset_check_state(connector->dev);
 }
 
 static int intel_dvo_mode_valid(struct drm_connector *connector,
@@ -267,15 +344,13 @@ static void intel_dvo_destroy(struct drm_connector *connector)
 }
 
 static const struct drm_encoder_helper_funcs intel_dvo_helper_funcs = {
-       .dpms = intel_dvo_dpms,
        .mode_fixup = intel_dvo_mode_fixup,
-       .prepare = intel_encoder_prepare,
        .mode_set = intel_dvo_mode_set,
-       .commit = intel_encoder_commit,
+       .disable = intel_encoder_noop,
 };
 
 static const struct drm_connector_funcs intel_dvo_connector_funcs = {
-       .dpms = drm_helper_connector_dpms,
+       .dpms = intel_dvo_dpms,
        .detect = intel_dvo_detect,
        .destroy = intel_dvo_destroy,
        .fill_modes = drm_helper_probe_single_connector_modes,
@@ -364,6 +439,11 @@ void intel_dvo_init(struct drm_device *dev)
        drm_encoder_init(dev, &intel_encoder->base,
                         &intel_dvo_enc_funcs, encoder_type);
 
+       intel_encoder->disable = intel_disable_dvo;
+       intel_encoder->enable = intel_enable_dvo;
+       intel_encoder->get_hw_state = intel_dvo_get_hw_state;
+       intel_connector->get_hw_state = intel_dvo_connector_get_hw_state;
+
        /* Now, try to find a controller */
        for (i = 0; i < ARRAY_SIZE(intel_dvo_devices); i++) {
                struct drm_connector *connector = &intel_connector->base;
@@ -396,17 +476,14 @@ void intel_dvo_init(struct drm_device *dev)
                intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
                switch (dvo->type) {
                case INTEL_DVO_CHIP_TMDS:
-                       intel_encoder->clone_mask =
-                               (1 << INTEL_DVO_TMDS_CLONE_BIT) |
-                               (1 << INTEL_ANALOG_CLONE_BIT);
+                       intel_encoder->cloneable = true;
                        drm_connector_init(dev, connector,
                                           &intel_dvo_connector_funcs,
                                           DRM_MODE_CONNECTOR_DVII);
                        encoder_type = DRM_MODE_ENCODER_TMDS;
                        break;
                case INTEL_DVO_CHIP_LVDS:
-                       intel_encoder->clone_mask =
-                               (1 << INTEL_DVO_LVDS_CLONE_BIT);
+                       intel_encoder->cloneable = false;
                        drm_connector_init(dev, connector,
                                           &intel_dvo_connector_funcs,
                                           DRM_MODE_CONNECTOR_LVDS);
index 98f602427eb8cb8fe9a8cc975ff1b4128ce68ae6..08f2b63d740ad0b66535b756e2e03482aa4d1b18 100644 (file)
@@ -151,6 +151,9 @@ static void g4x_write_infoframe(struct drm_encoder *encoder,
                I915_WRITE(VIDEO_DIP_DATA, *data);
                data++;
        }
+       /* Write every possible data byte to force correct ECC calculation. */
+       for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
+               I915_WRITE(VIDEO_DIP_DATA, 0);
        mmiowb();
 
        val |= g4x_infoframe_enable(frame);
@@ -186,6 +189,9 @@ static void ibx_write_infoframe(struct drm_encoder *encoder,
                I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
                data++;
        }
+       /* Write every possible data byte to force correct ECC calculation. */
+       for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
+               I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), 0);
        mmiowb();
 
        val |= g4x_infoframe_enable(frame);
@@ -224,6 +230,9 @@ static void cpt_write_infoframe(struct drm_encoder *encoder,
                I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
                data++;
        }
+       /* Write every possible data byte to force correct ECC calculation. */
+       for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
+               I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), 0);
        mmiowb();
 
        val |= g4x_infoframe_enable(frame);
@@ -259,6 +268,9 @@ static void vlv_write_infoframe(struct drm_encoder *encoder,
                I915_WRITE(VLV_TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
                data++;
        }
+       /* Write every possible data byte to force correct ECC calculation. */
+       for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
+               I915_WRITE(VLV_TVIDEO_DIP_DATA(intel_crtc->pipe), 0);
        mmiowb();
 
        val |= g4x_infoframe_enable(frame);
@@ -292,6 +304,9 @@ static void hsw_write_infoframe(struct drm_encoder *encoder,
                I915_WRITE(data_reg + i, *data);
                data++;
        }
+       /* Write every possible data byte to force correct ECC calculation. */
+       for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
+               I915_WRITE(data_reg + i, 0);
        mmiowb();
 
        val |= hsw_infoframe_enable(frame);
@@ -377,6 +392,7 @@ static void g4x_set_infoframes(struct drm_encoder *encoder,
                port = VIDEO_DIP_PORT_C;
                break;
        default:
+               BUG();
                return;
        }
 
@@ -435,6 +451,7 @@ static void ibx_set_infoframes(struct drm_encoder *encoder,
                port = VIDEO_DIP_PORT_D;
                break;
        default:
+               BUG();
                return;
        }
 
@@ -601,11 +618,32 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
        intel_hdmi->set_infoframes(encoder, adjusted_mode);
 }
 
-static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
+static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder,
+                                   enum pipe *pipe)
 {
-       struct drm_device *dev = encoder->dev;
+       struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
+       u32 tmp;
+
+       tmp = I915_READ(intel_hdmi->sdvox_reg);
+
+       if (!(tmp & SDVO_ENABLE))
+               return false;
+
+       if (HAS_PCH_CPT(dev))
+               *pipe = PORT_TO_PIPE_CPT(tmp);
+       else
+               *pipe = PORT_TO_PIPE(tmp);
+
+       return true;
+}
+
+static void intel_enable_hdmi(struct intel_encoder *encoder)
+{
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
        u32 temp;
        u32 enable_bits = SDVO_ENABLE;
 
@@ -617,31 +655,12 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
        /* HW workaround for IBX, we need to move the port to transcoder A
         * before disabling it. */
        if (HAS_PCH_IBX(dev)) {
-               struct drm_crtc *crtc = encoder->crtc;
+               struct drm_crtc *crtc = encoder->base.crtc;
                int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1;
 
-               if (mode != DRM_MODE_DPMS_ON) {
-                       if (temp & SDVO_PIPE_B_SELECT) {
-                               temp &= ~SDVO_PIPE_B_SELECT;
-                               I915_WRITE(intel_hdmi->sdvox_reg, temp);
-                               POSTING_READ(intel_hdmi->sdvox_reg);
-
-                               /* Again we need to write this twice. */
-                               I915_WRITE(intel_hdmi->sdvox_reg, temp);
-                               POSTING_READ(intel_hdmi->sdvox_reg);
-
-                               /* Transcoder selection bits only update
-                                * effectively on vblank. */
-                               if (crtc)
-                                       intel_wait_for_vblank(dev, pipe);
-                               else
-                                       msleep(50);
-                       }
-               } else {
-                       /* Restore the transcoder select bit. */
-                       if (pipe == PIPE_B)
-                               enable_bits |= SDVO_PIPE_B_SELECT;
-               }
+               /* Restore the transcoder select bit. */
+               if (pipe == PIPE_B)
+                       enable_bits |= SDVO_PIPE_B_SELECT;
        }
 
        /* HW workaround, need to toggle enable bit off and on for 12bpc, but
@@ -652,12 +671,64 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
                POSTING_READ(intel_hdmi->sdvox_reg);
        }
 
-       if (mode != DRM_MODE_DPMS_ON) {
-               temp &= ~enable_bits;
-       } else {
-               temp |= enable_bits;
+       temp |= enable_bits;
+
+       I915_WRITE(intel_hdmi->sdvox_reg, temp);
+       POSTING_READ(intel_hdmi->sdvox_reg);
+
+       /* HW workaround, need to write this twice for issue that may result
+        * in first write getting masked.
+        */
+       if (HAS_PCH_SPLIT(dev)) {
+               I915_WRITE(intel_hdmi->sdvox_reg, temp);
+               POSTING_READ(intel_hdmi->sdvox_reg);
+       }
+}
+
+static void intel_disable_hdmi(struct intel_encoder *encoder)
+{
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
+       u32 temp;
+       u32 enable_bits = SDVO_ENABLE | SDVO_AUDIO_ENABLE;
+
+       temp = I915_READ(intel_hdmi->sdvox_reg);
+
+       /* HW workaround for IBX, we need to move the port to transcoder A
+        * before disabling it. */
+       if (HAS_PCH_IBX(dev)) {
+               struct drm_crtc *crtc = encoder->base.crtc;
+               int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1;
+
+               if (temp & SDVO_PIPE_B_SELECT) {
+                       temp &= ~SDVO_PIPE_B_SELECT;
+                       I915_WRITE(intel_hdmi->sdvox_reg, temp);
+                       POSTING_READ(intel_hdmi->sdvox_reg);
+
+                       /* Again we need to write this twice. */
+                       I915_WRITE(intel_hdmi->sdvox_reg, temp);
+                       POSTING_READ(intel_hdmi->sdvox_reg);
+
+                       /* Transcoder selection bits only update
+                        * effectively on vblank. */
+                       if (crtc)
+                               intel_wait_for_vblank(dev, pipe);
+                       else
+                               msleep(50);
+               }
+       }
+
+       /* HW workaround, need to toggle enable bit off and on for 12bpc, but
+        * we do this anyway which shows more stable in testing.
+        */
+       if (HAS_PCH_SPLIT(dev)) {
+               I915_WRITE(intel_hdmi->sdvox_reg, temp & ~SDVO_ENABLE);
+               POSTING_READ(intel_hdmi->sdvox_reg);
        }
 
+       temp &= ~enable_bits;
+
        I915_WRITE(intel_hdmi->sdvox_reg, temp);
        POSTING_READ(intel_hdmi->sdvox_reg);
 
@@ -737,7 +808,6 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
                                                drm_detect_hdmi_monitor(edid);
                        intel_hdmi->has_audio = drm_detect_monitor_audio(edid);
                }
-               connector->display_info.raw_edid = NULL;
                kfree(edid);
        }
 
@@ -778,8 +848,6 @@ intel_hdmi_detect_audio(struct drm_connector *connector)
        if (edid) {
                if (edid->input & DRM_EDID_INPUT_DIGITAL)
                        has_audio = drm_detect_monitor_audio(edid);
-
-               connector->display_info.raw_edid = NULL;
                kfree(edid);
        }
 
@@ -833,9 +901,8 @@ intel_hdmi_set_property(struct drm_connector *connector,
 done:
        if (intel_hdmi->base.base.crtc) {
                struct drm_crtc *crtc = intel_hdmi->base.base.crtc;
-               drm_crtc_helper_set_mode(crtc, &crtc->mode,
-                                        crtc->x, crtc->y,
-                                        crtc->fb);
+               intel_set_mode(crtc, &crtc->mode,
+                              crtc->x, crtc->y, crtc->fb);
        }
 
        return 0;
@@ -849,23 +916,19 @@ static void intel_hdmi_destroy(struct drm_connector *connector)
 }
 
 static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs_hsw = {
-       .dpms = intel_ddi_dpms,
        .mode_fixup = intel_hdmi_mode_fixup,
-       .prepare = intel_encoder_prepare,
        .mode_set = intel_ddi_mode_set,
-       .commit = intel_encoder_commit,
+       .disable = intel_encoder_noop,
 };
 
 static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = {
-       .dpms = intel_hdmi_dpms,
        .mode_fixup = intel_hdmi_mode_fixup,
-       .prepare = intel_encoder_prepare,
        .mode_set = intel_hdmi_mode_set,
-       .commit = intel_encoder_commit,
+       .disable = intel_encoder_noop,
 };
 
 static const struct drm_connector_funcs intel_hdmi_connector_funcs = {
-       .dpms = drm_helper_connector_dpms,
+       .dpms = intel_connector_dpms,
        .detect = intel_hdmi_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .set_property = intel_hdmi_set_property,
@@ -889,7 +952,7 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *c
        intel_attach_broadcast_rgb_property(connector);
 }
 
-void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
+void intel_hdmi_init(struct drm_device *dev, int sdvox_reg, enum port port)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_connector *connector;
@@ -923,48 +986,25 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
        connector->doublescan_allowed = 0;
        intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
 
-       /* Set up the DDC bus. */
-       if (sdvox_reg == SDVOB) {
-               intel_encoder->clone_mask = (1 << INTEL_HDMIB_CLONE_BIT);
-               intel_hdmi->ddc_bus = GMBUS_PORT_DPB;
-               dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS;
-       } else if (sdvox_reg == SDVOC) {
-               intel_encoder->clone_mask = (1 << INTEL_HDMIC_CLONE_BIT);
-               intel_hdmi->ddc_bus = GMBUS_PORT_DPC;
-               dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS;
-       } else if (sdvox_reg == HDMIB) {
-               intel_encoder->clone_mask = (1 << INTEL_HDMID_CLONE_BIT);
-               intel_hdmi->ddc_bus = GMBUS_PORT_DPB;
-               dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS;
-       } else if (sdvox_reg == HDMIC) {
-               intel_encoder->clone_mask = (1 << INTEL_HDMIE_CLONE_BIT);
-               intel_hdmi->ddc_bus = GMBUS_PORT_DPC;
-               dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS;
-       } else if (sdvox_reg == HDMID) {
-               intel_encoder->clone_mask = (1 << INTEL_HDMIF_CLONE_BIT);
-               intel_hdmi->ddc_bus = GMBUS_PORT_DPD;
-               dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS;
-       } else if (sdvox_reg == DDI_BUF_CTL(PORT_B)) {
-               DRM_DEBUG_DRIVER("LPT: detected output on DDI B\n");
-               intel_encoder->clone_mask = (1 << INTEL_HDMIB_CLONE_BIT);
+       intel_encoder->cloneable = false;
+
+       intel_hdmi->ddi_port = port;
+       switch (port) {
+       case PORT_B:
                intel_hdmi->ddc_bus = GMBUS_PORT_DPB;
-               intel_hdmi->ddi_port = PORT_B;
                dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS;
-       } else if (sdvox_reg == DDI_BUF_CTL(PORT_C)) {
-               DRM_DEBUG_DRIVER("LPT: detected output on DDI C\n");
-               intel_encoder->clone_mask = (1 << INTEL_HDMIC_CLONE_BIT);
+               break;
+       case PORT_C:
                intel_hdmi->ddc_bus = GMBUS_PORT_DPC;
-               intel_hdmi->ddi_port = PORT_C;
                dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS;
-       } else if (sdvox_reg == DDI_BUF_CTL(PORT_D)) {
-               DRM_DEBUG_DRIVER("LPT: detected output on DDI D\n");
-               intel_encoder->clone_mask = (1 << INTEL_HDMID_CLONE_BIT);
+               break;
+       case PORT_D:
                intel_hdmi->ddc_bus = GMBUS_PORT_DPD;
-               intel_hdmi->ddi_port = PORT_D;
                dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS;
-       } else {
-               /* If we got an unknown sdvox_reg, things are pretty much broken
-                * in a way that we should let the kernel know about it */
+               break;
+       case PORT_A:
+               /* Internal port only for eDP. */
+       default:
                BUG();
        }
 
@@ -987,10 +1027,21 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
                intel_hdmi->set_infoframes = cpt_set_infoframes;
        }
 
-       if (IS_HASWELL(dev))
-               drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs_hsw);
-       else
-               drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs);
+       if (IS_HASWELL(dev)) {
+               intel_encoder->enable = intel_enable_ddi;
+               intel_encoder->disable = intel_disable_ddi;
+               intel_encoder->get_hw_state = intel_ddi_get_hw_state;
+               drm_encoder_helper_add(&intel_encoder->base,
+                                      &intel_hdmi_helper_funcs_hsw);
+       } else {
+               intel_encoder->enable = intel_enable_hdmi;
+               intel_encoder->disable = intel_disable_hdmi;
+               intel_encoder->get_hw_state = intel_hdmi_get_hw_state;
+               drm_encoder_helper_add(&intel_encoder->base,
+                                      &intel_hdmi_helper_funcs);
+       }
+       intel_connector->get_hw_state = intel_connector_get_hw_state;
+
 
        intel_hdmi_add_properties(intel_hdmi, connector);
 
index e9a6f6aaed855dfa5acad6026c0f95e79a33955e..40d72bd64e11a869a8fe01eb0b4d36e9f50b719e 100644 (file)
@@ -65,13 +65,40 @@ static struct intel_lvds *intel_attached_lvds(struct drm_connector *connector)
                            struct intel_lvds, base);
 }
 
+static bool intel_lvds_get_hw_state(struct intel_encoder *encoder,
+                                   enum pipe *pipe)
+{
+       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;
+       }
+
+       tmp = I915_READ(lvds_reg);
+
+       if (!(tmp & LVDS_PORT_EN))
+               return false;
+
+       if (HAS_PCH_CPT(dev))
+               *pipe = PORT_TO_PIPE_CPT(tmp);
+       else
+               *pipe = PORT_TO_PIPE(tmp);
+
+       return true;
+}
+
 /**
  * Sets the power state for the panel.
  */
-static void intel_lvds_enable(struct intel_lvds *intel_lvds)
+static void intel_enable_lvds(struct intel_encoder *encoder)
 {
-       struct drm_device *dev = intel_lvds->base.base.dev;
-       struct intel_crtc *intel_crtc = to_intel_crtc(intel_lvds->base.base.crtc);
+       struct drm_device *dev = encoder->base.dev;
+       struct intel_lvds *intel_lvds = to_intel_lvds(&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;
 
@@ -111,9 +138,10 @@ static void intel_lvds_enable(struct intel_lvds *intel_lvds)
        intel_panel_enable_backlight(dev, intel_crtc->pipe);
 }
 
-static void intel_lvds_disable(struct intel_lvds *intel_lvds)
+static void intel_disable_lvds(struct intel_encoder *encoder)
 {
-       struct drm_device *dev = intel_lvds->base.base.dev;
+       struct drm_device *dev = encoder->base.dev;
+       struct intel_lvds *intel_lvds = to_intel_lvds(&encoder->base);
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 ctl_reg, lvds_reg, stat_reg;
 
@@ -142,18 +170,6 @@ static void intel_lvds_disable(struct intel_lvds *intel_lvds)
        POSTING_READ(lvds_reg);
 }
 
-static void intel_lvds_dpms(struct drm_encoder *encoder, int mode)
-{
-       struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
-
-       if (mode == DRM_MODE_DPMS_ON)
-               intel_lvds_enable(intel_lvds);
-       else
-               intel_lvds_disable(intel_lvds);
-
-       /* XXX: We never power down the LVDS pairs. */
-}
-
 static int intel_lvds_mode_valid(struct drm_connector *connector,
                                 struct drm_display_mode *mode)
 {
@@ -234,9 +250,8 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
 {
        struct drm_device *dev = encoder->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
        struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
-       struct intel_encoder *tmp_encoder;
+       struct intel_crtc *intel_crtc = intel_lvds->base.new_crtc;
        u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
        int pipe;
 
@@ -246,14 +261,8 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
                return false;
        }
 
-       /* Should never happen!! */
-       for_each_encoder_on_crtc(dev, encoder->crtc, tmp_encoder) {
-               if (&tmp_encoder->base != encoder) {
-                       DRM_ERROR("Can't enable LVDS and another "
-                              "encoder on the same pipe\n");
-                       return false;
-               }
-       }
+       if (intel_encoder_check_is_cloned(&intel_lvds->base))
+               return false;
 
        /*
         * We have timings from the BIOS for the panel, put them in
@@ -405,23 +414,6 @@ out:
        return true;
 }
 
-static void intel_lvds_prepare(struct drm_encoder *encoder)
-{
-       struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
-
-       intel_lvds_disable(intel_lvds);
-}
-
-static void intel_lvds_commit(struct drm_encoder *encoder)
-{
-       struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
-
-       /* Always do a full power on as we do not know what state
-        * we were left in.
-        */
-       intel_lvds_enable(intel_lvds);
-}
-
 static void intel_lvds_mode_set(struct drm_encoder *encoder,
                                struct drm_display_mode *mode,
                                struct drm_display_mode *adjusted_mode)
@@ -535,7 +527,7 @@ static int intel_lid_notify(struct notifier_block *nb, unsigned long val,
        dev_priv->modeset_on_lid = 0;
 
        mutex_lock(&dev->mode_config.mutex);
-       drm_helper_resume_force_mode(dev);
+       intel_modeset_check_state(dev);
        mutex_unlock(&dev->mode_config.mutex);
 
        return NOTIFY_OK;
@@ -587,8 +579,8 @@ 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.
                         */
-                       drm_crtc_helper_set_mode(crtc, &crtc->mode,
-                               crtc->x, crtc->y, crtc->fb);
+                       intel_set_mode(crtc, &crtc->mode,
+                                      crtc->x, crtc->y, crtc->fb);
                }
        }
 
@@ -596,11 +588,9 @@ static int intel_lvds_set_property(struct drm_connector *connector,
 }
 
 static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = {
-       .dpms = intel_lvds_dpms,
        .mode_fixup = intel_lvds_mode_fixup,
-       .prepare = intel_lvds_prepare,
        .mode_set = intel_lvds_mode_set,
-       .commit = intel_lvds_commit,
+       .disable = intel_encoder_noop,
 };
 
 static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = {
@@ -610,7 +600,7 @@ static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs
 };
 
 static const struct drm_connector_funcs intel_lvds_connector_funcs = {
-       .dpms = drm_helper_connector_dpms,
+       .dpms = intel_connector_dpms,
        .detect = intel_lvds_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .set_property = intel_lvds_set_property,
@@ -972,10 +962,15 @@ bool intel_lvds_init(struct drm_device *dev)
        drm_encoder_init(dev, &intel_encoder->base, &intel_lvds_enc_funcs,
                         DRM_MODE_ENCODER_LVDS);
 
+       intel_encoder->enable = intel_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;
+
        intel_connector_attach_encoder(intel_connector, intel_encoder);
        intel_encoder->type = INTEL_OUTPUT_LVDS;
 
-       intel_encoder->clone_mask = (1 << INTEL_LVDS_CLONE_BIT);
+       intel_encoder->cloneable = false;
        if (HAS_PCH_SPLIT(dev))
                intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
        else if (IS_GEN4(dev))
index 29b72593fbb276fbce8b2ae00828cc7c48a06bf3..4bc1c0fc342ad10ac5545d278ced8c3456fcbb2a 100644 (file)
@@ -45,7 +45,6 @@ int intel_connector_update_modes(struct drm_connector *connector,
        drm_mode_connector_update_edid_property(connector, edid);
        ret = drm_add_edid_modes(connector, edid);
        drm_edid_to_eld(connector, edid);
-       connector->display_info.raw_edid = NULL;
        kfree(edid);
 
        return ret;
index 18bd0af855dc5605585f09a7edfb98637f6c98e6..e27c1701262824bfc167c83db4b7f36481e01f76 100644 (file)
@@ -427,6 +427,25 @@ blind_set:
        goto end;
 }
 
+static void intel_setup_cadls(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_opregion *opregion = &dev_priv->opregion;
+       int i = 0;
+       u32 disp_id;
+
+       /* Initialize the CADL field by duplicating the DIDL values.
+        * Technically, this is not always correct as display outputs may exist,
+        * but not active. This initialization is necessary for some Clevo
+        * laptops that check this field before processing the brightness and
+        * display switching hotkeys. Just like DIDL, CADL is NULL-terminated if
+        * there are less than eight devices. */
+       do {
+               disp_id = ioread32(&opregion->acpi->didl[i]);
+               iowrite32(disp_id, &opregion->acpi->cadl[i]);
+       } while (++i < 8 && disp_id != 0);
+}
+
 void intel_opregion_init(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -436,8 +455,10 @@ void intel_opregion_init(struct drm_device *dev)
                return;
 
        if (opregion->acpi) {
-               if (drm_core_check_feature(dev, DRIVER_MODESET))
+               if (drm_core_check_feature(dev, DRIVER_MODESET)) {
                        intel_didl_outputs(dev);
+                       intel_setup_cadls(dev);
+               }
 
                /* Notify BIOS we are ready to handle ACPI video ext notifs.
                 * Right now, all the events are handled by the ACPI video module.
index 830d0dd610e1276bc45f5636f77c324d08bf2a16..afd0f30ab882cf2834cfd2474a43bae41f69b532 100644 (file)
@@ -235,54 +235,6 @@ static int intel_overlay_do_wait_request(struct intel_overlay *overlay,
        return 0;
 }
 
-/* Workaround for i830 bug where pipe a must be enable to change control regs */
-static int
-i830_activate_pipe_a(struct drm_device *dev)
-{
-       drm_i915_private_t *dev_priv = dev->dev_private;
-       struct intel_crtc *crtc;
-       struct drm_crtc_helper_funcs *crtc_funcs;
-       struct drm_display_mode vesa_640x480 = {
-               DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
-                        752, 800, 0, 480, 489, 492, 525, 0,
-                        DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC)
-       }, *mode;
-
-       crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[0]);
-       if (crtc->dpms_mode == DRM_MODE_DPMS_ON)
-               return 0;
-
-       /* most i8xx have pipe a forced on, so don't trust dpms mode */
-       if (I915_READ(_PIPEACONF) & PIPECONF_ENABLE)
-               return 0;
-
-       crtc_funcs = crtc->base.helper_private;
-       if (crtc_funcs->dpms == NULL)
-               return 0;
-
-       DRM_DEBUG_DRIVER("Enabling pipe A in order to enable overlay\n");
-
-       mode = drm_mode_duplicate(dev, &vesa_640x480);
-
-       if (!drm_crtc_helper_set_mode(&crtc->base, mode,
-                                      crtc->base.x, crtc->base.y,
-                                      crtc->base.fb))
-               return 0;
-
-       crtc_funcs->dpms(&crtc->base, DRM_MODE_DPMS_ON);
-       return 1;
-}
-
-static void
-i830_deactivate_pipe_a(struct drm_device *dev)
-{
-       drm_i915_private_t *dev_priv = dev->dev_private;
-       struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[0];
-       struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
-
-       crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
-}
-
 /* overlay needs to be disable in OCMD reg */
 static int intel_overlay_on(struct intel_overlay *overlay)
 {
@@ -290,17 +242,12 @@ static int intel_overlay_on(struct intel_overlay *overlay)
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
        struct drm_i915_gem_request *request;
-       int pipe_a_quirk = 0;
        int ret;
 
        BUG_ON(overlay->active);
        overlay->active = 1;
 
-       if (IS_I830(dev)) {
-               pipe_a_quirk = i830_activate_pipe_a(dev);
-               if (pipe_a_quirk < 0)
-                       return pipe_a_quirk;
-       }
+       WARN_ON(IS_I830(dev) && !(dev_priv->quirks & QUIRK_PIPEA_FORCE));
 
        request = kzalloc(sizeof(*request), GFP_KERNEL);
        if (request == NULL) {
@@ -322,9 +269,6 @@ static int intel_overlay_on(struct intel_overlay *overlay)
 
        ret = intel_overlay_do_wait_request(overlay, request, NULL);
 out:
-       if (pipe_a_quirk)
-               i830_deactivate_pipe_a(dev);
-
        return ret;
 }
 
@@ -1439,7 +1383,7 @@ void intel_setup_overlay(struct drm_device *dev)
                }
                overlay->flip_addr = reg_bo->phys_obj->handle->busaddr;
        } else {
-               ret = i915_gem_object_pin(reg_bo, PAGE_SIZE, true);
+               ret = i915_gem_object_pin(reg_bo, PAGE_SIZE, true, false);
                if (ret) {
                        DRM_ERROR("failed to pin overlay register bo\n");
                        goto out_free_bo;
index ba8a27b1757ad97e774fe8266432161477a3561a..d69f8f49beb5bf2a67f87f9b708a5a99af1616ff 100644 (file)
@@ -31,6 +31,8 @@
 #include "../../../platform/x86/intel_ips.h"
 #include <linux/module.h>
 
+#define FORCEWAKE_ACK_TIMEOUT_MS 2
+
 /* FBC, or Frame Buffer Compression, is a technique employed to compress the
  * framebuffer contents in-memory, aiming at reducing the required bandwidth
  * during in-memory transfers and, therefore, reduce the power packet.
@@ -593,7 +595,7 @@ static void i915_ironlake_get_mem_freq(struct drm_device *dev)
                break;
        }
 
-       dev_priv->r_t = dev_priv->mem_freq;
+       dev_priv->ips.r_t = dev_priv->mem_freq;
 
        switch (csipll & 0x3ff) {
        case 0x00c:
@@ -625,11 +627,11 @@ static void i915_ironlake_get_mem_freq(struct drm_device *dev)
        }
 
        if (dev_priv->fsb_freq == 3200) {
-               dev_priv->c_m = 0;
+               dev_priv->ips.c_m = 0;
        } else if (dev_priv->fsb_freq > 3200 && dev_priv->fsb_freq <= 4800) {
-               dev_priv->c_m = 1;
+               dev_priv->ips.c_m = 1;
        } else {
-               dev_priv->c_m = 2;
+               dev_priv->ips.c_m = 2;
        }
 }
 
@@ -2138,7 +2140,7 @@ intel_alloc_context_page(struct drm_device *dev)
                return NULL;
        }
 
-       ret = i915_gem_object_pin(ctx, 4096, true);
+       ret = i915_gem_object_pin(ctx, 4096, true, false);
        if (ret) {
                DRM_ERROR("failed to pin power context: %d\n", ret);
                goto err_unref;
@@ -2160,11 +2162,22 @@ err_unref:
        return NULL;
 }
 
+/**
+ * Lock protecting IPS related data structures
+ */
+DEFINE_SPINLOCK(mchdev_lock);
+
+/* Global for IPS driver to get at the current i915 device. Protected by
+ * mchdev_lock. */
+static struct drm_i915_private *i915_mch_dev;
+
 bool ironlake_set_drps(struct drm_device *dev, u8 val)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u16 rgvswctl;
 
+       assert_spin_locked(&mchdev_lock);
+
        rgvswctl = I915_READ16(MEMSWCTL);
        if (rgvswctl & MEMCTL_CMD_STS) {
                DRM_DEBUG("gpu busy, RCS change rejected\n");
@@ -2188,6 +2201,8 @@ static void ironlake_enable_drps(struct drm_device *dev)
        u32 rgvmodectl = I915_READ(MEMMODECTL);
        u8 fmax, fmin, fstart, vstart;
 
+       spin_lock_irq(&mchdev_lock);
+
        /* Enable temp reporting */
        I915_WRITE16(PMMISC, I915_READ(PMMISC) | MCPPCE_EN);
        I915_WRITE16(TSC1, I915_READ(TSC1) | TSE);
@@ -2211,12 +2226,12 @@ static void ironlake_enable_drps(struct drm_device *dev)
        vstart = (I915_READ(PXVFREQ_BASE + (fstart * 4)) & PXVFREQ_PX_MASK) >>
                PXVFREQ_PX_SHIFT;
 
-       dev_priv->fmax = fmax; /* IPS callback will increase this */
-       dev_priv->fstart = fstart;
+       dev_priv->ips.fmax = fmax; /* IPS callback will increase this */
+       dev_priv->ips.fstart = fstart;
 
-       dev_priv->max_delay = fstart;
-       dev_priv->min_delay = fmin;
-       dev_priv->cur_delay = fstart;
+       dev_priv->ips.max_delay = fstart;
+       dev_priv->ips.min_delay = fmin;
+       dev_priv->ips.cur_delay = fstart;
 
        DRM_DEBUG_DRIVER("fmax: %d, fmin: %d, fstart: %d\n",
                         fmax, fmin, fstart);
@@ -2233,23 +2248,29 @@ static void ironlake_enable_drps(struct drm_device *dev)
        rgvmodectl |= MEMMODE_SWMODE_EN;
        I915_WRITE(MEMMODECTL, rgvmodectl);
 
-       if (wait_for((I915_READ(MEMSWCTL) & MEMCTL_CMD_STS) == 0, 10))
+       if (wait_for_atomic((I915_READ(MEMSWCTL) & MEMCTL_CMD_STS) == 0, 10))
                DRM_ERROR("stuck trying to change perf mode\n");
-       msleep(1);
+       mdelay(1);
 
        ironlake_set_drps(dev, fstart);
 
-       dev_priv->last_count1 = I915_READ(0x112e4) + I915_READ(0x112e8) +
+       dev_priv->ips.last_count1 = I915_READ(0x112e4) + I915_READ(0x112e8) +
                I915_READ(0x112e0);
-       dev_priv->last_time1 = jiffies_to_msecs(jiffies);
-       dev_priv->last_count2 = I915_READ(0x112f4);
-       getrawmonotonic(&dev_priv->last_time2);
+       dev_priv->ips.last_time1 = jiffies_to_msecs(jiffies);
+       dev_priv->ips.last_count2 = I915_READ(0x112f4);
+       getrawmonotonic(&dev_priv->ips.last_time2);
+
+       spin_unlock_irq(&mchdev_lock);
 }
 
 static void ironlake_disable_drps(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u16 rgvswctl = I915_READ16(MEMSWCTL);
+       u16 rgvswctl;
+
+       spin_lock_irq(&mchdev_lock);
+
+       rgvswctl = I915_READ16(MEMSWCTL);
 
        /* Ack interrupts, disable EFC interrupt */
        I915_WRITE(MEMINTREN, I915_READ(MEMINTREN) & ~MEMINT_EVAL_CHG_EN);
@@ -2259,31 +2280,54 @@ static void ironlake_disable_drps(struct drm_device *dev)
        I915_WRITE(DEIMR, I915_READ(DEIMR) | DE_PCU_EVENT);
 
        /* Go back to the starting frequency */
-       ironlake_set_drps(dev, dev_priv->fstart);
-       msleep(1);
+       ironlake_set_drps(dev, dev_priv->ips.fstart);
+       mdelay(1);
        rgvswctl |= MEMCTL_CMD_STS;
        I915_WRITE(MEMSWCTL, rgvswctl);
-       msleep(1);
+       mdelay(1);
 
+       spin_unlock_irq(&mchdev_lock);
 }
 
-void gen6_set_rps(struct drm_device *dev, u8 val)
+/* There's a funny hw issue where the hw returns all 0 when reading from
+ * GEN6_RP_INTERRUPT_LIMITS. Hence we always need to compute the desired value
+ * ourselves, instead of doing a rmw cycle (which might result in us clearing
+ * all limits and the gpu stuck at whatever frequency it is at atm).
+ */
+static u32 gen6_rps_limits(struct drm_i915_private *dev_priv, u8 *val)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
        u32 limits;
 
        limits = 0;
-       if (val >= dev_priv->max_delay)
-               val = dev_priv->max_delay;
-       else
-               limits |= dev_priv->max_delay << 24;
 
-       if (val <= dev_priv->min_delay)
-               val = dev_priv->min_delay;
-       else
-               limits |= dev_priv->min_delay << 16;
+       if (*val >= dev_priv->rps.max_delay)
+               *val = dev_priv->rps.max_delay;
+       limits |= dev_priv->rps.max_delay << 24;
+
+       /* Only set the down limit when we've reached the lowest level to avoid
+        * getting more interrupts, otherwise leave this clear. This prevents a
+        * race in the hw when coming out of rc6: There's a tiny window where
+        * the hw runs at the minimal clock before selecting the desired
+        * frequency, if the down threshold expires in that window we will not
+        * receive a down interrupt. */
+       if (*val <= dev_priv->rps.min_delay) {
+               *val = dev_priv->rps.min_delay;
+               limits |= dev_priv->rps.min_delay << 16;
+       }
 
-       if (val == dev_priv->cur_delay)
+       return limits;
+}
+
+void gen6_set_rps(struct drm_device *dev, u8 val)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 limits = gen6_rps_limits(dev_priv, &val);
+
+       WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+       WARN_ON(val > dev_priv->rps.max_delay);
+       WARN_ON(val < dev_priv->rps.min_delay);
+
+       if (val == dev_priv->rps.cur_delay)
                return;
 
        I915_WRITE(GEN6_RPNSWREQ,
@@ -2296,7 +2340,11 @@ void gen6_set_rps(struct drm_device *dev, u8 val)
         */
        I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, limits);
 
-       dev_priv->cur_delay = val;
+       POSTING_READ(GEN6_RPNSWREQ);
+
+       dev_priv->rps.cur_delay = val;
+
+       trace_intel_gpu_freq_change(val * 50);
 }
 
 static void gen6_disable_rps(struct drm_device *dev)
@@ -2312,40 +2360,40 @@ static void gen6_disable_rps(struct drm_device *dev)
         * register (PMIMR) to mask PM interrupts. The only risk is in leaving
         * stale bits in PMIIR and PMIMR which gen6_enable_rps will clean up. */
 
-       spin_lock_irq(&dev_priv->rps_lock);
-       dev_priv->pm_iir = 0;
-       spin_unlock_irq(&dev_priv->rps_lock);
+       spin_lock_irq(&dev_priv->rps.lock);
+       dev_priv->rps.pm_iir = 0;
+       spin_unlock_irq(&dev_priv->rps.lock);
 
        I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR));
 }
 
 int intel_enable_rc6(const struct drm_device *dev)
 {
-       /*
-        * Respect the kernel parameter if it is set
-        */
+       /* Respect the kernel parameter if it is set */
        if (i915_enable_rc6 >= 0)
                return i915_enable_rc6;
 
-       /*
-        * Disable RC6 on Ironlake
-        */
-       if (INTEL_INFO(dev)->gen == 5)
-               return 0;
+       if (INTEL_INFO(dev)->gen == 5) {
+#ifdef CONFIG_INTEL_IOMMU
+               /* Disable rc6 on ilk if VT-d is on. */
+               if (intel_iommu_gfx_mapped)
+                       return false;
+#endif
+               DRM_DEBUG_DRIVER("Ironlake: only RC6 available\n");
+               return INTEL_RC6_ENABLE;
+       }
 
-       /* On Haswell, only RC6 is available. So let's enable it by default to
-        * provide better testing and coverage since the beginning.
-        */
-       if (IS_HASWELL(dev))
+       if (IS_HASWELL(dev)) {
+               DRM_DEBUG_DRIVER("Haswell: only RC6 available\n");
                return INTEL_RC6_ENABLE;
+       }
 
-       /*
-        * Disable rc6 on Sandybridge
-        */
+       /* snb/ivb have more than one rc6 state. */
        if (INTEL_INFO(dev)->gen == 6) {
                DRM_DEBUG_DRIVER("Sandybridge: deep RC6 disabled\n");
                return INTEL_RC6_ENABLE;
        }
+
        DRM_DEBUG_DRIVER("RC6 and deep RC6 enabled\n");
        return (INTEL_RC6_ENABLE | INTEL_RC6p_ENABLE);
 }
@@ -2383,9 +2431,9 @@ static void gen6_enable_rps(struct drm_device *dev)
        gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS);
 
        /* In units of 100MHz */
-       dev_priv->max_delay = rp_state_cap & 0xff;
-       dev_priv->min_delay = (rp_state_cap & 0xff0000) >> 16;
-       dev_priv->cur_delay = 0;
+       dev_priv->rps.max_delay = rp_state_cap & 0xff;
+       dev_priv->rps.min_delay = (rp_state_cap & 0xff0000) >> 16;
+       dev_priv->rps.cur_delay = 0;
 
        /* disable the counters and set deterministic thresholds */
        I915_WRITE(GEN6_RC_CONTROL, 0);
@@ -2438,8 +2486,8 @@ static void gen6_enable_rps(struct drm_device *dev)
 
        I915_WRITE(GEN6_RP_DOWN_TIMEOUT, 1000000);
        I915_WRITE(GEN6_RP_INTERRUPT_LIMITS,
-                  dev_priv->max_delay << 24 |
-                  dev_priv->min_delay << 16);
+                  dev_priv->rps.max_delay << 24 |
+                  dev_priv->rps.min_delay << 16);
 
        I915_WRITE(GEN6_RP_UP_THRESHOLD, 59400);
        I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 245000);
@@ -2477,7 +2525,7 @@ static void gen6_enable_rps(struct drm_device *dev)
                     500))
                DRM_ERROR("timeout waiting for pcode mailbox to finish\n");
        if (pcu_mbox & (1<<31)) { /* OC supported */
-               dev_priv->max_delay = pcu_mbox & 0xff;
+               dev_priv->rps.max_delay = pcu_mbox & 0xff;
                DRM_DEBUG_DRIVER("overclocking supported, adjusting frequency max to %dMHz\n", pcu_mbox * 50);
        }
 
@@ -2485,10 +2533,10 @@ static void gen6_enable_rps(struct drm_device *dev)
 
        /* requires MSI enabled */
        I915_WRITE(GEN6_PMIER, GEN6_PM_DEFERRED_EVENTS);
-       spin_lock_irq(&dev_priv->rps_lock);
-       WARN_ON(dev_priv->pm_iir != 0);
+       spin_lock_irq(&dev_priv->rps.lock);
+       WARN_ON(dev_priv->rps.pm_iir != 0);
        I915_WRITE(GEN6_PMIMR, 0);
-       spin_unlock_irq(&dev_priv->rps_lock);
+       spin_unlock_irq(&dev_priv->rps.lock);
        /* enable all PM interrupts */
        I915_WRITE(GEN6_PMINTRMSK, 0);
 
@@ -2520,9 +2568,9 @@ static void gen6_update_ring_freq(struct drm_device *dev)
         * to use for memory access.  We do this by specifying the IA frequency
         * the PCU should use as a reference to determine the ring frequency.
         */
-       for (gpu_freq = dev_priv->max_delay; gpu_freq >= dev_priv->min_delay;
+       for (gpu_freq = dev_priv->rps.max_delay; gpu_freq >= dev_priv->rps.min_delay;
             gpu_freq--) {
-               int diff = dev_priv->max_delay - gpu_freq;
+               int diff = dev_priv->rps.max_delay - gpu_freq;
 
                /*
                 * For GPU frequencies less than 750MHz, just use the lowest
@@ -2686,14 +2734,16 @@ static const struct cparams {
        { 0, 800, 231, 23784 },
 };
 
-unsigned long i915_chipset_val(struct drm_i915_private *dev_priv)
+static unsigned long __i915_chipset_val(struct drm_i915_private *dev_priv)
 {
        u64 total_count, diff, ret;
        u32 count1, count2, count3, m = 0, c = 0;
        unsigned long now = jiffies_to_msecs(jiffies), diff1;
        int i;
 
-       diff1 = now - dev_priv->last_time1;
+       assert_spin_locked(&mchdev_lock);
+
+       diff1 = now - dev_priv->ips.last_time1;
 
        /* Prevent division-by-zero if we are asking too fast.
         * Also, we don't get interesting results if we are polling
@@ -2701,7 +2751,7 @@ unsigned long i915_chipset_val(struct drm_i915_private *dev_priv)
         * in such cases.
         */
        if (diff1 <= 10)
-               return dev_priv->chipset_power;
+               return dev_priv->ips.chipset_power;
 
        count1 = I915_READ(DMIEC);
        count2 = I915_READ(DDREC);
@@ -2710,16 +2760,16 @@ unsigned long i915_chipset_val(struct drm_i915_private *dev_priv)
        total_count = count1 + count2 + count3;
 
        /* FIXME: handle per-counter overflow */
-       if (total_count < dev_priv->last_count1) {
-               diff = ~0UL - dev_priv->last_count1;
+       if (total_count < dev_priv->ips.last_count1) {
+               diff = ~0UL - dev_priv->ips.last_count1;
                diff += total_count;
        } else {
-               diff = total_count - dev_priv->last_count1;
+               diff = total_count - dev_priv->ips.last_count1;
        }
 
        for (i = 0; i < ARRAY_SIZE(cparams); i++) {
-               if (cparams[i].i == dev_priv->c_m &&
-                   cparams[i].t == dev_priv->r_t) {
+               if (cparams[i].i == dev_priv->ips.c_m &&
+                   cparams[i].t == dev_priv->ips.r_t) {
                        m = cparams[i].m;
                        c = cparams[i].c;
                        break;
@@ -2730,14 +2780,30 @@ unsigned long i915_chipset_val(struct drm_i915_private *dev_priv)
        ret = ((m * diff) + c);
        ret = div_u64(ret, 10);
 
-       dev_priv->last_count1 = total_count;
-       dev_priv->last_time1 = now;
+       dev_priv->ips.last_count1 = total_count;
+       dev_priv->ips.last_time1 = now;
 
-       dev_priv->chipset_power = ret;
+       dev_priv->ips.chipset_power = ret;
 
        return ret;
 }
 
+unsigned long i915_chipset_val(struct drm_i915_private *dev_priv)
+{
+       unsigned long val;
+
+       if (dev_priv->info->gen != 5)
+               return 0;
+
+       spin_lock_irq(&mchdev_lock);
+
+       val = __i915_chipset_val(dev_priv);
+
+       spin_unlock_irq(&mchdev_lock);
+
+       return val;
+}
+
 unsigned long i915_mch_val(struct drm_i915_private *dev_priv)
 {
        unsigned long m, x, b;
@@ -2894,18 +2960,17 @@ static u16 pvid_to_extvid(struct drm_i915_private *dev_priv, u8 pxvid)
                return v_table[pxvid].vd;
 }
 
-void i915_update_gfx_val(struct drm_i915_private *dev_priv)
+static void __i915_update_gfx_val(struct drm_i915_private *dev_priv)
 {
        struct timespec now, diff1;
        u64 diff;
        unsigned long diffms;
        u32 count;
 
-       if (dev_priv->info->gen != 5)
-               return;
+       assert_spin_locked(&mchdev_lock);
 
        getrawmonotonic(&now);
-       diff1 = timespec_sub(now, dev_priv->last_time2);
+       diff1 = timespec_sub(now, dev_priv->ips.last_time2);
 
        /* Don't divide by 0 */
        diffms = diff1.tv_sec * 1000 + diff1.tv_nsec / 1000000;
@@ -2914,28 +2979,42 @@ void i915_update_gfx_val(struct drm_i915_private *dev_priv)
 
        count = I915_READ(GFXEC);
 
-       if (count < dev_priv->last_count2) {
-               diff = ~0UL - dev_priv->last_count2;
+       if (count < dev_priv->ips.last_count2) {
+               diff = ~0UL - dev_priv->ips.last_count2;
                diff += count;
        } else {
-               diff = count - dev_priv->last_count2;
+               diff = count - dev_priv->ips.last_count2;
        }
 
-       dev_priv->last_count2 = count;
-       dev_priv->last_time2 = now;
+       dev_priv->ips.last_count2 = count;
+       dev_priv->ips.last_time2 = now;
 
        /* More magic constants... */
        diff = diff * 1181;
        diff = div_u64(diff, diffms * 10);
-       dev_priv->gfx_power = diff;
+       dev_priv->ips.gfx_power = diff;
 }
 
-unsigned long i915_gfx_val(struct drm_i915_private *dev_priv)
+void i915_update_gfx_val(struct drm_i915_private *dev_priv)
+{
+       if (dev_priv->info->gen != 5)
+               return;
+
+       spin_lock_irq(&mchdev_lock);
+
+       __i915_update_gfx_val(dev_priv);
+
+       spin_unlock_irq(&mchdev_lock);
+}
+
+static unsigned long __i915_gfx_val(struct drm_i915_private *dev_priv)
 {
        unsigned long t, corr, state1, corr2, state2;
        u32 pxvid, ext_v;
 
-       pxvid = I915_READ(PXVFREQ_BASE + (dev_priv->cur_delay * 4));
+       assert_spin_locked(&mchdev_lock);
+
+       pxvid = I915_READ(PXVFREQ_BASE + (dev_priv->rps.cur_delay * 4));
        pxvid = (pxvid >> 24) & 0x7f;
        ext_v = pvid_to_extvid(dev_priv, pxvid);
 
@@ -2955,27 +3034,31 @@ unsigned long i915_gfx_val(struct drm_i915_private *dev_priv)
 
        corr = corr * ((150142 * state1) / 10000 - 78642);
        corr /= 100000;
-       corr2 = (corr * dev_priv->corr);
+       corr2 = (corr * dev_priv->ips.corr);
 
        state2 = (corr2 * state1) / 10000;
        state2 /= 100; /* convert to mW */
 
-       i915_update_gfx_val(dev_priv);
+       __i915_update_gfx_val(dev_priv);
 
-       return dev_priv->gfx_power + state2;
+       return dev_priv->ips.gfx_power + state2;
 }
 
-/* Global for IPS driver to get at the current i915 device */
-static struct drm_i915_private *i915_mch_dev;
-/*
- * Lock protecting IPS related data structures
- *   - i915_mch_dev
- *   - dev_priv->max_delay
- *   - dev_priv->min_delay
- *   - dev_priv->fmax
- *   - dev_priv->gpu_busy
- */
-static DEFINE_SPINLOCK(mchdev_lock);
+unsigned long i915_gfx_val(struct drm_i915_private *dev_priv)
+{
+       unsigned long val;
+
+       if (dev_priv->info->gen != 5)
+               return 0;
+
+       spin_lock_irq(&mchdev_lock);
+
+       val = __i915_gfx_val(dev_priv);
+
+       spin_unlock_irq(&mchdev_lock);
+
+       return val;
+}
 
 /**
  * i915_read_mch_val - return value for IPS use
@@ -2988,18 +3071,18 @@ unsigned long i915_read_mch_val(void)
        struct drm_i915_private *dev_priv;
        unsigned long chipset_val, graphics_val, ret = 0;
 
-       spin_lock(&mchdev_lock);
+       spin_lock_irq(&mchdev_lock);
        if (!i915_mch_dev)
                goto out_unlock;
        dev_priv = i915_mch_dev;
 
-       chipset_val = i915_chipset_val(dev_priv);
-       graphics_val = i915_gfx_val(dev_priv);
+       chipset_val = __i915_chipset_val(dev_priv);
+       graphics_val = __i915_gfx_val(dev_priv);
 
        ret = chipset_val + graphics_val;
 
 out_unlock:
-       spin_unlock(&mchdev_lock);
+       spin_unlock_irq(&mchdev_lock);
 
        return ret;
 }
@@ -3015,18 +3098,18 @@ bool i915_gpu_raise(void)
        struct drm_i915_private *dev_priv;
        bool ret = true;
 
-       spin_lock(&mchdev_lock);
+       spin_lock_irq(&mchdev_lock);
        if (!i915_mch_dev) {
                ret = false;
                goto out_unlock;
        }
        dev_priv = i915_mch_dev;
 
-       if (dev_priv->max_delay > dev_priv->fmax)
-               dev_priv->max_delay--;
+       if (dev_priv->ips.max_delay > dev_priv->ips.fmax)
+               dev_priv->ips.max_delay--;
 
 out_unlock:
-       spin_unlock(&mchdev_lock);
+       spin_unlock_irq(&mchdev_lock);
 
        return ret;
 }
@@ -3043,18 +3126,18 @@ bool i915_gpu_lower(void)
        struct drm_i915_private *dev_priv;
        bool ret = true;
 
-       spin_lock(&mchdev_lock);
+       spin_lock_irq(&mchdev_lock);
        if (!i915_mch_dev) {
                ret = false;
                goto out_unlock;
        }
        dev_priv = i915_mch_dev;
 
-       if (dev_priv->max_delay < dev_priv->min_delay)
-               dev_priv->max_delay++;
+       if (dev_priv->ips.max_delay < dev_priv->ips.min_delay)
+               dev_priv->ips.max_delay++;
 
 out_unlock:
-       spin_unlock(&mchdev_lock);
+       spin_unlock_irq(&mchdev_lock);
 
        return ret;
 }
@@ -3068,17 +3151,20 @@ EXPORT_SYMBOL_GPL(i915_gpu_lower);
 bool i915_gpu_busy(void)
 {
        struct drm_i915_private *dev_priv;
+       struct intel_ring_buffer *ring;
        bool ret = false;
+       int i;
 
-       spin_lock(&mchdev_lock);
+       spin_lock_irq(&mchdev_lock);
        if (!i915_mch_dev)
                goto out_unlock;
        dev_priv = i915_mch_dev;
 
-       ret = dev_priv->busy;
+       for_each_ring(ring, dev_priv, i)
+               ret |= !list_empty(&ring->request_list);
 
 out_unlock:
-       spin_unlock(&mchdev_lock);
+       spin_unlock_irq(&mchdev_lock);
 
        return ret;
 }
@@ -3095,20 +3181,20 @@ bool i915_gpu_turbo_disable(void)
        struct drm_i915_private *dev_priv;
        bool ret = true;
 
-       spin_lock(&mchdev_lock);
+       spin_lock_irq(&mchdev_lock);
        if (!i915_mch_dev) {
                ret = false;
                goto out_unlock;
        }
        dev_priv = i915_mch_dev;
 
-       dev_priv->max_delay = dev_priv->fstart;
+       dev_priv->ips.max_delay = dev_priv->ips.fstart;
 
-       if (!ironlake_set_drps(dev_priv->dev, dev_priv->fstart))
+       if (!ironlake_set_drps(dev_priv->dev, dev_priv->ips.fstart))
                ret = false;
 
 out_unlock:
-       spin_unlock(&mchdev_lock);
+       spin_unlock_irq(&mchdev_lock);
 
        return ret;
 }
@@ -3136,19 +3222,20 @@ ips_ping_for_i915_load(void)
 
 void intel_gpu_ips_init(struct drm_i915_private *dev_priv)
 {
-       spin_lock(&mchdev_lock);
+       /* We only register the i915 ips part with intel-ips once everything is
+        * set up, to avoid intel-ips sneaking in and reading bogus values. */
+       spin_lock_irq(&mchdev_lock);
        i915_mch_dev = dev_priv;
-       dev_priv->mchdev_lock = &mchdev_lock;
-       spin_unlock(&mchdev_lock);
+       spin_unlock_irq(&mchdev_lock);
 
        ips_ping_for_i915_load();
 }
 
 void intel_gpu_ips_teardown(void)
 {
-       spin_lock(&mchdev_lock);
+       spin_lock_irq(&mchdev_lock);
        i915_mch_dev = NULL;
-       spin_unlock(&mchdev_lock);
+       spin_unlock_irq(&mchdev_lock);
 }
 static void intel_init_emon(struct drm_device *dev)
 {
@@ -3218,7 +3305,7 @@ static void intel_init_emon(struct drm_device *dev)
 
        lcfuse = I915_READ(LCFUSE02);
 
-       dev_priv->corr = (lcfuse & LCFUSE_HIV_MASK);
+       dev_priv->ips.corr = (lcfuse & LCFUSE_HIV_MASK);
 }
 
 void intel_disable_gt_powersave(struct drm_device *dev)
@@ -3731,42 +3818,6 @@ void intel_init_clock_gating(struct drm_device *dev)
                dev_priv->display.init_pch_clock_gating(dev);
 }
 
-static void gen6_sanitize_pm(struct drm_device *dev)
-{
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 limits, delay, old;
-
-       gen6_gt_force_wake_get(dev_priv);
-
-       old = limits = I915_READ(GEN6_RP_INTERRUPT_LIMITS);
-       /* Make sure we continue to get interrupts
-        * until we hit the minimum or maximum frequencies.
-        */
-       limits &= ~(0x3f << 16 | 0x3f << 24);
-       delay = dev_priv->cur_delay;
-       if (delay < dev_priv->max_delay)
-               limits |= (dev_priv->max_delay & 0x3f) << 24;
-       if (delay > dev_priv->min_delay)
-               limits |= (dev_priv->min_delay & 0x3f) << 16;
-
-       if (old != limits) {
-               /* Note that the known failure case is to read back 0. */
-               DRM_DEBUG_DRIVER("Power management discrepancy: GEN6_RP_INTERRUPT_LIMITS "
-                                "expected %08x, was %08x\n", limits, old);
-               I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, limits);
-       }
-
-       gen6_gt_force_wake_put(dev_priv);
-}
-
-void intel_sanitize_pm(struct drm_device *dev)
-{
-       struct drm_i915_private *dev_priv = dev->dev_private;
-
-       if (dev_priv->display.sanitize_pm)
-               dev_priv->display.sanitize_pm(dev);
-}
-
 /* Starting with Haswell, we have different power wells for
  * different parts of the GPU. This attempts to enable them all.
  */
@@ -3852,7 +3903,6 @@ void intel_init_pm(struct drm_device *dev)
                                dev_priv->display.update_wm = NULL;
                        }
                        dev_priv->display.init_clock_gating = gen6_init_clock_gating;
-                       dev_priv->display.sanitize_pm = gen6_sanitize_pm;
                } else if (IS_IVYBRIDGE(dev)) {
                        /* FIXME: detect B0+ stepping and use auto training */
                        if (SNB_READ_WM0_LATENCY()) {
@@ -3864,7 +3914,6 @@ void intel_init_pm(struct drm_device *dev)
                                dev_priv->display.update_wm = NULL;
                        }
                        dev_priv->display.init_clock_gating = ivybridge_init_clock_gating;
-                       dev_priv->display.sanitize_pm = gen6_sanitize_pm;
                } else if (IS_HASWELL(dev)) {
                        if (SNB_READ_WM0_LATENCY()) {
                                dev_priv->display.update_wm = sandybridge_update_wm;
@@ -3876,7 +3925,6 @@ void intel_init_pm(struct drm_device *dev)
                                dev_priv->display.update_wm = NULL;
                        }
                        dev_priv->display.init_clock_gating = haswell_init_clock_gating;
-                       dev_priv->display.sanitize_pm = gen6_sanitize_pm;
                } else
                        dev_priv->display.update_wm = NULL;
        } else if (IS_VALLEYVIEW(dev)) {
@@ -3955,14 +4003,16 @@ static void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv)
        else
                forcewake_ack = FORCEWAKE_ACK;
 
-       if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1) == 0, 500))
-               DRM_ERROR("Force wake wait timed out\n");
+       if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & 1) == 0,
+                           FORCEWAKE_ACK_TIMEOUT_MS))
+               DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n");
 
        I915_WRITE_NOTRACE(FORCEWAKE, 1);
-       POSTING_READ(FORCEWAKE);
+       POSTING_READ(ECOBUS); /* something from same cacheline, but !FORCEWAKE */
 
-       if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1), 500))
-               DRM_ERROR("Force wake wait timed out\n");
+       if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & 1),
+                           FORCEWAKE_ACK_TIMEOUT_MS))
+               DRM_ERROR("Timed out waiting for forcewake to ack request.\n");
 
        __gen6_gt_wait_for_thread_c0(dev_priv);
 }
@@ -3976,14 +4026,16 @@ static void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv)
        else
                forcewake_ack = FORCEWAKE_MT_ACK;
 
-       if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1) == 0, 500))
-               DRM_ERROR("Force wake wait timed out\n");
+       if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & 1) == 0,
+                           FORCEWAKE_ACK_TIMEOUT_MS))
+               DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n");
 
        I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_ENABLE(1));
-       POSTING_READ(FORCEWAKE_MT);
+       POSTING_READ(ECOBUS); /* something from same cacheline, but !FORCEWAKE */
 
-       if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1), 500))
-               DRM_ERROR("Force wake wait timed out\n");
+       if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & 1),
+                           FORCEWAKE_ACK_TIMEOUT_MS))
+               DRM_ERROR("Timed out waiting for forcewake to ack request.\n");
 
        __gen6_gt_wait_for_thread_c0(dev_priv);
 }
@@ -4016,14 +4068,14 @@ void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv)
 static void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv)
 {
        I915_WRITE_NOTRACE(FORCEWAKE, 0);
-       POSTING_READ(FORCEWAKE);
+       /* gen6_gt_check_fifodbg doubles as the POSTING_READ */
        gen6_gt_check_fifodbg(dev_priv);
 }
 
 static void __gen6_gt_force_wake_mt_put(struct drm_i915_private *dev_priv)
 {
        I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_DISABLE(1));
-       POSTING_READ(FORCEWAKE_MT);
+       /* gen6_gt_check_fifodbg doubles as the POSTING_READ */
        gen6_gt_check_fifodbg(dev_priv);
 }
 
@@ -4062,24 +4114,24 @@ int __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv)
 
 static void vlv_force_wake_get(struct drm_i915_private *dev_priv)
 {
-       /* Already awake? */
-       if ((I915_READ(0x130094) & 0xa1) == 0xa1)
-               return;
+       if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & 1) == 0,
+                           FORCEWAKE_ACK_TIMEOUT_MS))
+               DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n");
 
-       I915_WRITE_NOTRACE(FORCEWAKE_VLV, 0xffffffff);
-       POSTING_READ(FORCEWAKE_VLV);
+       I915_WRITE_NOTRACE(FORCEWAKE_VLV, _MASKED_BIT_ENABLE(1));
 
-       if (wait_for_atomic_us((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & 1), 500))
-               DRM_ERROR("Force wake wait timed out\n");
+       if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & 1),
+                           FORCEWAKE_ACK_TIMEOUT_MS))
+               DRM_ERROR("Timed out waiting for forcewake to ack request.\n");
 
        __gen6_gt_wait_for_thread_c0(dev_priv);
 }
 
 static void vlv_force_wake_put(struct drm_i915_private *dev_priv)
 {
-       I915_WRITE_NOTRACE(FORCEWAKE_VLV, 0xffff0000);
-       /* FIXME: confirm VLV behavior with Punit folks */
-       POSTING_READ(FORCEWAKE_VLV);
+       I915_WRITE_NOTRACE(FORCEWAKE_VLV, _MASKED_BIT_DISABLE(1));
+       /* The below doubles as a POSTING_READ */
+       gen6_gt_check_fifodbg(dev_priv);
 }
 
 void intel_gt_init(struct drm_device *dev)
index e2a73b38abe96e7dc49674d39806c5ca3cab6e51..984a0c5fbf5d9113d275a12af9674e0a45e809d6 100644 (file)
@@ -262,6 +262,83 @@ gen6_render_ring_flush(struct intel_ring_buffer *ring,
        return 0;
 }
 
+static int
+gen7_render_ring_cs_stall_wa(struct intel_ring_buffer *ring)
+{
+       int ret;
+
+       ret = intel_ring_begin(ring, 4);
+       if (ret)
+               return ret;
+
+       intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4));
+       intel_ring_emit(ring, PIPE_CONTROL_CS_STALL |
+                             PIPE_CONTROL_STALL_AT_SCOREBOARD);
+       intel_ring_emit(ring, 0);
+       intel_ring_emit(ring, 0);
+       intel_ring_advance(ring);
+
+       return 0;
+}
+
+static int
+gen7_render_ring_flush(struct intel_ring_buffer *ring,
+                      u32 invalidate_domains, u32 flush_domains)
+{
+       u32 flags = 0;
+       struct pipe_control *pc = ring->private;
+       u32 scratch_addr = pc->gtt_offset + 128;
+       int ret;
+
+       /*
+        * Ensure that any following seqno writes only happen when the render
+        * cache is indeed flushed.
+        *
+        * Workaround: 4th PIPE_CONTROL command (except the ones with only
+        * read-cache invalidate bits set) must have the CS_STALL bit set. We
+        * don't try to be clever and just set it unconditionally.
+        */
+       flags |= PIPE_CONTROL_CS_STALL;
+
+       /* Just flush everything.  Experiments have shown that reducing the
+        * number of bits based on the write domains has little performance
+        * impact.
+        */
+       if (flush_domains) {
+               flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH;
+               flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH;
+       }
+       if (invalidate_domains) {
+               flags |= PIPE_CONTROL_TLB_INVALIDATE;
+               flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE;
+               flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE;
+               flags |= PIPE_CONTROL_VF_CACHE_INVALIDATE;
+               flags |= PIPE_CONTROL_CONST_CACHE_INVALIDATE;
+               flags |= PIPE_CONTROL_STATE_CACHE_INVALIDATE;
+               /*
+                * TLB invalidate requires a post-sync write.
+                */
+               flags |= PIPE_CONTROL_QW_WRITE;
+
+               /* Workaround: we must issue a pipe_control with CS-stall bit
+                * set before a pipe_control command that has the state cache
+                * invalidate bit set. */
+               gen7_render_ring_cs_stall_wa(ring);
+       }
+
+       ret = intel_ring_begin(ring, 4);
+       if (ret)
+               return ret;
+
+       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, 0);
+       intel_ring_advance(ring);
+
+       return 0;
+}
+
 static void ring_write_tail(struct intel_ring_buffer *ring,
                            u32 value)
 {
@@ -382,12 +459,12 @@ init_pipe_control(struct intel_ring_buffer *ring)
 
        i915_gem_object_set_cache_level(obj, I915_CACHE_LLC);
 
-       ret = i915_gem_object_pin(obj, 4096, true);
+       ret = i915_gem_object_pin(obj, 4096, true, false);
        if (ret)
                goto err_unref;
 
        pc->gtt_offset = obj->gtt_offset;
-       pc->cpu_page =  kmap(obj->pages[0]);
+       pc->cpu_page =  kmap(sg_page(obj->pages->sgl));
        if (pc->cpu_page == NULL)
                goto err_unpin;
 
@@ -414,7 +491,8 @@ cleanup_pipe_control(struct intel_ring_buffer *ring)
                return;
 
        obj = pc->obj;
-       kunmap(obj->pages[0]);
+
+       kunmap(sg_page(obj->pages->sgl));
        i915_gem_object_unpin(obj);
        drm_gem_object_unreference(&obj->base);
 
@@ -462,7 +540,7 @@ static int init_render_ring(struct intel_ring_buffer *ring)
        if (INTEL_INFO(dev)->gen >= 6)
                I915_WRITE(INSTPM, _MASKED_BIT_ENABLE(INSTPM_FORCE_ORDERING));
 
-       if (IS_IVYBRIDGE(dev))
+       if (HAS_L3_GPU_CACHE(dev))
                I915_WRITE_IMR(ring, ~GEN6_RENDER_L3_PARITY_ERROR);
 
        return ret;
@@ -628,26 +706,24 @@ pc_render_add_request(struct intel_ring_buffer *ring,
 }
 
 static u32
-gen6_ring_get_seqno(struct intel_ring_buffer *ring)
+gen6_ring_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency)
 {
-       struct drm_device *dev = ring->dev;
-
        /* Workaround to force correct ordering between irq and seqno writes on
         * ivb (and maybe also on snb) by reading from a CS register (like
         * ACTHD) before reading the status page. */
-       if (IS_GEN6(dev) || IS_GEN7(dev))
+       if (!lazy_coherency)
                intel_ring_get_active_head(ring);
        return intel_read_status_page(ring, I915_GEM_HWS_INDEX);
 }
 
 static u32
-ring_get_seqno(struct intel_ring_buffer *ring)
+ring_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency)
 {
        return intel_read_status_page(ring, I915_GEM_HWS_INDEX);
 }
 
 static u32
-pc_render_get_seqno(struct intel_ring_buffer *ring)
+pc_render_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency)
 {
        struct pipe_control *pc = ring->private;
        return pc->cpu_page[0];
@@ -852,7 +928,7 @@ gen6_ring_get_irq(struct intel_ring_buffer *ring)
 
        spin_lock_irqsave(&dev_priv->irq_lock, flags);
        if (ring->irq_refcount++ == 0) {
-               if (IS_IVYBRIDGE(dev) && ring->id == RCS)
+               if (HAS_L3_GPU_CACHE(dev) && ring->id == RCS)
                        I915_WRITE_IMR(ring, ~(ring->irq_enable_mask |
                                                GEN6_RENDER_L3_PARITY_ERROR));
                else
@@ -875,7 +951,7 @@ gen6_ring_put_irq(struct intel_ring_buffer *ring)
 
        spin_lock_irqsave(&dev_priv->irq_lock, flags);
        if (--ring->irq_refcount == 0) {
-               if (IS_IVYBRIDGE(dev) && ring->id == RCS)
+               if (HAS_L3_GPU_CACHE(dev) && ring->id == RCS)
                        I915_WRITE_IMR(ring, ~GEN6_RENDER_L3_PARITY_ERROR);
                else
                        I915_WRITE_IMR(ring, ~0);
@@ -951,7 +1027,7 @@ static void cleanup_status_page(struct intel_ring_buffer *ring)
        if (obj == NULL)
                return;
 
-       kunmap(obj->pages[0]);
+       kunmap(sg_page(obj->pages->sgl));
        i915_gem_object_unpin(obj);
        drm_gem_object_unreference(&obj->base);
        ring->status_page.obj = NULL;
@@ -972,13 +1048,13 @@ static int init_status_page(struct intel_ring_buffer *ring)
 
        i915_gem_object_set_cache_level(obj, I915_CACHE_LLC);
 
-       ret = i915_gem_object_pin(obj, 4096, true);
+       ret = i915_gem_object_pin(obj, 4096, true, false);
        if (ret != 0) {
                goto err_unref;
        }
 
        ring->status_page.gfx_addr = obj->gtt_offset;
-       ring->status_page.page_addr = kmap(obj->pages[0]);
+       ring->status_page.page_addr = kmap(sg_page(obj->pages->sgl));
        if (ring->status_page.page_addr == NULL) {
                ret = -ENOMEM;
                goto err_unpin;
@@ -1010,7 +1086,6 @@ static int intel_init_ring_buffer(struct drm_device *dev,
        ring->dev = dev;
        INIT_LIST_HEAD(&ring->active_list);
        INIT_LIST_HEAD(&ring->request_list);
-       INIT_LIST_HEAD(&ring->gpu_write_list);
        ring->size = 32 * PAGE_SIZE;
 
        init_waitqueue_head(&ring->irq_queue);
@@ -1030,7 +1105,7 @@ static int intel_init_ring_buffer(struct drm_device *dev,
 
        ring->obj = obj;
 
-       ret = i915_gem_object_pin(obj, PAGE_SIZE, true);
+       ret = i915_gem_object_pin(obj, PAGE_SIZE, true, false);
        if (ret)
                goto err_unref;
 
@@ -1379,7 +1454,9 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
 
        if (INTEL_INFO(dev)->gen >= 6) {
                ring->add_request = gen6_add_request;
-               ring->flush = gen6_render_ring_flush;
+               ring->flush = gen7_render_ring_flush;
+               if (INTEL_INFO(dev)->gen == 6)
+                       ring->flush = gen6_render_ring_flush;
                ring->irq_get = gen6_ring_get_irq;
                ring->irq_put = gen6_ring_put_irq;
                ring->irq_enable_mask = GT_USER_INTERRUPT;
@@ -1481,7 +1558,6 @@ int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size)
        ring->dev = dev;
        INIT_LIST_HEAD(&ring->active_list);
        INIT_LIST_HEAD(&ring->request_list);
-       INIT_LIST_HEAD(&ring->gpu_write_list);
 
        ring->size = size;
        ring->effective_size = ring->size;
@@ -1574,3 +1650,41 @@ int intel_init_blt_ring_buffer(struct drm_device *dev)
 
        return intel_init_ring_buffer(dev, ring);
 }
+
+int
+intel_ring_flush_all_caches(struct intel_ring_buffer *ring)
+{
+       int ret;
+
+       if (!ring->gpu_caches_dirty)
+               return 0;
+
+       ret = ring->flush(ring, 0, I915_GEM_GPU_DOMAINS);
+       if (ret)
+               return ret;
+
+       trace_i915_gem_ring_flush(ring, 0, I915_GEM_GPU_DOMAINS);
+
+       ring->gpu_caches_dirty = false;
+       return 0;
+}
+
+int
+intel_ring_invalidate_all_caches(struct intel_ring_buffer *ring)
+{
+       uint32_t flush_domains;
+       int ret;
+
+       flush_domains = 0;
+       if (ring->gpu_caches_dirty)
+               flush_domains = I915_GEM_GPU_DOMAINS;
+
+       ret = ring->flush(ring, I915_GEM_GPU_DOMAINS, flush_domains);
+       if (ret)
+               return ret;
+
+       trace_i915_gem_ring_flush(ring, I915_GEM_GPU_DOMAINS, flush_domains);
+
+       ring->gpu_caches_dirty = false;
+       return 0;
+}
index 1d3c81fdad92087d2caf58c3d1b79753f5c75319..2ea7a311a1f0ed8753d800cc48a48e6873bd336d 100644 (file)
@@ -72,7 +72,14 @@ struct  intel_ring_buffer {
                                  u32   flush_domains);
        int             (*add_request)(struct intel_ring_buffer *ring,
                                       u32 *seqno);
-       u32             (*get_seqno)(struct intel_ring_buffer *ring);
+       /* Some chipsets are not quite as coherent as advertised and need
+        * an expensive kick to force a true read of the up-to-date seqno.
+        * However, the up-to-date seqno is not always required and the last
+        * seen value is good enough. Note that the seqno will always be
+        * monotonic, even if not coherent.
+        */
+       u32             (*get_seqno)(struct intel_ring_buffer *ring,
+                                    bool lazy_coherency);
        int             (*dispatch_execbuffer)(struct intel_ring_buffer *ring,
                                               u32 offset, u32 length);
        void            (*cleanup)(struct intel_ring_buffer *ring);
@@ -100,15 +107,6 @@ struct  intel_ring_buffer {
         */
        struct list_head request_list;
 
-       /**
-        * List of objects currently pending a GPU write flush.
-        *
-        * All elements on this list will belong to either the
-        * active_list or flushing_list, last_rendering_seqno can
-        * be used to differentiate between the two elements.
-        */
-       struct list_head gpu_write_list;
-
        /**
         * Do we have some not yet emitted requests outstanding?
         */
@@ -204,6 +202,8 @@ static inline void intel_ring_emit(struct intel_ring_buffer *ring,
 void intel_ring_advance(struct intel_ring_buffer *ring);
 
 u32 intel_ring_get_seqno(struct intel_ring_buffer *ring);
+int intel_ring_flush_all_caches(struct intel_ring_buffer *ring);
+int intel_ring_invalidate_all_caches(struct intel_ring_buffer *ring);
 
 int intel_init_render_ring_buffer(struct drm_device *dev);
 int intel_init_bsd_ring_buffer(struct drm_device *dev);
index 123afd357611a6fbbd4c235aa7449afd6ac15dff..39c319827f91e7b8dee9e78376eae97e31cffce8 100644 (file)
@@ -97,7 +97,7 @@ struct intel_sdvo {
        /*
         * Hotplug activation bits for this device
         */
-       uint8_t hotplug_active[2];
+       uint16_t hotplug_active;
 
        /**
         * This is used to select the color range of RBG outputs in HDMI mode.
@@ -628,6 +628,14 @@ static bool intel_sdvo_set_active_outputs(struct intel_sdvo *intel_sdvo,
                                    &outputs, sizeof(outputs));
 }
 
+static bool intel_sdvo_get_active_outputs(struct intel_sdvo *intel_sdvo,
+                                         u16 *outputs)
+{
+       return intel_sdvo_get_value(intel_sdvo,
+                                   SDVO_CMD_GET_ACTIVE_OUTPUTS,
+                                   outputs, sizeof(*outputs));
+}
+
 static bool intel_sdvo_set_encoder_power_state(struct intel_sdvo *intel_sdvo,
                                               int mode)
 {
@@ -1142,51 +1150,132 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
        intel_sdvo_write_sdvox(intel_sdvo, sdvox);
 }
 
-static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode)
+static bool intel_sdvo_connector_get_hw_state(struct intel_connector *connector)
 {
-       struct drm_device *dev = encoder->dev;
+       struct intel_sdvo_connector *intel_sdvo_connector =
+               to_intel_sdvo_connector(&connector->base);
+       struct intel_sdvo *intel_sdvo = intel_attached_sdvo(&connector->base);
+       u16 active_outputs;
+
+       intel_sdvo_get_active_outputs(intel_sdvo, &active_outputs);
+
+       if (active_outputs & intel_sdvo_connector->output_flag)
+               return true;
+       else
+               return false;
+}
+
+static bool intel_sdvo_get_hw_state(struct intel_encoder *encoder,
+                                   enum pipe *pipe)
+{
+       struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_sdvo *intel_sdvo = to_intel_sdvo(encoder);
-       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       struct intel_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base);
+       u32 tmp;
+
+       tmp = I915_READ(intel_sdvo->sdvo_reg);
+
+       if (!(tmp & SDVO_ENABLE))
+               return false;
+
+       if (HAS_PCH_CPT(dev))
+               *pipe = PORT_TO_PIPE_CPT(tmp);
+       else
+               *pipe = PORT_TO_PIPE(tmp);
+
+       return true;
+}
+
+static void intel_disable_sdvo(struct intel_encoder *encoder)
+{
+       struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+       struct intel_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base);
+       u32 temp;
+
+       intel_sdvo_set_active_outputs(intel_sdvo, 0);
+       if (0)
+               intel_sdvo_set_encoder_power_state(intel_sdvo,
+                                                  DRM_MODE_DPMS_OFF);
+
+       temp = I915_READ(intel_sdvo->sdvo_reg);
+       if ((temp & SDVO_ENABLE) != 0) {
+               intel_sdvo_write_sdvox(intel_sdvo, temp & ~SDVO_ENABLE);
+       }
+}
+
+static void intel_enable_sdvo(struct intel_encoder *encoder)
+{
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base);
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
        u32 temp;
+       bool input1, input2;
+       int i;
+       u8 status;
+
+       temp = I915_READ(intel_sdvo->sdvo_reg);
+       if ((temp & SDVO_ENABLE) == 0)
+               intel_sdvo_write_sdvox(intel_sdvo, temp | SDVO_ENABLE);
+       for (i = 0; i < 2; i++)
+               intel_wait_for_vblank(dev, intel_crtc->pipe);
+
+       status = intel_sdvo_get_trained_inputs(intel_sdvo, &input1, &input2);
+       /* Warn if the device reported failure to sync.
+        * A lot of SDVO devices fail to notify of sync, but it's
+        * a given it the status is a success, we succeeded.
+        */
+       if (status == SDVO_CMD_STATUS_SUCCESS && !input1) {
+               DRM_DEBUG_KMS("First %s output reported failure to "
+                               "sync\n", SDVO_NAME(intel_sdvo));
+       }
+
+       if (0)
+               intel_sdvo_set_encoder_power_state(intel_sdvo,
+                                                  DRM_MODE_DPMS_ON);
+       intel_sdvo_set_active_outputs(intel_sdvo, intel_sdvo->attached_output);
+}
+
+static void intel_sdvo_dpms(struct drm_connector *connector, int mode)
+{
+       struct drm_crtc *crtc;
+       struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
+
+       /* dvo supports only 2 dpms states. */
+       if (mode != DRM_MODE_DPMS_ON)
+               mode = DRM_MODE_DPMS_OFF;
+
+       if (mode == connector->dpms)
+               return;
+
+       connector->dpms = mode;
+
+       /* Only need to change hw state when actually enabled */
+       crtc = intel_sdvo->base.base.crtc;
+       if (!crtc) {
+               intel_sdvo->base.connectors_active = false;
+               return;
+       }
 
        if (mode != DRM_MODE_DPMS_ON) {
                intel_sdvo_set_active_outputs(intel_sdvo, 0);
                if (0)
                        intel_sdvo_set_encoder_power_state(intel_sdvo, mode);
 
-               if (mode == DRM_MODE_DPMS_OFF) {
-                       temp = I915_READ(intel_sdvo->sdvo_reg);
-                       if ((temp & SDVO_ENABLE) != 0) {
-                               intel_sdvo_write_sdvox(intel_sdvo, temp & ~SDVO_ENABLE);
-                       }
-               }
+               intel_sdvo->base.connectors_active = false;
+
+               intel_crtc_update_dpms(crtc);
        } else {
-               bool input1, input2;
-               int i;
-               u8 status;
-
-               temp = I915_READ(intel_sdvo->sdvo_reg);
-               if ((temp & SDVO_ENABLE) == 0)
-                       intel_sdvo_write_sdvox(intel_sdvo, temp | SDVO_ENABLE);
-               for (i = 0; i < 2; i++)
-                       intel_wait_for_vblank(dev, intel_crtc->pipe);
-
-               status = intel_sdvo_get_trained_inputs(intel_sdvo, &input1, &input2);
-               /* Warn if the device reported failure to sync.
-                * A lot of SDVO devices fail to notify of sync, but it's
-                * a given it the status is a success, we succeeded.
-                */
-               if (status == SDVO_CMD_STATUS_SUCCESS && !input1) {
-                       DRM_DEBUG_KMS("First %s output reported failure to "
-                                       "sync\n", SDVO_NAME(intel_sdvo));
-               }
+               intel_sdvo->base.connectors_active = true;
+
+               intel_crtc_update_dpms(crtc);
 
                if (0)
                        intel_sdvo_set_encoder_power_state(intel_sdvo, mode);
                intel_sdvo_set_active_outputs(intel_sdvo, intel_sdvo->attached_output);
        }
-       return;
+
+       intel_modeset_check_state(connector->dev);
 }
 
 static int intel_sdvo_mode_valid(struct drm_connector *connector,
@@ -1251,25 +1340,29 @@ static bool intel_sdvo_get_capabilities(struct intel_sdvo *intel_sdvo, struct in
        return true;
 }
 
-static int intel_sdvo_supports_hotplug(struct intel_sdvo *intel_sdvo)
+static uint16_t intel_sdvo_get_hotplug_support(struct intel_sdvo *intel_sdvo)
 {
        struct drm_device *dev = intel_sdvo->base.base.dev;
-       u8 response[2];
+       uint16_t hotplug;
 
        /* HW Erratum: SDVO Hotplug is broken on all i945G chips, there's noise
         * on the line. */
        if (IS_I945G(dev) || IS_I945GM(dev))
-               return false;
+               return 0;
 
-       return intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_HOT_PLUG_SUPPORT,
-                                   &response, 2) && response[0];
+       if (!intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_HOT_PLUG_SUPPORT,
+                                       &hotplug, sizeof(hotplug)))
+               return 0;
+
+       return hotplug;
 }
 
 static void intel_sdvo_enable_hotplug(struct intel_encoder *encoder)
 {
        struct intel_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base);
 
-       intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &intel_sdvo->hotplug_active, 2);
+       intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_ACTIVE_HOT_PLUG,
+                       &intel_sdvo->hotplug_active, 2);
 }
 
 static bool
@@ -1345,7 +1438,6 @@ intel_sdvo_tmds_sink_detect(struct drm_connector *connector)
                        }
                } else
                        status = connector_status_disconnected;
-               connector->display_info.raw_edid = NULL;
                kfree(edid);
        }
 
@@ -1419,7 +1511,6 @@ intel_sdvo_detect(struct drm_connector *connector, bool force)
                        else
                                ret = connector_status_disconnected;
 
-                       connector->display_info.raw_edid = NULL;
                        kfree(edid);
                } else
                        ret = connector_status_connected;
@@ -1465,7 +1556,6 @@ static void intel_sdvo_get_ddc_modes(struct drm_connector *connector)
                        drm_add_edid_modes(connector, edid);
                }
 
-               connector->display_info.raw_edid = NULL;
                kfree(edid);
        }
 }
@@ -1837,8 +1927,8 @@ set_value:
 done:
        if (intel_sdvo->base.base.crtc) {
                struct drm_crtc *crtc = intel_sdvo->base.base.crtc;
-               drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x,
-                                        crtc->y, crtc->fb);
+               intel_set_mode(crtc, &crtc->mode,
+                              crtc->x, crtc->y, crtc->fb);
        }
 
        return 0;
@@ -1846,15 +1936,13 @@ done:
 }
 
 static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = {
-       .dpms = intel_sdvo_dpms,
        .mode_fixup = intel_sdvo_mode_fixup,
-       .prepare = intel_encoder_prepare,
        .mode_set = intel_sdvo_mode_set,
-       .commit = intel_encoder_commit,
+       .disable = intel_encoder_noop,
 };
 
 static const struct drm_connector_funcs intel_sdvo_connector_funcs = {
-       .dpms = drm_helper_connector_dpms,
+       .dpms = intel_sdvo_dpms,
        .detect = intel_sdvo_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .set_property = intel_sdvo_set_property,
@@ -2026,6 +2114,7 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector,
        connector->base.base.interlace_allowed = 1;
        connector->base.base.doublescan_allowed = 0;
        connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB;
+       connector->base.get_hw_state = intel_sdvo_connector_get_hw_state;
 
        intel_connector_attach_encoder(&connector->base, &encoder->base);
        drm_sysfs_connector_add(&connector->base.base);
@@ -2064,17 +2153,18 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device)
 
        intel_connector = &intel_sdvo_connector->base;
        connector = &intel_connector->base;
-       if (intel_sdvo_supports_hotplug(intel_sdvo) & (1 << device)) {
+       if (intel_sdvo_get_hotplug_support(intel_sdvo) &
+               intel_sdvo_connector->output_flag) {
                connector->polled = DRM_CONNECTOR_POLL_HPD;
-               intel_sdvo->hotplug_active[0] |= 1 << device;
+               intel_sdvo->hotplug_active |= intel_sdvo_connector->output_flag;
                /* Some SDVO devices have one-shot hotplug interrupts.
                 * Ensure that they get re-enabled when an interrupt happens.
                 */
                intel_encoder->hot_plug = intel_sdvo_enable_hotplug;
                intel_sdvo_enable_hotplug(intel_encoder);
-       }
-       else
+       } else {
                connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
+       }
        encoder->encoder_type = DRM_MODE_ENCODER_TMDS;
        connector->connector_type = DRM_MODE_CONNECTOR_DVID;
 
@@ -2082,8 +2172,7 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device)
                connector->connector_type = DRM_MODE_CONNECTOR_HDMIA;
                intel_sdvo->is_hdmi = true;
        }
-       intel_sdvo->base.clone_mask = ((1 << INTEL_SDVO_NON_TV_CLONE_BIT) |
-                                      (1 << INTEL_ANALOG_CLONE_BIT));
+       intel_sdvo->base.cloneable = true;
 
        intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo);
        if (intel_sdvo->is_hdmi)
@@ -2114,7 +2203,7 @@ intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, int type)
 
        intel_sdvo->is_tv = true;
        intel_sdvo->base.needs_tv_clock = true;
-       intel_sdvo->base.clone_mask = 1 << INTEL_SDVO_TV_CLONE_BIT;
+       intel_sdvo->base.cloneable = false;
 
        intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo);
 
@@ -2157,8 +2246,7 @@ intel_sdvo_analog_init(struct intel_sdvo *intel_sdvo, int device)
                intel_sdvo_connector->output_flag = SDVO_OUTPUT_RGB1;
        }
 
-       intel_sdvo->base.clone_mask = ((1 << INTEL_SDVO_NON_TV_CLONE_BIT) |
-                                      (1 << INTEL_ANALOG_CLONE_BIT));
+       intel_sdvo->base.cloneable = true;
 
        intel_sdvo_connector_init(intel_sdvo_connector,
                                  intel_sdvo);
@@ -2190,8 +2278,10 @@ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, int device)
                intel_sdvo_connector->output_flag = SDVO_OUTPUT_LVDS1;
        }
 
-       intel_sdvo->base.clone_mask = ((1 << INTEL_ANALOG_CLONE_BIT) |
-                                      (1 << INTEL_SDVO_LVDS_CLONE_BIT));
+       /* SDVO LVDS is cloneable because the SDVO encoder does the upscaling,
+        * as opposed to native LVDS, where we upscale with the panel-fitter
+        * (and hence only the native LVDS resolution could be cloned). */
+       intel_sdvo->base.cloneable = true;
 
        intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo);
        if (!intel_sdvo_create_enhance_property(intel_sdvo, intel_sdvo_connector))
@@ -2576,6 +2666,10 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob)
 
        drm_encoder_helper_add(&intel_encoder->base, &intel_sdvo_helper_funcs);
 
+       intel_encoder->disable = intel_disable_sdvo;
+       intel_encoder->enable = intel_enable_sdvo;
+       intel_encoder->get_hw_state = intel_sdvo_get_hw_state;
+
        /* In default case sdvo lvds is false */
        if (!intel_sdvo_get_capabilities(intel_sdvo, &intel_sdvo->caps))
                goto err;
@@ -2590,7 +2684,7 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob)
        /* Only enable the hotplug irq if we need it, to work around noisy
         * hotplug lines.
         */
-       if (intel_sdvo->hotplug_active[0])
+       if (intel_sdvo->hotplug_active)
                dev_priv->hotplug_supported_mask |= hotplug_mask;
 
        intel_sdvo_select_ddc_bus(dev_priv, intel_sdvo, sdvo_reg);
index befce6c4970496a821e2a84b8b4e6bda29c04f9d..d2c5c8f3baf35251e5445d97a7c3f11513a8f620 100644 (file)
@@ -836,22 +836,37 @@ static struct intel_tv *intel_attached_tv(struct drm_connector *connector)
                            base);
 }
 
+static bool
+intel_tv_get_hw_state(struct intel_encoder *encoder, enum pipe *pipe)
+{
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 tmp = I915_READ(TV_CTL);
+
+       if (!(tmp & TV_ENC_ENABLE))
+               return false;
+
+       *pipe = PORT_TO_PIPE(tmp);
+
+       return true;
+}
+
 static void
-intel_tv_dpms(struct drm_encoder *encoder, int mode)
+intel_enable_tv(struct intel_encoder *encoder)
 {
-       struct drm_device *dev = encoder->dev;
+       struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
 
-       switch (mode) {
-       case DRM_MODE_DPMS_ON:
-               I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE);
-               break;
-       case DRM_MODE_DPMS_STANDBY:
-       case DRM_MODE_DPMS_SUSPEND:
-       case DRM_MODE_DPMS_OFF:
-               I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE);
-               break;
-       }
+       I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE);
+}
+
+static void
+intel_disable_tv(struct intel_encoder *encoder)
+{
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE);
 }
 
 static const struct tv_mode *
@@ -895,17 +910,14 @@ intel_tv_mode_fixup(struct drm_encoder *encoder,
                    const struct drm_display_mode *mode,
                    struct drm_display_mode *adjusted_mode)
 {
-       struct drm_device *dev = encoder->dev;
        struct intel_tv *intel_tv = enc_to_intel_tv(encoder);
        const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
-       struct intel_encoder *other_encoder;
 
        if (!tv_mode)
                return false;
 
-       for_each_encoder_on_crtc(dev, encoder->crtc, other_encoder)
-               if (&other_encoder->base != encoder)
-                       return false;
+       if (intel_encoder_check_is_cloned(&intel_tv->base))
+               return false;
 
        adjusted_mode->clock = tv_mode->clock;
        return true;
@@ -1303,12 +1315,9 @@ intel_tv_detect(struct drm_connector *connector, bool force)
        if (force) {
                struct intel_load_detect_pipe tmp;
 
-               if (intel_get_load_detect_pipe(&intel_tv->base, connector,
-                                              &mode, &tmp)) {
+               if (intel_get_load_detect_pipe(connector, &mode, &tmp)) {
                        type = intel_tv_detect_type(intel_tv, connector);
-                       intel_release_load_detect_pipe(&intel_tv->base,
-                                                      connector,
-                                                      &tmp);
+                       intel_release_load_detect_pipe(connector, &tmp);
                } else
                        return connector_status_unknown;
        } else
@@ -1474,22 +1483,20 @@ intel_tv_set_property(struct drm_connector *connector, struct drm_property *prop
        }
 
        if (changed && crtc)
-               drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x,
-                               crtc->y, crtc->fb);
+               intel_set_mode(crtc, &crtc->mode,
+                              crtc->x, crtc->y, crtc->fb);
 out:
        return ret;
 }
 
 static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = {
-       .dpms = intel_tv_dpms,
        .mode_fixup = intel_tv_mode_fixup,
-       .prepare = intel_encoder_prepare,
        .mode_set = intel_tv_mode_set,
-       .commit = intel_encoder_commit,
+       .disable = intel_encoder_noop,
 };
 
 static const struct drm_connector_funcs intel_tv_connector_funcs = {
-       .dpms = drm_helper_connector_dpms,
+       .dpms = intel_connector_dpms,
        .detect = intel_tv_detect,
        .destroy = intel_tv_destroy,
        .set_property = intel_tv_set_property,
@@ -1619,10 +1626,15 @@ intel_tv_init(struct drm_device *dev)
        drm_encoder_init(dev, &intel_encoder->base, &intel_tv_enc_funcs,
                         DRM_MODE_ENCODER_TVDAC);
 
+       intel_encoder->enable = intel_enable_tv;
+       intel_encoder->disable = intel_disable_tv;
+       intel_encoder->get_hw_state = intel_tv_get_hw_state;
+       intel_connector->get_hw_state = intel_connector_get_hw_state;
+
        intel_connector_attach_encoder(intel_connector, intel_encoder);
        intel_encoder->type = INTEL_OUTPUT_TVOUT;
        intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
-       intel_encoder->clone_mask = (1 << INTEL_TV_CLONE_BIT);
+       intel_encoder->cloneable = false;
        intel_encoder->base.possible_crtcs = ((1 << 0) | (1 << 1));
        intel_encoder->base.possible_clones = (1 << INTEL_OUTPUT_TVOUT);
        intel_tv->type = DRM_MODE_CONNECTOR_Unknown;
index 6f13b3563234498d08397b254190070d845362d1..d22cbbf3a2025a7596b21c56d208da857b509c90 100644 (file)
@@ -195,7 +195,6 @@ struct mga_device {
                struct drm_global_reference mem_global_ref;
                struct ttm_bo_global_ref bo_global_ref;
                struct ttm_bo_device bdev;
-               atomic_t validate_sequence;
        } ttm;
 
        u32 reg_1e24; /* SE model number */
index b69642d5d850581694fe8741056a5d0529f4eeed..c7420e83c0b98ee98c2e4a86ed9d362f0d224291 100644 (file)
@@ -1399,7 +1399,6 @@ static int mga_vga_get_modes(struct drm_connector *connector)
        if (edid) {
                drm_mode_connector_update_edid_property(connector, edid);
                ret = drm_add_edid_modes(connector, edid);
-               connector->display_info.raw_edid = NULL;
                kfree(edid);
        }
        return ret;
index 7e289d2ad8e40f75d13b504fadbbc25c99cdeac2..e754aa32edf1f6d9538bdad2d40e70ba5b25b41f 100644 (file)
@@ -289,9 +289,7 @@ dp_link_train_update(struct drm_device *dev, struct dp_state *dp, u32 delay)
        if (ret)
                return ret;
 
-       NV_DEBUG_KMS(dev, "status %02x %02x %02x %02x %02x %02x\n",
-                    dp->stat[0], dp->stat[1], dp->stat[2], dp->stat[3],
-                    dp->stat[4], dp->stat[5]);
+       NV_DEBUG_KMS(dev, "status %*ph\n", 6, dp->stat);
        return 0;
 }
 
index f03490534893e4772a75b4b4731b69be6286f744..c399d510b27a8611aa4524b3cffa582f9f033da7 100644 (file)
@@ -22,6 +22,7 @@
  * Authors: Ben Skeggs
  */
 
+#include <linux/dmi.h>
 #include "drmP.h"
 #include "nouveau_drv.h"
 #include "nouveau_hw.h"
@@ -110,13 +111,25 @@ nv50_gpio_isr(struct drm_device *dev)
                nv_wr32(dev, 0xe074, intr1);
 }
 
+static struct dmi_system_id gpio_reset_ids[] = {
+       {
+               .ident = "Apple Macbook 10,1",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro10,1"),
+               }
+       },
+       { }
+};
+
 int
 nv50_gpio_init(struct drm_device *dev)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
 
        /* initialise gpios and routing to vbios defaults */
-       nouveau_gpio_reset(dev);
+       if (dmi_check_system(gpio_reset_ids))
+               nouveau_gpio_reset(dev);
 
        /* disable, and ack any pending gpio interrupts */
        nv_wr32(dev, 0xe050, 0x00000000);
index 3623b98ed3fe2ab529617e37ceb41dfcf4fd5882..d48224b0d6b421838ea6880d58856b191357034a 100644 (file)
@@ -653,9 +653,7 @@ static bool radeon_dp_get_link_status(struct radeon_connector *radeon_connector,
                return false;
        }
 
-       DRM_DEBUG_KMS("link status %02x %02x %02x %02x %02x %02x\n",
-                 link_status[0], link_status[1], link_status[2],
-                 link_status[3], link_status[4], link_status[5]);
+       DRM_DEBUG_KMS("link status %*ph\n", 6, link_status);
        return true;
 }
 
diff --git a/drivers/gpu/drm/shmobile/Kconfig b/drivers/gpu/drm/shmobile/Kconfig
new file mode 100644 (file)
index 0000000..7e7d52b
--- /dev/null
@@ -0,0 +1,10 @@
+config DRM_SHMOBILE
+       tristate "DRM Support for SH Mobile"
+       depends on DRM && (SUPERH || ARCH_SHMOBILE)
+       select DRM_KMS_HELPER
+       select DRM_KMS_CMA_HELPER
+       select DRM_GEM_CMA_HELPER
+       help
+         Choose this option if you have an SH Mobile chipset.
+         If M is selected the module will be called shmob-drm.
+
diff --git a/drivers/gpu/drm/shmobile/Makefile b/drivers/gpu/drm/shmobile/Makefile
new file mode 100644 (file)
index 0000000..4c3eeb3
--- /dev/null
@@ -0,0 +1,7 @@
+shmob-drm-y := shmob_drm_backlight.o \
+              shmob_drm_crtc.o \
+              shmob_drm_drv.o \
+              shmob_drm_kms.o \
+              shmob_drm_plane.o
+
+obj-$(CONFIG_DRM_SHMOBILE)     += shmob-drm.o
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_backlight.c b/drivers/gpu/drm/shmobile/shmob_drm_backlight.c
new file mode 100644 (file)
index 0000000..463aee1
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * shmob_drm_backlight.c  --  SH Mobile DRM Backlight
+ *
+ * Copyright (C) 2012 Renesas Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/backlight.h>
+
+#include "shmob_drm_backlight.h"
+#include "shmob_drm_crtc.h"
+#include "shmob_drm_drv.h"
+
+static int shmob_drm_backlight_update(struct backlight_device *bdev)
+{
+       struct shmob_drm_connector *scon = bl_get_data(bdev);
+       struct shmob_drm_device *sdev = scon->connector.dev->dev_private;
+       const struct shmob_drm_backlight_data *bdata = &sdev->pdata->backlight;
+       int brightness = bdev->props.brightness;
+
+       if (bdev->props.power != FB_BLANK_UNBLANK ||
+           bdev->props.state & BL_CORE_SUSPENDED)
+               brightness = 0;
+
+       return bdata->set_brightness(brightness);
+}
+
+static int shmob_drm_backlight_get_brightness(struct backlight_device *bdev)
+{
+       struct shmob_drm_connector *scon = bl_get_data(bdev);
+       struct shmob_drm_device *sdev = scon->connector.dev->dev_private;
+       const struct shmob_drm_backlight_data *bdata = &sdev->pdata->backlight;
+
+       return bdata->get_brightness();
+}
+
+static const struct backlight_ops shmob_drm_backlight_ops = {
+       .options        = BL_CORE_SUSPENDRESUME,
+       .update_status  = shmob_drm_backlight_update,
+       .get_brightness = shmob_drm_backlight_get_brightness,
+};
+
+void shmob_drm_backlight_dpms(struct shmob_drm_connector *scon, int mode)
+{
+       if (scon->backlight == NULL)
+               return;
+
+       scon->backlight->props.power = mode == DRM_MODE_DPMS_ON
+                                    ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
+       backlight_update_status(scon->backlight);
+}
+
+int shmob_drm_backlight_init(struct shmob_drm_connector *scon)
+{
+       struct shmob_drm_device *sdev = scon->connector.dev->dev_private;
+       const struct shmob_drm_backlight_data *bdata = &sdev->pdata->backlight;
+       struct drm_connector *connector = &scon->connector;
+       struct drm_device *dev = connector->dev;
+       struct backlight_device *backlight;
+
+       if (!bdata->max_brightness)
+               return 0;
+
+       backlight = backlight_device_register(bdata->name, dev->dev, scon,
+                                             &shmob_drm_backlight_ops, NULL);
+       if (IS_ERR(backlight)) {
+               dev_err(dev->dev, "unable to register backlight device: %ld\n",
+                       PTR_ERR(backlight));
+               return PTR_ERR(backlight);
+       }
+
+       backlight->props.max_brightness = bdata->max_brightness;
+       backlight->props.brightness = bdata->max_brightness;
+       backlight->props.power = FB_BLANK_POWERDOWN;
+       backlight_update_status(backlight);
+
+       scon->backlight = backlight;
+       return 0;
+}
+
+void shmob_drm_backlight_exit(struct shmob_drm_connector *scon)
+{
+       backlight_device_unregister(scon->backlight);
+}
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_backlight.h b/drivers/gpu/drm/shmobile/shmob_drm_backlight.h
new file mode 100644 (file)
index 0000000..9477595
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * shmob_drm_backlight.h  --  SH Mobile DRM Backlight
+ *
+ * Copyright (C) 2012 Renesas Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __SHMOB_DRM_BACKLIGHT_H__
+#define __SHMOB_DRM_BACKLIGHT_H__
+
+struct shmob_drm_connector;
+
+void shmob_drm_backlight_dpms(struct shmob_drm_connector *scon, int mode);
+int shmob_drm_backlight_init(struct shmob_drm_connector *scon);
+void shmob_drm_backlight_exit(struct shmob_drm_connector *scon);
+
+#endif /* __SHMOB_DRM_BACKLIGHT_H__ */
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
new file mode 100644 (file)
index 0000000..0e7a930
--- /dev/null
@@ -0,0 +1,763 @@
+/*
+ * shmob_drm_crtc.c  --  SH Mobile DRM CRTCs
+ *
+ * Copyright (C) 2012 Renesas Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/backlight.h>
+#include <linux/clk.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include <video/sh_mobile_meram.h>
+
+#include "shmob_drm_backlight.h"
+#include "shmob_drm_crtc.h"
+#include "shmob_drm_drv.h"
+#include "shmob_drm_kms.h"
+#include "shmob_drm_plane.h"
+#include "shmob_drm_regs.h"
+
+/*
+ * TODO: panel support
+ */
+
+/* -----------------------------------------------------------------------------
+ * Clock management
+ */
+
+static void shmob_drm_clk_on(struct shmob_drm_device *sdev)
+{
+       if (sdev->clock)
+               clk_enable(sdev->clock);
+#if 0
+       if (sdev->meram_dev && sdev->meram_dev->pdev)
+               pm_runtime_get_sync(&sdev->meram_dev->pdev->dev);
+#endif
+}
+
+static void shmob_drm_clk_off(struct shmob_drm_device *sdev)
+{
+#if 0
+       if (sdev->meram_dev && sdev->meram_dev->pdev)
+               pm_runtime_put_sync(&sdev->meram_dev->pdev->dev);
+#endif
+       if (sdev->clock)
+               clk_disable(sdev->clock);
+}
+
+/* -----------------------------------------------------------------------------
+ * CRTC
+ */
+
+static void shmob_drm_crtc_setup_geometry(struct shmob_drm_crtc *scrtc)
+{
+       struct drm_crtc *crtc = &scrtc->crtc;
+       struct shmob_drm_device *sdev = crtc->dev->dev_private;
+       const struct shmob_drm_interface_data *idata = &sdev->pdata->iface;
+       const struct drm_display_mode *mode = &crtc->mode;
+       u32 value;
+
+       value = sdev->ldmt1r
+             | ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : LDMT1R_VPOL)
+             | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : LDMT1R_HPOL)
+             | ((idata->flags & SHMOB_DRM_IFACE_FL_DWPOL) ? LDMT1R_DWPOL : 0)
+             | ((idata->flags & SHMOB_DRM_IFACE_FL_DIPOL) ? LDMT1R_DIPOL : 0)
+             | ((idata->flags & SHMOB_DRM_IFACE_FL_DAPOL) ? LDMT1R_DAPOL : 0)
+             | ((idata->flags & SHMOB_DRM_IFACE_FL_HSCNT) ? LDMT1R_HSCNT : 0)
+             | ((idata->flags & SHMOB_DRM_IFACE_FL_DWCNT) ? LDMT1R_DWCNT : 0);
+       lcdc_write(sdev, LDMT1R, value);
+
+       if (idata->interface >= SHMOB_DRM_IFACE_SYS8A &&
+           idata->interface <= SHMOB_DRM_IFACE_SYS24) {
+               /* Setup SYS bus. */
+               value = (idata->sys.cs_setup << LDMT2R_CSUP_SHIFT)
+                     | (idata->sys.vsync_active_high ? LDMT2R_RSV : 0)
+                     | (idata->sys.vsync_dir_input ? LDMT2R_VSEL : 0)
+                     | (idata->sys.write_setup << LDMT2R_WCSC_SHIFT)
+                     | (idata->sys.write_cycle << LDMT2R_WCEC_SHIFT)
+                     | (idata->sys.write_strobe << LDMT2R_WCLW_SHIFT);
+               lcdc_write(sdev, LDMT2R, value);
+
+               value = (idata->sys.read_latch << LDMT3R_RDLC_SHIFT)
+                     | (idata->sys.read_setup << LDMT3R_RCSC_SHIFT)
+                     | (idata->sys.read_cycle << LDMT3R_RCEC_SHIFT)
+                     | (idata->sys.read_strobe << LDMT3R_RCLW_SHIFT);
+               lcdc_write(sdev, LDMT3R, value);
+       }
+
+       value = ((mode->hdisplay / 8) << 16)                    /* HDCN */
+             | (mode->htotal / 8);                             /* HTCN */
+       lcdc_write(sdev, LDHCNR, value);
+
+       value = (((mode->hsync_end - mode->hsync_start) / 8) << 16) /* HSYNW */
+             | (mode->hsync_start / 8);                        /* HSYNP */
+       lcdc_write(sdev, LDHSYNR, value);
+
+       value = ((mode->hdisplay & 7) << 24) | ((mode->htotal & 7) << 16)
+             | (((mode->hsync_end - mode->hsync_start) & 7) << 8)
+             | (mode->hsync_start & 7);
+       lcdc_write(sdev, LDHAJR, value);
+
+       value = ((mode->vdisplay) << 16)                        /* VDLN */
+             | mode->vtotal;                                   /* VTLN */
+       lcdc_write(sdev, LDVLNR, value);
+
+       value = ((mode->vsync_end - mode->vsync_start) << 16)   /* VSYNW */
+             | mode->vsync_start;                              /* VSYNP */
+       lcdc_write(sdev, LDVSYNR, value);
+}
+
+static void shmob_drm_crtc_start_stop(struct shmob_drm_crtc *scrtc, bool start)
+{
+       struct shmob_drm_device *sdev = scrtc->crtc.dev->dev_private;
+       u32 value;
+
+       value = lcdc_read(sdev, LDCNT2R);
+       if (start)
+               lcdc_write(sdev, LDCNT2R, value | LDCNT2R_DO);
+       else
+               lcdc_write(sdev, LDCNT2R, value & ~LDCNT2R_DO);
+
+       /* Wait until power is applied/stopped. */
+       while (1) {
+               value = lcdc_read(sdev, LDPMR) & LDPMR_LPS;
+               if ((start && value) || (!start && !value))
+                       break;
+
+               cpu_relax();
+       }
+
+       if (!start) {
+               /* Stop the dot clock. */
+               lcdc_write(sdev, LDDCKSTPR, LDDCKSTPR_DCKSTP);
+       }
+}
+
+/*
+ * shmob_drm_crtc_start - Configure and start the LCDC
+ * @scrtc: the SH Mobile CRTC
+ *
+ * Configure and start the LCDC device. External devices (clocks, MERAM, panels,
+ * ...) are not touched by this function.
+ */
+static void shmob_drm_crtc_start(struct shmob_drm_crtc *scrtc)
+{
+       struct drm_crtc *crtc = &scrtc->crtc;
+       struct shmob_drm_device *sdev = crtc->dev->dev_private;
+       const struct shmob_drm_interface_data *idata = &sdev->pdata->iface;
+       const struct shmob_drm_format_info *format;
+       struct drm_device *dev = sdev->ddev;
+       struct drm_plane *plane;
+       u32 value;
+
+       if (scrtc->started)
+               return;
+
+       format = shmob_drm_format_info(crtc->fb->pixel_format);
+       if (WARN_ON(format == NULL))
+               return;
+
+       /* Enable clocks before accessing the hardware. */
+       shmob_drm_clk_on(sdev);
+
+       /* Reset and enable the LCDC. */
+       lcdc_write(sdev, LDCNT2R, lcdc_read(sdev, LDCNT2R) | LDCNT2R_BR);
+       lcdc_wait_bit(sdev, LDCNT2R, LDCNT2R_BR, 0);
+       lcdc_write(sdev, LDCNT2R, LDCNT2R_ME);
+
+       /* Stop the LCDC first and disable all interrupts. */
+       shmob_drm_crtc_start_stop(scrtc, false);
+       lcdc_write(sdev, LDINTR, 0);
+
+       /* Configure power supply, dot clocks and start them. */
+       lcdc_write(sdev, LDPMR, 0);
+
+       value = sdev->lddckr;
+       if (idata->clk_div) {
+               /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider
+                * denominator.
+                */
+               lcdc_write(sdev, LDDCKPAT1R, 0);
+               lcdc_write(sdev, LDDCKPAT2R, (1 << (idata->clk_div / 2)) - 1);
+
+               if (idata->clk_div == 1)
+                       value |= LDDCKR_MOSEL;
+               else
+                       value |= idata->clk_div;
+       }
+
+       lcdc_write(sdev, LDDCKR, value);
+       lcdc_write(sdev, LDDCKSTPR, 0);
+       lcdc_wait_bit(sdev, LDDCKSTPR, ~0, 0);
+
+       /* TODO: Setup SYS panel */
+
+       /* Setup geometry, format, frame buffer memory and operation mode. */
+       shmob_drm_crtc_setup_geometry(scrtc);
+
+       /* TODO: Handle YUV colorspaces. Hardcode REC709 for now. */
+       lcdc_write(sdev, LDDFR, format->lddfr | LDDFR_CF1);
+       lcdc_write(sdev, LDMLSR, scrtc->line_size);
+       lcdc_write(sdev, LDSA1R, scrtc->dma[0]);
+       if (format->yuv)
+               lcdc_write(sdev, LDSA2R, scrtc->dma[1]);
+       lcdc_write(sdev, LDSM1R, 0);
+
+       /* Word and long word swap. */
+       switch (format->fourcc) {
+       case DRM_FORMAT_RGB565:
+       case DRM_FORMAT_NV21:
+       case DRM_FORMAT_NV61:
+       case DRM_FORMAT_NV42:
+               value = LDDDSR_LS | LDDDSR_WS;
+               break;
+       case DRM_FORMAT_RGB888:
+       case DRM_FORMAT_NV12:
+       case DRM_FORMAT_NV16:
+       case DRM_FORMAT_NV24:
+               value = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS;
+               break;
+       case DRM_FORMAT_ARGB8888:
+       default:
+               value = LDDDSR_LS;
+               break;
+       }
+       lcdc_write(sdev, LDDDSR, value);
+
+       /* Setup planes. */
+       list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+               if (plane->crtc == crtc)
+                       shmob_drm_plane_setup(plane);
+       }
+
+       /* Enable the display output. */
+       lcdc_write(sdev, LDCNT1R, LDCNT1R_DE);
+
+       shmob_drm_crtc_start_stop(scrtc, true);
+
+       scrtc->started = true;
+}
+
+static void shmob_drm_crtc_stop(struct shmob_drm_crtc *scrtc)
+{
+       struct drm_crtc *crtc = &scrtc->crtc;
+       struct shmob_drm_device *sdev = crtc->dev->dev_private;
+
+       if (!scrtc->started)
+               return;
+
+       /* Disable the MERAM cache. */
+       if (scrtc->cache) {
+               sh_mobile_meram_cache_free(sdev->meram, scrtc->cache);
+               scrtc->cache = NULL;
+       }
+
+       /* Stop the LCDC. */
+       shmob_drm_crtc_start_stop(scrtc, false);
+
+       /* Disable the display output. */
+       lcdc_write(sdev, LDCNT1R, 0);
+
+       /* Stop clocks. */
+       shmob_drm_clk_off(sdev);
+
+       scrtc->started = false;
+}
+
+void shmob_drm_crtc_suspend(struct shmob_drm_crtc *scrtc)
+{
+       shmob_drm_crtc_stop(scrtc);
+}
+
+void shmob_drm_crtc_resume(struct shmob_drm_crtc *scrtc)
+{
+       if (scrtc->dpms != DRM_MODE_DPMS_ON)
+               return;
+
+       shmob_drm_crtc_start(scrtc);
+}
+
+static void shmob_drm_crtc_compute_base(struct shmob_drm_crtc *scrtc,
+                                       int x, int y)
+{
+       struct drm_crtc *crtc = &scrtc->crtc;
+       struct drm_framebuffer *fb = crtc->fb;
+       struct shmob_drm_device *sdev = crtc->dev->dev_private;
+       struct drm_gem_cma_object *gem;
+       unsigned int bpp;
+
+       bpp = scrtc->format->yuv ? 8 : scrtc->format->bpp;
+       gem = drm_fb_cma_get_gem_obj(fb, 0);
+       scrtc->dma[0] = gem->paddr + fb->offsets[0]
+                     + y * fb->pitches[0] + x * bpp / 8;
+
+       if (scrtc->format->yuv) {
+               bpp = scrtc->format->bpp - 8;
+               gem = drm_fb_cma_get_gem_obj(fb, 1);
+               scrtc->dma[1] = gem->paddr + fb->offsets[1]
+                             + y / (bpp == 4 ? 2 : 1) * fb->pitches[1]
+                             + x * (bpp == 16 ? 2 : 1);
+       }
+
+       if (scrtc->cache)
+               sh_mobile_meram_cache_update(sdev->meram, scrtc->cache,
+                                            scrtc->dma[0], scrtc->dma[1],
+                                            &scrtc->dma[0], &scrtc->dma[1]);
+}
+
+static void shmob_drm_crtc_update_base(struct shmob_drm_crtc *scrtc)
+{
+       struct drm_crtc *crtc = &scrtc->crtc;
+       struct shmob_drm_device *sdev = crtc->dev->dev_private;
+
+       shmob_drm_crtc_compute_base(scrtc, crtc->x, crtc->y);
+
+       lcdc_write_mirror(sdev, LDSA1R, scrtc->dma[0]);
+       if (scrtc->format->yuv)
+               lcdc_write_mirror(sdev, LDSA2R, scrtc->dma[1]);
+
+       lcdc_write(sdev, LDRCNTR, lcdc_read(sdev, LDRCNTR) ^ LDRCNTR_MRS);
+}
+
+#define to_shmob_crtc(c)       container_of(c, struct shmob_drm_crtc, crtc)
+
+static void shmob_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+       struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc);
+
+       if (scrtc->dpms == mode)
+               return;
+
+       if (mode == DRM_MODE_DPMS_ON)
+               shmob_drm_crtc_start(scrtc);
+       else
+               shmob_drm_crtc_stop(scrtc);
+
+       scrtc->dpms = mode;
+}
+
+static bool shmob_drm_crtc_mode_fixup(struct drm_crtc *crtc,
+                                     const struct drm_display_mode *mode,
+                                     struct drm_display_mode *adjusted_mode)
+{
+       return true;
+}
+
+static void shmob_drm_crtc_mode_prepare(struct drm_crtc *crtc)
+{
+       shmob_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+}
+
+static int shmob_drm_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 shmob_drm_crtc *scrtc = to_shmob_crtc(crtc);
+       struct shmob_drm_device *sdev = crtc->dev->dev_private;
+       const struct sh_mobile_meram_cfg *mdata = sdev->pdata->meram;
+       const struct shmob_drm_format_info *format;
+       void *cache;
+
+       format = shmob_drm_format_info(crtc->fb->pixel_format);
+       if (format == NULL) {
+               dev_dbg(sdev->dev, "mode_set: unsupported format %08x\n",
+                       crtc->fb->pixel_format);
+               return -EINVAL;
+       }
+
+       scrtc->format = format;
+       scrtc->line_size = crtc->fb->pitches[0];
+
+       if (sdev->meram) {
+               /* Enable MERAM cache if configured. We need to de-init
+                * configured ICBs before we can re-initialize them.
+                */
+               if (scrtc->cache) {
+                       sh_mobile_meram_cache_free(sdev->meram, scrtc->cache);
+                       scrtc->cache = NULL;
+               }
+
+               cache = sh_mobile_meram_cache_alloc(sdev->meram, mdata,
+                                                   crtc->fb->pitches[0],
+                                                   adjusted_mode->vdisplay,
+                                                   format->meram,
+                                                   &scrtc->line_size);
+               if (!IS_ERR(cache))
+                       scrtc->cache = cache;
+       }
+
+       shmob_drm_crtc_compute_base(scrtc, x, y);
+
+       return 0;
+}
+
+static void shmob_drm_crtc_mode_commit(struct drm_crtc *crtc)
+{
+       shmob_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+static int shmob_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+                                       struct drm_framebuffer *old_fb)
+{
+       shmob_drm_crtc_update_base(to_shmob_crtc(crtc));
+
+       return 0;
+}
+
+static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
+       .dpms = shmob_drm_crtc_dpms,
+       .mode_fixup = shmob_drm_crtc_mode_fixup,
+       .prepare = shmob_drm_crtc_mode_prepare,
+       .commit = shmob_drm_crtc_mode_commit,
+       .mode_set = shmob_drm_crtc_mode_set,
+       .mode_set_base = shmob_drm_crtc_mode_set_base,
+};
+
+void shmob_drm_crtc_cancel_page_flip(struct shmob_drm_crtc *scrtc,
+                                    struct drm_file *file)
+{
+       struct drm_pending_vblank_event *event;
+       struct drm_device *dev = scrtc->crtc.dev;
+       unsigned long flags;
+
+       /* Destroy the pending vertical blanking event associated with the
+        * pending page flip, if any, and disable vertical blanking interrupts.
+        */
+       spin_lock_irqsave(&dev->event_lock, flags);
+       event = scrtc->event;
+       if (event && event->base.file_priv == file) {
+               scrtc->event = NULL;
+               event->base.destroy(&event->base);
+               drm_vblank_put(dev, 0);
+       }
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+void shmob_drm_crtc_finish_page_flip(struct shmob_drm_crtc *scrtc)
+{
+       struct drm_pending_vblank_event *event;
+       struct drm_device *dev = scrtc->crtc.dev;
+       struct timeval vblanktime;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       event = scrtc->event;
+       scrtc->event = NULL;
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       if (event == NULL)
+               return;
+
+       event->event.sequence = drm_vblank_count_and_time(dev, 0, &vblanktime);
+       event->event.tv_sec = vblanktime.tv_sec;
+       event->event.tv_usec = vblanktime.tv_usec;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       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);
+
+       drm_vblank_put(dev, 0);
+}
+
+static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc,
+                                   struct drm_framebuffer *fb,
+                                   struct drm_pending_vblank_event *event)
+{
+       struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc);
+       struct drm_device *dev = scrtc->crtc.dev;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       if (scrtc->event != NULL) {
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+               return -EBUSY;
+       }
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       crtc->fb = fb;
+       shmob_drm_crtc_update_base(scrtc);
+
+       if (event) {
+               event->pipe = 0;
+               spin_lock_irqsave(&dev->event_lock, flags);
+               scrtc->event = event;
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+               drm_vblank_get(dev, 0);
+       }
+
+       return 0;
+}
+
+static const struct drm_crtc_funcs crtc_funcs = {
+       .destroy = drm_crtc_cleanup,
+       .set_config = drm_crtc_helper_set_config,
+       .page_flip = shmob_drm_crtc_page_flip,
+};
+
+int shmob_drm_crtc_create(struct shmob_drm_device *sdev)
+{
+       struct drm_crtc *crtc = &sdev->crtc.crtc;
+       int ret;
+
+       sdev->crtc.dpms = DRM_MODE_DPMS_OFF;
+
+       ret = drm_crtc_init(sdev->ddev, crtc, &crtc_funcs);
+       if (ret < 0)
+               return ret;
+
+       drm_crtc_helper_add(crtc, &crtc_helper_funcs);
+
+       return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Encoder
+ */
+
+#define to_shmob_encoder(e) \
+       container_of(e, struct shmob_drm_encoder, encoder)
+
+static void shmob_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+       struct shmob_drm_encoder *senc = to_shmob_encoder(encoder);
+       struct shmob_drm_device *sdev = encoder->dev->dev_private;
+       struct shmob_drm_connector *scon = &sdev->connector;
+
+       if (senc->dpms == mode)
+               return;
+
+       shmob_drm_backlight_dpms(scon, mode);
+
+       senc->dpms = mode;
+}
+
+static bool shmob_drm_encoder_mode_fixup(struct drm_encoder *encoder,
+                                        const struct drm_display_mode *mode,
+                                        struct drm_display_mode *adjusted_mode)
+{
+       struct drm_device *dev = encoder->dev;
+       struct shmob_drm_device *sdev = dev->dev_private;
+       struct drm_connector *connector = &sdev->connector.connector;
+       const struct drm_display_mode *panel_mode;
+
+       if (list_empty(&connector->modes)) {
+               dev_dbg(dev->dev, "mode_fixup: empty modes list\n");
+               return false;
+       }
+
+       /* The flat panel mode is fixed, just copy it to the adjusted mode. */
+       panel_mode = list_first_entry(&connector->modes,
+                                     struct drm_display_mode, head);
+       drm_mode_copy(adjusted_mode, panel_mode);
+
+       return true;
+}
+
+static void shmob_drm_encoder_mode_prepare(struct drm_encoder *encoder)
+{
+       /* No-op, everything is handled in the CRTC code. */
+}
+
+static void shmob_drm_encoder_mode_set(struct drm_encoder *encoder,
+                                      struct drm_display_mode *mode,
+                                      struct drm_display_mode *adjusted_mode)
+{
+       /* No-op, everything is handled in the CRTC code. */
+}
+
+static void shmob_drm_encoder_mode_commit(struct drm_encoder *encoder)
+{
+       /* No-op, everything is handled in the CRTC code. */
+}
+
+static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
+       .dpms = shmob_drm_encoder_dpms,
+       .mode_fixup = shmob_drm_encoder_mode_fixup,
+       .prepare = shmob_drm_encoder_mode_prepare,
+       .commit = shmob_drm_encoder_mode_commit,
+       .mode_set = shmob_drm_encoder_mode_set,
+};
+
+static void shmob_drm_encoder_destroy(struct drm_encoder *encoder)
+{
+       drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs encoder_funcs = {
+       .destroy = shmob_drm_encoder_destroy,
+};
+
+int shmob_drm_encoder_create(struct shmob_drm_device *sdev)
+{
+       struct drm_encoder *encoder = &sdev->encoder.encoder;
+       int ret;
+
+       sdev->encoder.dpms = DRM_MODE_DPMS_OFF;
+
+       encoder->possible_crtcs = 1;
+
+       ret = drm_encoder_init(sdev->ddev, encoder, &encoder_funcs,
+                              DRM_MODE_ENCODER_LVDS);
+       if (ret < 0)
+               return ret;
+
+       drm_encoder_helper_add(encoder, &encoder_helper_funcs);
+
+       return 0;
+}
+
+void shmob_drm_crtc_enable_vblank(struct shmob_drm_device *sdev, bool enable)
+{
+       unsigned long flags;
+       u32 ldintr;
+
+       /* Be careful not to acknowledge any pending interrupt. */
+       spin_lock_irqsave(&sdev->irq_lock, flags);
+       ldintr = lcdc_read(sdev, LDINTR) | LDINTR_STATUS_MASK;
+       if (enable)
+               ldintr |= LDINTR_VEE;
+       else
+               ldintr &= ~LDINTR_VEE;
+       lcdc_write(sdev, LDINTR, ldintr);
+       spin_unlock_irqrestore(&sdev->irq_lock, flags);
+}
+
+/* -----------------------------------------------------------------------------
+ * Connector
+ */
+
+#define to_shmob_connector(c) \
+       container_of(c, struct shmob_drm_connector, connector)
+
+static int shmob_drm_connector_get_modes(struct drm_connector *connector)
+{
+       struct shmob_drm_device *sdev = connector->dev->dev_private;
+       struct drm_display_mode *mode;
+
+       mode = drm_mode_create(connector->dev);
+       if (mode == NULL)
+               return 0;
+
+       mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
+       mode->clock = sdev->pdata->panel.mode.clock;
+       mode->hdisplay = sdev->pdata->panel.mode.hdisplay;
+       mode->hsync_start = sdev->pdata->panel.mode.hsync_start;
+       mode->hsync_end = sdev->pdata->panel.mode.hsync_end;
+       mode->htotal = sdev->pdata->panel.mode.htotal;
+       mode->vdisplay = sdev->pdata->panel.mode.vdisplay;
+       mode->vsync_start = sdev->pdata->panel.mode.vsync_start;
+       mode->vsync_end = sdev->pdata->panel.mode.vsync_end;
+       mode->vtotal = sdev->pdata->panel.mode.vtotal;
+       mode->flags = sdev->pdata->panel.mode.flags;
+
+       drm_mode_set_name(mode);
+       drm_mode_probed_add(connector, mode);
+
+       connector->display_info.width_mm = sdev->pdata->panel.width_mm;
+       connector->display_info.height_mm = sdev->pdata->panel.height_mm;
+
+       return 1;
+}
+
+static int shmob_drm_connector_mode_valid(struct drm_connector *connector,
+                                         struct drm_display_mode *mode)
+{
+       return MODE_OK;
+}
+
+static struct drm_encoder *
+shmob_drm_connector_best_encoder(struct drm_connector *connector)
+{
+       struct shmob_drm_connector *scon = to_shmob_connector(connector);
+
+       return scon->encoder;
+}
+
+static const struct drm_connector_helper_funcs connector_helper_funcs = {
+       .get_modes = shmob_drm_connector_get_modes,
+       .mode_valid = shmob_drm_connector_mode_valid,
+       .best_encoder = shmob_drm_connector_best_encoder,
+};
+
+static void shmob_drm_connector_destroy(struct drm_connector *connector)
+{
+       struct shmob_drm_connector *scon = to_shmob_connector(connector);
+
+       shmob_drm_backlight_exit(scon);
+       drm_sysfs_connector_remove(connector);
+       drm_connector_cleanup(connector);
+}
+
+static enum drm_connector_status
+shmob_drm_connector_detect(struct drm_connector *connector, bool force)
+{
+       return connector_status_connected;
+}
+
+static const struct drm_connector_funcs connector_funcs = {
+       .dpms = drm_helper_connector_dpms,
+       .detect = shmob_drm_connector_detect,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .destroy = shmob_drm_connector_destroy,
+};
+
+int shmob_drm_connector_create(struct shmob_drm_device *sdev,
+                              struct drm_encoder *encoder)
+{
+       struct drm_connector *connector = &sdev->connector.connector;
+       int ret;
+
+       sdev->connector.encoder = encoder;
+
+       connector->display_info.width_mm = sdev->pdata->panel.width_mm;
+       connector->display_info.height_mm = sdev->pdata->panel.height_mm;
+
+       ret = drm_connector_init(sdev->ddev, connector, &connector_funcs,
+                                DRM_MODE_CONNECTOR_LVDS);
+       if (ret < 0)
+               return ret;
+
+       drm_connector_helper_add(connector, &connector_helper_funcs);
+       ret = drm_sysfs_connector_add(connector);
+       if (ret < 0)
+               goto err_cleanup;
+
+       ret = shmob_drm_backlight_init(&sdev->connector);
+       if (ret < 0)
+               goto err_sysfs;
+
+       ret = drm_mode_connector_attach_encoder(connector, encoder);
+       if (ret < 0)
+               goto err_backlight;
+
+       connector->encoder = encoder;
+
+       drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+       drm_connector_property_set_value(connector,
+               sdev->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
+
+       return 0;
+
+err_backlight:
+       shmob_drm_backlight_exit(&sdev->connector);
+err_sysfs:
+       drm_sysfs_connector_remove(connector);
+err_cleanup:
+       drm_connector_cleanup(connector);
+       return ret;
+}
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.h b/drivers/gpu/drm/shmobile/shmob_drm_crtc.h
new file mode 100644 (file)
index 0000000..e5bd109
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * shmob_drm_crtc.h  --  SH Mobile DRM CRTCs
+ *
+ * Copyright (C) 2012 Renesas Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __SHMOB_DRM_CRTC_H__
+#define __SHMOB_DRM_CRTC_H__
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+
+struct backlight_device;
+struct shmob_drm_device;
+
+struct shmob_drm_crtc {
+       struct drm_crtc crtc;
+
+       struct drm_pending_vblank_event *event;
+       int dpms;
+
+       const struct shmob_drm_format_info *format;
+       void *cache;
+       unsigned long dma[2];
+       unsigned int line_size;
+       bool started;
+};
+
+struct shmob_drm_encoder {
+       struct drm_encoder encoder;
+       int dpms;
+};
+
+struct shmob_drm_connector {
+       struct drm_connector connector;
+       struct drm_encoder *encoder;
+
+       struct backlight_device *backlight;
+};
+
+int shmob_drm_crtc_create(struct shmob_drm_device *sdev);
+void shmob_drm_crtc_enable_vblank(struct shmob_drm_device *sdev, bool enable);
+void shmob_drm_crtc_cancel_page_flip(struct shmob_drm_crtc *scrtc,
+                                    struct drm_file *file);
+void shmob_drm_crtc_finish_page_flip(struct shmob_drm_crtc *scrtc);
+void shmob_drm_crtc_suspend(struct shmob_drm_crtc *scrtc);
+void shmob_drm_crtc_resume(struct shmob_drm_crtc *scrtc);
+
+int shmob_drm_encoder_create(struct shmob_drm_device *sdev);
+int shmob_drm_connector_create(struct shmob_drm_device *sdev,
+                              struct drm_encoder *encoder);
+
+#endif /* __SHMOB_DRM_CRTC_H__ */
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c
new file mode 100644 (file)
index 0000000..c71d493
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+ * shmob_drm_drv.c  --  SH Mobile DRM driver
+ *
+ * Copyright (C) 2012 Renesas Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "shmob_drm_crtc.h"
+#include "shmob_drm_drv.h"
+#include "shmob_drm_kms.h"
+#include "shmob_drm_plane.h"
+#include "shmob_drm_regs.h"
+
+/* -----------------------------------------------------------------------------
+ * Hardware initialization
+ */
+
+static int __devinit shmob_drm_init_interface(struct shmob_drm_device *sdev)
+{
+       static const u32 ldmt1r[] = {
+               [SHMOB_DRM_IFACE_RGB8] = LDMT1R_MIFTYP_RGB8,
+               [SHMOB_DRM_IFACE_RGB9] = LDMT1R_MIFTYP_RGB9,
+               [SHMOB_DRM_IFACE_RGB12A] = LDMT1R_MIFTYP_RGB12A,
+               [SHMOB_DRM_IFACE_RGB12B] = LDMT1R_MIFTYP_RGB12B,
+               [SHMOB_DRM_IFACE_RGB16] = LDMT1R_MIFTYP_RGB16,
+               [SHMOB_DRM_IFACE_RGB18] = LDMT1R_MIFTYP_RGB18,
+               [SHMOB_DRM_IFACE_RGB24] = LDMT1R_MIFTYP_RGB24,
+               [SHMOB_DRM_IFACE_YUV422] = LDMT1R_MIFTYP_YCBCR,
+               [SHMOB_DRM_IFACE_SYS8A] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8A,
+               [SHMOB_DRM_IFACE_SYS8B] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8B,
+               [SHMOB_DRM_IFACE_SYS8C] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8C,
+               [SHMOB_DRM_IFACE_SYS8D] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8D,
+               [SHMOB_DRM_IFACE_SYS9] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS9,
+               [SHMOB_DRM_IFACE_SYS12] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS12,
+               [SHMOB_DRM_IFACE_SYS16A] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16A,
+               [SHMOB_DRM_IFACE_SYS16B] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16B,
+               [SHMOB_DRM_IFACE_SYS16C] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16C,
+               [SHMOB_DRM_IFACE_SYS18] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS18,
+               [SHMOB_DRM_IFACE_SYS24] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS24,
+       };
+
+       if (sdev->pdata->iface.interface >= ARRAY_SIZE(ldmt1r)) {
+               dev_err(sdev->dev, "invalid interface type %u\n",
+                       sdev->pdata->iface.interface);
+               return -EINVAL;
+       }
+
+       sdev->ldmt1r = ldmt1r[sdev->pdata->iface.interface];
+       return 0;
+}
+
+static int __devinit shmob_drm_setup_clocks(struct shmob_drm_device *sdev,
+                                           enum shmob_drm_clk_source clksrc)
+{
+       struct clk *clk;
+       char *clkname;
+
+       switch (clksrc) {
+       case SHMOB_DRM_CLK_BUS:
+               clkname = "bus_clk";
+               sdev->lddckr = LDDCKR_ICKSEL_BUS;
+               break;
+       case SHMOB_DRM_CLK_PERIPHERAL:
+               clkname = "peripheral_clk";
+               sdev->lddckr = LDDCKR_ICKSEL_MIPI;
+               break;
+       case SHMOB_DRM_CLK_EXTERNAL:
+               clkname = NULL;
+               sdev->lddckr = LDDCKR_ICKSEL_HDMI;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       clk = clk_get(sdev->dev, clkname);
+       if (IS_ERR(clk)) {
+               dev_err(sdev->dev, "cannot get dot clock %s\n", clkname);
+               return PTR_ERR(clk);
+       }
+
+       sdev->clock = clk;
+       return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DRM operations
+ */
+
+static int shmob_drm_unload(struct drm_device *dev)
+{
+       struct shmob_drm_device *sdev = dev->dev_private;
+
+       drm_kms_helper_poll_fini(dev);
+       drm_mode_config_cleanup(dev);
+       drm_vblank_cleanup(dev);
+       drm_irq_uninstall(dev);
+
+       if (sdev->clock)
+               clk_put(sdev->clock);
+
+       if (sdev->mmio)
+               iounmap(sdev->mmio);
+
+       dev->dev_private = NULL;
+       kfree(sdev);
+
+       return 0;
+}
+
+static int shmob_drm_load(struct drm_device *dev, unsigned long flags)
+{
+       struct shmob_drm_platform_data *pdata = dev->dev->platform_data;
+       struct platform_device *pdev = dev->platformdev;
+       struct shmob_drm_device *sdev;
+       struct resource *res;
+       unsigned int i;
+       int ret;
+
+       if (pdata == NULL) {
+               dev_err(dev->dev, "no platform data\n");
+               return -EINVAL;
+       }
+
+       sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
+       if (sdev == NULL) {
+               dev_err(dev->dev, "failed to allocate private data\n");
+               return -ENOMEM;
+       }
+
+       sdev->dev = &pdev->dev;
+       sdev->pdata = pdata;
+       spin_lock_init(&sdev->irq_lock);
+
+       sdev->ddev = dev;
+       dev->dev_private = sdev;
+
+       /* I/O resources and clocks */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res == NULL) {
+               dev_err(&pdev->dev, "failed to get memory resource\n");
+               ret = -EINVAL;
+               goto done;
+       }
+
+       sdev->mmio = ioremap_nocache(res->start, resource_size(res));
+       if (sdev->mmio == NULL) {
+               dev_err(&pdev->dev, "failed to remap memory resource\n");
+               ret = -ENOMEM;
+               goto done;
+       }
+
+       ret = shmob_drm_setup_clocks(sdev, pdata->clk_source);
+       if (ret < 0)
+               goto done;
+
+       ret = shmob_drm_init_interface(sdev);
+       if (ret < 0)
+               goto done;
+
+       ret = shmob_drm_modeset_init(sdev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to initialize mode setting\n");
+               goto done;
+       }
+
+       for (i = 0; i < 4; ++i) {
+               ret = shmob_drm_plane_create(sdev, i);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "failed to create plane %u\n", i);
+                       goto done;
+               }
+       }
+
+       ret = drm_vblank_init(dev, 1);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to initialize vblank\n");
+               goto done;
+       }
+
+       ret = drm_irq_install(dev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to install IRQ handler\n");
+               goto done;
+       }
+
+done:
+       if (ret)
+               shmob_drm_unload(dev);
+
+       return ret;
+}
+
+static void shmob_drm_preclose(struct drm_device *dev, struct drm_file *file)
+{
+       struct shmob_drm_device *sdev = dev->dev_private;
+
+       shmob_drm_crtc_cancel_page_flip(&sdev->crtc, file);
+}
+
+static irqreturn_t shmob_drm_irq(int irq, void *arg)
+{
+       struct drm_device *dev = arg;
+       struct shmob_drm_device *sdev = dev->dev_private;
+       unsigned long flags;
+       u32 status;
+
+       /* Acknowledge interrupts. Putting interrupt enable and interrupt flag
+        * bits in the same register is really brain-dead design and requires
+        * taking a spinlock.
+        */
+       spin_lock_irqsave(&sdev->irq_lock, flags);
+       status = lcdc_read(sdev, LDINTR);
+       lcdc_write(sdev, LDINTR, status ^ LDINTR_STATUS_MASK);
+       spin_unlock_irqrestore(&sdev->irq_lock, flags);
+
+       if (status & LDINTR_VES) {
+               drm_handle_vblank(dev, 0);
+               shmob_drm_crtc_finish_page_flip(&sdev->crtc);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int shmob_drm_enable_vblank(struct drm_device *dev, int crtc)
+{
+       struct shmob_drm_device *sdev = dev->dev_private;
+
+       shmob_drm_crtc_enable_vblank(sdev, true);
+
+       return 0;
+}
+
+static void shmob_drm_disable_vblank(struct drm_device *dev, int crtc)
+{
+       struct shmob_drm_device *sdev = dev->dev_private;
+
+       shmob_drm_crtc_enable_vblank(sdev, false);
+}
+
+static const struct file_operations shmob_drm_fops = {
+       .owner          = THIS_MODULE,
+       .open           = drm_open,
+       .release        = drm_release,
+       .unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = drm_compat_ioctl,
+#endif
+       .poll           = drm_poll,
+       .read           = drm_read,
+       .fasync         = drm_fasync,
+       .llseek         = no_llseek,
+       .mmap           = drm_gem_cma_mmap,
+};
+
+static struct drm_driver shmob_drm_driver = {
+       .driver_features        = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET,
+       .load                   = shmob_drm_load,
+       .unload                 = shmob_drm_unload,
+       .preclose               = shmob_drm_preclose,
+       .irq_handler            = shmob_drm_irq,
+       .get_vblank_counter     = drm_vblank_count,
+       .enable_vblank          = shmob_drm_enable_vblank,
+       .disable_vblank         = shmob_drm_disable_vblank,
+       .gem_free_object        = drm_gem_cma_free_object,
+       .gem_vm_ops             = &drm_gem_cma_vm_ops,
+       .dumb_create            = drm_gem_cma_dumb_create,
+       .dumb_map_offset        = drm_gem_cma_dumb_map_offset,
+       .dumb_destroy           = drm_gem_cma_dumb_destroy,
+       .fops                   = &shmob_drm_fops,
+       .name                   = "shmob-drm",
+       .desc                   = "Renesas SH Mobile DRM",
+       .date                   = "20120424",
+       .major                  = 1,
+       .minor                  = 0,
+};
+
+/* -----------------------------------------------------------------------------
+ * Power management
+ */
+
+#if CONFIG_PM_SLEEP
+static int shmob_drm_pm_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct drm_device *ddev = platform_get_drvdata(pdev);
+       struct shmob_drm_device *sdev = ddev->dev_private;
+
+       drm_kms_helper_poll_disable(ddev);
+       shmob_drm_crtc_suspend(&sdev->crtc);
+
+       return 0;
+}
+
+static int shmob_drm_pm_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct drm_device *ddev = platform_get_drvdata(pdev);
+       struct shmob_drm_device *sdev = ddev->dev_private;
+
+       mutex_lock(&sdev->ddev->mode_config.mutex);
+       shmob_drm_crtc_resume(&sdev->crtc);
+       mutex_unlock(&sdev->ddev->mode_config.mutex);
+
+       drm_kms_helper_poll_enable(sdev->ddev);
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops shmob_drm_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(shmob_drm_pm_suspend, shmob_drm_pm_resume)
+};
+
+/* -----------------------------------------------------------------------------
+ * Platform driver
+ */
+
+static int __devinit shmob_drm_probe(struct platform_device *pdev)
+{
+       return drm_platform_init(&shmob_drm_driver, pdev);
+}
+
+static int __devexit shmob_drm_remove(struct platform_device *pdev)
+{
+       drm_platform_exit(&shmob_drm_driver, pdev);
+
+       return 0;
+}
+
+static struct platform_driver shmob_drm_platform_driver = {
+       .probe          = shmob_drm_probe,
+       .remove         = __devexit_p(shmob_drm_remove),
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = "shmob-drm",
+               .pm     = &shmob_drm_pm_ops,
+       },
+};
+
+module_platform_driver(shmob_drm_platform_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Renesas SH Mobile DRM Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.h b/drivers/gpu/drm/shmobile/shmob_drm_drv.h
new file mode 100644 (file)
index 0000000..4d46b81
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * shmob_drm.h  --  SH Mobile DRM driver
+ *
+ * Copyright (C) 2012 Renesas Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __SHMOB_DRM_DRV_H__
+#define __SHMOB_DRM_DRV_H__
+
+#include <linux/kernel.h>
+#include <linux/platform_data/shmob_drm.h>
+#include <linux/spinlock.h>
+
+#include "shmob_drm_crtc.h"
+
+struct clk;
+struct device;
+struct drm_device;
+struct sh_mobile_meram_info;
+
+struct shmob_drm_device {
+       struct device *dev;
+       const struct shmob_drm_platform_data *pdata;
+
+       void __iomem *mmio;
+       struct clk *clock;
+       struct sh_mobile_meram_info *meram;
+       u32 lddckr;
+       u32 ldmt1r;
+
+       spinlock_t irq_lock;            /* Protects hardware LDINTR register */
+
+       struct drm_device *ddev;
+
+       struct shmob_drm_crtc crtc;
+       struct shmob_drm_encoder encoder;
+       struct shmob_drm_connector connector;
+};
+
+#endif /* __SHMOB_DRM_DRV_H__ */
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_kms.c b/drivers/gpu/drm/shmobile/shmob_drm_kms.c
new file mode 100644 (file)
index 0000000..c291ee3
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * shmob_drm_kms.c  --  SH Mobile DRM Mode Setting
+ *
+ * Copyright (C) 2012 Renesas Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include <video/sh_mobile_meram.h>
+
+#include "shmob_drm_crtc.h"
+#include "shmob_drm_drv.h"
+#include "shmob_drm_kms.h"
+#include "shmob_drm_regs.h"
+
+/* -----------------------------------------------------------------------------
+ * Format helpers
+ */
+
+static const struct shmob_drm_format_info shmob_drm_format_infos[] = {
+       {
+               .fourcc = DRM_FORMAT_RGB565,
+               .bpp = 16,
+               .yuv = false,
+               .lddfr = LDDFR_PKF_RGB16,
+               .meram = SH_MOBILE_MERAM_PF_RGB,
+       }, {
+               .fourcc = DRM_FORMAT_RGB888,
+               .bpp = 24,
+               .yuv = false,
+               .lddfr = LDDFR_PKF_RGB24,
+               .meram = SH_MOBILE_MERAM_PF_RGB,
+       }, {
+               .fourcc = DRM_FORMAT_ARGB8888,
+               .bpp = 32,
+               .yuv = false,
+               .lddfr = LDDFR_PKF_ARGB32,
+               .meram = SH_MOBILE_MERAM_PF_RGB,
+       }, {
+               .fourcc = DRM_FORMAT_NV12,
+               .bpp = 12,
+               .yuv = true,
+               .lddfr = LDDFR_CC | LDDFR_YF_420,
+               .meram = SH_MOBILE_MERAM_PF_NV,
+       }, {
+               .fourcc = DRM_FORMAT_NV21,
+               .bpp = 12,
+               .yuv = true,
+               .lddfr = LDDFR_CC | LDDFR_YF_420,
+               .meram = SH_MOBILE_MERAM_PF_NV,
+       }, {
+               .fourcc = DRM_FORMAT_NV16,
+               .bpp = 16,
+               .yuv = true,
+               .lddfr = LDDFR_CC | LDDFR_YF_422,
+               .meram = SH_MOBILE_MERAM_PF_NV,
+       }, {
+               .fourcc = DRM_FORMAT_NV61,
+               .bpp = 16,
+               .yuv = true,
+               .lddfr = LDDFR_CC | LDDFR_YF_422,
+               .meram = SH_MOBILE_MERAM_PF_NV,
+       }, {
+               .fourcc = DRM_FORMAT_NV24,
+               .bpp = 24,
+               .yuv = true,
+               .lddfr = LDDFR_CC | LDDFR_YF_444,
+               .meram = SH_MOBILE_MERAM_PF_NV24,
+       }, {
+               .fourcc = DRM_FORMAT_NV42,
+               .bpp = 24,
+               .yuv = true,
+               .lddfr = LDDFR_CC | LDDFR_YF_444,
+               .meram = SH_MOBILE_MERAM_PF_NV24,
+       },
+};
+
+const struct shmob_drm_format_info *shmob_drm_format_info(u32 fourcc)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(shmob_drm_format_infos); ++i) {
+               if (shmob_drm_format_infos[i].fourcc == fourcc)
+                       return &shmob_drm_format_infos[i];
+       }
+
+       return NULL;
+}
+
+/* -----------------------------------------------------------------------------
+ * Frame buffer
+ */
+
+static struct drm_framebuffer *
+shmob_drm_fb_create(struct drm_device *dev, struct drm_file *file_priv,
+                   struct drm_mode_fb_cmd2 *mode_cmd)
+{
+       const struct shmob_drm_format_info *format;
+
+       format = shmob_drm_format_info(mode_cmd->pixel_format);
+       if (format == NULL) {
+               dev_dbg(dev->dev, "unsupported pixel format %08x\n",
+                       mode_cmd->pixel_format);
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (mode_cmd->pitches[0] & 7 || mode_cmd->pitches[0] >= 65536) {
+               dev_dbg(dev->dev, "valid pitch value %u\n",
+                       mode_cmd->pitches[0]);
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (format->yuv) {
+               unsigned int chroma_cpp = format->bpp == 24 ? 2 : 1;
+
+               if (mode_cmd->pitches[1] != mode_cmd->pitches[0] * chroma_cpp) {
+                       dev_dbg(dev->dev,
+                               "luma and chroma pitches do not match\n");
+                       return ERR_PTR(-EINVAL);
+               }
+       }
+
+       return drm_fb_cma_create(dev, file_priv, mode_cmd);
+}
+
+static const struct drm_mode_config_funcs shmob_drm_mode_config_funcs = {
+       .fb_create = shmob_drm_fb_create,
+};
+
+int shmob_drm_modeset_init(struct shmob_drm_device *sdev)
+{
+       drm_mode_config_init(sdev->ddev);
+
+       shmob_drm_crtc_create(sdev);
+       shmob_drm_encoder_create(sdev);
+       shmob_drm_connector_create(sdev, &sdev->encoder.encoder);
+
+       drm_kms_helper_poll_init(sdev->ddev);
+
+       sdev->ddev->mode_config.min_width = 0;
+       sdev->ddev->mode_config.min_height = 0;
+       sdev->ddev->mode_config.max_width = 4095;
+       sdev->ddev->mode_config.max_height = 4095;
+       sdev->ddev->mode_config.funcs = &shmob_drm_mode_config_funcs;
+
+       drm_helper_disable_unused_functions(sdev->ddev);
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_kms.h b/drivers/gpu/drm/shmobile/shmob_drm_kms.h
new file mode 100644 (file)
index 0000000..9495c91
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * shmob_drm_kms.h  --  SH Mobile DRM Mode Setting
+ *
+ * Copyright (C) 2012 Renesas Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __SHMOB_DRM_KMS_H__
+#define __SHMOB_DRM_KMS_H__
+
+#include <linux/types.h>
+
+struct drm_gem_cma_object;
+struct shmob_drm_device;
+
+struct shmob_drm_format_info {
+       u32 fourcc;
+       unsigned int bpp;
+       bool yuv;
+       u32 lddfr;
+       unsigned int meram;
+};
+
+const struct shmob_drm_format_info *shmob_drm_format_info(u32 fourcc);
+
+int shmob_drm_modeset_init(struct shmob_drm_device *sdev);
+
+#endif /* __SHMOB_DRM_KMS_H__ */
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/shmobile/shmob_drm_plane.c
new file mode 100644 (file)
index 0000000..e1eb899
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * shmob_drm_plane.c  --  SH Mobile DRM Planes
+ *
+ * Copyright (C) 2012 Renesas Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include <video/sh_mobile_meram.h>
+
+#include "shmob_drm_drv.h"
+#include "shmob_drm_kms.h"
+#include "shmob_drm_plane.h"
+#include "shmob_drm_regs.h"
+
+struct shmob_drm_plane {
+       struct drm_plane plane;
+       unsigned int index;
+       unsigned int alpha;
+
+       const struct shmob_drm_format_info *format;
+       unsigned long dma[2];
+
+       unsigned int src_x;
+       unsigned int src_y;
+       unsigned int crtc_x;
+       unsigned int crtc_y;
+       unsigned int crtc_w;
+       unsigned int crtc_h;
+};
+
+#define to_shmob_plane(p)      container_of(p, struct shmob_drm_plane, plane)
+
+static void shmob_drm_plane_compute_base(struct shmob_drm_plane *splane,
+                                        struct drm_framebuffer *fb,
+                                        int x, int y)
+{
+       struct drm_gem_cma_object *gem;
+       unsigned int bpp;
+
+       bpp = splane->format->yuv ? 8 : splane->format->bpp;
+       gem = drm_fb_cma_get_gem_obj(fb, 0);
+       splane->dma[0] = gem->paddr + fb->offsets[0]
+                      + y * fb->pitches[0] + x * bpp / 8;
+
+       if (splane->format->yuv) {
+               bpp = splane->format->bpp - 8;
+               gem = drm_fb_cma_get_gem_obj(fb, 1);
+               splane->dma[1] = gem->paddr + fb->offsets[1]
+                              + y / (bpp == 4 ? 2 : 1) * fb->pitches[1]
+                              + x * (bpp == 16 ? 2 : 1);
+       }
+}
+
+static void __shmob_drm_plane_setup(struct shmob_drm_plane *splane,
+                                   struct drm_framebuffer *fb)
+{
+       struct shmob_drm_device *sdev = splane->plane.dev->dev_private;
+       u32 format;
+
+       /* TODO: Support ROP3 mode */
+       format = LDBBSIFR_EN | (splane->alpha << LDBBSIFR_LAY_SHIFT);
+
+       switch (splane->format->fourcc) {
+       case DRM_FORMAT_RGB565:
+       case DRM_FORMAT_NV21:
+       case DRM_FORMAT_NV61:
+       case DRM_FORMAT_NV42:
+               format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW;
+               break;
+       case DRM_FORMAT_RGB888:
+       case DRM_FORMAT_NV12:
+       case DRM_FORMAT_NV16:
+       case DRM_FORMAT_NV24:
+               format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW | LDBBSIFR_SWPB;
+               break;
+       case DRM_FORMAT_ARGB8888:
+       default:
+               format |= LDBBSIFR_SWPL;
+               break;
+       }
+
+       switch (splane->format->fourcc) {
+       case DRM_FORMAT_RGB565:
+               format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB16;
+               break;
+       case DRM_FORMAT_RGB888:
+               format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB24;
+               break;
+       case DRM_FORMAT_ARGB8888:
+               format |= LDBBSIFR_AL_PK | LDBBSIFR_RY | LDDFR_PKF_ARGB32;
+               break;
+       case DRM_FORMAT_NV12:
+       case DRM_FORMAT_NV21:
+               format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_420;
+               break;
+       case DRM_FORMAT_NV16:
+       case DRM_FORMAT_NV61:
+               format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_422;
+               break;
+       case DRM_FORMAT_NV24:
+       case DRM_FORMAT_NV42:
+               format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_444;
+               break;
+       }
+
+#define plane_reg_dump(sdev, splane, reg) \
+       dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x 0x%08x\n", __func__, \
+               splane->index, #reg, \
+               lcdc_read(sdev, reg(splane->index)), \
+               lcdc_read(sdev, reg(splane->index) + LCDC_SIDE_B_OFFSET))
+
+       plane_reg_dump(sdev, splane, LDBnBSIFR);
+       plane_reg_dump(sdev, splane, LDBnBSSZR);
+       plane_reg_dump(sdev, splane, LDBnBLOCR);
+       plane_reg_dump(sdev, splane, LDBnBSMWR);
+       plane_reg_dump(sdev, splane, LDBnBSAYR);
+       plane_reg_dump(sdev, splane, LDBnBSACR);
+
+       lcdc_write(sdev, LDBCR, LDBCR_UPC(splane->index));
+       dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x\n", __func__, splane->index,
+               "LDBCR", lcdc_read(sdev, LDBCR));
+
+       lcdc_write(sdev, LDBnBSIFR(splane->index), format);
+
+       lcdc_write(sdev, LDBnBSSZR(splane->index),
+                  (splane->crtc_h << LDBBSSZR_BVSS_SHIFT) |
+                  (splane->crtc_w << LDBBSSZR_BHSS_SHIFT));
+       lcdc_write(sdev, LDBnBLOCR(splane->index),
+                  (splane->crtc_y << LDBBLOCR_CVLC_SHIFT) |
+                  (splane->crtc_x << LDBBLOCR_CHLC_SHIFT));
+       lcdc_write(sdev, LDBnBSMWR(splane->index),
+                  fb->pitches[0] << LDBBSMWR_BSMW_SHIFT);
+
+       shmob_drm_plane_compute_base(splane, fb, splane->src_x, splane->src_y);
+
+       lcdc_write(sdev, LDBnBSAYR(splane->index), splane->dma[0]);
+       if (splane->format->yuv)
+               lcdc_write(sdev, LDBnBSACR(splane->index), splane->dma[1]);
+
+       lcdc_write(sdev, LDBCR,
+                  LDBCR_UPF(splane->index) | LDBCR_UPD(splane->index));
+       dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x\n", __func__, splane->index,
+               "LDBCR", lcdc_read(sdev, LDBCR));
+
+       plane_reg_dump(sdev, splane, LDBnBSIFR);
+       plane_reg_dump(sdev, splane, LDBnBSSZR);
+       plane_reg_dump(sdev, splane, LDBnBLOCR);
+       plane_reg_dump(sdev, splane, LDBnBSMWR);
+       plane_reg_dump(sdev, splane, LDBnBSAYR);
+       plane_reg_dump(sdev, splane, LDBnBSACR);
+}
+
+void shmob_drm_plane_setup(struct drm_plane *plane)
+{
+       struct shmob_drm_plane *splane = to_shmob_plane(plane);
+
+       if (plane->fb == NULL || !plane->enabled)
+               return;
+
+       __shmob_drm_plane_setup(splane, plane->fb);
+}
+
+static int
+shmob_drm_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 shmob_drm_plane *splane = to_shmob_plane(plane);
+       struct shmob_drm_device *sdev = plane->dev->dev_private;
+       const struct shmob_drm_format_info *format;
+
+       format = shmob_drm_format_info(fb->pixel_format);
+       if (format == NULL) {
+               dev_dbg(sdev->dev, "update_plane: unsupported format %08x\n",
+                       fb->pixel_format);
+               return -EINVAL;
+       }
+
+       if (src_w >> 16 != crtc_w || src_h >> 16 != crtc_h) {
+               dev_dbg(sdev->dev, "%s: scaling not supported\n", __func__);
+               return -EINVAL;
+       }
+
+       splane->format = format;
+
+       splane->src_x = src_x >> 16;
+       splane->src_y = src_y >> 16;
+       splane->crtc_x = crtc_x;
+       splane->crtc_y = crtc_y;
+       splane->crtc_w = crtc_w;
+       splane->crtc_h = crtc_h;
+
+       __shmob_drm_plane_setup(splane, fb);
+       return 0;
+}
+
+static int shmob_drm_plane_disable(struct drm_plane *plane)
+{
+       struct shmob_drm_plane *splane = to_shmob_plane(plane);
+       struct shmob_drm_device *sdev = plane->dev->dev_private;
+
+       splane->format = NULL;
+
+       lcdc_write(sdev, LDBnBSIFR(splane->index), 0);
+       return 0;
+}
+
+static void shmob_drm_plane_destroy(struct drm_plane *plane)
+{
+       struct shmob_drm_plane *splane = to_shmob_plane(plane);
+
+       shmob_drm_plane_disable(plane);
+       drm_plane_cleanup(plane);
+       kfree(splane);
+}
+
+static const struct drm_plane_funcs shmob_drm_plane_funcs = {
+       .update_plane = shmob_drm_plane_update,
+       .disable_plane = shmob_drm_plane_disable,
+       .destroy = shmob_drm_plane_destroy,
+};
+
+static const uint32_t formats[] = {
+       DRM_FORMAT_RGB565,
+       DRM_FORMAT_RGB888,
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_NV12,
+       DRM_FORMAT_NV21,
+       DRM_FORMAT_NV16,
+       DRM_FORMAT_NV61,
+       DRM_FORMAT_NV24,
+       DRM_FORMAT_NV42,
+};
+
+int shmob_drm_plane_create(struct shmob_drm_device *sdev, unsigned int index)
+{
+       struct shmob_drm_plane *splane;
+       int ret;
+
+       splane = kzalloc(sizeof(*splane), GFP_KERNEL);
+       if (splane == NULL)
+               return -ENOMEM;
+
+       splane->index = index;
+       splane->alpha = 255;
+
+       ret = drm_plane_init(sdev->ddev, &splane->plane, 1,
+                            &shmob_drm_plane_funcs, formats,
+                            ARRAY_SIZE(formats), false);
+       if (ret < 0)
+               kfree(splane);
+
+       return ret;
+}
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.h b/drivers/gpu/drm/shmobile/shmob_drm_plane.h
new file mode 100644 (file)
index 0000000..99623d0
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * shmob_drm_plane.h  --  SH Mobile DRM Planes
+ *
+ * Copyright (C) 2012 Renesas Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __SHMOB_DRM_PLANE_H__
+#define __SHMOB_DRM_PLANE_H__
+
+struct shmob_drm_device;
+
+int shmob_drm_plane_create(struct shmob_drm_device *sdev, unsigned int index);
+void shmob_drm_plane_setup(struct drm_plane *plane);
+
+#endif /* __SHMOB_DRM_PLANE_H__ */
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_regs.h b/drivers/gpu/drm/shmobile/shmob_drm_regs.h
new file mode 100644 (file)
index 0000000..7923cdd
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * shmob_drm_regs.h  --  SH Mobile DRM registers
+ *
+ * Copyright (C) 2012 Renesas Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __SHMOB_DRM_REGS_H__
+#define __SHMOB_DRM_REGS_H__
+
+#include <linux/io.h>
+
+/* Register definitions */
+#define LDDCKPAT1R             0x400
+#define LDDCKPAT2R             0x404
+#define LDDCKR                 0x410
+#define LDDCKR_ICKSEL_BUS      (0 << 16)
+#define LDDCKR_ICKSEL_MIPI     (1 << 16)
+#define LDDCKR_ICKSEL_HDMI     (2 << 16)
+#define LDDCKR_ICKSEL_EXT      (3 << 16)
+#define LDDCKR_ICKSEL_MASK     (7 << 16)
+#define LDDCKR_MOSEL           (1 << 6)
+#define LDDCKSTPR              0x414
+#define LDDCKSTPR_DCKSTS       (1 << 16)
+#define LDDCKSTPR_DCKSTP       (1 << 0)
+#define LDMT1R                 0x418
+#define LDMT1R_VPOL            (1 << 28)
+#define LDMT1R_HPOL            (1 << 27)
+#define LDMT1R_DWPOL           (1 << 26)
+#define LDMT1R_DIPOL           (1 << 25)
+#define LDMT1R_DAPOL           (1 << 24)
+#define LDMT1R_HSCNT           (1 << 17)
+#define LDMT1R_DWCNT           (1 << 16)
+#define LDMT1R_IFM             (1 << 12)
+#define LDMT1R_MIFTYP_RGB8     (0x0 << 0)
+#define LDMT1R_MIFTYP_RGB9     (0x4 << 0)
+#define LDMT1R_MIFTYP_RGB12A   (0x5 << 0)
+#define LDMT1R_MIFTYP_RGB12B   (0x6 << 0)
+#define LDMT1R_MIFTYP_RGB16    (0x7 << 0)
+#define LDMT1R_MIFTYP_RGB18    (0xa << 0)
+#define LDMT1R_MIFTYP_RGB24    (0xb << 0)
+#define LDMT1R_MIFTYP_YCBCR    (0xf << 0)
+#define LDMT1R_MIFTYP_SYS8A    (0x0 << 0)
+#define LDMT1R_MIFTYP_SYS8B    (0x1 << 0)
+#define LDMT1R_MIFTYP_SYS8C    (0x2 << 0)
+#define LDMT1R_MIFTYP_SYS8D    (0x3 << 0)
+#define LDMT1R_MIFTYP_SYS9     (0x4 << 0)
+#define LDMT1R_MIFTYP_SYS12    (0x5 << 0)
+#define LDMT1R_MIFTYP_SYS16A   (0x7 << 0)
+#define LDMT1R_MIFTYP_SYS16B   (0x8 << 0)
+#define LDMT1R_MIFTYP_SYS16C   (0x9 << 0)
+#define LDMT1R_MIFTYP_SYS18    (0xa << 0)
+#define LDMT1R_MIFTYP_SYS24    (0xb << 0)
+#define LDMT1R_MIFTYP_MASK     (0xf << 0)
+#define LDMT2R                 0x41c
+#define LDMT2R_CSUP_MASK       (7 << 26)
+#define LDMT2R_CSUP_SHIFT      26
+#define LDMT2R_RSV             (1 << 25)
+#define LDMT2R_VSEL            (1 << 24)
+#define LDMT2R_WCSC_MASK       (0xff << 16)
+#define LDMT2R_WCSC_SHIFT      16
+#define LDMT2R_WCEC_MASK       (0xff << 8)
+#define LDMT2R_WCEC_SHIFT      8
+#define LDMT2R_WCLW_MASK       (0xff << 0)
+#define LDMT2R_WCLW_SHIFT      0
+#define LDMT3R                 0x420
+#define LDMT3R_RDLC_MASK       (0x3f << 24)
+#define LDMT3R_RDLC_SHIFT      24
+#define LDMT3R_RCSC_MASK       (0xff << 16)
+#define LDMT3R_RCSC_SHIFT      16
+#define LDMT3R_RCEC_MASK       (0xff << 8)
+#define LDMT3R_RCEC_SHIFT      8
+#define LDMT3R_RCLW_MASK       (0xff << 0)
+#define LDMT3R_RCLW_SHIFT      0
+#define LDDFR                  0x424
+#define LDDFR_CF1              (1 << 18)
+#define LDDFR_CF0              (1 << 17)
+#define LDDFR_CC               (1 << 16)
+#define LDDFR_YF_420           (0 << 8)
+#define LDDFR_YF_422           (1 << 8)
+#define LDDFR_YF_444           (2 << 8)
+#define LDDFR_YF_MASK          (3 << 8)
+#define LDDFR_PKF_ARGB32       (0x00 << 0)
+#define LDDFR_PKF_RGB16                (0x03 << 0)
+#define LDDFR_PKF_RGB24                (0x0b << 0)
+#define LDDFR_PKF_MASK         (0x1f << 0)
+#define LDSM1R                 0x428
+#define LDSM1R_OS              (1 << 0)
+#define LDSM2R                 0x42c
+#define LDSM2R_OSTRG           (1 << 0)
+#define LDSA1R                 0x430
+#define LDSA2R                 0x434
+#define LDMLSR                 0x438
+#define LDWBFR                 0x43c
+#define LDWBCNTR               0x440
+#define LDWBAR                 0x444
+#define LDHCNR                 0x448
+#define LDHSYNR                        0x44c
+#define LDVLNR                 0x450
+#define LDVSYNR                        0x454
+#define LDHPDR                 0x458
+#define LDVPDR                 0x45c
+#define LDPMR                  0x460
+#define LDPMR_LPS              (3 << 0)
+#define LDINTR                 0x468
+#define LDINTR_FE              (1 << 10)
+#define LDINTR_VSE             (1 << 9)
+#define LDINTR_VEE             (1 << 8)
+#define LDINTR_FS              (1 << 2)
+#define LDINTR_VSS             (1 << 1)
+#define LDINTR_VES             (1 << 0)
+#define LDINTR_STATUS_MASK     (0xff << 0)
+#define LDSR                   0x46c
+#define LDSR_MSS               (1 << 10)
+#define LDSR_MRS               (1 << 8)
+#define LDSR_AS                        (1 << 1)
+#define LDCNT1R                        0x470
+#define LDCNT1R_DE             (1 << 0)
+#define LDCNT2R                        0x474
+#define LDCNT2R_BR             (1 << 8)
+#define LDCNT2R_MD             (1 << 3)
+#define LDCNT2R_SE             (1 << 2)
+#define LDCNT2R_ME             (1 << 1)
+#define LDCNT2R_DO             (1 << 0)
+#define LDRCNTR                        0x478
+#define LDRCNTR_SRS            (1 << 17)
+#define LDRCNTR_SRC            (1 << 16)
+#define LDRCNTR_MRS            (1 << 1)
+#define LDRCNTR_MRC            (1 << 0)
+#define LDDDSR                 0x47c
+#define LDDDSR_LS              (1 << 2)
+#define LDDDSR_WS              (1 << 1)
+#define LDDDSR_BS              (1 << 0)
+#define LDHAJR                 0x4a0
+
+#define LDDWD0R                        0x800
+#define LDDWDxR_WDACT          (1 << 28)
+#define LDDWDxR_RSW            (1 << 24)
+#define LDDRDR                 0x840
+#define LDDRDR_RSR             (1 << 24)
+#define LDDRDR_DRD_MASK                (0x3ffff << 0)
+#define LDDWAR                 0x900
+#define LDDWAR_WA              (1 << 0)
+#define LDDRAR                 0x904
+#define LDDRAR_RA              (1 << 0)
+
+#define LDBCR                  0xb00
+#define LDBCR_UPC(n)           (1 << ((n) + 16))
+#define LDBCR_UPF(n)           (1 << ((n) + 8))
+#define LDBCR_UPD(n)           (1 << ((n) + 0))
+#define LDBnBSIFR(n)           (0xb20 + (n) * 0x20 + 0x00)
+#define LDBBSIFR_EN            (1 << 31)
+#define LDBBSIFR_VS            (1 << 29)
+#define LDBBSIFR_BRSEL         (1 << 28)
+#define LDBBSIFR_MX            (1 << 27)
+#define LDBBSIFR_MY            (1 << 26)
+#define LDBBSIFR_CV3           (3 << 24)
+#define LDBBSIFR_CV2           (2 << 24)
+#define LDBBSIFR_CV1           (1 << 24)
+#define LDBBSIFR_CV0           (0 << 24)
+#define LDBBSIFR_CV_MASK       (3 << 24)
+#define LDBBSIFR_LAY_MASK      (0xff << 16)
+#define LDBBSIFR_LAY_SHIFT     16
+#define LDBBSIFR_ROP3_MASK     (0xff << 16)
+#define LDBBSIFR_ROP3_SHIFT    16
+#define LDBBSIFR_AL_PL8                (3 << 14)
+#define LDBBSIFR_AL_PL1                (2 << 14)
+#define LDBBSIFR_AL_PK         (1 << 14)
+#define LDBBSIFR_AL_1          (0 << 14)
+#define LDBBSIFR_AL_MASK       (3 << 14)
+#define LDBBSIFR_SWPL          (1 << 10)
+#define LDBBSIFR_SWPW          (1 << 9)
+#define LDBBSIFR_SWPB          (1 << 8)
+#define LDBBSIFR_RY            (1 << 7)
+#define LDBBSIFR_CHRR_420      (2 << 0)
+#define LDBBSIFR_CHRR_422      (1 << 0)
+#define LDBBSIFR_CHRR_444      (0 << 0)
+#define LDBBSIFR_RPKF_ARGB32   (0x00 << 0)
+#define LDBBSIFR_RPKF_RGB16    (0x03 << 0)
+#define LDBBSIFR_RPKF_RGB24    (0x0b << 0)
+#define LDBBSIFR_RPKF_MASK     (0x1f << 0)
+#define LDBnBSSZR(n)           (0xb20 + (n) * 0x20 + 0x04)
+#define LDBBSSZR_BVSS_MASK     (0xfff << 16)
+#define LDBBSSZR_BVSS_SHIFT    16
+#define LDBBSSZR_BHSS_MASK     (0xfff << 0)
+#define LDBBSSZR_BHSS_SHIFT    0
+#define LDBnBLOCR(n)           (0xb20 + (n) * 0x20 + 0x08)
+#define LDBBLOCR_CVLC_MASK     (0xfff << 16)
+#define LDBBLOCR_CVLC_SHIFT    16
+#define LDBBLOCR_CHLC_MASK     (0xfff << 0)
+#define LDBBLOCR_CHLC_SHIFT    0
+#define LDBnBSMWR(n)           (0xb20 + (n) * 0x20 + 0x0c)
+#define LDBBSMWR_BSMWA_MASK    (0xffff << 16)
+#define LDBBSMWR_BSMWA_SHIFT   16
+#define LDBBSMWR_BSMW_MASK     (0xffff << 0)
+#define LDBBSMWR_BSMW_SHIFT    0
+#define LDBnBSAYR(n)           (0xb20 + (n) * 0x20 + 0x10)
+#define LDBBSAYR_FG1A_MASK     (0xff << 24)
+#define LDBBSAYR_FG1A_SHIFT    24
+#define LDBBSAYR_FG1R_MASK     (0xff << 16)
+#define LDBBSAYR_FG1R_SHIFT    16
+#define LDBBSAYR_FG1G_MASK     (0xff << 8)
+#define LDBBSAYR_FG1G_SHIFT    8
+#define LDBBSAYR_FG1B_MASK     (0xff << 0)
+#define LDBBSAYR_FG1B_SHIFT    0
+#define LDBnBSACR(n)           (0xb20 + (n) * 0x20 + 0x14)
+#define LDBBSACR_FG2A_MASK     (0xff << 24)
+#define LDBBSACR_FG2A_SHIFT    24
+#define LDBBSACR_FG2R_MASK     (0xff << 16)
+#define LDBBSACR_FG2R_SHIFT    16
+#define LDBBSACR_FG2G_MASK     (0xff << 8)
+#define LDBBSACR_FG2G_SHIFT    8
+#define LDBBSACR_FG2B_MASK     (0xff << 0)
+#define LDBBSACR_FG2B_SHIFT    0
+#define LDBnBSAAR(n)           (0xb20 + (n) * 0x20 + 0x18)
+#define LDBBSAAR_AP_MASK       (0xff << 24)
+#define LDBBSAAR_AP_SHIFT      24
+#define LDBBSAAR_R_MASK                (0xff << 16)
+#define LDBBSAAR_R_SHIFT       16
+#define LDBBSAAR_GY_MASK       (0xff << 8)
+#define LDBBSAAR_GY_SHIFT      8
+#define LDBBSAAR_B_MASK                (0xff << 0)
+#define LDBBSAAR_B_SHIFT       0
+#define LDBnBPPCR(n)           (0xb20 + (n) * 0x20 + 0x1c)
+#define LDBBPPCR_AP_MASK       (0xff << 24)
+#define LDBBPPCR_AP_SHIFT      24
+#define LDBBPPCR_R_MASK                (0xff << 16)
+#define LDBBPPCR_R_SHIFT       16
+#define LDBBPPCR_GY_MASK       (0xff << 8)
+#define LDBBPPCR_GY_SHIFT      8
+#define LDBBPPCR_B_MASK                (0xff << 0)
+#define LDBBPPCR_B_SHIFT       0
+#define LDBnBBGCL(n)           (0xb10 + (n) * 0x04)
+#define LDBBBGCL_BGA_MASK      (0xff << 24)
+#define LDBBBGCL_BGA_SHIFT     24
+#define LDBBBGCL_BGR_MASK      (0xff << 16)
+#define LDBBBGCL_BGR_SHIFT     16
+#define LDBBBGCL_BGG_MASK      (0xff << 8)
+#define LDBBBGCL_BGG_SHIFT     8
+#define LDBBBGCL_BGB_MASK      (0xff << 0)
+#define LDBBBGCL_BGB_SHIFT     0
+
+#define LCDC_SIDE_B_OFFSET     0x1000
+#define LCDC_MIRROR_OFFSET     0x2000
+
+static inline bool lcdc_is_banked(u32 reg)
+{
+       switch (reg) {
+       case LDMT1R:
+       case LDMT2R:
+       case LDMT3R:
+       case LDDFR:
+       case LDSM1R:
+       case LDSA1R:
+       case LDSA2R:
+       case LDMLSR:
+       case LDWBFR:
+       case LDWBCNTR:
+       case LDWBAR:
+       case LDHCNR:
+       case LDHSYNR:
+       case LDVLNR:
+       case LDVSYNR:
+       case LDHPDR:
+       case LDVPDR:
+       case LDHAJR:
+               return true;
+       default:
+               return reg >= LDBnBBGCL(0) && reg <= LDBnBPPCR(3);
+       }
+}
+
+static inline void lcdc_write_mirror(struct shmob_drm_device *sdev, u32 reg,
+                                    u32 data)
+{
+       iowrite32(data, sdev->mmio + reg + LCDC_MIRROR_OFFSET);
+}
+
+static inline void lcdc_write(struct shmob_drm_device *sdev, u32 reg, u32 data)
+{
+       iowrite32(data, sdev->mmio + reg);
+       if (lcdc_is_banked(reg))
+               iowrite32(data, sdev->mmio + reg + LCDC_SIDE_B_OFFSET);
+}
+
+static inline u32 lcdc_read(struct shmob_drm_device *sdev, u32 reg)
+{
+       return ioread32(sdev->mmio + reg);
+}
+
+static inline int lcdc_wait_bit(struct shmob_drm_device *sdev, u32 reg,
+                               u32 mask, u32 until)
+{
+       unsigned long timeout = jiffies + msecs_to_jiffies(5);
+
+       while ((lcdc_read(sdev, reg) & mask) != until) {
+               if (time_after(jiffies, timeout))
+                       return -ETIMEDOUT;
+               cpu_relax();
+       }
+
+       return 0;
+}
+
+#endif /* __SHMOB_DRM_REGS_H__ */
index f8187ead7b373336c21e603caa448bad99d50b47..0df71eacd58757b7288f8683985d7f4ce9920697 100644 (file)
@@ -472,7 +472,7 @@ pgprot_t ttm_io_prot(uint32_t caching_flags, pgprot_t tmp)
        else
                tmp = pgprot_noncached(tmp);
 #endif
-#if defined(__sparc__)
+#if defined(__sparc__) || defined(__mips__)
        if (!(caching_flags & TTM_PL_FLAG_CACHED))
                tmp = pgprot_noncached(tmp);
 #endif
index 4f9e548b2eec67e5a701f7017bb7a4ff95fb2f99..969d765320885cf45da6f9ea3a566e12ae21e6d3 100644 (file)
@@ -1060,7 +1060,7 @@ int ttm_dma_page_alloc_init(struct ttm_mem_global *glob, unsigned max_pages)
 
        _manager = kzalloc(sizeof(*_manager), GFP_KERNEL);
        if (!_manager)
-               goto err_manager;
+               goto err;
 
        mutex_init(&_manager->lock);
        INIT_LIST_HEAD(&_manager->pools);
@@ -1078,9 +1078,6 @@ int ttm_dma_page_alloc_init(struct ttm_mem_global *glob, unsigned max_pages)
        }
        ttm_dma_pool_mm_shrink_init(_manager);
        return 0;
-err_manager:
-       kfree(_manager);
-       _manager = NULL;
 err:
        return ret;
 }
index fa09daf9a50c30adb728bda9d29ba0319ce478c8..11c9369d420879c05449ca4ff16228f4ec8e0bd1 100644 (file)
@@ -290,8 +290,6 @@ int ttm_tt_swapin(struct ttm_tt *ttm)
        struct file *swap_storage;
        struct page *from_page;
        struct page *to_page;
-       void *from_virtual;
-       void *to_virtual;
        int i;
        int ret = -ENOMEM;
 
@@ -311,11 +309,7 @@ int ttm_tt_swapin(struct ttm_tt *ttm)
                        goto out_err;
 
                preempt_disable();
-               from_virtual = kmap_atomic(from_page);
-               to_virtual = kmap_atomic(to_page);
-               memcpy(to_virtual, from_virtual, PAGE_SIZE);
-               kunmap_atomic(to_virtual);
-               kunmap_atomic(from_virtual);
+               copy_highpage(to_page, from_page);
                preempt_enable();
                page_cache_release(from_page);
        }
@@ -336,8 +330,6 @@ int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistent_swap_storage)
        struct file *swap_storage;
        struct page *from_page;
        struct page *to_page;
-       void *from_virtual;
-       void *to_virtual;
        int i;
        int ret = -ENOMEM;
 
@@ -367,11 +359,7 @@ int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistent_swap_storage)
                        goto out_err;
                }
                preempt_disable();
-               from_virtual = kmap_atomic(from_page);
-               to_virtual = kmap_atomic(to_page);
-               memcpy(to_virtual, from_virtual, PAGE_SIZE);
-               kunmap_atomic(to_virtual);
-               kunmap_atomic(from_virtual);
+               copy_highpage(to_page, from_page);
                preempt_enable();
                set_page_dirty(to_page);
                mark_page_accessed(to_page);
index ba055e9ca0077875a3a49d3533d729a95ccf98fd..2d98ff92f3baaa33171ae1506edcca8992281f8b 100644 (file)
@@ -57,11 +57,8 @@ static int udl_get_modes(struct drm_connector *connector)
 
        edid = (struct edid *)udl_get_edid(udl);
 
-       connector->display_info.raw_edid = (char *)edid;
-
        drm_mode_connector_update_edid_property(connector, edid);
        ret = drm_add_edid_modes(connector, edid);
-       connector->display_info.raw_edid = NULL;
        kfree(edid);
        return ret;
 }
index 0731ab2e6c06e1c4c6d7369b9648031f3f36805e..0476bfe06d145805a6348d25d8974c13fcbd2f77 100644 (file)
@@ -16,7 +16,7 @@
 #include "udl_drv.h"
 
 /* dummy encoder */
-void udl_enc_destroy(struct drm_encoder *encoder)
+static void udl_enc_destroy(struct drm_encoder *encoder)
 {
        drm_encoder_cleanup(encoder);
        kfree(encoder);
index ce9a61179925cd540f4d4956b991513ca53b27e2..0e47e9614b780fc47269bfe63b31bc52682d0b39 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/fb.h>
+#include <linux/dma-buf.h>
 
 #include "drmP.h"
 #include "drm.h"
@@ -356,12 +357,12 @@ static struct fb_ops udlfb_ops = {
        .fb_release = udl_fb_release,
 };
 
-void udl_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
+static void udl_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
                           u16 blue, int regno)
 {
 }
 
-void udl_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
+static void udl_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
                             u16 *blue, int regno)
 {
        *red = 0;
@@ -377,16 +378,33 @@ static int udl_user_framebuffer_dirty(struct drm_framebuffer *fb,
 {
        struct udl_framebuffer *ufb = to_udl_fb(fb);
        int i;
+       int ret = 0;
 
        if (!ufb->active_16)
                return 0;
 
+       if (ufb->obj->base.import_attach) {
+               ret = dma_buf_begin_cpu_access(ufb->obj->base.import_attach->dmabuf,
+                                              0, ufb->obj->base.size,
+                                              DMA_FROM_DEVICE);
+               if (ret)
+                       return ret;
+       }
+
        for (i = 0; i < num_clips; i++) {
-               udl_handle_damage(ufb, clips[i].x1, clips[i].y1,
+               ret = udl_handle_damage(ufb, clips[i].x1, clips[i].y1,
                                  clips[i].x2 - clips[i].x1,
                                  clips[i].y2 - clips[i].y1);
+               if (ret)
+                       break;
        }
-       return 0;
+
+       if (ufb->obj->base.import_attach) {
+               dma_buf_end_cpu_access(ufb->obj->base.import_attach->dmabuf,
+                                      0, ufb->obj->base.size,
+                                      DMA_FROM_DEVICE);
+       }
+       return ret;
 }
 
 static void udl_user_framebuffer_destroy(struct drm_framebuffer *fb)
index 291ecc1455850400f6e596cde59dfac1acda83d4..47b256375831bcaf6434ad1416d4ec99982de1f6 100644 (file)
@@ -181,11 +181,6 @@ int udl_gem_vmap(struct udl_gem_object *obj)
        int ret;
 
        if (obj->base.import_attach) {
-               ret = dma_buf_begin_cpu_access(obj->base.import_attach->dmabuf,
-                                              0, obj->base.size, DMA_BIDIRECTIONAL);
-               if (ret)
-                       return -EINVAL;
-
                obj->vmapping = dma_buf_vmap(obj->base.import_attach->dmabuf);
                if (!obj->vmapping)
                        return -ENOMEM;
@@ -206,8 +201,6 @@ void udl_gem_vunmap(struct udl_gem_object *obj)
 {
        if (obj->base.import_attach) {
                dma_buf_vunmap(obj->base.import_attach->dmabuf, obj->vmapping);
-               dma_buf_end_cpu_access(obj->base.import_attach->dmabuf, 0,
-                                      obj->base.size, DMA_BIDIRECTIONAL);
                return;
        }
 
index 4c2d836a0893f36f91247a5ce38d6cfed883faa8..40bf468c46c27b492bb00f48d671a36f53d0f86f 100644 (file)
@@ -41,11 +41,8 @@ static int udl_parse_vendor_descriptor(struct drm_device *dev,
        total_len = usb_get_descriptor(usbdev, 0x5f, /* vendor specific */
                                    0, desc, MAX_VENDOR_DESCRIPTOR_SIZE);
        if (total_len > 5) {
-               DRM_INFO("vendor descriptor length:%x data:%02x %02x %02x %02x" \
-                       "%02x %02x %02x %02x %02x %02x %02x\n",
-                       total_len, desc[0],
-                       desc[1], desc[2], desc[3], desc[4], desc[5], desc[6],
-                       desc[7], desc[8], desc[9], desc[10]);
+               DRM_INFO("vendor descriptor length:%x data:%*ph\n",
+                       total_len, 11, desc);
 
                if ((desc[0] != total_len) || /* descriptor length */
                    (desc[1] != 0x5f) ||   /* vendor descriptor type */
index 9159d48d1dfd570521c43755596466788411beba..8f258158c1fe669389b6841975108517f002c6a7 100644 (file)
@@ -391,7 +391,7 @@ static const struct drm_crtc_funcs udl_crtc_funcs = {
        .destroy = udl_crtc_destroy,
 };
 
-int udl_crtc_init(struct drm_device *dev)
+static int udl_crtc_init(struct drm_device *dev)
 {
        struct drm_crtc *crtc;
 
index b9320e2608dd55e0287321b7bd43e266a741d0c7..f9fff657a90bdc0472dc70359863fc2769347a85 100644 (file)
@@ -126,10 +126,10 @@ static void udl_compress_hline16(
 
        while ((pixel_end > pixel) &&
               (cmd_buffer_end - MIN_RLX_CMD_BYTES > cmd)) {
-               uint8_t *raw_pixels_count_byte = 0;
-               uint8_t *cmd_pixels_count_byte = 0;
-               const u8 *raw_pixel_start = 0;
-               const u8 *cmd_pixel_start, *cmd_pixel_end = 0;
+               uint8_t *raw_pixels_count_byte = NULL;
+               uint8_t *cmd_pixels_count_byte = NULL;
+               const u8 *raw_pixel_start = NULL;
+               const u8 *cmd_pixel_start, *cmd_pixel_end = NULL;
 
                prefetchw((void *) cmd); /* pull in one cache line at least */
 
index ba2c35dbf10e3a7c90095d3fce4265dd3cfd14ff..09f44c5dd49d4610441c30d71ae3b45b4761ed3e 100644 (file)
@@ -438,7 +438,6 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
                DRM_ERROR("Failed allocating a device private struct.\n");
                return -ENOMEM;
        }
-       memset(dev_priv, 0, sizeof(*dev_priv));
 
        pci_set_master(dev->pdev);
 
index c50724bd30f6e052e2f80e6f823379241944ff60..54743943d8b328320de41e231763b3878c4a4744 100644 (file)
@@ -483,7 +483,6 @@ static int do_surface_dirty_sou(struct vmw_private *dev_priv,
        }
 
        /* only need to do this once */
-       memset(cmd, 0, fifo_size);
        cmd->header.id = cpu_to_le32(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN);
        cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header));
 
index 77d2df04c97b0dbe6c34eb11b9e566ae07d5ae28..60c4e1e85913fd435ba7c3fe977db50e4fb00f6c 100644 (file)
@@ -519,6 +519,8 @@ static void tpkbd_remove_tp(struct hid_device *hdev)
        led_classdev_unregister(&data_pointer->led_mute);
 
        hid_set_drvdata(hdev, NULL);
+       kfree(data_pointer->led_micmute.name);
+       kfree(data_pointer->led_mute.name);
        kfree(data_pointer);
 }
 
index 4d524b5f52f5631179e9092d6ee471522d3735a3..9500f2f3f8fea702432e683a56dc0eea53dbf3fe 100644 (file)
@@ -193,6 +193,7 @@ static struct hid_ll_driver logi_dj_ll_driver;
 static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf,
                                        size_t count,
                                        unsigned char report_type);
+static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev);
 
 static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev,
                                                struct dj_report *dj_report)
@@ -233,6 +234,7 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
        if (dj_report->report_params[DEVICE_PAIRED_PARAM_SPFUNCTION] &
            SPFUNCTION_DEVICE_LIST_EMPTY) {
                dbg_hid("%s: device list is empty\n", __func__);
+               djrcv_dev->querying_devices = false;
                return;
        }
 
@@ -243,6 +245,12 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
                return;
        }
 
+       if (djrcv_dev->paired_dj_devices[dj_report->device_index]) {
+               /* The device is already known. No need to reallocate it. */
+               dbg_hid("%s: device is already known\n", __func__);
+               return;
+       }
+
        dj_hiddev = hid_allocate_device();
        if (IS_ERR(dj_hiddev)) {
                dev_err(&djrcv_hdev->dev, "%s: hid_allocate_device failed\n",
@@ -306,6 +314,7 @@ static void delayedwork_callback(struct work_struct *work)
        struct dj_report dj_report;
        unsigned long flags;
        int count;
+       int retval;
 
        dbg_hid("%s\n", __func__);
 
@@ -338,6 +347,25 @@ static void delayedwork_callback(struct work_struct *work)
                logi_dj_recv_destroy_djhid_device(djrcv_dev, &dj_report);
                break;
        default:
+       /* A normal report (i. e. not belonging to a pair/unpair notification)
+        * arriving here, means that the report arrived but we did not have a
+        * paired dj_device associated to the report's device_index, this
+        * means that the original "device paired" notification corresponding
+        * to this dj_device never arrived to this driver. The reason is that
+        * hid-core discards all packets coming from a device while probe() is
+        * executing. */
+       if (!djrcv_dev->paired_dj_devices[dj_report.device_index]) {
+               /* ok, we don't know the device, just re-ask the
+                * receiver for the list of connected devices. */
+               retval = logi_dj_recv_query_paired_devices(djrcv_dev);
+               if (!retval) {
+                       /* everything went fine, so just leave */
+                       break;
+               }
+               dev_err(&djrcv_dev->hdev->dev,
+                       "%s:logi_dj_recv_query_paired_devices "
+                       "error:%d\n", __func__, retval);
+               }
                dbg_hid("%s: unexpected report type\n", __func__);
        }
 }
@@ -368,6 +396,12 @@ static void logi_dj_recv_forward_null_report(struct dj_receiver_dev *djrcv_dev,
        if (!djdev) {
                dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
                        " is NULL, index %d\n", dj_report->device_index);
+               kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
+
+               if (schedule_work(&djrcv_dev->work) == 0) {
+                       dbg_hid("%s: did not schedule the work item, was already "
+                       "queued\n", __func__);
+               }
                return;
        }
 
@@ -398,6 +432,12 @@ static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev,
        if (dj_device == NULL) {
                dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
                        " is NULL, index %d\n", dj_report->device_index);
+               kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
+
+               if (schedule_work(&djrcv_dev->work) == 0) {
+                       dbg_hid("%s: did not schedule the work item, was already "
+                       "queued\n", __func__);
+               }
                return;
        }
 
@@ -439,6 +479,10 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
        struct dj_report *dj_report;
        int retval;
 
+       /* no need to protect djrcv_dev->querying_devices */
+       if (djrcv_dev->querying_devices)
+               return 0;
+
        dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL);
        if (!dj_report)
                return -ENOMEM;
@@ -450,6 +494,7 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
        return retval;
 }
 
+
 static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
                                          unsigned timeout)
 {
index fd28a5e0ca3b84b8407791421b25f8b17bf7815a..4a4000340ce1ed8cf6be1f81ee8e0f21d8ba5f5a 100644 (file)
@@ -101,6 +101,7 @@ struct dj_receiver_dev {
        struct work_struct work;
        struct kfifo notif_fifo;
        spinlock_t lock;
+       bool querying_devices;
 };
 
 struct dj_device {
index cfec802cf9ca949b705d24a1d862a702af223193..f915eb1c29f7030e0612c80812e2b32ba25334b4 100644 (file)
@@ -87,10 +87,18 @@ static ssize_t ad7314_show_temperature(struct device *dev,
        }
 }
 
+static ssize_t ad7314_show_name(struct device *dev,
+                               struct device_attribute *devattr, char *buf)
+{
+       return sprintf(buf, "%s\n", to_spi_device(dev)->modalias);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, ad7314_show_name, NULL);
 static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
                          ad7314_show_temperature, NULL, 0);
 
 static struct attribute *ad7314_attributes[] = {
+       &dev_attr_name.attr,
        &sensor_dev_attr_temp1_input.dev_attr.attr,
        NULL,
 };
index e65c6e45d36b05a24241680558fd2012be3f3c85..7bf4ce3d405e4903e26c010cc9040ce87e7067ba 100644 (file)
@@ -139,6 +139,12 @@ static ssize_t show_voltage(struct device *dev,
        }
 }
 
+static ssize_t ads7871_show_name(struct device *dev,
+                                struct device_attribute *devattr, char *buf)
+{
+       return sprintf(buf, "%s\n", to_spi_device(dev)->modalias);
+}
+
 static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_voltage, NULL, 0);
 static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_voltage, NULL, 1);
 static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_voltage, NULL, 2);
@@ -148,6 +154,8 @@ static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_voltage, NULL, 5);
 static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_voltage, NULL, 6);
 static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_voltage, NULL, 7);
 
+static DEVICE_ATTR(name, S_IRUGO, ads7871_show_name, NULL);
+
 static struct attribute *ads7871_attributes[] = {
        &sensor_dev_attr_in0_input.dev_attr.attr,
        &sensor_dev_attr_in1_input.dev_attr.attr,
@@ -157,6 +165,7 @@ static struct attribute *ads7871_attributes[] = {
        &sensor_dev_attr_in5_input.dev_attr.attr,
        &sensor_dev_attr_in6_input.dev_attr.attr,
        &sensor_dev_attr_in7_input.dev_attr.attr,
+       &dev_attr_name.attr,
        NULL
 };
 
index 282708860517e8de41e2f86c3aeb55a118aaca17..8f3f6f2c45fd1a5ba95664a4ba92a6ffe7e6323c 100644 (file)
 
 #define APPLESMC_MAX_DATA_LENGTH 32
 
-/* wait up to 32 ms for a status change. */
+/* wait up to 128 ms for a status change. */
 #define APPLESMC_MIN_WAIT      0x0010
 #define APPLESMC_RETRY_WAIT    0x0100
-#define APPLESMC_MAX_WAIT      0x8000
+#define APPLESMC_MAX_WAIT      0x20000
 
 #define APPLESMC_READ_CMD      0x10
 #define APPLESMC_WRITE_CMD     0x11
index 0fa356fe82ccc00fc6a70704627da99bb66fc49a..984a3f13923b615b7c6849cd59b396689680ed20 100644 (file)
@@ -815,17 +815,20 @@ static int __init coretemp_init(void)
        if (err)
                goto exit;
 
+       get_online_cpus();
        for_each_online_cpu(i)
                get_core_online(i);
 
 #ifndef CONFIG_HOTPLUG_CPU
        if (list_empty(&pdev_list)) {
+               put_online_cpus();
                err = -ENODEV;
                goto exit_driver_unreg;
        }
 #endif
 
        register_hotcpu_notifier(&coretemp_cpu_notifier);
+       put_online_cpus();
        return 0;
 
 #ifndef CONFIG_HOTPLUG_CPU
@@ -840,6 +843,7 @@ static void __exit coretemp_exit(void)
 {
        struct pdev_entry *p, *n;
 
+       get_online_cpus();
        unregister_hotcpu_notifier(&coretemp_cpu_notifier);
        mutex_lock(&pdev_list_mutex);
        list_for_each_entry_safe(p, n, &pdev_list, list) {
@@ -848,6 +852,7 @@ static void __exit coretemp_exit(void)
                kfree(p);
        }
        mutex_unlock(&pdev_list_mutex);
+       put_online_cpus();
        platform_driver_unregister(&coretemp_driver);
 }
 
index 2764b78a784b9be83f9df1fd48f558452d9b8d61..af69073b3fe87a323e8986f76268c3ec1aa78522 100644 (file)
@@ -129,12 +129,12 @@ static bool __devinit fam15h_power_is_internal_node0(struct pci_dev *f4)
  * counter saturations resulting in bogus power readings.
  * We correct this value ourselves to cope with older BIOSes.
  */
-static DEFINE_PCI_DEVICE_TABLE(affected_device) = {
+static const struct pci_device_id affected_device[] = {
        { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
        { 0 }
 };
 
-static void __devinit tweak_runavg_range(struct pci_dev *pdev)
+static void tweak_runavg_range(struct pci_dev *pdev)
 {
        u32 val;
 
@@ -158,6 +158,16 @@ static void __devinit tweak_runavg_range(struct pci_dev *pdev)
                REG_TDP_RUNNING_AVERAGE, val);
 }
 
+#ifdef CONFIG_PM
+static int fam15h_power_resume(struct pci_dev *pdev)
+{
+       tweak_runavg_range(pdev);
+       return 0;
+}
+#else
+#define fam15h_power_resume NULL
+#endif
+
 static void __devinit fam15h_power_init_data(struct pci_dev *f4,
                                             struct fam15h_power_data *data)
 {
@@ -256,6 +266,7 @@ static struct pci_driver fam15h_power_driver = {
        .id_table = fam15h_power_id_table,
        .probe = fam15h_power_probe,
        .remove = __devexit_p(fam15h_power_remove),
+       .resume = fam15h_power_resume,
 };
 
 module_pci_driver(fam15h_power_driver);
index 7f3f4a385729375c002409387d157f3565b30e04..602148299f68db03a81683ab95c14e39614e5d8d 100644 (file)
@@ -69,22 +69,6 @@ struct ina2xx_data {
        u16 regs[INA2XX_MAX_REGISTERS];
 };
 
-int ina2xx_read_word(struct i2c_client *client, int reg)
-{
-       int val = i2c_smbus_read_word_data(client, reg);
-       if (unlikely(val < 0)) {
-               dev_dbg(&client->dev,
-                       "Failed to read register: %d\n", reg);
-               return val;
-       }
-       return be16_to_cpu(val);
-}
-
-void ina2xx_write_word(struct i2c_client *client, int reg, int data)
-{
-       i2c_smbus_write_word_data(client, reg, cpu_to_be16(data));
-}
-
 static struct ina2xx_data *ina2xx_update_device(struct device *dev)
 {
        struct i2c_client *client = to_i2c_client(dev);
@@ -102,7 +86,7 @@ static struct ina2xx_data *ina2xx_update_device(struct device *dev)
 
                /* Read all registers */
                for (i = 0; i < data->registers; i++) {
-                       int rv = ina2xx_read_word(client, i);
+                       int rv = i2c_smbus_read_word_swapped(client, i);
                        if (rv < 0) {
                                ret = ERR_PTR(rv);
                                goto abort;
@@ -279,22 +263,26 @@ static int ina2xx_probe(struct i2c_client *client,
        switch (data->kind) {
        case ina219:
                /* device configuration */
-               ina2xx_write_word(client, INA2XX_CONFIG, INA219_CONFIG_DEFAULT);
+               i2c_smbus_write_word_swapped(client, INA2XX_CONFIG,
+                                            INA219_CONFIG_DEFAULT);
 
                /* set current LSB to 1mA, shunt is in uOhms */
                /* (equation 13 in datasheet) */
-               ina2xx_write_word(client, INA2XX_CALIBRATION, 40960000 / shunt);
+               i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION,
+                                            40960000 / shunt);
                dev_info(&client->dev,
                         "power monitor INA219 (Rshunt = %li uOhm)\n", shunt);
                data->registers = INA219_REGISTERS;
                break;
        case ina226:
                /* device configuration */
-               ina2xx_write_word(client, INA2XX_CONFIG, INA226_CONFIG_DEFAULT);
+               i2c_smbus_write_word_swapped(client, INA2XX_CONFIG,
+                                            INA226_CONFIG_DEFAULT);
 
                /* set current LSB to 1mA, shunt is in uOhms */
                /* (equation 1 in datasheet)*/
-               ina2xx_write_word(client, INA2XX_CALIBRATION, 5120000 / shunt);
+               i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION,
+                                            5120000 / shunt);
                dev_info(&client->dev,
                         "power monitor INA226 (Rshunt = %li uOhm)\n", shunt);
                data->registers = INA226_REGISTERS;
index 0018c7dd0097de5045f646d98e715713ea7edba4..1a174f0a3cdeb9bd854d13fa61171a63c33e41d7 100644 (file)
@@ -44,12 +44,13 @@ static ssize_t madc_read(struct device *dev,
                         struct device_attribute *devattr, char *buf)
 {
        struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct twl4030_madc_request req;
+       struct twl4030_madc_request req = {
+               .channels = 1 << attr->index,
+               .method = TWL4030_MADC_SW2,
+               .type = TWL4030_MADC_WAIT,
+       };
        long val;
 
-       req.channels = (1 << attr->index);
-       req.method = TWL4030_MADC_SW2;
-       req.func_cb = NULL;
        val = twl4030_madc_conversion(&req);
        if (val < 0)
                return val;
index ee4ebc198a9448e868f45af079e8f3d5f51968ae..2e56c6ce9fb654cb0532f4cbef756fd99f618e9d 100644 (file)
@@ -328,6 +328,7 @@ static int __init via_cputemp_init(void)
        if (err)
                goto exit;
 
+       get_online_cpus();
        for_each_online_cpu(i) {
                struct cpuinfo_x86 *c = &cpu_data(i);
 
@@ -347,12 +348,14 @@ static int __init via_cputemp_init(void)
 
 #ifndef CONFIG_HOTPLUG_CPU
        if (list_empty(&pdev_list)) {
+               put_online_cpus();
                err = -ENODEV;
                goto exit_driver_unreg;
        }
 #endif
 
        register_hotcpu_notifier(&via_cputemp_cpu_notifier);
+       put_online_cpus();
        return 0;
 
 #ifndef CONFIG_HOTPLUG_CPU
@@ -367,6 +370,7 @@ static void __exit via_cputemp_exit(void)
 {
        struct pdev_entry *p, *n;
 
+       get_online_cpus();
        unregister_hotcpu_notifier(&via_cputemp_cpu_notifier);
        mutex_lock(&pdev_list_mutex);
        list_for_each_entry_safe(p, n, &pdev_list, list) {
@@ -375,6 +379,7 @@ static void __exit via_cputemp_exit(void)
                kfree(p);
        }
        mutex_unlock(&pdev_list_mutex);
+       put_online_cpus();
        platform_driver_unregister(&via_cputemp_driver);
 }
 
index 1201a15784c3a0eec329affa6f6edf1546df0b51..db713c0dfba4d6c6272ac19c79b793595a9cdad7 100644 (file)
@@ -552,7 +552,7 @@ EXPORT_SYMBOL_GPL(hwspin_lock_request_specific);
  */
 int hwspin_lock_free(struct hwspinlock *hwlock)
 {
-       struct device *dev = hwlock->bank->dev;
+       struct device *dev;
        struct hwspinlock *tmp;
        int ret;
 
@@ -561,6 +561,7 @@ int hwspin_lock_free(struct hwspinlock *hwlock)
                return -EINVAL;
        }
 
+       dev = hwlock->bank->dev;
        mutex_lock(&hwspinlock_tree_lock);
 
        /* make sure the hwspinlock is used */
index 73133b1063f012416d2a957f3fc2432ace395439..6f5f98d69af7c26b2fd7b895106e11745ce7d155 100644 (file)
@@ -476,17 +476,17 @@ static int pca_init(struct i2c_adapter *adap)
                /* To avoid integer overflow, use clock/100 for calculations */
                clock = pca_clock(pca_data) / 100;
 
-               if (pca_data->i2c_clock > 10000) {
+               if (pca_data->i2c_clock > 1000000) {
                        mode = I2C_PCA_MODE_TURBO;
                        min_tlow = 14;
                        min_thi  = 5;
                        raise_fall_time = 22; /* Raise 11e-8s, Fall 11e-8s */
-               } else if (pca_data->i2c_clock > 4000) {
+               } else if (pca_data->i2c_clock > 400000) {
                        mode = I2C_PCA_MODE_FASTP;
                        min_tlow = 17;
                        min_thi  = 9;
                        raise_fall_time = 22; /* Raise 11e-8s, Fall 11e-8s */
-               } else if (pca_data->i2c_clock > 1000) {
+               } else if (pca_data->i2c_clock > 100000) {
                        mode = I2C_PCA_MODE_FAST;
                        min_tlow = 44;
                        min_thi  = 20;
index 088c5c1ed17dfe831c4345ee8f02dd0ef4e1c82d..51f05b8520edb3f95b983d5002859afd386586e4 100644 (file)
@@ -365,10 +365,6 @@ static int mxs_i2c_get_ofdata(struct mxs_i2c_dev *i2c)
        struct device_node *node = dev->of_node;
        int ret;
 
-       if (!node)
-               return -EINVAL;
-
-       i2c->speed = &mxs_i2c_95kHz_config;
        ret = of_property_read_u32(node, "clock-frequency", &speed);
        if (ret)
                dev_warn(dev, "No I2C speed selected, using 100kHz\n");
@@ -419,10 +415,13 @@ static int __devinit mxs_i2c_probe(struct platform_device *pdev)
                return err;
 
        i2c->dev = dev;
+       i2c->speed = &mxs_i2c_95kHz_config;
 
-       err = mxs_i2c_get_ofdata(i2c);
-       if (err)
-               return err;
+       if (dev->of_node) {
+               err = mxs_i2c_get_ofdata(i2c);
+               if (err)
+                       return err;
+       }
 
        platform_set_drvdata(pdev, i2c);
 
index 5d54416770b01e7816cc85cd7dcbf403bf407442..8488bddfe46596109249edd242a3ad0ebc7cfe8b 100644 (file)
@@ -48,8 +48,9 @@ enum {
        mcntrl_afie = 0x00000002,
        mcntrl_naie = 0x00000004,
        mcntrl_drmie = 0x00000008,
-       mcntrl_daie = 0x00000020,
-       mcntrl_rffie = 0x00000040,
+       mcntrl_drsie = 0x00000010,
+       mcntrl_rffie = 0x00000020,
+       mcntrl_daie = 0x00000040,
        mcntrl_tffie = 0x00000080,
        mcntrl_reset = 0x00000100,
        mcntrl_cdbmode = 0x00000400,
@@ -290,31 +291,37 @@ static int i2c_pnx_master_rcv(struct i2c_pnx_algo_data *alg_data)
         * or we didn't 'ask' for it yet.
         */
        if (ioread32(I2C_REG_STS(alg_data)) & mstatus_rfe) {
-               dev_dbg(&alg_data->adapter.dev,
-                       "%s(): Write dummy data to fill Rx-fifo...\n",
-                       __func__);
+               /* 'Asking' is done asynchronously, e.g. dummy TX of several
+                * bytes is done before the first actual RX arrives in FIFO.
+                * Therefore, ordered bytes (via TX) are counted separately.
+                */
+               if (alg_data->mif.order) {
+                       dev_dbg(&alg_data->adapter.dev,
+                               "%s(): Write dummy data to fill Rx-fifo...\n",
+                               __func__);
 
-               if (alg_data->mif.len == 1) {
-                       /* Last byte, do not acknowledge next rcv. */
-                       val |= stop_bit;
+                       if (alg_data->mif.order == 1) {
+                               /* Last byte, do not acknowledge next rcv. */
+                               val |= stop_bit;
+
+                               /*
+                                * Enable interrupt RFDAIE (data in Rx fifo),
+                                * and disable DRMIE (need data for Tx)
+                                */
+                               ctl = ioread32(I2C_REG_CTL(alg_data));
+                               ctl |= mcntrl_rffie | mcntrl_daie;
+                               ctl &= ~mcntrl_drmie;
+                               iowrite32(ctl, I2C_REG_CTL(alg_data));
+                       }
 
                        /*
-                        * Enable interrupt RFDAIE (data in Rx fifo),
-                        * and disable DRMIE (need data for Tx)
+                        * Now we'll 'ask' for data:
+                        * For each byte we want to receive, we must
+                        * write a (dummy) byte to the Tx-FIFO.
                         */
-                       ctl = ioread32(I2C_REG_CTL(alg_data));
-                       ctl |= mcntrl_rffie | mcntrl_daie;
-                       ctl &= ~mcntrl_drmie;
-                       iowrite32(ctl, I2C_REG_CTL(alg_data));
+                       iowrite32(val, I2C_REG_TX(alg_data));
+                       alg_data->mif.order--;
                }
-
-               /*
-                * Now we'll 'ask' for data:
-                * For each byte we want to receive, we must
-                * write a (dummy) byte to the Tx-FIFO.
-                */
-               iowrite32(val, I2C_REG_TX(alg_data));
-
                return 0;
        }
 
@@ -514,6 +521,7 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 
                alg_data->mif.buf = pmsg->buf;
                alg_data->mif.len = pmsg->len;
+               alg_data->mif.order = pmsg->len;
                alg_data->mif.mode = (pmsg->flags & I2C_M_RD) ?
                        I2C_SMBUS_READ : I2C_SMBUS_WRITE;
                alg_data->mif.ret = 0;
@@ -566,6 +574,7 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
        /* Cleanup to be sure... */
        alg_data->mif.buf = NULL;
        alg_data->mif.len = 0;
+       alg_data->mif.order = 0;
 
        dev_dbg(&alg_data->adapter.dev, "%s(): exiting, stat = %x\n",
                __func__, ioread32(I2C_REG_STS(alg_data)));
index f61780a02374d1f855af861092c3ebae5b35cd80..3bd5540238a7e6d683fd903cfacc14c8b05b92d5 100644 (file)
@@ -617,7 +617,7 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
        st->adc_clk = clk_get(&pdev->dev, "adc_op_clk");
        if (IS_ERR(st->adc_clk)) {
                dev_err(&pdev->dev, "Failed to get the ADC clock.\n");
-               ret = PTR_ERR(st->clk);
+               ret = PTR_ERR(st->adc_clk);
                goto error_disable_clk;
        }
 
index 51f42061dae9de4d375795e89e5a95b28bbc0405..6cfd4d8fd0bd8e240dd1311698a2cf603fcfed15 100644 (file)
@@ -1361,11 +1361,11 @@ static int abort_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
        struct tid_info *t = dev->rdev.lldi.tids;
 
        ep = lookup_tid(t, tid);
-       PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
        if (!ep) {
                printk(KERN_WARNING MOD "Abort rpl to freed endpoint\n");
                return 0;
        }
+       PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
        mutex_lock(&ep->com.mutex);
        switch (ep->com.state) {
        case ABORTING:
index cb5b7f7d4d3876904bb6b515273747c37787465d..b29a4246ef41ef6068372398ed23134cceae3384 100644 (file)
@@ -2219,7 +2219,6 @@ static bool ocrdma_poll_success_scqe(struct ocrdma_qp *qp,
        u32 wqe_idx;
 
        if (!qp->wqe_wr_id_tbl[tail].signaled) {
-               expand = true;  /* CQE cannot be consumed yet */
                *polled = false;    /* WC cannot be consumed yet */
        } else {
                ibwc->status = IB_WC_SUCCESS;
@@ -2227,10 +2226,11 @@ static bool ocrdma_poll_success_scqe(struct ocrdma_qp *qp,
                ibwc->qp = &qp->ibqp;
                ocrdma_update_wc(qp, ibwc, tail);
                *polled = true;
-               wqe_idx = le32_to_cpu(cqe->wq.wqeidx) & OCRDMA_CQE_WQEIDX_MASK;
-               if (tail != wqe_idx)
-                       expand = true; /* Coalesced CQE can't be consumed yet */
        }
+       wqe_idx = le32_to_cpu(cqe->wq.wqeidx) & OCRDMA_CQE_WQEIDX_MASK;
+       if (tail != wqe_idx)
+               expand = true; /* Coalesced CQE can't be consumed yet */
+
        ocrdma_hwq_inc_tail(&qp->sq);
        return expand;
 }
index 19f1e6c45fb6847caaac2a45af4e69d4df981ca4..ccb119143d20568eb0570ab5b1360a78ca888e08 100644 (file)
@@ -471,9 +471,10 @@ static int subn_get_portinfo(struct ib_smp *smp, struct ib_device *ibdev,
                if (port_num != port) {
                        ibp = to_iport(ibdev, port_num);
                        ret = check_mkey(ibp, smp, 0);
-                       if (ret)
+                       if (ret) {
                                ret = IB_MAD_RESULT_FAILURE;
                                goto bail;
+                       }
                }
        }
 
index ca43901ed861b8d2bc31c70a9e570d9db3d2f8c1..0af216d21f8790c31af507022bbc722627f41671 100644 (file)
@@ -262,7 +262,10 @@ struct ipoib_ethtool_st {
        u16     max_coalesced_frames;
 };
 
+struct ipoib_neigh_table;
+
 struct ipoib_neigh_hash {
+       struct ipoib_neigh_table       *ntbl;
        struct ipoib_neigh __rcu      **buckets;
        struct rcu_head                 rcu;
        u32                             mask;
@@ -271,9 +274,9 @@ struct ipoib_neigh_hash {
 
 struct ipoib_neigh_table {
        struct ipoib_neigh_hash __rcu  *htbl;
-       rwlock_t                        rwlock;
        atomic_t                        entries;
        struct completion               flushed;
+       struct completion               deleted;
 };
 
 /*
index 3e2085a3ee474fc0111c7034ca013bfda7d63d7e..1e19b5ae7c479a5865837ffa4b08f7f5c89bdcdc 100644 (file)
@@ -546,15 +546,15 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr,
        struct ipoib_neigh *neigh;
        unsigned long flags;
 
+       spin_lock_irqsave(&priv->lock, flags);
        neigh = ipoib_neigh_alloc(daddr, dev);
        if (!neigh) {
+               spin_unlock_irqrestore(&priv->lock, flags);
                ++dev->stats.tx_dropped;
                dev_kfree_skb_any(skb);
                return;
        }
 
-       spin_lock_irqsave(&priv->lock, flags);
-
        path = __path_find(dev, daddr + 4);
        if (!path) {
                path = path_rec_create(dev, daddr + 4);
@@ -863,10 +863,10 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
        if (test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags))
                return;
 
-       write_lock_bh(&ntbl->rwlock);
+       spin_lock_irqsave(&priv->lock, flags);
 
        htbl = rcu_dereference_protected(ntbl->htbl,
-                                        lockdep_is_held(&ntbl->rwlock));
+                                        lockdep_is_held(&priv->lock));
 
        if (!htbl)
                goto out_unlock;
@@ -883,16 +883,14 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
                struct ipoib_neigh __rcu **np = &htbl->buckets[i];
 
                while ((neigh = rcu_dereference_protected(*np,
-                                                         lockdep_is_held(&ntbl->rwlock))) != NULL) {
+                                                         lockdep_is_held(&priv->lock))) != NULL) {
                        /* was the neigh idle for two GC periods */
                        if (time_after(neigh_obsolete, neigh->alive)) {
                                rcu_assign_pointer(*np,
                                                   rcu_dereference_protected(neigh->hnext,
-                                                                            lockdep_is_held(&ntbl->rwlock)));
+                                                                            lockdep_is_held(&priv->lock)));
                                /* remove from path/mc list */
-                               spin_lock_irqsave(&priv->lock, flags);
                                list_del(&neigh->list);
-                               spin_unlock_irqrestore(&priv->lock, flags);
                                call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
                        } else {
                                np = &neigh->hnext;
@@ -902,7 +900,7 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
        }
 
 out_unlock:
-       write_unlock_bh(&ntbl->rwlock);
+       spin_unlock_irqrestore(&priv->lock, flags);
 }
 
 static void ipoib_reap_neigh(struct work_struct *work)
@@ -947,10 +945,8 @@ struct ipoib_neigh *ipoib_neigh_alloc(u8 *daddr,
        struct ipoib_neigh *neigh;
        u32 hash_val;
 
-       write_lock_bh(&ntbl->rwlock);
-
        htbl = rcu_dereference_protected(ntbl->htbl,
-                                        lockdep_is_held(&ntbl->rwlock));
+                                        lockdep_is_held(&priv->lock));
        if (!htbl) {
                neigh = NULL;
                goto out_unlock;
@@ -961,10 +957,10 @@ struct ipoib_neigh *ipoib_neigh_alloc(u8 *daddr,
         */
        hash_val = ipoib_addr_hash(htbl, daddr);
        for (neigh = rcu_dereference_protected(htbl->buckets[hash_val],
-                                              lockdep_is_held(&ntbl->rwlock));
+                                              lockdep_is_held(&priv->lock));
             neigh != NULL;
             neigh = rcu_dereference_protected(neigh->hnext,
-                                              lockdep_is_held(&ntbl->rwlock))) {
+                                              lockdep_is_held(&priv->lock))) {
                if (memcmp(daddr, neigh->daddr, INFINIBAND_ALEN) == 0) {
                        /* found, take one ref on behalf of the caller */
                        if (!atomic_inc_not_zero(&neigh->refcnt)) {
@@ -987,12 +983,11 @@ struct ipoib_neigh *ipoib_neigh_alloc(u8 *daddr,
        /* put in hash */
        rcu_assign_pointer(neigh->hnext,
                           rcu_dereference_protected(htbl->buckets[hash_val],
-                                                    lockdep_is_held(&ntbl->rwlock)));
+                                                    lockdep_is_held(&priv->lock)));
        rcu_assign_pointer(htbl->buckets[hash_val], neigh);
        atomic_inc(&ntbl->entries);
 
 out_unlock:
-       write_unlock_bh(&ntbl->rwlock);
 
        return neigh;
 }
@@ -1040,35 +1035,29 @@ void ipoib_neigh_free(struct ipoib_neigh *neigh)
        struct ipoib_neigh *n;
        u32 hash_val;
 
-       write_lock_bh(&ntbl->rwlock);
-
        htbl = rcu_dereference_protected(ntbl->htbl,
-                                       lockdep_is_held(&ntbl->rwlock));
+                                       lockdep_is_held(&priv->lock));
        if (!htbl)
-               goto out_unlock;
+               return;
 
        hash_val = ipoib_addr_hash(htbl, neigh->daddr);
        np = &htbl->buckets[hash_val];
        for (n = rcu_dereference_protected(*np,
-                                           lockdep_is_held(&ntbl->rwlock));
+                                           lockdep_is_held(&priv->lock));
             n != NULL;
             n = rcu_dereference_protected(*np,
-                                       lockdep_is_held(&ntbl->rwlock))) {
+                                       lockdep_is_held(&priv->lock))) {
                if (n == neigh) {
                        /* found */
                        rcu_assign_pointer(*np,
                                           rcu_dereference_protected(neigh->hnext,
-                                                                    lockdep_is_held(&ntbl->rwlock)));
+                                                                    lockdep_is_held(&priv->lock)));
                        call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
-                       goto out_unlock;
+                       return;
                } else {
                        np = &n->hnext;
                }
        }
-
-out_unlock:
-       write_unlock_bh(&ntbl->rwlock);
-
 }
 
 static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
@@ -1080,7 +1069,6 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
 
        clear_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags);
        ntbl->htbl = NULL;
-       rwlock_init(&ntbl->rwlock);
        htbl = kzalloc(sizeof(*htbl), GFP_KERNEL);
        if (!htbl)
                return -ENOMEM;
@@ -1095,6 +1083,7 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
        htbl->mask = (size - 1);
        htbl->buckets = buckets;
        ntbl->htbl = htbl;
+       htbl->ntbl = ntbl;
        atomic_set(&ntbl->entries, 0);
 
        /* start garbage collection */
@@ -1111,9 +1100,11 @@ static void neigh_hash_free_rcu(struct rcu_head *head)
                                                    struct ipoib_neigh_hash,
                                                    rcu);
        struct ipoib_neigh __rcu **buckets = htbl->buckets;
+       struct ipoib_neigh_table *ntbl = htbl->ntbl;
 
        kfree(buckets);
        kfree(htbl);
+       complete(&ntbl->deleted);
 }
 
 void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid)
@@ -1125,10 +1116,10 @@ void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid)
        int i;
 
        /* remove all neigh connected to a given path or mcast */
-       write_lock_bh(&ntbl->rwlock);
+       spin_lock_irqsave(&priv->lock, flags);
 
        htbl = rcu_dereference_protected(ntbl->htbl,
-                                        lockdep_is_held(&ntbl->rwlock));
+                                        lockdep_is_held(&priv->lock));
 
        if (!htbl)
                goto out_unlock;
@@ -1138,16 +1129,14 @@ void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid)
                struct ipoib_neigh __rcu **np = &htbl->buckets[i];
 
                while ((neigh = rcu_dereference_protected(*np,
-                                                         lockdep_is_held(&ntbl->rwlock))) != NULL) {
+                                                         lockdep_is_held(&priv->lock))) != NULL) {
                        /* delete neighs belong to this parent */
                        if (!memcmp(gid, neigh->daddr + 4, sizeof (union ib_gid))) {
                                rcu_assign_pointer(*np,
                                                   rcu_dereference_protected(neigh->hnext,
-                                                                            lockdep_is_held(&ntbl->rwlock)));
+                                                                            lockdep_is_held(&priv->lock)));
                                /* remove from parent list */
-                               spin_lock_irqsave(&priv->lock, flags);
                                list_del(&neigh->list);
-                               spin_unlock_irqrestore(&priv->lock, flags);
                                call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
                        } else {
                                np = &neigh->hnext;
@@ -1156,7 +1145,7 @@ void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid)
                }
        }
 out_unlock:
-       write_unlock_bh(&ntbl->rwlock);
+       spin_unlock_irqrestore(&priv->lock, flags);
 }
 
 static void ipoib_flush_neighs(struct ipoib_dev_priv *priv)
@@ -1164,37 +1153,44 @@ static void ipoib_flush_neighs(struct ipoib_dev_priv *priv)
        struct ipoib_neigh_table *ntbl = &priv->ntbl;
        struct ipoib_neigh_hash *htbl;
        unsigned long flags;
-       int i;
+       int i, wait_flushed = 0;
 
-       write_lock_bh(&ntbl->rwlock);
+       init_completion(&priv->ntbl.flushed);
+
+       spin_lock_irqsave(&priv->lock, flags);
 
        htbl = rcu_dereference_protected(ntbl->htbl,
-                                       lockdep_is_held(&ntbl->rwlock));
+                                       lockdep_is_held(&priv->lock));
        if (!htbl)
                goto out_unlock;
 
+       wait_flushed = atomic_read(&priv->ntbl.entries);
+       if (!wait_flushed)
+               goto free_htbl;
+
        for (i = 0; i < htbl->size; i++) {
                struct ipoib_neigh *neigh;
                struct ipoib_neigh __rcu **np = &htbl->buckets[i];
 
                while ((neigh = rcu_dereference_protected(*np,
-                                                         lockdep_is_held(&ntbl->rwlock))) != NULL) {
+                                      lockdep_is_held(&priv->lock))) != NULL) {
                        rcu_assign_pointer(*np,
                                           rcu_dereference_protected(neigh->hnext,
-                                                                    lockdep_is_held(&ntbl->rwlock)));
+                                                                    lockdep_is_held(&priv->lock)));
                        /* remove from path/mc list */
-                       spin_lock_irqsave(&priv->lock, flags);
                        list_del(&neigh->list);
-                       spin_unlock_irqrestore(&priv->lock, flags);
                        call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
                }
        }
 
+free_htbl:
        rcu_assign_pointer(ntbl->htbl, NULL);
        call_rcu(&htbl->rcu, neigh_hash_free_rcu);
 
 out_unlock:
-       write_unlock_bh(&ntbl->rwlock);
+       spin_unlock_irqrestore(&priv->lock, flags);
+       if (wait_flushed)
+               wait_for_completion(&priv->ntbl.flushed);
 }
 
 static void ipoib_neigh_hash_uninit(struct net_device *dev)
@@ -1203,7 +1199,7 @@ static void ipoib_neigh_hash_uninit(struct net_device *dev)
        int stopped;
 
        ipoib_dbg(priv, "ipoib_neigh_hash_uninit\n");
-       init_completion(&priv->ntbl.flushed);
+       init_completion(&priv->ntbl.deleted);
        set_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags);
 
        /* Stop GC if called at init fail need to cancel work */
@@ -1211,10 +1207,9 @@ static void ipoib_neigh_hash_uninit(struct net_device *dev)
        if (!stopped)
                cancel_delayed_work(&priv->neigh_reap_task);
 
-       if (atomic_read(&priv->ntbl.entries)) {
-               ipoib_flush_neighs(priv);
-               wait_for_completion(&priv->ntbl.flushed);
-       }
+       ipoib_flush_neighs(priv);
+
+       wait_for_completion(&priv->ntbl.deleted);
 }
 
 
index 13f4aa7593c834f2ee1475c6471cad8e0bcd12f8..75367249f447f497851e76a0a4312b6000989ff2 100644 (file)
@@ -707,9 +707,7 @@ out:
                neigh = ipoib_neigh_get(dev, daddr);
                spin_lock_irqsave(&priv->lock, flags);
                if (!neigh) {
-                       spin_unlock_irqrestore(&priv->lock, flags);
                        neigh = ipoib_neigh_alloc(daddr, dev);
-                       spin_lock_irqsave(&priv->lock, flags);
                        if (neigh) {
                                kref_get(&mcast->ah->ref);
                                neigh->ah       = mcast->ah;
index ce68e361558c650ae8eb2032798048c8030cc309..cdc252612c0b35c453b976355c13b26f6d6767e1 100644 (file)
@@ -516,9 +516,9 @@ static int __devinit imx_keypad_probe(struct platform_device *pdev)
        input_set_drvdata(input_dev, keypad);
 
        /* Ensure that the keypad will stay dormant until opened */
-       clk_enable(keypad->clk);
+       clk_prepare_enable(keypad->clk);
        imx_keypad_inhibit(keypad);
-       clk_disable(keypad->clk);
+       clk_disable_unprepare(keypad->clk);
 
        error = request_irq(irq, imx_keypad_irq_handler, 0,
                            pdev->name, keypad);
index f06231b7cab1ef762d7ad5db073b3a6b64d39987..84ec691c05aa983751492a8dff29c5ca87b27ad2 100644 (file)
@@ -74,8 +74,8 @@ static int __devinit ab8500_ponkey_probe(struct platform_device *pdev)
 
        ponkey->idev = input;
        ponkey->ab8500 = ab8500;
-       ponkey->irq_dbf = ab8500_irq_get_virq(ab8500, irq_dbf);
-       ponkey->irq_dbr = ab8500_irq_get_virq(ab8500, irq_dbr);
+       ponkey->irq_dbf = irq_dbf;
+       ponkey->irq_dbr = irq_dbr;
 
        input->name = "AB8500 POn(PowerOn) Key";
        input->dev.parent = &pdev->dev;
index 3f5649f190824408a25c495bdfd4da88b8febe9a..a261d857691957a98e732124f6370c6f8280451a 100644 (file)
@@ -721,6 +721,17 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
 
        switch (psmouse->packet[0] >> FSP_PKT_TYPE_SHIFT) {
        case FSP_PKT_TYPE_ABS:
+
+               if ((packet[0] == 0x48 || packet[0] == 0x49) &&
+                   packet[1] == 0 && packet[2] == 0) {
+                       /*
+                        * Ignore coordinate noise when finger leaving the
+                        * surface, otherwise cursor may jump to upper-left
+                        * corner.
+                        */
+                       packet[3] &= 0xf0;
+               }
+
                abs_x = GET_ABS_X(packet);
                abs_y = GET_ABS_Y(packet);
 
index 2ffd110bd5bc756e461051d39ba31238202a0fa7..2e77246c2e5ac8293569ecd19fe1df673c394c75 100644 (file)
@@ -72,7 +72,7 @@ static int amba_kmi_open(struct serio *io)
        unsigned int divisor;
        int ret;
 
-       ret = clk_enable(kmi->clk);
+       ret = clk_prepare_enable(kmi->clk);
        if (ret)
                goto out;
 
@@ -92,7 +92,7 @@ static int amba_kmi_open(struct serio *io)
        return 0;
 
  clk_disable:
-       clk_disable(kmi->clk);
+       clk_disable_unprepare(kmi->clk);
  out:
        return ret;
 }
@@ -104,7 +104,7 @@ static void amba_kmi_close(struct serio *io)
        writeb(0, KMICR);
 
        free_irq(kmi->irq, kmi);
-       clk_disable(kmi->clk);
+       clk_disable_unprepare(kmi->clk);
 }
 
 static int __devinit amba_kmi_probe(struct amba_device *dev,
index 6918773ce02443e67181c62823023597e3ba44ce..d6cc77a53c7e2bdbe94ee3c138ab8c86f0d90135 100644 (file)
@@ -333,6 +333,12 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "EQUIUM A110"),
                },
        },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE C850D"),
+               },
+       },
        {
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "ALIENWARE"),
index b06a5e3a665ea1864c8824f6d7d172f87d0b3ac1..64957770b52209fcc527e3f015e21816b3165bc7 100644 (file)
@@ -566,9 +566,12 @@ static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file,
        }
 
        read = min_t(size_t, count, tsdata->raw_bufsize - *off);
-       error = copy_to_user(buf, tsdata->raw_buffer + *off, read);
-       if (!error)
-               *off += read;
+       if (copy_to_user(buf, tsdata->raw_buffer + *off, read)) {
+               error = -EFAULT;
+               goto out;
+       }
+
+       *off += read;
 out:
        mutex_unlock(&tsdata->mutex);
        return error ?: read;
index e32709e0dd65193286bccc2e81bd4e86d717c643..721fdb3597ca9d9ecb3f971f389f6852395ff39c 100644 (file)
@@ -304,6 +304,45 @@ static int e2i_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
 #define EGALAX_PKT_TYPE_REPT           0x80
 #define EGALAX_PKT_TYPE_DIAG           0x0A
 
+static int egalax_init(struct usbtouch_usb *usbtouch)
+{
+       int ret, i;
+       unsigned char *buf;
+       struct usb_device *udev = interface_to_usbdev(usbtouch->interface);
+
+       /*
+        * An eGalax diagnostic packet kicks the device into using the right
+        * protocol.  We send a "check active" packet.  The response will be
+        * read later and ignored.
+        */
+
+       buf = kmalloc(3, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       buf[0] = EGALAX_PKT_TYPE_DIAG;
+       buf[1] = 1;     /* length */
+       buf[2] = 'A';   /* command - check active */
+
+       for (i = 0; i < 3; i++) {
+               ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                                     0,
+                                     USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                                     0, 0, buf, 3,
+                                     USB_CTRL_SET_TIMEOUT);
+               if (ret >= 0) {
+                       ret = 0;
+                       break;
+               }
+               if (ret != -EPIPE)
+                       break;
+       }
+
+       kfree(buf);
+
+       return ret;
+}
+
 static int egalax_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
 {
        if ((pkt[0] & EGALAX_PKT_TYPE_MASK) != EGALAX_PKT_TYPE_REPT)
@@ -1056,6 +1095,7 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
                .process_pkt    = usbtouch_process_multi,
                .get_pkt_len    = egalax_get_pkt_len,
                .read_data      = egalax_read_data,
+               .init           = egalax_init,
        },
 #endif
 
index fa6ca473372539fda128a7a1ec46d9c4d13c3c4a..dceaec821b0e5324cbfa2d92bdb7334845bf9262 100644 (file)
@@ -857,8 +857,9 @@ avm_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
        switch (cmd) {
        case CLOSE_CHANNEL:
                test_and_clear_bit(FLG_OPEN, &bch->Flags);
+               cancel_work_sync(&bch->workq);
                spin_lock_irqsave(&fc->lock, flags);
-               mISDN_freebchannel(bch);
+               mISDN_clear_bchannel(bch);
                modehdlc(bch, ISDN_P_NONE);
                spin_unlock_irqrestore(&fc->lock, flags);
                ch->protocol = ISDN_P_NONE;
index 5e402cf2e79506b82288140334da60eb4e923e6d..f02794203bb193b41291efc3bc6d8457b2043883 100644 (file)
@@ -5059,6 +5059,7 @@ hfcmulti_init(struct hm_map *m, struct pci_dev *pdev,
                                printk(KERN_INFO
                                       "HFC-E1 #%d has overlapping B-channels on fragment #%d\n",
                                       E1_cnt + 1, pt);
+                               kfree(hc);
                                return -EINVAL;
                        }
                        maskcheck |= hc->bmask[pt];
@@ -5086,6 +5087,7 @@ hfcmulti_init(struct hm_map *m, struct pci_dev *pdev,
        if ((poll >> 1) > sizeof(hc->silence_data)) {
                printk(KERN_ERR "HFCMULTI error: silence_data too small, "
                       "please fix\n");
+               kfree(hc);
                return -EINVAL;
        }
        for (i = 0; i < (poll >> 1); i++)
index 752e0825591fbed9e820044495d42b9a80842320..ccd7d851be26d27913a26656cdef1fc838c90870 100644 (file)
@@ -1406,8 +1406,9 @@ hscx_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
        switch (cmd) {
        case CLOSE_CHANNEL:
                test_and_clear_bit(FLG_OPEN, &bch->Flags);
+               cancel_work_sync(&bch->workq);
                spin_lock_irqsave(hx->ip->hwlock, flags);
-               mISDN_freebchannel(bch);
+               mISDN_clear_bchannel(bch);
                hscx_mode(hx, ISDN_P_NONE);
                spin_unlock_irqrestore(hx->ip->hwlock, flags);
                ch->protocol = ISDN_P_NONE;
index be5973ded6d6e4288fe8fdebd0967150228ff937..182ecf0626c2098e3c38c4da0eeea54a1197ce7d 100644 (file)
@@ -1588,8 +1588,9 @@ isar_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
        switch (cmd) {
        case CLOSE_CHANNEL:
                test_and_clear_bit(FLG_OPEN, &bch->Flags);
+               cancel_work_sync(&bch->workq);
                spin_lock_irqsave(ich->is->hwlock, flags);
-               mISDN_freebchannel(bch);
+               mISDN_clear_bchannel(bch);
                modeisar(ich, ISDN_P_NONE);
                spin_unlock_irqrestore(ich->is->hwlock, flags);
                ch->protocol = ISDN_P_NONE;
index c3e3e76862731496b6bea5d35b3ca8ef5662e486..9bcade59eb73bdf24f72e8fa5a6e08e4be641f1c 100644 (file)
@@ -812,8 +812,9 @@ nj_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
        switch (cmd) {
        case CLOSE_CHANNEL:
                test_and_clear_bit(FLG_OPEN, &bch->Flags);
+               cancel_work_sync(&bch->workq);
                spin_lock_irqsave(&card->lock, flags);
-               mISDN_freebchannel(bch);
+               mISDN_clear_bchannel(bch);
                mode_tiger(bc, ISDN_P_NONE);
                spin_unlock_irqrestore(&card->lock, flags);
                ch->protocol = ISDN_P_NONE;
index 26a86b8460992e5e98c722f8b6487fb8ff1fe932..335fe6455002c708cfb0be66318b2473f4cdfcc7 100644 (file)
@@ -1054,8 +1054,9 @@ w6692_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
        switch (cmd) {
        case CLOSE_CHANNEL:
                test_and_clear_bit(FLG_OPEN, &bch->Flags);
+               cancel_work_sync(&bch->workq);
                spin_lock_irqsave(&card->lock, flags);
-               mISDN_freebchannel(bch);
+               mISDN_clear_bchannel(bch);
                w6692_mode(bc, ISDN_P_NONE);
                spin_unlock_irqrestore(&card->lock, flags);
                ch->protocol = ISDN_P_NONE;
index ef34fd40867cb6b7f5b767542c4da2e173d6b9cc..2602be23f341287468524c9fbd79e33a18e21aa6 100644 (file)
@@ -148,17 +148,16 @@ mISDN_clear_bchannel(struct bchannel *ch)
        ch->next_minlen = ch->init_minlen;
        ch->maxlen = ch->init_maxlen;
        ch->next_maxlen = ch->init_maxlen;
+       skb_queue_purge(&ch->rqueue);
+       ch->rcount = 0;
 }
 EXPORT_SYMBOL(mISDN_clear_bchannel);
 
-int
+void
 mISDN_freebchannel(struct bchannel *ch)
 {
+       cancel_work_sync(&ch->workq);
        mISDN_clear_bchannel(ch);
-       skb_queue_purge(&ch->rqueue);
-       ch->rcount = 0;
-       flush_work_sync(&ch->workq);
-       return 0;
 }
 EXPORT_SYMBOL(mISDN_freebchannel);
 
index 3f6203a4c7ea0dd730b80a6b441b99e8a4da3216..308e87b417e05a74e4429bac63e1eb2e7af43885 100644 (file)
@@ -7619,6 +7619,8 @@ static int remove_and_add_spares(struct mddev *mddev)
                        }
                }
        }
+       if (removed)
+               set_bit(MD_CHANGE_DEVS, &mddev->flags);
        return spares;
 }
 
@@ -7632,9 +7634,11 @@ static void reap_sync_thread(struct mddev *mddev)
            !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) {
                /* success...*/
                /* activate any spares */
-               if (mddev->pers->spare_active(mddev))
+               if (mddev->pers->spare_active(mddev)) {
                        sysfs_notify(&mddev->kobj, NULL,
                                     "degraded");
+                       set_bit(MD_CHANGE_DEVS, &mddev->flags);
+               }
        }
        if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) &&
            mddev->pers->finish_reshape)
index adda94df5eb2352775e64fb7fae4e88c6e89a98b..7031b865b3a030488614764528295d509c663ba5 100644 (file)
@@ -393,6 +393,8 @@ static int calc_degraded(struct r5conf *conf)
        degraded = 0;
        for (i = 0; i < conf->previous_raid_disks; i++) {
                struct md_rdev *rdev = rcu_dereference(conf->disks[i].rdev);
+               if (rdev && test_bit(Faulty, &rdev->flags))
+                       rdev = rcu_dereference(conf->disks[i].replacement);
                if (!rdev || test_bit(Faulty, &rdev->flags))
                        degraded++;
                else if (test_bit(In_sync, &rdev->flags))
@@ -417,6 +419,8 @@ static int calc_degraded(struct r5conf *conf)
        degraded2 = 0;
        for (i = 0; i < conf->raid_disks; i++) {
                struct md_rdev *rdev = rcu_dereference(conf->disks[i].rdev);
+               if (rdev && test_bit(Faulty, &rdev->flags))
+                       rdev = rcu_dereference(conf->disks[i].replacement);
                if (!rdev || test_bit(Faulty, &rdev->flags))
                        degraded2++;
                else if (test_bit(In_sync, &rdev->flags))
@@ -4192,7 +4196,7 @@ static void make_request(struct mddev *mddev, struct bio * bi)
                        finish_wait(&conf->wait_for_overlap, &w);
                        set_bit(STRIPE_HANDLE, &sh->state);
                        clear_bit(STRIPE_DELAYED, &sh->state);
-                       if ((bi->bi_rw & REQ_NOIDLE) &&
+                       if ((bi->bi_rw & REQ_SYNC) &&
                            !test_and_set_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
                                atomic_inc(&conf->preread_active_stripes);
                        release_stripe_plug(mddev, sh);
index b67a3018b13645f2e15f9c773f92d6f9e5acba06..ce229ea933d1388467aed017b9f65d4a2f9ef3a1 100644 (file)
@@ -470,7 +470,8 @@ static int __devinit device_800_init(struct pm80x_chip *chip,
 
        ret =
            mfd_add_devices(chip->dev, 0, &onkey_devs[0],
-                           ARRAY_SIZE(onkey_devs), &onkey_resources[0], 0);
+                           ARRAY_SIZE(onkey_devs), &onkey_resources[0], 0,
+                           NULL);
        if (ret < 0) {
                dev_err(chip->dev, "Failed to add onkey subdev\n");
                goto out_dev;
@@ -481,7 +482,7 @@ static int __devinit device_800_init(struct pm80x_chip *chip,
                rtc_devs[0].platform_data = pdata->rtc;
                rtc_devs[0].pdata_size = sizeof(struct pm80x_rtc_pdata);
                ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
-                                     ARRAY_SIZE(rtc_devs), NULL, 0);
+                                     ARRAY_SIZE(rtc_devs), NULL, 0, NULL);
                if (ret < 0) {
                        dev_err(chip->dev, "Failed to add rtc subdev\n");
                        goto out_dev;
index 6146583589f61b53af6918db338861f116596a5b..c20a31136f045ccd57c0c19ec62ceb6b229bc861 100644 (file)
@@ -216,7 +216,8 @@ static int __devinit device_805_init(struct pm80x_chip *chip)
        }
 
        ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
-                             ARRAY_SIZE(codec_devs), &codec_resources[0], 0);
+                             ARRAY_SIZE(codec_devs), &codec_resources[0], 0,
+                             NULL);
        if (ret < 0) {
                dev_err(chip->dev, "Failed to add codec subdev\n");
                goto out_codec;
index d09918cf1b1556a74edb622e0174d3ceffdf7ca6..b73f033b2c602fadce09dd97d0c7623581962e12 100644 (file)
@@ -637,7 +637,7 @@ static void __devinit device_bk_init(struct pm860x_chip *chip,
                        bk_devs[i].resources = &bk_resources[j];
                        ret = mfd_add_devices(chip->dev, 0,
                                              &bk_devs[i], 1,
-                                             &bk_resources[j], 0);
+                                             &bk_resources[j], 0, NULL);
                        if (ret < 0) {
                                dev_err(chip->dev, "Failed to add "
                                        "backlight subdev\n");
@@ -672,7 +672,7 @@ static void __devinit device_led_init(struct pm860x_chip *chip,
                        led_devs[i].resources = &led_resources[j],
                        ret = mfd_add_devices(chip->dev, 0,
                                              &led_devs[i], 1,
-                                             &led_resources[j], 0);
+                                             &led_resources[j], 0, NULL);
                        if (ret < 0) {
                                dev_err(chip->dev, "Failed to add "
                                        "led subdev\n");
@@ -709,7 +709,7 @@ static void __devinit device_regulator_init(struct pm860x_chip *chip,
                regulator_devs[i].resources = &regulator_resources[seq];
 
                ret = mfd_add_devices(chip->dev, 0, &regulator_devs[i], 1,
-                                     &regulator_resources[seq], 0);
+                                     &regulator_resources[seq], 0, NULL);
                if (ret < 0) {
                        dev_err(chip->dev, "Failed to add regulator subdev\n");
                        goto out;
@@ -733,7 +733,7 @@ static void __devinit device_rtc_init(struct pm860x_chip *chip,
        rtc_devs[0].resources = &rtc_resources[0];
        ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
                              ARRAY_SIZE(rtc_devs), &rtc_resources[0],
-                             chip->irq_base);
+                             chip->irq_base, NULL);
        if (ret < 0)
                dev_err(chip->dev, "Failed to add rtc subdev\n");
 }
@@ -752,7 +752,7 @@ static void __devinit device_touch_init(struct pm860x_chip *chip,
        touch_devs[0].resources = &touch_resources[0];
        ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
                              ARRAY_SIZE(touch_devs), &touch_resources[0],
-                             chip->irq_base);
+                             chip->irq_base, NULL);
        if (ret < 0)
                dev_err(chip->dev, "Failed to add touch subdev\n");
 }
@@ -770,7 +770,7 @@ static void __devinit device_power_init(struct pm860x_chip *chip,
        power_devs[0].num_resources = ARRAY_SIZE(battery_resources);
        power_devs[0].resources = &battery_resources[0],
        ret = mfd_add_devices(chip->dev, 0, &power_devs[0], 1,
-                             &battery_resources[0], chip->irq_base);
+                             &battery_resources[0], chip->irq_base, NULL);
        if (ret < 0)
                dev_err(chip->dev, "Failed to add battery subdev\n");
 
@@ -779,7 +779,7 @@ static void __devinit device_power_init(struct pm860x_chip *chip,
        power_devs[1].num_resources = ARRAY_SIZE(charger_resources);
        power_devs[1].resources = &charger_resources[0],
        ret = mfd_add_devices(chip->dev, 0, &power_devs[1], 1,
-                             &charger_resources[0], chip->irq_base);
+                             &charger_resources[0], chip->irq_base, NULL);
        if (ret < 0)
                dev_err(chip->dev, "Failed to add charger subdev\n");
 
@@ -788,7 +788,7 @@ static void __devinit device_power_init(struct pm860x_chip *chip,
        power_devs[2].num_resources = ARRAY_SIZE(preg_resources);
        power_devs[2].resources = &preg_resources[0],
        ret = mfd_add_devices(chip->dev, 0, &power_devs[2], 1,
-                             &preg_resources[0], chip->irq_base);
+                             &preg_resources[0], chip->irq_base, NULL);
        if (ret < 0)
                dev_err(chip->dev, "Failed to add preg subdev\n");
 }
@@ -802,7 +802,7 @@ static void __devinit device_onkey_init(struct pm860x_chip *chip,
        onkey_devs[0].resources = &onkey_resources[0],
        ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
                              ARRAY_SIZE(onkey_devs), &onkey_resources[0],
-                             chip->irq_base);
+                             chip->irq_base, NULL);
        if (ret < 0)
                dev_err(chip->dev, "Failed to add onkey subdev\n");
 }
@@ -815,7 +815,8 @@ static void __devinit device_codec_init(struct pm860x_chip *chip,
        codec_devs[0].num_resources = ARRAY_SIZE(codec_resources);
        codec_devs[0].resources = &codec_resources[0],
        ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
-                             ARRAY_SIZE(codec_devs), &codec_resources[0], 0);
+                             ARRAY_SIZE(codec_devs), &codec_resources[0], 0,
+                             NULL);
        if (ret < 0)
                dev_err(chip->dev, "Failed to add codec subdev\n");
 }
index 44a3fdbadef40df1e09b12884f44caa46b76f0ac..f1beb4971f87f580090df39b38dbdc5e9937140e 100644 (file)
@@ -424,7 +424,7 @@ static int aat2870_i2c_probe(struct i2c_client *client,
        }
 
        ret = mfd_add_devices(aat2870->dev, 0, aat2870_devs,
-                             ARRAY_SIZE(aat2870_devs), NULL, 0);
+                             ARRAY_SIZE(aat2870_devs), NULL, 0, NULL);
        if (ret != 0) {
                dev_err(aat2870->dev, "Failed to add subdev: %d\n", ret);
                goto out_disable;
index 78fca2902c8da38fd07660e381e8ed55c0d78c2d..01781ae5d0d7f3de38c811dad727ecc179c94be7 100644 (file)
@@ -946,7 +946,7 @@ static int __devinit ab3100_probe(struct i2c_client *client,
        }
 
        err = mfd_add_devices(&client->dev, 0, ab3100_devs,
-               ARRAY_SIZE(ab3100_devs), NULL, 0);
+                             ARRAY_SIZE(ab3100_devs), NULL, 0, NULL);
 
        ab3100_setup_debugfs(ab3100);
 
index 626b4ecaf64761fdd3cd43ac3f02986d6830e467..47adf800024e01f8cdf05856eb3b356226c527f3 100644 (file)
@@ -1418,25 +1418,25 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
 
        ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs,
                        ARRAY_SIZE(abx500_common_devs), NULL,
-                       ab8500->irq_base);
+                       ab8500->irq_base, ab8500->domain);
        if (ret)
                goto out_freeirq;
 
        if (is_ab9540(ab8500))
                ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
                                ARRAY_SIZE(ab9540_devs), NULL,
-                               ab8500->irq_base);
+                               ab8500->irq_base, ab8500->domain);
        else
                ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
                                ARRAY_SIZE(ab8500_devs), NULL,
-                               ab8500->irq_base);
+                               ab8500->irq_base, ab8500->domain);
        if (ret)
                goto out_freeirq;
 
        if (is_ab9540(ab8500) || is_ab8505(ab8500))
                ret = mfd_add_devices(ab8500->dev, 0, ab9540_ab8505_devs,
                                ARRAY_SIZE(ab9540_ab8505_devs), NULL,
-                               ab8500->irq_base);
+                               ab8500->irq_base, ab8500->domain);
        if (ret)
                goto out_freeirq;
 
@@ -1444,7 +1444,7 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
                /* Add battery management devices */
                ret = mfd_add_devices(ab8500->dev, 0, ab8500_bm_devs,
                                      ARRAY_SIZE(ab8500_bm_devs), NULL,
-                                     ab8500->irq_base);
+                                     ab8500->irq_base, ab8500->domain);
                if (ret)
                        dev_err(ab8500->dev, "error adding bm devices\n");
        }
index c7983e862549a0b79775cae3e76c7cf0c867ad83..1b48f2094806c75fa8914657978f1b7bf814ae93 100644 (file)
@@ -316,7 +316,7 @@ int __devinit arizona_dev_init(struct arizona *arizona)
        }
 
        ret = mfd_add_devices(arizona->dev, -1, early_devs,
-                             ARRAY_SIZE(early_devs), NULL, 0);
+                             ARRAY_SIZE(early_devs), NULL, 0, NULL);
        if (ret != 0) {
                dev_err(dev, "Failed to add early children: %d\n", ret);
                return ret;
@@ -516,11 +516,11 @@ int __devinit arizona_dev_init(struct arizona *arizona)
        switch (arizona->type) {
        case WM5102:
                ret = mfd_add_devices(arizona->dev, -1, wm5102_devs,
-                                     ARRAY_SIZE(wm5102_devs), NULL, 0);
+                                     ARRAY_SIZE(wm5102_devs), NULL, 0, NULL);
                break;
        case WM5110:
                ret = mfd_add_devices(arizona->dev, -1, wm5110_devs,
-                                     ARRAY_SIZE(wm5102_devs), NULL, 0);
+                                     ARRAY_SIZE(wm5102_devs), NULL, 0, NULL);
                break;
        }
 
index 683e18a23329802875d03f92d53e354a6474ad9d..62f0883a7630c360ab9e52f9fa11f306d1a3efee 100644 (file)
@@ -913,14 +913,14 @@ static int __init asic3_mfd_probe(struct platform_device *pdev,
        if (pdata->clock_rate) {
                ds1wm_pdata.clock_rate = pdata->clock_rate;
                ret = mfd_add_devices(&pdev->dev, pdev->id,
-                       &asic3_cell_ds1wm, 1, mem, asic->irq_base);
+                       &asic3_cell_ds1wm, 1, mem, asic->irq_base, NULL);
                if (ret < 0)
                        goto out;
        }
 
        if (mem_sdio && (irq >= 0)) {
                ret = mfd_add_devices(&pdev->dev, pdev->id,
-                       &asic3_cell_mmc, 1, mem_sdio, irq);
+                       &asic3_cell_mmc, 1, mem_sdio, irq, NULL);
                if (ret < 0)
                        goto out;
        }
@@ -934,7 +934,7 @@ static int __init asic3_mfd_probe(struct platform_device *pdev,
                        asic3_cell_leds[i].pdata_size = sizeof(pdata->leds[i]);
                }
                ret = mfd_add_devices(&pdev->dev, 0,
-                       asic3_cell_leds, ASIC3_NUM_LEDS, NULL, 0);
+                       asic3_cell_leds, ASIC3_NUM_LEDS, NULL, 0, NULL);
        }
 
  out:
index 3419e726de478cb330801d1dfb1db42d2a5d1748..2b282133c725b1b6fbb9c0f10442760640bfcd47 100644 (file)
@@ -149,7 +149,7 @@ static int __devinit cs5535_mfd_probe(struct pci_dev *pdev,
        }
 
        err = mfd_add_devices(&pdev->dev, -1, cs5535_mfd_cells,
-                       ARRAY_SIZE(cs5535_mfd_cells), NULL, 0);
+                             ARRAY_SIZE(cs5535_mfd_cells), NULL, 0, NULL);
        if (err) {
                dev_err(&pdev->dev, "MFD add devices failed: %d\n", err);
                goto err_disable;
index 2544910e1fd604f5f6184009a1a208fa5f838cf2..a0a62b24621b831cb0c7c43e62692bd061eb7163 100644 (file)
@@ -803,7 +803,7 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id)
                dev_err(da9052->dev, "DA9052 ADC IRQ failed ret=%d\n", ret);
 
        ret = mfd_add_devices(da9052->dev, -1, da9052_subdev_info,
-                             ARRAY_SIZE(da9052_subdev_info), NULL, 0);
+                             ARRAY_SIZE(da9052_subdev_info), NULL, 0, NULL);
        if (ret)
                goto err;
 
index 4e2af2cb2d26a76534c884c3cc57fe97f3c30d52..45e83a68641b81d0a5f7605b19027e2cd5b1aec0 100644 (file)
@@ -129,7 +129,7 @@ static int __init davinci_vc_probe(struct platform_device *pdev)
        cell->pdata_size = sizeof(*davinci_vc);
 
        ret = mfd_add_devices(&pdev->dev, pdev->id, davinci_vc->cells,
-                             DAVINCI_VC_CELLS, NULL, 0);
+                             DAVINCI_VC_CELLS, NULL, 0, NULL);
        if (ret != 0) {
                dev_err(&pdev->dev, "fail to register client devices\n");
                goto fail4;
index 7040a0081130c93ce6b73145355abec0a8c571b8..0e63cdd9b52abc44666c57994623e120f11644b2 100644 (file)
@@ -3010,7 +3010,7 @@ static int __devinit db8500_prcmu_probe(struct platform_device *pdev)
                prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
 
        err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs,
-                       ARRAY_SIZE(db8500_prcmu_devs), NULL, 0);
+                             ARRAY_SIZE(db8500_prcmu_devs), NULL, 0, NULL);
        if (err) {
                pr_err("prcmu: Failed to add subdevices\n");
                return err;
index 04c7093d6499cb88f330b2f768713e49e58c9ffc..9e5453d21a6806263d17bc6ada0c07a3a2a7013a 100644 (file)
@@ -168,7 +168,7 @@ static int __init pasic3_probe(struct platform_device *pdev)
                /* the first 5 PASIC3 registers control the DS1WM */
                ds1wm_resources[0].end = (5 << asic->bus_shift) - 1;
                ret = mfd_add_devices(&pdev->dev, pdev->id,
-                               &ds1wm_cell, 1, r, irq);
+                                     &ds1wm_cell, 1, r, irq, NULL);
                if (ret < 0)
                        dev_warn(dev, "failed to register DS1WM\n");
        }
@@ -176,7 +176,8 @@ static int __init pasic3_probe(struct platform_device *pdev)
        if (pdata && pdata->led_pdata) {
                led_cell.platform_data = pdata->led_pdata;
                led_cell.pdata_size = sizeof(struct pasic3_leds_machinfo);
-               ret = mfd_add_devices(&pdev->dev, pdev->id, &led_cell, 1, r, 0);
+               ret = mfd_add_devices(&pdev->dev, pdev->id, &led_cell, 1, r,
+                                     0, NULL);
                if (ret < 0)
                        dev_warn(dev, "failed to register LED device\n");
        }
index 59df5584cb58f54a25a424ca2551417a231fcb4d..266bdc5bd96d17ea1bc05967d911aa4b9b7fc8bb 100644 (file)
@@ -344,13 +344,13 @@ static int __devinit intel_msic_init_devices(struct intel_msic *msic)
                        continue;
 
                ret = mfd_add_devices(&pdev->dev, -1, &msic_devs[i], 1, NULL,
-                                     pdata->irq[i]);
+                                     pdata->irq[i], NULL);
                if (ret)
                        goto fail;
        }
 
        ret = mfd_add_devices(&pdev->dev, 0, msic_other_devs,
-                             ARRAY_SIZE(msic_other_devs), NULL, 0);
+                             ARRAY_SIZE(msic_other_devs), NULL, 0, NULL);
        if (ret)
                goto fail;
 
index 2ea99989551af85a4796c69e5fc2b65ba7a951e9..965c4801df8a1765e069ecb8707df451984b080a 100644 (file)
@@ -147,7 +147,7 @@ static int __devinit cmodio_probe_submodules(struct cmodio_device *priv)
        }
 
        return mfd_add_devices(&pdev->dev, 0, priv->cells,
-                              num_probed, NULL, pdev->irq);
+                              num_probed, NULL, pdev->irq, NULL);
 }
 
 /*
index 87662a17dec62d527a3af1b8b7fb8c782c85dd92..c6b6d7dda517528081a9e3f7e96d96458846ef67 100644 (file)
@@ -287,7 +287,8 @@ static int __devinit jz4740_adc_probe(struct platform_device *pdev)
        writeb(0xff, adc->base + JZ_REG_ADC_CTRL);
 
        ret = mfd_add_devices(&pdev->dev, 0, jz4740_adc_cells,
-               ARRAY_SIZE(jz4740_adc_cells), mem_base, irq_base);
+                             ARRAY_SIZE(jz4740_adc_cells), mem_base,
+                             irq_base, NULL);
        if (ret < 0)
                goto err_clk_put;
 
index 0b2879b87fd999f537bbfc652da9a29c90eb1042..24212f45b201458961373df48c08d9a1f7f9536d 100644 (file)
@@ -393,7 +393,8 @@ static int __devinit lm3533_device_als_init(struct lm3533 *lm3533)
        lm3533_als_devs[0].platform_data = pdata->als;
        lm3533_als_devs[0].pdata_size = sizeof(*pdata->als);
 
-       ret = mfd_add_devices(lm3533->dev, 0, lm3533_als_devs, 1, NULL, 0);
+       ret = mfd_add_devices(lm3533->dev, 0, lm3533_als_devs, 1, NULL,
+                             0, NULL);
        if (ret) {
                dev_err(lm3533->dev, "failed to add ALS device\n");
                return ret;
@@ -422,7 +423,7 @@ static int __devinit lm3533_device_bl_init(struct lm3533 *lm3533)
        }
 
        ret = mfd_add_devices(lm3533->dev, 0, lm3533_bl_devs,
-                                       pdata->num_backlights, NULL, 0);
+                             pdata->num_backlights, NULL, 0, NULL);
        if (ret) {
                dev_err(lm3533->dev, "failed to add backlight devices\n");
                return ret;
@@ -451,7 +452,7 @@ static int __devinit lm3533_device_led_init(struct lm3533 *lm3533)
        }
 
        ret = mfd_add_devices(lm3533->dev, 0, lm3533_led_devs,
-                                               pdata->num_leds, NULL, 0);
+                             pdata->num_leds, NULL, 0, NULL);
        if (ret) {
                dev_err(lm3533->dev, "failed to add LED devices\n");
                return ret;
index 027cc8f861324de8f10bc7e9a1aa66af9e6ef093..092ad4b44b6d67b9b4ee039fccc73da1748d3a2c 100644 (file)
@@ -750,7 +750,7 @@ gpe0_done:
 
        lpc_ich_finalize_cell(&lpc_ich_cells[LPC_GPIO], id);
        ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_GPIO],
-                               1, NULL, 0);
+                             1, NULL, 0, NULL);
 
 gpio_done:
        if (acpi_conflict)
@@ -765,7 +765,6 @@ static int __devinit lpc_ich_init_wdt(struct pci_dev *dev,
        u32 base_addr_cfg;
        u32 base_addr;
        int ret;
-       bool acpi_conflict = false;
        struct resource *res;
 
        /* Setup power management base register */
@@ -780,20 +779,11 @@ static int __devinit lpc_ich_init_wdt(struct pci_dev *dev,
        res = wdt_io_res(ICH_RES_IO_TCO);
        res->start = base_addr + ACPIBASE_TCO_OFF;
        res->end = base_addr + ACPIBASE_TCO_END;
-       ret = acpi_check_resource_conflict(res);
-       if (ret) {
-               acpi_conflict = true;
-               goto wdt_done;
-       }
 
        res = wdt_io_res(ICH_RES_IO_SMI);
        res->start = base_addr + ACPIBASE_SMI_OFF;
        res->end = base_addr + ACPIBASE_SMI_END;
-       ret = acpi_check_resource_conflict(res);
-       if (ret) {
-               acpi_conflict = true;
-               goto wdt_done;
-       }
+
        lpc_ich_enable_acpi_space(dev);
 
        /*
@@ -813,21 +803,13 @@ static int __devinit lpc_ich_init_wdt(struct pci_dev *dev,
                res = wdt_mem_res(ICH_RES_MEM_GCS);
                res->start = base_addr + ACPIBASE_GCS_OFF;
                res->end = base_addr + ACPIBASE_GCS_END;
-               ret = acpi_check_resource_conflict(res);
-               if (ret) {
-                       acpi_conflict = true;
-                       goto wdt_done;
-               }
        }
 
        lpc_ich_finalize_cell(&lpc_ich_cells[LPC_WDT], id);
        ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_WDT],
-                               1, NULL, 0);
+                             1, NULL, 0, NULL);
 
 wdt_done:
-       if (acpi_conflict)
-               pr_warn("Resource conflict(s) found affecting %s\n",
-                               lpc_ich_cells[LPC_WDT].name);
        return ret;
 }
 
index 9f20abc5e3937065238ff1f3240c27cde9cbb4f6..f6b9c5c96b24f7d68e80b9ca2db8f202c2e3dd91 100644 (file)
@@ -127,7 +127,8 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev,
                lpc_sch_cells[i].id = id->device;
 
        ret = mfd_add_devices(&dev->dev, 0,
-                       lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL, 0);
+                             lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL,
+                             0, NULL);
        if (ret)
                goto out_dev;
 
@@ -153,7 +154,8 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev,
                        tunnelcreek_cells[i].id = id->device;
 
                ret = mfd_add_devices(&dev->dev, 0, tunnelcreek_cells,
-                       ARRAY_SIZE(tunnelcreek_cells), NULL, 0);
+                                     ARRAY_SIZE(tunnelcreek_cells), NULL,
+                                     0, NULL);
        }
 
        return ret;
index c03e12b51924060704641c0152ad6e629d167a72..d9e24c849a00a3f21aad864442293aa2db0ca64d 100644 (file)
@@ -126,7 +126,7 @@ static int max77686_i2c_probe(struct i2c_client *i2c,
        max77686_irq_init(max77686);
 
        ret = mfd_add_devices(max77686->dev, -1, max77686_devs,
-                             ARRAY_SIZE(max77686_devs), NULL, 0);
+                             ARRAY_SIZE(max77686_devs), NULL, 0, NULL);
 
        if (ret < 0)
                goto err_mfd;
index 2b403569e0a6411a92c0e7cb66c7d703d60cc0cf..1029d018c73921828f34740b4034c0cd7df5c3bd 100644 (file)
@@ -137,6 +137,9 @@ static void max77693_irq_mask(struct irq_data *data)
        const struct max77693_irq_data *irq_data =
                                irq_to_max77693_irq(max77693, data->irq);
 
+       if (irq_data->group >= MAX77693_IRQ_GROUP_NR)
+               return;
+
        if (irq_data->group >= MUIC_INT1 && irq_data->group <= MUIC_INT3)
                max77693->irq_masks_cur[irq_data->group] &= ~irq_data->mask;
        else
@@ -149,6 +152,9 @@ static void max77693_irq_unmask(struct irq_data *data)
        const struct max77693_irq_data *irq_data =
            irq_to_max77693_irq(max77693, data->irq);
 
+       if (irq_data->group >= MAX77693_IRQ_GROUP_NR)
+               return;
+
        if (irq_data->group >= MUIC_INT1 && irq_data->group <= MUIC_INT3)
                max77693->irq_masks_cur[irq_data->group] |= irq_data->mask;
        else
@@ -200,7 +206,7 @@ static irqreturn_t max77693_irq_thread(int irq, void *data)
 
        if (irq_src & MAX77693_IRQSRC_MUIC)
                /* MUIC INT1 ~ INT3 */
-               max77693_bulk_read(max77693->regmap, MAX77693_MUIC_REG_INT1,
+               max77693_bulk_read(max77693->regmap_muic, MAX77693_MUIC_REG_INT1,
                        MAX77693_NUM_IRQ_MUIC_REGS, &irq_reg[MUIC_INT1]);
 
        /* Apply masking */
@@ -255,7 +261,8 @@ int max77693_irq_init(struct max77693_dev *max77693)
 {
        struct irq_domain *domain;
        int i;
-       int ret;
+       int ret = 0;
+       u8 intsrc_mask;
 
        mutex_init(&max77693->irqlock);
 
@@ -287,19 +294,38 @@ int max77693_irq_init(struct max77693_dev *max77693)
                                        &max77693_irq_domain_ops, max77693);
        if (!domain) {
                dev_err(max77693->dev, "could not create irq domain\n");
-               return -ENODEV;
+               ret = -ENODEV;
+               goto err_irq;
        }
        max77693->irq_domain = domain;
 
+       /* Unmask max77693 interrupt */
+       ret = max77693_read_reg(max77693->regmap,
+                       MAX77693_PMIC_REG_INTSRC_MASK, &intsrc_mask);
+       if (ret < 0) {
+               dev_err(max77693->dev, "fail to read PMIC register\n");
+               goto err_irq;
+       }
+
+       intsrc_mask &= ~(MAX77693_IRQSRC_CHG);
+       intsrc_mask &= ~(MAX77693_IRQSRC_FLASH);
+       intsrc_mask &= ~(MAX77693_IRQSRC_MUIC);
+       ret = max77693_write_reg(max77693->regmap,
+                       MAX77693_PMIC_REG_INTSRC_MASK, intsrc_mask);
+       if (ret < 0) {
+               dev_err(max77693->dev, "fail to write PMIC register\n");
+               goto err_irq;
+       }
+
        ret = request_threaded_irq(max77693->irq, NULL, max77693_irq_thread,
                                   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
                                   "max77693-irq", max77693);
-
        if (ret)
                dev_err(max77693->dev, "Failed to request IRQ %d: %d\n",
                        max77693->irq, ret);
 
-       return 0;
+err_irq:
+       return ret;
 }
 
 void max77693_irq_exit(struct max77693_dev *max77693)
index a1811cb50ec75fc7c1dffd02aca98f132d7e4810..cc5155e20494726c2ae6954e64128f61973ebafd 100644 (file)
@@ -152,6 +152,20 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
        max77693->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC);
        i2c_set_clientdata(max77693->haptic, max77693);
 
+       /*
+        * Initialize register map for MUIC device because use regmap-muic
+        * instance of MUIC device when irq of max77693 is initialized
+        * before call max77693-muic probe() function.
+        */
+       max77693->regmap_muic = devm_regmap_init_i2c(max77693->muic,
+                                        &max77693_regmap_config);
+       if (IS_ERR(max77693->regmap_muic)) {
+               ret = PTR_ERR(max77693->regmap_muic);
+               dev_err(max77693->dev,
+                       "failed to allocate register map: %d\n", ret);
+               goto err_regmap;
+       }
+
        ret = max77693_irq_init(max77693);
        if (ret < 0)
                goto err_irq;
@@ -159,7 +173,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
        pm_runtime_set_active(max77693->dev);
 
        ret = mfd_add_devices(max77693->dev, -1, max77693_devs,
-                       ARRAY_SIZE(max77693_devs), NULL, 0);
+                             ARRAY_SIZE(max77693_devs), NULL, 0, NULL);
        if (ret < 0)
                goto err_mfd;
 
index 825a7f06d9ba5ade6281810bec19c209187561b2..ee53757beca7e8344c15a66bbe5d51bcce7b9da1 100644 (file)
@@ -598,7 +598,7 @@ int __devinit max8925_device_init(struct max8925_chip *chip,
 
        ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
                              ARRAY_SIZE(rtc_devs),
-                             &rtc_resources[0], chip->irq_base);
+                             &rtc_resources[0], chip->irq_base, NULL);
        if (ret < 0) {
                dev_err(chip->dev, "Failed to add rtc subdev\n");
                goto out;
@@ -606,7 +606,7 @@ int __devinit max8925_device_init(struct max8925_chip *chip,
 
        ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
                              ARRAY_SIZE(onkey_devs),
-                             &onkey_resources[0], 0);
+                             &onkey_resources[0], 0, NULL);
        if (ret < 0) {
                dev_err(chip->dev, "Failed to add onkey subdev\n");
                goto out_dev;
@@ -615,7 +615,7 @@ int __devinit max8925_device_init(struct max8925_chip *chip,
        if (pdata) {
                ret = mfd_add_devices(chip->dev, 0, &regulator_devs[0],
                                      ARRAY_SIZE(regulator_devs),
-                                     &regulator_resources[0], 0);
+                                     &regulator_resources[0], 0, NULL);
                if (ret < 0) {
                        dev_err(chip->dev, "Failed to add regulator subdev\n");
                        goto out_dev;
@@ -625,7 +625,7 @@ int __devinit max8925_device_init(struct max8925_chip *chip,
        if (pdata && pdata->backlight) {
                ret = mfd_add_devices(chip->dev, 0, &backlight_devs[0],
                                      ARRAY_SIZE(backlight_devs),
-                                     &backlight_resources[0], 0);
+                                     &backlight_resources[0], 0, NULL);
                if (ret < 0) {
                        dev_err(chip->dev, "Failed to add backlight subdev\n");
                        goto out_dev;
@@ -635,7 +635,7 @@ int __devinit max8925_device_init(struct max8925_chip *chip,
        if (pdata && pdata->power) {
                ret = mfd_add_devices(chip->dev, 0, &power_devs[0],
                                        ARRAY_SIZE(power_devs),
-                                       &power_supply_resources[0], 0);
+                                     &power_supply_resources[0], 0, NULL);
                if (ret < 0) {
                        dev_err(chip->dev, "Failed to add power supply "
                                "subdev\n");
@@ -646,7 +646,7 @@ int __devinit max8925_device_init(struct max8925_chip *chip,
        if (pdata && pdata->touch) {
                ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
                                      ARRAY_SIZE(touch_devs),
-                                     &touch_resources[0], 0);
+                                     &touch_resources[0], 0, NULL);
                if (ret < 0) {
                        dev_err(chip->dev, "Failed to add touch subdev\n");
                        goto out_dev;
index 10b629c245b6770d304dde9bf978891c2a5ac800..f123517065ec911ff6e4154aa25a3f8e1dd0bd98 100644 (file)
@@ -160,7 +160,7 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
 
        mfd_add_devices(max8997->dev, -1, max8997_devs,
                        ARRAY_SIZE(max8997_devs),
-                       NULL, 0);
+                       NULL, 0, NULL);
 
        /*
         * TODO: enable others (flash, muic, rtc, battery, ...) and
index 6ef56d28c05686bf298f589cebf91c43fcc707b8..d7218cc90945a643ee03cc670852d42078131503 100644 (file)
@@ -161,13 +161,13 @@ static int max8998_i2c_probe(struct i2c_client *i2c,
        switch (id->driver_data) {
        case TYPE_LP3974:
                ret = mfd_add_devices(max8998->dev, -1,
-                               lp3974_devs, ARRAY_SIZE(lp3974_devs),
-                               NULL, 0);
+                                     lp3974_devs, ARRAY_SIZE(lp3974_devs),
+                                     NULL, 0, NULL);
                break;
        case TYPE_MAX8998:
                ret = mfd_add_devices(max8998->dev, -1,
-                               max8998_devs, ARRAY_SIZE(max8998_devs),
-                               NULL, 0);
+                                     max8998_devs, ARRAY_SIZE(max8998_devs),
+                                     NULL, 0, NULL);
                break;
        default:
                ret = -EINVAL;
index b801dc72f041a125fcf9a52e25e6d594ee052d92..1ec79b54bd2f12f304c57f92633cc8c125a0d389 100644 (file)
@@ -612,7 +612,7 @@ static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx,
        if (!cell.name)
                return -ENOMEM;
 
-       return mfd_add_devices(mc13xxx->dev, -1, &cell, 1, NULL, 0);
+       return mfd_add_devices(mc13xxx->dev, -1, &cell, 1, NULL, 0, NULL);
 }
 
 static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format)
index 0c3a01cde2f7615960fb2c9cc20ba7489bf00fbd..f8b77711ad2da4c26acade369d0bc6443631427b 100644 (file)
@@ -74,12 +74,11 @@ static int mfd_platform_add_cell(struct platform_device *pdev,
 static int mfd_add_device(struct device *parent, int id,
                          const struct mfd_cell *cell,
                          struct resource *mem_base,
-                         int irq_base)
+                         int irq_base, struct irq_domain *domain)
 {
        struct resource *res;
        struct platform_device *pdev;
        struct device_node *np = NULL;
-       struct irq_domain *domain = NULL;
        int ret = -ENOMEM;
        int r;
 
@@ -97,7 +96,6 @@ static int mfd_add_device(struct device *parent, int id,
                for_each_child_of_node(parent->of_node, np) {
                        if (of_device_is_compatible(np, cell->of_compatible)) {
                                pdev->dev.of_node = np;
-                               domain = irq_find_host(parent->of_node);
                                break;
                        }
                }
@@ -177,7 +175,7 @@ fail_alloc:
 int mfd_add_devices(struct device *parent, int id,
                    struct mfd_cell *cells, int n_devs,
                    struct resource *mem_base,
-                   int irq_base)
+                   int irq_base, struct irq_domain *domain)
 {
        int i;
        int ret = 0;
@@ -191,7 +189,8 @@ int mfd_add_devices(struct device *parent, int id,
        for (i = 0; i < n_devs; i++) {
                atomic_set(&cnts[i], 0);
                cells[i].usage_count = &cnts[i];
-               ret = mfd_add_device(parent, id, cells + i, mem_base, irq_base);
+               ret = mfd_add_device(parent, id, cells + i, mem_base,
+                                    irq_base, domain);
                if (ret)
                        break;
        }
@@ -247,7 +246,8 @@ int mfd_clone_cell(const char *cell, const char **clones, size_t n_clones)
        for (i = 0; i < n_clones; i++) {
                cell_entry.name = clones[i];
                /* don't give up if a single call fails; just report error */
-               if (mfd_add_device(pdev->dev.parent, -1, &cell_entry, NULL, 0))
+               if (mfd_add_device(pdev->dev.parent, -1, &cell_entry, NULL, 0,
+                                  NULL))
                        dev_err(dev, "failed to create platform device '%s'\n",
                                        clones[i]);
        }
index c4a69f193a1df1985abfbaeeffb8e39cda933493..a345f9bb7b4758765725cf2c056303950ecc1c02 100644 (file)
@@ -453,7 +453,8 @@ static int __devinit palmas_i2c_probe(struct i2c_client *i2c,
 
        ret = mfd_add_devices(palmas->dev, -1,
                              children, ARRAY_SIZE(palmas_children),
-                             NULL, regmap_irq_chip_get_base(palmas->irq_data));
+                             NULL, regmap_irq_chip_get_base(palmas->irq_data),
+                             NULL);
        kfree(children);
 
        if (ret < 0)
index cdc1df7fa0e94d10a26059c18dd347c045dcf2cd..3a8fa88567b18385461fbed0bbc9ff30e8d0fffc 100644 (file)
@@ -289,7 +289,7 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c,
        }
 
        ret = mfd_add_devices(rc5t583->dev, -1, rc5t583_subdevs,
-                       ARRAY_SIZE(rc5t583_subdevs), NULL, 0);
+                             ARRAY_SIZE(rc5t583_subdevs), NULL, 0, NULL);
        if (ret) {
                dev_err(&i2c->dev, "add mfd devices failed: %d\n", ret);
                goto err_add_devs;
index 685d61e431adfa4f8b733a13bd5c93cfb71c9a44..0f70dce611605fb5e92419c5ac393c2079b74833 100644 (file)
@@ -87,7 +87,8 @@ static int __devinit rdc321x_sb_probe(struct pci_dev *pdev,
        rdc321x_wdt_pdata.sb_pdev = pdev;
 
        return mfd_add_devices(&pdev->dev, -1,
-               rdc321x_sb_cells, ARRAY_SIZE(rdc321x_sb_cells), NULL, 0);
+                              rdc321x_sb_cells, ARRAY_SIZE(rdc321x_sb_cells),
+                              NULL, 0, NULL);
 }
 
 static void __devexit rdc321x_sb_remove(struct pci_dev *pdev)
index 2988efde11ebc60b6a714044f79d6a7cd9a44dba..49d361a618d06f5f26d17c49a8c9d48e20547caf 100644 (file)
@@ -141,19 +141,19 @@ static int sec_pmic_probe(struct i2c_client *i2c,
        switch (sec_pmic->device_type) {
        case S5M8751X:
                ret = mfd_add_devices(sec_pmic->dev, -1, s5m8751_devs,
-                                       ARRAY_SIZE(s5m8751_devs), NULL, 0);
+                                     ARRAY_SIZE(s5m8751_devs), NULL, 0, NULL);
                break;
        case S5M8763X:
                ret = mfd_add_devices(sec_pmic->dev, -1, s5m8763_devs,
-                                       ARRAY_SIZE(s5m8763_devs), NULL, 0);
+                                     ARRAY_SIZE(s5m8763_devs), NULL, 0, NULL);
                break;
        case S5M8767X:
                ret = mfd_add_devices(sec_pmic->dev, -1, s5m8767_devs,
-                                       ARRAY_SIZE(s5m8767_devs), NULL, 0);
+                                     ARRAY_SIZE(s5m8767_devs), NULL, 0, NULL);
                break;
        case S2MPS11X:
                ret = mfd_add_devices(sec_pmic->dev, -1, s2mps11_devs,
-                                       ARRAY_SIZE(s2mps11_devs), NULL, 0);
+                                     ARRAY_SIZE(s2mps11_devs), NULL, 0, NULL);
                break;
        default:
                /* If this happens the probe function is problem */
index d31fed07aefbc51bfb620c492a2f05197886eb36..d35da6820beae8ee2c7e01e1faf51cc48c96972b 100644 (file)
@@ -407,7 +407,7 @@ static int __devinit sta2x11_mfd_probe(struct pci_dev *pdev,
                              sta2x11_mfd_bar0,
                              ARRAY_SIZE(sta2x11_mfd_bar0),
                              &pdev->resource[0],
-                             0);
+                             0, NULL);
        if (err) {
                dev_err(&pdev->dev, "mfd_add_devices[0] failed: %d\n", err);
                goto err_disable;
@@ -417,7 +417,7 @@ static int __devinit sta2x11_mfd_probe(struct pci_dev *pdev,
                              sta2x11_mfd_bar1,
                              ARRAY_SIZE(sta2x11_mfd_bar1),
                              &pdev->resource[1],
-                             0);
+                             0, NULL);
        if (err) {
                dev_err(&pdev->dev, "mfd_add_devices[1] failed: %d\n", err);
                goto err_disable;
index 2dd8d49cb30bc7d63d14250e673b2c12eb4cef3f..c94f521f392cb0b5214385ff57ac6e18e8762907 100644 (file)
@@ -962,7 +962,7 @@ static int __devinit stmpe_add_device(struct stmpe *stmpe,
                                      struct mfd_cell *cell, int irq)
 {
        return mfd_add_devices(stmpe->dev, stmpe->pdata->id, cell, 1,
-                              NULL, stmpe->irq_base + irq);
+                              NULL, stmpe->irq_base + irq, NULL);
 }
 
 static int __devinit stmpe_devices_init(struct stmpe *stmpe)
index 2d9e8799e733c6644c18aa9335e9796ef0c0709f..b32940ec903425d37010b79eed9a16e4b3c20761 100644 (file)
@@ -388,7 +388,7 @@ static int t7l66xb_probe(struct platform_device *dev)
 
        ret = mfd_add_devices(&dev->dev, dev->id,
                              t7l66xb_cells, ARRAY_SIZE(t7l66xb_cells),
-                             iomem, t7l66xb->irq_base);
+                             iomem, t7l66xb->irq_base, NULL);
 
        if (!ret)
                return 0;
index 048bf0532a095014e03358b01af1f4cd58585b97..b56ba6b43294b77e536b12247c0e8b1d18085701 100644 (file)
@@ -262,8 +262,8 @@ static int __devinit tc3589x_device_init(struct tc3589x *tc3589x)
 
        if (blocks & TC3589x_BLOCK_GPIO) {
                ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_gpio,
-                               ARRAY_SIZE(tc3589x_dev_gpio), NULL,
-                               tc3589x->irq_base);
+                                     ARRAY_SIZE(tc3589x_dev_gpio), NULL,
+                                     tc3589x->irq_base, NULL);
                if (ret) {
                        dev_err(tc3589x->dev, "failed to add gpio child\n");
                        return ret;
@@ -273,8 +273,8 @@ static int __devinit tc3589x_device_init(struct tc3589x *tc3589x)
 
        if (blocks & TC3589x_BLOCK_KEYPAD) {
                ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_keypad,
-                               ARRAY_SIZE(tc3589x_dev_keypad), NULL,
-                               tc3589x->irq_base);
+                                     ARRAY_SIZE(tc3589x_dev_keypad), NULL,
+                                     tc3589x->irq_base, NULL);
                if (ret) {
                        dev_err(tc3589x->dev, "failed to keypad child\n");
                        return ret;
index d20a284ad4baca528c36eb351200ff16fdd5282e..413c891102f867d32b3bd62b25bb02120fe0159e 100644 (file)
@@ -192,7 +192,7 @@ static int __devinit tc6387xb_probe(struct platform_device *dev)
        printk(KERN_INFO "Toshiba tc6387xb initialised\n");
 
        ret = mfd_add_devices(&dev->dev, dev->id, tc6387xb_cells,
-                             ARRAY_SIZE(tc6387xb_cells), iomem, irq);
+                             ARRAY_SIZE(tc6387xb_cells), iomem, irq, NULL);
 
        if (!ret)
                return 0;
index 9612264f0e6dcf7832ebf2f4736815b4eabc6a4b..dcab026fcbb25070a7c79c8e2601b3b911079367 100644 (file)
@@ -700,8 +700,8 @@ static int __devinit tc6393xb_probe(struct platform_device *dev)
        tc6393xb_cells[TC6393XB_CELL_FB].pdata_size = sizeof(*tcpd->fb_data);
 
        ret = mfd_add_devices(&dev->dev, dev->id,
-                       tc6393xb_cells, ARRAY_SIZE(tc6393xb_cells),
-                       iomem, tcpd->irq_base);
+                             tc6393xb_cells, ARRAY_SIZE(tc6393xb_cells),
+                             iomem, tcpd->irq_base, NULL);
 
        if (!ret)
                return 0;
index 4fb0e6c8e8fe0fbfee94299b7c0de45920621a25..7c3675a74f93414c806543f4c50bc44cbb8ad46f 100644 (file)
@@ -412,7 +412,7 @@ static int __devinit ti_ssp_probe(struct platform_device *pdev)
                cells[id].data_size     = data->pdata_size;
        }
 
-       error = mfd_add_devices(dev, 0, cells, 2, NULL, 0);
+       error = mfd_add_devices(dev, 0, cells, 2, NULL, 0, NULL);
        if (error < 0) {
                dev_err(dev, "cannot add mfd cells\n");
                goto error_enable;
index a447f4ec11fb757ee38755aa44da467f83229d1f..cccc626c83c80c2bf79cf03c89936d5249a44714 100644 (file)
@@ -757,25 +757,25 @@ static int __devinit timb_probe(struct pci_dev *dev,
                err = mfd_add_devices(&dev->dev, -1,
                        timberdale_cells_bar0_cfg0,
                        ARRAY_SIZE(timberdale_cells_bar0_cfg0),
-                       &dev->resource[0], msix_entries[0].vector);
+                       &dev->resource[0], msix_entries[0].vector, NULL);
                break;
        case TIMB_HW_VER1:
                err = mfd_add_devices(&dev->dev, -1,
                        timberdale_cells_bar0_cfg1,
                        ARRAY_SIZE(timberdale_cells_bar0_cfg1),
-                       &dev->resource[0], msix_entries[0].vector);
+                       &dev->resource[0], msix_entries[0].vector, NULL);
                break;
        case TIMB_HW_VER2:
                err = mfd_add_devices(&dev->dev, -1,
                        timberdale_cells_bar0_cfg2,
                        ARRAY_SIZE(timberdale_cells_bar0_cfg2),
-                       &dev->resource[0], msix_entries[0].vector);
+                       &dev->resource[0], msix_entries[0].vector, NULL);
                break;
        case TIMB_HW_VER3:
                err = mfd_add_devices(&dev->dev, -1,
                        timberdale_cells_bar0_cfg3,
                        ARRAY_SIZE(timberdale_cells_bar0_cfg3),
-                       &dev->resource[0], msix_entries[0].vector);
+                       &dev->resource[0], msix_entries[0].vector, NULL);
                break;
        default:
                dev_err(&dev->dev, "Uknown IP setup: %d.%d.%d\n",
@@ -792,7 +792,7 @@ static int __devinit timb_probe(struct pci_dev *dev,
 
        err = mfd_add_devices(&dev->dev, 0,
                timberdale_cells_bar1, ARRAY_SIZE(timberdale_cells_bar1),
-               &dev->resource[1], msix_entries[0].vector);
+               &dev->resource[1], msix_entries[0].vector, NULL);
        if (err) {
                dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
                goto err_mfd2;
@@ -803,7 +803,7 @@ static int __devinit timb_probe(struct pci_dev *dev,
                ((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER3)) {
                err = mfd_add_devices(&dev->dev, 1, timberdale_cells_bar2,
                        ARRAY_SIZE(timberdale_cells_bar2),
-                       &dev->resource[2], msix_entries[0].vector);
+                       &dev->resource[2], msix_entries[0].vector, NULL);
                if (err) {
                        dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
                        goto err_mfd2;
index a293b978e27ce19b116025e3ab4e87be6ae0dd74..14051bdc714b80197f258c14afd9cf629667e765 100644 (file)
@@ -188,7 +188,7 @@ static int __devinit tps6105x_probe(struct i2c_client *client,
        }
 
        ret = mfd_add_devices(&client->dev, 0, tps6105x_cells,
-               ARRAY_SIZE(tps6105x_cells), NULL, 0);
+                             ARRAY_SIZE(tps6105x_cells), NULL, 0, NULL);
        if (ret)
                goto fail;
 
index 33ba7723c967435b67c49a6f3f78212a63204b4e..1b203499c74402c59c19bf29a8ce6aa7b51a058b 100644 (file)
@@ -100,7 +100,7 @@ static int tps6507x_i2c_probe(struct i2c_client *i2c,
 
        ret = mfd_add_devices(tps6507x->dev, -1,
                              tps6507x_devs, ARRAY_SIZE(tps6507x_devs),
-                             NULL, 0);
+                             NULL, 0, NULL);
 
        if (ret < 0)
                goto err;
index 80e24f4b47bffce67679b7e637627e9e7c769466..50fd87c87a1cca64e21104401c1adbff1b4cf45c 100644 (file)
@@ -292,7 +292,7 @@ static int __devinit tps65090_i2c_probe(struct i2c_client *client,
        }
 
        ret = mfd_add_devices(tps65090->dev, -1, tps65090s,
-               ARRAY_SIZE(tps65090s), NULL, 0);
+                             ARRAY_SIZE(tps65090s), NULL, 0, NULL);
        if (ret) {
                dev_err(&client->dev, "add mfd devices failed with err: %d\n",
                        ret);
index 61c097a98f5de7eb45fd0ccb8fdc44dd53c869fa..a95e9421b73580df95402f718a4166898a1c41dd 100644 (file)
 #include <linux/slab.h>
 #include <linux/regmap.h>
 #include <linux/err.h>
-#include <linux/regulator/of_regulator.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 
 #include <linux/mfd/core.h>
 #include <linux/mfd/tps65217.h>
 
+static struct mfd_cell tps65217s[] = {
+       {
+               .name = "tps65217-pmic",
+       },
+};
+
 /**
  * tps65217_reg_read: Read a single tps65217 register.
  *
@@ -133,83 +140,48 @@ int tps65217_clear_bits(struct tps65217 *tps, unsigned int reg,
 }
 EXPORT_SYMBOL_GPL(tps65217_clear_bits);
 
-#ifdef CONFIG_OF
-static struct of_regulator_match reg_matches[] = {
-       { .name = "dcdc1", .driver_data = (void *)TPS65217_DCDC_1 },
-       { .name = "dcdc2", .driver_data = (void *)TPS65217_DCDC_2 },
-       { .name = "dcdc3", .driver_data = (void *)TPS65217_DCDC_3 },
-       { .name = "ldo1", .driver_data = (void *)TPS65217_LDO_1 },
-       { .name = "ldo2", .driver_data = (void *)TPS65217_LDO_2 },
-       { .name = "ldo3", .driver_data = (void *)TPS65217_LDO_3 },
-       { .name = "ldo4", .driver_data = (void *)TPS65217_LDO_4 },
-};
-
-static struct tps65217_board *tps65217_parse_dt(struct i2c_client *client)
-{
-       struct device_node *node = client->dev.of_node;
-       struct tps65217_board *pdata;
-       struct device_node *regs;
-       int count = ARRAY_SIZE(reg_matches);
-       int ret, i;
-
-       regs = of_find_node_by_name(node, "regulators");
-       if (!regs)
-               return NULL;
-
-       ret = of_regulator_match(&client->dev, regs, reg_matches, count);
-       of_node_put(regs);
-       if ((ret < 0) || (ret > count))
-               return NULL;
-
-       count = ret;
-       pdata = devm_kzalloc(&client->dev, count * sizeof(*pdata), GFP_KERNEL);
-       if (!pdata)
-               return NULL;
-
-       for (i = 0; i < count; i++) {
-               if (!reg_matches[i].init_data || !reg_matches[i].of_node)
-                       continue;
-
-               pdata->tps65217_init_data[i] = reg_matches[i].init_data;
-               pdata->of_node[i] = reg_matches[i].of_node;
-       }
-
-       return pdata;
-}
-
-static struct of_device_id tps65217_of_match[] = {
-       { .compatible = "ti,tps65217", },
-       { },
-};
-#else
-static struct tps65217_board *tps65217_parse_dt(struct i2c_client *client)
-{
-       return NULL;
-}
-#endif
-
 static struct regmap_config tps65217_regmap_config = {
        .reg_bits = 8,
        .val_bits = 8,
 };
 
+static const struct of_device_id tps65217_of_match[] = {
+       { .compatible = "ti,tps65217", .data = (void *)TPS65217 },
+       { /* sentinel */ },
+};
+
 static int __devinit tps65217_probe(struct i2c_client *client,
                                const struct i2c_device_id *ids)
 {
        struct tps65217 *tps;
-       struct regulator_init_data *reg_data;
-       struct tps65217_board *pdata = client->dev.platform_data;
-       int i, ret;
        unsigned int version;
+       unsigned int chip_id = ids->driver_data;
+       const struct of_device_id *match;
+       int ret;
 
-       if (!pdata && client->dev.of_node)
-               pdata = tps65217_parse_dt(client);
+       if (client->dev.of_node) {
+               match = of_match_device(tps65217_of_match, &client->dev);
+               if (!match) {
+                       dev_err(&client->dev,
+                               "Failed to find matching dt id\n");
+                       return -EINVAL;
+               }
+               chip_id = (unsigned int)match->data;
+       }
+
+       if (!chip_id) {
+               dev_err(&client->dev, "id is null.\n");
+               return -ENODEV;
+       }
 
        tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
        if (!tps)
                return -ENOMEM;
 
-       tps->pdata = pdata;
+       i2c_set_clientdata(client, tps);
+       tps->dev = &client->dev;
+       tps->id = chip_id;
+
        tps->regmap = devm_regmap_init_i2c(client, &tps65217_regmap_config);
        if (IS_ERR(tps->regmap)) {
                ret = PTR_ERR(tps->regmap);
@@ -218,8 +190,12 @@ static int __devinit tps65217_probe(struct i2c_client *client,
                return ret;
        }
 
-       i2c_set_clientdata(client, tps);
-       tps->dev = &client->dev;
+       ret = mfd_add_devices(tps->dev, -1, tps65217s,
+                             ARRAY_SIZE(tps65217s), NULL, 0, NULL);
+       if (ret < 0) {
+               dev_err(tps->dev, "mfd_add_devices failed: %d\n", ret);
+               return ret;
+       }
 
        ret = tps65217_reg_read(tps, TPS65217_REG_CHIPID, &version);
        if (ret < 0) {
@@ -232,41 +208,21 @@ static int __devinit tps65217_probe(struct i2c_client *client,
                        (version & TPS65217_CHIPID_CHIP_MASK) >> 4,
                        version & TPS65217_CHIPID_REV_MASK);
 
-       for (i = 0; i < TPS65217_NUM_REGULATOR; i++) {
-               struct platform_device *pdev;
-
-               pdev = platform_device_alloc("tps65217-pmic", i);
-               if (!pdev) {
-                       dev_err(tps->dev, "Cannot create regulator %d\n", i);
-                       continue;
-               }
-
-               pdev->dev.parent = tps->dev;
-               pdev->dev.of_node = pdata->of_node[i];
-               reg_data = pdata->tps65217_init_data[i];
-               platform_device_add_data(pdev, reg_data, sizeof(*reg_data));
-               tps->regulator_pdev[i] = pdev;
-
-               platform_device_add(pdev);
-       }
-
        return 0;
 }
 
 static int __devexit tps65217_remove(struct i2c_client *client)
 {
        struct tps65217 *tps = i2c_get_clientdata(client);
-       int i;
 
-       for (i = 0; i < TPS65217_NUM_REGULATOR; i++)
-               platform_device_unregister(tps->regulator_pdev[i]);
+       mfd_remove_devices(tps->dev);
 
        return 0;
 }
 
 static const struct i2c_device_id tps65217_id_table[] = {
-       {"tps65217", 0xF0},
-       {/* end of list */}
+       {"tps65217", TPS65217},
+       { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(i2c, tps65217_id_table);
 
index 353c34812120fc46e37140c0b6a04babdd5cb080..5f58370ccf5502b1d6a729f2daa0db411a6e3e1d 100644 (file)
@@ -493,7 +493,8 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
        }
 
        ret = mfd_add_devices(tps6586x->dev, -1,
-                       tps6586x_cell, ARRAY_SIZE(tps6586x_cell), NULL, 0);
+                             tps6586x_cell, ARRAY_SIZE(tps6586x_cell),
+                             NULL, 0, NULL);
        if (ret < 0) {
                dev_err(&client->dev, "mfd_add_devices failed: %d\n", ret);
                goto err_mfd_add;
index 1c563792c777ba8f04c194a2a99c39159d8871f7..d3ce4d569deb57c2b707dc73d6cb7cf600962cab 100644 (file)
@@ -254,7 +254,7 @@ static __devinit int tps65910_i2c_probe(struct i2c_client *i2c,
 
        ret = mfd_add_devices(tps65910->dev, -1,
                              tps65910s, ARRAY_SIZE(tps65910s),
-                             NULL, 0);
+                             NULL, 0, NULL);
        if (ret < 0) {
                dev_err(&i2c->dev, "mfd_add_devices failed: %d\n", ret);
                return ret;
index 74fd8cb5f37224e576f58398c20fda9f4884d9e7..4658b5bdcd84488d3379a2ef4f33e334eb96ea2f 100644 (file)
@@ -146,7 +146,7 @@ int tps65912_device_init(struct tps65912 *tps65912)
 
        ret = mfd_add_devices(tps65912->dev, -1,
                              tps65912s, ARRAY_SIZE(tps65912s),
-                             NULL, 0);
+                             NULL, 0, NULL);
        if (ret < 0)
                goto err;
 
index 838ce4eb444e24ce44bd3b120fe21a9f977d6e75..77c9acb145831c5b03fd04cbc08d0f6e0bfd47ec 100644 (file)
@@ -223,7 +223,7 @@ static int __devinit twl4030_audio_probe(struct platform_device *pdev)
 
        if (childs)
                ret = mfd_add_devices(&pdev->dev, pdev->id, audio->cells,
-                                     childs, NULL, 0);
+                                     childs, NULL, 0, NULL);
        else {
                dev_err(&pdev->dev, "No platform data found for childs\n");
                ret = -ENODEV;
index b0fad0ffca560b0714a9574b42d4c87c89ea3e4d..3dca5c195a200505c3796c488b34b65f8a32b7e6 100644 (file)
@@ -632,7 +632,7 @@ static int __devinit twl6040_probe(struct i2c_client *client,
        }
 
        ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children,
-                             NULL, 0);
+                             NULL, 0, NULL);
        if (ret)
                goto mfd_err;
 
index 872aff21e4be6fa682eb01aad72b04492efd622e..b9a636d44c7f95979adcd17172e9d7803fa5d5fe 100644 (file)
@@ -102,7 +102,7 @@ static __devinit int vx855_probe(struct pci_dev *pdev,
        vx855_gpio_resources[1].end = vx855_gpio_resources[1].start + 3;
 
        ret = mfd_add_devices(&pdev->dev, -1, vx855_cells, ARRAY_SIZE(vx855_cells),
-                       NULL, 0);
+                       NULL, 0, NULL);
 
        /* we always return -ENODEV here in order to enable other
         * drivers like old, not-yet-platform_device ported i2c-viapro */
index f39b756df561dcfd5fee7fa310222bb27748d708..86e0e4309fc274e41ec73a20dc1fb675e06fb0fc 100644 (file)
@@ -241,7 +241,7 @@ static int __devinit wl1273_core_probe(struct i2c_client *client,
                __func__, children);
 
        r = mfd_add_devices(&client->dev, -1, core->cells,
-                           children, NULL, 0);
+                           children, NULL, 0, NULL);
        if (r)
                goto err;
 
index 946698fd2dc6a4dc9b3a7868bc7f44c920a46201..3017310359403248719495fe39692698271ba7ed 100644 (file)
@@ -1813,27 +1813,27 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
        case WM8310:
                ret = mfd_add_devices(wm831x->dev, wm831x_num,
                                      wm8310_devs, ARRAY_SIZE(wm8310_devs),
-                                     NULL, 0);
+                                     NULL, 0, NULL);
                break;
 
        case WM8311:
                ret = mfd_add_devices(wm831x->dev, wm831x_num,
                                      wm8311_devs, ARRAY_SIZE(wm8311_devs),
-                                     NULL, 0);
+                                     NULL, 0, NULL);
                if (!pdata || !pdata->disable_touch)
                        mfd_add_devices(wm831x->dev, wm831x_num,
                                        touch_devs, ARRAY_SIZE(touch_devs),
-                                       NULL, 0);
+                                       NULL, 0, NULL);
                break;
 
        case WM8312:
                ret = mfd_add_devices(wm831x->dev, wm831x_num,
                                      wm8312_devs, ARRAY_SIZE(wm8312_devs),
-                                     NULL, 0);
+                                     NULL, 0, NULL);
                if (!pdata || !pdata->disable_touch)
                        mfd_add_devices(wm831x->dev, wm831x_num,
                                        touch_devs, ARRAY_SIZE(touch_devs),
-                                       NULL, 0);
+                                       NULL, 0, NULL);
                break;
 
        case WM8320:
@@ -1842,7 +1842,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
        case WM8326:
                ret = mfd_add_devices(wm831x->dev, wm831x_num,
                                      wm8320_devs, ARRAY_SIZE(wm8320_devs),
-                                     NULL, 0);
+                                     NULL, 0, NULL);
                break;
 
        default:
@@ -1867,7 +1867,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
        if (ret & WM831X_XTAL_ENA) {
                ret = mfd_add_devices(wm831x->dev, wm831x_num,
                                      rtc_devs, ARRAY_SIZE(rtc_devs),
-                                     NULL, 0);
+                                     NULL, 0, NULL);
                if (ret != 0) {
                        dev_err(wm831x->dev, "Failed to add RTC: %d\n", ret);
                        goto err_irq;
@@ -1880,7 +1880,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
                /* Treat errors as non-critical */
                ret = mfd_add_devices(wm831x->dev, wm831x_num, backlight_devs,
                                      ARRAY_SIZE(backlight_devs), NULL,
-                                     0);
+                                     0, NULL);
                if (ret < 0)
                        dev_err(wm831x->dev, "Failed to add backlight: %d\n",
                                ret);
index 4b7d378551d58daf515532dbcaea2c6f35c17f1e..639ca359242f849cebfe407333e6107c0bca4bbb 100644 (file)
@@ -70,7 +70,7 @@ static int wm8400_register_codec(struct wm8400 *wm8400)
                .pdata_size = sizeof(*wm8400),
        };
 
-       return mfd_add_devices(wm8400->dev, -1, &cell, 1, NULL, 0);
+       return mfd_add_devices(wm8400->dev, -1, &cell, 1, NULL, 0, NULL);
 }
 
 /*
index eec74aa55fdfe28c46476c3360e1632bfd51cf7f..2febf88cfce8847383be5a4fe8ab80ae89fe48e2 100644 (file)
@@ -414,7 +414,7 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq)
        ret = mfd_add_devices(wm8994->dev, -1,
                              wm8994_regulator_devs,
                              ARRAY_SIZE(wm8994_regulator_devs),
-                             NULL, 0);
+                             NULL, 0, NULL);
        if (ret != 0) {
                dev_err(wm8994->dev, "Failed to add children: %d\n", ret);
                goto err;
@@ -648,7 +648,7 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq)
 
        ret = mfd_add_devices(wm8994->dev, -1,
                              wm8994_devs, ARRAY_SIZE(wm8994_devs),
-                             NULL, 0);
+                             NULL, 0, NULL);
        if (ret != 0) {
                dev_err(wm8994->dev, "Failed to add children: %d\n", ret);
                goto err_irq;
index 98ee4381991189d8814600967acac3b170525b1d..7edadee487bab16c0cbcf514ab59540f35cc2a41 100644 (file)
@@ -1391,7 +1391,6 @@ static irqreturn_t ican3_irq(int irq, void *dev_id)
  */
 static int ican3_reset_module(struct ican3_dev *mod)
 {
-       u8 val = 1 << mod->num;
        unsigned long start;
        u8 runold, runnew;
 
@@ -1405,8 +1404,7 @@ static int ican3_reset_module(struct ican3_dev *mod)
        runold = ioread8(mod->dpm + TARGET_RUNNING);
 
        /* reset the module */
-       iowrite8(val, &mod->ctrl->reset_assert);
-       iowrite8(val, &mod->ctrl->reset_deassert);
+       iowrite8(0x00, &mod->dpmctrl->hwreset);
 
        /* wait until the module has finished resetting and is running */
        start = jiffies;
index a580db29e50360f8be44403e5893764261562549..26e7129332abc7e18bd7b27cd3f49d67976c3a16 100644 (file)
 #define INSTRUCTION_LOAD_TXB(n)        (0x40 + 2 * (n))
 #define INSTRUCTION_READ_RXB(n)        (((n) == 0) ? 0x90 : 0x94)
 #define INSTRUCTION_RESET      0xC0
+#define RTS_TXB0               0x01
+#define RTS_TXB1               0x02
+#define RTS_TXB2               0x04
+#define INSTRUCTION_RTS(n)     (0x80 | ((n) & 0x07))
+
 
 /* MPC251x registers */
 #define CANSTAT              0x0e
@@ -397,6 +402,7 @@ static void mcp251x_hw_tx_frame(struct spi_device *spi, u8 *buf,
 static void mcp251x_hw_tx(struct spi_device *spi, struct can_frame *frame,
                          int tx_buf_idx)
 {
+       struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
        u32 sid, eid, exide, rtr;
        u8 buf[SPI_TRANSFER_BUF_LEN];
 
@@ -418,7 +424,10 @@ static void mcp251x_hw_tx(struct spi_device *spi, struct can_frame *frame,
        buf[TXBDLC_OFF] = (rtr << DLC_RTR_SHIFT) | frame->can_dlc;
        memcpy(buf + TXBDAT_OFF, frame->data, frame->can_dlc);
        mcp251x_hw_tx_frame(spi, buf, frame->can_dlc, tx_buf_idx);
-       mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx), TXBCTRL_TXREQ);
+
+       /* use INSTRUCTION_RTS, to avoid "repeated frame problem" */
+       priv->spi_tx_buf[0] = INSTRUCTION_RTS(1 << tx_buf_idx);
+       mcp251x_spi_trans(priv->spi, 1);
 }
 
 static void mcp251x_hw_rx_frame(struct spi_device *spi, u8 *buf,
index 527dbcf9533561489bb33332f03462ac5f405f43..9ded21e79db5866602706ca568dea6613bc0a727 100644 (file)
@@ -984,12 +984,12 @@ static int __devexit ti_hecc_remove(struct platform_device *pdev)
        struct net_device *ndev = platform_get_drvdata(pdev);
        struct ti_hecc_priv *priv = netdev_priv(ndev);
 
+       unregister_candev(ndev);
        clk_disable(priv->clk);
        clk_put(priv->clk);
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        iounmap(priv->base);
        release_mem_region(res->start, resource_size(res));
-       unregister_candev(ndev);
        free_candev(ndev);
        platform_set_drvdata(pdev, NULL);
 
index af20c6ee2cd9292dfa41c8693e6d8253dfe667e4..e8e97a7d1d06df9a209c741406af32c19a1313e2 100644 (file)
@@ -662,14 +662,16 @@ void bnx2x_csum_validate(struct sk_buff *skb, union eth_rx_cqe *cqe,
                                 struct bnx2x_fastpath *fp,
                                 struct bnx2x_eth_q_stats *qstats)
 {
-       /* Do nothing if no IP/L4 csum validation was done */
-
+       /* Do nothing if no L4 csum validation was done.
+        * We do not check whether IP csum was validated. For IPv4 we assume
+        * that if the card got as far as validating the L4 csum, it also
+        * validated the IP csum. IPv6 has no IP csum.
+        */
        if (cqe->fast_path_cqe.status_flags &
-           (ETH_FAST_PATH_RX_CQE_IP_XSUM_NO_VALIDATION_FLG |
-            ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG))
+           ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG)
                return;
 
-       /* If both IP/L4 validation were done, check if an error was found. */
+       /* If L4 validation was done, check if an error was found. */
 
        if (cqe->fast_path_cqe.type_error_flags &
            (ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG |
index 21b553229ea4c176dfe6c53c60ddde9df3f3f977..dfd86a55f1dcab583ad08342a21eaae9a97be53f 100644 (file)
@@ -710,17 +710,15 @@ static inline u16 bnx2x_tx_avail(struct bnx2x *bp,
        prod = txdata->tx_bd_prod;
        cons = txdata->tx_bd_cons;
 
-       /* NUM_TX_RINGS = number of "next-page" entries
-          It will be used as a threshold */
-       used = SUB_S16(prod, cons) + (s16)NUM_TX_RINGS;
+       used = SUB_S16(prod, cons);
 
 #ifdef BNX2X_STOP_ON_ERROR
        WARN_ON(used < 0);
-       WARN_ON(used > bp->tx_ring_size);
-       WARN_ON((bp->tx_ring_size - used) > MAX_TX_AVAIL);
+       WARN_ON(used > txdata->tx_ring_size);
+       WARN_ON((txdata->tx_ring_size - used) > MAX_TX_AVAIL);
 #endif
 
-       return (s16)(bp->tx_ring_size) - used;
+       return (s16)(txdata->tx_ring_size) - used;
 }
 
 static inline int bnx2x_tx_queue_has_work(struct bnx2x_fp_txdata *txdata)
@@ -1088,6 +1086,7 @@ static inline void bnx2x_init_txdata(struct bnx2x *bp,
        txdata->txq_index = txq_index;
        txdata->tx_cons_sb = tx_cons_sb;
        txdata->parent_fp = fp;
+       txdata->tx_ring_size = IS_FCOE_FP(fp) ? MAX_TX_AVAIL : bp->tx_ring_size;
 
        DP(NETIF_MSG_IFUP, "created tx data cid %d, txq %d\n",
           txdata->cid, txdata->txq_index);
index 3e4cff9b1ebee60a4fa4cedbb90fdeaecc73b580..b926f58e983bbfe083e004c362df6203efb17a3e 100644 (file)
@@ -401,11 +401,11 @@ static const struct reg_addr reg_addrs[] = {
        { 0x70000, 8, RI_ALL_ONLINE },
        { 0x70020, 8184, RI_ALL_OFFLINE },
        { 0x78000, 8192, RI_E3E3B0_OFFLINE },
-       { 0x85000, 3, RI_ALL_ONLINE },
-       { 0x8501c, 7, RI_ALL_ONLINE },
-       { 0x85048, 1, RI_ALL_ONLINE },
-       { 0x85200, 32, RI_ALL_ONLINE },
-       { 0xb0000, 16384, RI_E1H_ONLINE },
+       { 0x85000, 3, RI_ALL_OFFLINE },
+       { 0x8501c, 7, RI_ALL_OFFLINE },
+       { 0x85048, 1, RI_ALL_OFFLINE },
+       { 0x85200, 32, RI_ALL_OFFLINE },
+       { 0xb0000, 16384, RI_E1H_OFFLINE },
        { 0xc1000, 7, RI_ALL_ONLINE },
        { 0xc103c, 2, RI_E2E3E3B0_ONLINE },
        { 0xc1800, 2, RI_ALL_ONLINE },
@@ -581,17 +581,12 @@ static const struct reg_addr reg_addrs[] = {
        { 0x140188, 3, RI_E1E1HE2E3_ONLINE },
        { 0x140194, 13, RI_ALL_ONLINE },
        { 0x140200, 6, RI_E1E1HE2E3_ONLINE },
-       { 0x140220, 4, RI_E2E3_ONLINE },
-       { 0x140240, 4, RI_E2E3_ONLINE },
        { 0x140260, 4, RI_E2E3_ONLINE },
        { 0x140280, 4, RI_E2E3_ONLINE },
-       { 0x1402a0, 4, RI_E2E3_ONLINE },
-       { 0x1402c0, 4, RI_E2E3_ONLINE },
        { 0x1402e0, 2, RI_E2E3_ONLINE },
        { 0x1402e8, 2, RI_E2E3E3B0_ONLINE },
        { 0x1402f0, 9, RI_E2E3_ONLINE },
        { 0x140314, 44, RI_E3B0_ONLINE },
-       { 0x1403d0, 70, RI_E3B0_ONLINE },
        { 0x144000, 4, RI_E1E1H_ONLINE },
        { 0x148000, 4, RI_E1E1H_ONLINE },
        { 0x14c000, 4, RI_E1E1H_ONLINE },
@@ -704,7 +699,6 @@ static const struct reg_addr reg_addrs[] = {
        { 0x180398, 1, RI_E2E3E3B0_ONLINE },
        { 0x1803a0, 5, RI_E2E3E3B0_ONLINE },
        { 0x1803b4, 2, RI_E3E3B0_ONLINE },
-       { 0x180400, 1, RI_ALL_ONLINE },
        { 0x180404, 255, RI_E1E1H_OFFLINE },
        { 0x181000, 4, RI_ALL_ONLINE },
        { 0x181010, 1020, RI_ALL_OFFLINE },
@@ -800,9 +794,9 @@ static const struct reg_addr reg_addrs[] = {
        { 0x1b905c, 1, RI_E3E3B0_ONLINE },
        { 0x1b9064, 1, RI_E3B0_ONLINE },
        { 0x1b9080, 10, RI_E3B0_ONLINE },
-       { 0x1b9400, 14, RI_E2E3E3B0_ONLINE },
-       { 0x1b943c, 19, RI_E2E3E3B0_ONLINE },
-       { 0x1b9490, 10, RI_E2E3E3B0_ONLINE },
+       { 0x1b9400, 14, RI_E2E3E3B0_OFFLINE },
+       { 0x1b943c, 19, RI_E2E3E3B0_OFFLINE },
+       { 0x1b9490, 10, RI_E2E3E3B0_OFFLINE },
        { 0x1c0000, 2, RI_ALL_ONLINE },
        { 0x200000, 65, RI_ALL_ONLINE },
        { 0x20014c, 2, RI_E1HE2E3E3B0_ONLINE },
@@ -814,7 +808,6 @@ static const struct reg_addr reg_addrs[] = {
        { 0x200398, 1, RI_E2E3E3B0_ONLINE },
        { 0x2003a0, 1, RI_E2E3E3B0_ONLINE },
        { 0x2003a8, 2, RI_E2E3E3B0_ONLINE },
-       { 0x200400, 1, RI_ALL_ONLINE },
        { 0x200404, 255, RI_E1E1H_OFFLINE },
        { 0x202000, 4, RI_ALL_ONLINE },
        { 0x202010, 2044, RI_ALL_OFFLINE },
@@ -921,7 +914,6 @@ static const struct reg_addr reg_addrs[] = {
        { 0x280398, 1, RI_E2E3E3B0_ONLINE },
        { 0x2803a0, 1, RI_E2E3E3B0_ONLINE },
        { 0x2803a8, 2, RI_E2E3E3B0_ONLINE },
-       { 0x280400, 1, RI_ALL_ONLINE },
        { 0x280404, 255, RI_E1E1H_OFFLINE },
        { 0x282000, 4, RI_ALL_ONLINE },
        { 0x282010, 2044, RI_ALL_OFFLINE },
@@ -1031,7 +1023,6 @@ static const struct reg_addr reg_addrs[] = {
        { 0x300398, 1, RI_E2E3E3B0_ONLINE },
        { 0x3003a0, 1, RI_E2E3E3B0_ONLINE },
        { 0x3003a8, 2, RI_E2E3E3B0_ONLINE },
-       { 0x300400, 1, RI_ALL_ONLINE },
        { 0x300404, 255, RI_E1E1H_OFFLINE },
        { 0x302000, 4, RI_ALL_ONLINE },
        { 0x302010, 2044, RI_ALL_OFFLINE },
index c37a68d68090e1326b6f491c9f4e28f26a78f87b..ebf40cd7aa1050d716683e806eda505050bf1e40 100644 (file)
@@ -775,7 +775,7 @@ static void bnx2x_get_regs(struct net_device *dev,
        struct bnx2x *bp = netdev_priv(dev);
        struct dump_hdr dump_hdr = {0};
 
-       regs->version = 0;
+       regs->version = 1;
        memset(p, 0, regs->len);
 
        if (!netif_running(bp->dev))
@@ -1587,6 +1587,12 @@ static int bnx2x_set_pauseparam(struct net_device *dev,
                        bp->link_params.req_flow_ctrl[cfg_idx] =
                                BNX2X_FLOW_CTRL_AUTO;
                }
+               bp->link_params.req_fc_auto_adv = BNX2X_FLOW_CTRL_NONE;
+               if (epause->rx_pause)
+                       bp->link_params.req_fc_auto_adv |= BNX2X_FLOW_CTRL_RX;
+
+               if (epause->tx_pause)
+                       bp->link_params.req_fc_auto_adv |= BNX2X_FLOW_CTRL_TX;
        }
 
        DP(BNX2X_MSG_ETHTOOL,
index f4beb46c4709af8291ed7aecc373d7a72a366f59..b046beb435b2c490f70ef3bc2f67bf3af16bfcb5 100644 (file)
@@ -2667,9 +2667,11 @@ int bnx2x_update_pfc(struct link_params *params,
                return bnx2x_status;
 
        DP(NETIF_MSG_LINK, "About to update PFC in BMAC\n");
-       if (CHIP_IS_E3(bp))
-               bnx2x_update_pfc_xmac(params, vars, 0);
-       else {
+
+       if (CHIP_IS_E3(bp)) {
+               if (vars->mac_type == MAC_TYPE_XMAC)
+                       bnx2x_update_pfc_xmac(params, vars, 0);
+       } else {
                val = REG_RD(bp, MISC_REG_RESET_REG_2);
                if ((val &
                     (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << params->port))
@@ -5432,7 +5434,7 @@ static int bnx2x_get_link_speed_duplex(struct bnx2x_phy *phy,
                switch (speed_mask) {
                case GP_STATUS_10M:
                        vars->line_speed = SPEED_10;
-                       if (vars->duplex == DUPLEX_FULL)
+                       if (is_duplex == DUPLEX_FULL)
                                vars->link_status |= LINK_10TFD;
                        else
                                vars->link_status |= LINK_10THD;
@@ -5440,7 +5442,7 @@ static int bnx2x_get_link_speed_duplex(struct bnx2x_phy *phy,
 
                case GP_STATUS_100M:
                        vars->line_speed = SPEED_100;
-                       if (vars->duplex == DUPLEX_FULL)
+                       if (is_duplex == DUPLEX_FULL)
                                vars->link_status |= LINK_100TXFD;
                        else
                                vars->link_status |= LINK_100TXHD;
@@ -5449,7 +5451,7 @@ static int bnx2x_get_link_speed_duplex(struct bnx2x_phy *phy,
                case GP_STATUS_1G:
                case GP_STATUS_1G_KX:
                        vars->line_speed = SPEED_1000;
-                       if (vars->duplex == DUPLEX_FULL)
+                       if (is_duplex == DUPLEX_FULL)
                                vars->link_status |= LINK_1000TFD;
                        else
                                vars->link_status |= LINK_1000THD;
@@ -5457,7 +5459,7 @@ static int bnx2x_get_link_speed_duplex(struct bnx2x_phy *phy,
 
                case GP_STATUS_2_5G:
                        vars->line_speed = SPEED_2500;
-                       if (vars->duplex == DUPLEX_FULL)
+                       if (is_duplex == DUPLEX_FULL)
                                vars->link_status |= LINK_2500TFD;
                        else
                                vars->link_status |= LINK_2500THD;
@@ -5531,6 +5533,7 @@ static int bnx2x_link_settings_status(struct bnx2x_phy *phy,
 
        if (gp_status & MDIO_GP_STATUS_TOP_AN_STATUS1_LINK_STATUS) {
                if (SINGLE_MEDIA_DIRECT(params)) {
+                       vars->duplex = duplex;
                        bnx2x_flow_ctrl_resolve(phy, params, vars, gp_status);
                        if (phy->req_line_speed == SPEED_AUTO_NEG)
                                bnx2x_xgxs_an_resolve(phy, params, vars,
@@ -5625,6 +5628,7 @@ static int bnx2x_warpcore_read_status(struct bnx2x_phy *phy,
                                        LINK_STATUS_PARALLEL_DETECTION_USED;
                        }
                        bnx2x_ext_phy_resolve_fc(phy, params, vars);
+                       vars->duplex = duplex;
                }
        }
 
index 21054987257a12960b859594db72830c42e21059..0875ecfe33729e2c2860dd5149e62042ddcbc6d7 100644 (file)
@@ -7561,8 +7561,14 @@ int bnx2x_set_mac_one(struct bnx2x *bp, u8 *mac,
        }
 
        rc = bnx2x_config_vlan_mac(bp, &ramrod_param);
-       if (rc < 0)
+
+       if (rc == -EEXIST) {
+               DP(BNX2X_MSG_SP, "Failed to schedule ADD operations: %d\n", rc);
+               /* do not treat adding same MAC as error */
+               rc = 0;
+       } else if (rc < 0)
                BNX2X_ERR("%s MAC failed\n", (set ? "Set" : "Del"));
+
        return rc;
 }
 
@@ -9825,12 +9831,13 @@ static void __devinit bnx2x_get_igu_cam_info(struct bnx2x *bp)
        }
 
 #ifdef CONFIG_PCI_MSI
-       /*
-        * It's expected that number of CAM entries for this functions is equal
-        * to the number evaluated based on the MSI-X table size. We want a
-        * harsh warning if these values are different!
+       /* Due to new PF resource allocation by MFW T7.4 and above, it's
+        * optional that number of CAM entries will not be equal to the value
+        * advertised in PCI.
+        * Driver should use the minimal value of both as the actual status
+        * block count
         */
-       WARN_ON(bp->igu_sb_cnt != igu_sb_cnt);
+       bp->igu_sb_cnt = min_t(int, bp->igu_sb_cnt, igu_sb_cnt);
 #endif
 
        if (igu_sb_cnt == 0)
@@ -10294,13 +10301,11 @@ static void __devinit bnx2x_get_fcoe_info(struct bnx2x *bp)
                                dev_info.port_hw_config[port].
                                 fcoe_wwn_node_name_lower);
        } else if (!IS_MF_SD(bp)) {
-               u32 cfg = MF_CFG_RD(bp, func_ext_config[func].func_cfg);
-
                /*
                 * Read the WWN info only if the FCoE feature is enabled for
                 * this function.
                 */
-               if (cfg & MACP_FUNC_CFG_FLAGS_FCOE_OFFLOAD)
+               if (BNX2X_MF_EXT_PROTOCOL_FCOE(bp) && !CHIP_IS_E1x(bp))
                        bnx2x_get_ext_wwn_info(bp, func);
 
        } else if (IS_MF_FCOE_SD(bp))
@@ -11073,7 +11078,14 @@ static int bnx2x_set_uc_list(struct bnx2x *bp)
        netdev_for_each_uc_addr(ha, dev) {
                rc = bnx2x_set_mac_one(bp, bnx2x_uc_addr(ha), mac_obj, true,
                                       BNX2X_UC_LIST_MAC, &ramrod_flags);
-               if (rc < 0) {
+               if (rc == -EEXIST) {
+                       DP(BNX2X_MSG_SP,
+                          "Failed to schedule ADD operations: %d\n", rc);
+                       /* do not treat adding same MAC as error */
+                       rc = 0;
+
+               } else if (rc < 0) {
+
                        BNX2X_ERR("Failed to schedule ADD operations: %d\n",
                                  rc);
                        return rc;
index 332db64dd5bea11eed0cf878565c5c2e573f5c3e..a1d0446b39b356dd69e0b77e69f6285ba37edb63 100644 (file)
@@ -101,6 +101,11 @@ static void bnx2x_hw_stats_post(struct bnx2x *bp)
        if (CHIP_REV_IS_SLOW(bp))
                return;
 
+       /* Update MCP's statistics if possible */
+       if (bp->func_stx)
+               memcpy(bnx2x_sp(bp, func_stats), &bp->func_stats,
+                      sizeof(bp->func_stats));
+
        /* loader */
        if (bp->executer_idx) {
                int loader_idx = PMF_DMAE_C(bp);
@@ -128,8 +133,6 @@ static void bnx2x_hw_stats_post(struct bnx2x *bp)
 
        } else if (bp->func_stx) {
                *stats_comp = 0;
-               memcpy(bnx2x_sp(bp, func_stats), &bp->func_stats,
-                      sizeof(bp->func_stats));
                bnx2x_post_dmae(bp, dmae, INIT_DMAE_C(bp));
        }
 }
@@ -1151,9 +1154,11 @@ static void bnx2x_stats_update(struct bnx2x *bp)
        if (bp->port.pmf)
                bnx2x_hw_stats_update(bp);
 
-       if (bnx2x_storm_stats_update(bp) && (bp->stats_pending++ == 3)) {
-               BNX2X_ERR("storm stats were not updated for 3 times\n");
-               bnx2x_panic();
+       if (bnx2x_storm_stats_update(bp)) {
+               if (bp->stats_pending++ == 3) {
+                       BNX2X_ERR("storm stats were not updated for 3 times\n");
+                       bnx2x_panic();
+               }
                return;
        }
 
index 77884191a8c6448a7eea54c94b8ba8d9ce0fef83..4e980a7886fb58758fa84071b2494f56364da1f2 100644 (file)
@@ -1086,7 +1086,7 @@ static int __init at91ether_probe(struct platform_device *pdev)
        /* Clock */
        lp->ether_clk = clk_get(&pdev->dev, "ether_clk");
        if (IS_ERR(lp->ether_clk)) {
-               res = -ENODEV;
+               res = PTR_ERR(lp->ether_clk);
                goto err_ioumap;
        }
        clk_enable(lp->ether_clk);
index 8971921cc1c84966170b7f7a6350732f93b416e9..ab6762caa95702f492bbb457deae8986257bdcc4 100644 (file)
@@ -1773,6 +1773,7 @@ static int gfar_get_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
 }
 
 int gfar_phc_index = -1;
+EXPORT_SYMBOL(gfar_phc_index);
 
 static int gfar_get_ts_info(struct net_device *dev,
                            struct ethtool_ts_info *info)
index c08e5d40fecb425c7f6c26b8de269d270bac727f..0daa66b8eca088735974f7ee45595d22c6be4c56 100644 (file)
@@ -515,7 +515,7 @@ static int gianfar_ptp_probe(struct platform_device *dev)
                err = PTR_ERR(etsects->clock);
                goto no_clock;
        }
-       gfar_phc_clock = ptp_clock_index(etsects->clock);
+       gfar_phc_index = ptp_clock_index(etsects->clock);
 
        dev_set_drvdata(&dev->dev, etsects);
 
@@ -539,7 +539,7 @@ static int gianfar_ptp_remove(struct platform_device *dev)
        gfar_write(&etsects->regs->tmr_temask, 0);
        gfar_write(&etsects->regs->tmr_ctrl,   0);
 
-       gfar_phc_clock = -1;
+       gfar_phc_index = -1;
        ptp_clock_unregister(etsects->clock);
        iounmap(etsects->regs);
        release_resource(etsects->rsrc);
index bd1f1ef91e1910f81f454a7332337870fa031331..ba4e0cea3506f80da5cc36a69f22994a7a3e470e 100644 (file)
@@ -139,8 +139,11 @@ struct znet_private {
 /* Only one can be built-in;-> */
 static struct net_device *znet_dev;
 
+#define NETIDBLK_MAGIC         "NETIDBLK"
+#define NETIDBLK_MAGIC_SIZE    8
+
 struct netidblk {
-       char magic[8];          /* The magic number (string) "NETIDBLK" */
+       char magic[NETIDBLK_MAGIC_SIZE];        /* The magic number (string) "NETIDBLK" */
        unsigned char netid[8]; /* The physical station address */
        char nettype, globalopt;
        char vendor[8];         /* The machine vendor and product name. */
@@ -373,14 +376,16 @@ static int __init znet_probe (void)
        struct znet_private *znet;
        struct net_device *dev;
        char *p;
+       char *plast = phys_to_virt(0x100000 - NETIDBLK_MAGIC_SIZE);
        int err = -ENOMEM;
 
        /* This code scans the region 0xf0000 to 0xfffff for a "NETIDBLK". */
-       for(p = (char *)phys_to_virt(0xf0000); p < (char *)phys_to_virt(0x100000); p++)
-               if (*p == 'N'  &&  strncmp(p, "NETIDBLK", 8) == 0)
+       for(p = (char *)phys_to_virt(0xf0000); p <= plast; p++)
+               if (*p == 'N' &&
+                   strncmp(p, NETIDBLK_MAGIC, NETIDBLK_MAGIC_SIZE) == 0)
                        break;
 
-       if (p >= (char *)phys_to_virt(0x100000)) {
+       if (p > plast) {
                if (znet_debug > 1)
                        printk(KERN_INFO "No Z-Note ethernet adaptor found.\n");
                return -ENODEV;
index 9010cea68bc3094a4b9b18b06cae1aa7d9796cd7..b68d28a130e664e2042bbb9a4335710964f861a7 100644 (file)
@@ -472,14 +472,9 @@ static void ibmveth_cleanup(struct ibmveth_adapter *adapter)
        }
 
        if (adapter->rx_queue.queue_addr != NULL) {
-               if (!dma_mapping_error(dev, adapter->rx_queue.queue_dma)) {
-                       dma_unmap_single(dev,
-                                       adapter->rx_queue.queue_dma,
-                                       adapter->rx_queue.queue_len,
-                                       DMA_BIDIRECTIONAL);
-                       adapter->rx_queue.queue_dma = DMA_ERROR_CODE;
-               }
-               kfree(adapter->rx_queue.queue_addr);
+               dma_free_coherent(dev, adapter->rx_queue.queue_len,
+                                 adapter->rx_queue.queue_addr,
+                                 adapter->rx_queue.queue_dma);
                adapter->rx_queue.queue_addr = NULL;
        }
 
@@ -556,10 +551,13 @@ static int ibmveth_open(struct net_device *netdev)
                goto err_out;
        }
 
+       dev = &adapter->vdev->dev;
+
        adapter->rx_queue.queue_len = sizeof(struct ibmveth_rx_q_entry) *
                                                rxq_entries;
-       adapter->rx_queue.queue_addr = kmalloc(adapter->rx_queue.queue_len,
-                                               GFP_KERNEL);
+       adapter->rx_queue.queue_addr =
+           dma_alloc_coherent(dev, adapter->rx_queue.queue_len,
+                              &adapter->rx_queue.queue_dma, GFP_KERNEL);
 
        if (!adapter->rx_queue.queue_addr) {
                netdev_err(netdev, "unable to allocate rx queue pages\n");
@@ -567,19 +565,13 @@ static int ibmveth_open(struct net_device *netdev)
                goto err_out;
        }
 
-       dev = &adapter->vdev->dev;
-
        adapter->buffer_list_dma = dma_map_single(dev,
                        adapter->buffer_list_addr, 4096, DMA_BIDIRECTIONAL);
        adapter->filter_list_dma = dma_map_single(dev,
                        adapter->filter_list_addr, 4096, DMA_BIDIRECTIONAL);
-       adapter->rx_queue.queue_dma = dma_map_single(dev,
-                       adapter->rx_queue.queue_addr,
-                       adapter->rx_queue.queue_len, DMA_BIDIRECTIONAL);
 
        if ((dma_mapping_error(dev, adapter->buffer_list_dma)) ||
-           (dma_mapping_error(dev, adapter->filter_list_dma)) ||
-           (dma_mapping_error(dev, adapter->rx_queue.queue_dma))) {
+           (dma_mapping_error(dev, adapter->filter_list_dma))) {
                netdev_err(netdev, "unable to map filter or buffer list "
                           "pages\n");
                rc = -ENOMEM;
index 3bfbb8df898935f4acf47a52ebb8be3f0f683a67..bde337ee1a34f954e90c583711f155eec87b633e 100644 (file)
@@ -3149,6 +3149,17 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb,
                return NETDEV_TX_OK;
        }
 
+       /* On PCI/PCI-X HW, if packet size is less than ETH_ZLEN,
+        * packets may get corrupted during padding by HW.
+        * To WA this issue, pad all small packets manually.
+        */
+       if (skb->len < ETH_ZLEN) {
+               if (skb_pad(skb, ETH_ZLEN - skb->len))
+                       return NETDEV_TX_OK;
+               skb->len = ETH_ZLEN;
+               skb_set_tail_pointer(skb, ETH_ZLEN);
+       }
+
        mss = skb_shinfo(skb)->gso_size;
        /* The controller does a simple calculation to
         * make sure there is enough room in the FIFO before
index daf41792366147c7381e9f8f7f1017489c55a57e..31d02649be41f2317d16f178f88bc1f19153352a 100644 (file)
@@ -227,9 +227,10 @@ int mlx4_UNMAP_ICM_AUX(struct mlx4_dev *dev)
                        MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE);
 }
 
-int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj)
+int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj)
 {
-       int i = (obj & (table->num_obj - 1)) / (MLX4_TABLE_CHUNK_SIZE / table->obj_size);
+       u32 i = (obj & (table->num_obj - 1)) /
+                       (MLX4_TABLE_CHUNK_SIZE / table->obj_size);
        int ret = 0;
 
        mutex_lock(&table->mutex);
@@ -262,16 +263,18 @@ out:
        return ret;
 }
 
-void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj)
+void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj)
 {
-       int i;
+       u32 i;
+       u64 offset;
 
        i = (obj & (table->num_obj - 1)) / (MLX4_TABLE_CHUNK_SIZE / table->obj_size);
 
        mutex_lock(&table->mutex);
 
        if (--table->icm[i]->refcount == 0) {
-               mlx4_UNMAP_ICM(dev, table->virt + i * MLX4_TABLE_CHUNK_SIZE,
+               offset = (u64) i * MLX4_TABLE_CHUNK_SIZE;
+               mlx4_UNMAP_ICM(dev, table->virt + offset,
                               MLX4_TABLE_CHUNK_SIZE / MLX4_ICM_PAGE_SIZE);
                mlx4_free_icm(dev, table->icm[i], table->coherent);
                table->icm[i] = NULL;
@@ -280,9 +283,11 @@ void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj)
        mutex_unlock(&table->mutex);
 }
 
-void *mlx4_table_find(struct mlx4_icm_table *table, int obj, dma_addr_t *dma_handle)
+void *mlx4_table_find(struct mlx4_icm_table *table, u32 obj,
+                       dma_addr_t *dma_handle)
 {
-       int idx, offset, dma_offset, i;
+       int offset, dma_offset, i;
+       u64 idx;
        struct mlx4_icm_chunk *chunk;
        struct mlx4_icm *icm;
        struct page *page = NULL;
@@ -292,7 +297,7 @@ void *mlx4_table_find(struct mlx4_icm_table *table, int obj, dma_addr_t *dma_han
 
        mutex_lock(&table->mutex);
 
-       idx = (obj & (table->num_obj - 1)) * table->obj_size;
+       idx = (u64) (obj & (table->num_obj - 1)) * table->obj_size;
        icm = table->icm[idx / MLX4_TABLE_CHUNK_SIZE];
        dma_offset = offset = idx % MLX4_TABLE_CHUNK_SIZE;
 
@@ -326,10 +331,11 @@ out:
 }
 
 int mlx4_table_get_range(struct mlx4_dev *dev, struct mlx4_icm_table *table,
-                        int start, int end)
+                        u32 start, u32 end)
 {
        int inc = MLX4_TABLE_CHUNK_SIZE / table->obj_size;
-       int i, err;
+       int err;
+       u32 i;
 
        for (i = start; i <= end; i += inc) {
                err = mlx4_table_get(dev, table, i);
@@ -349,9 +355,9 @@ fail:
 }
 
 void mlx4_table_put_range(struct mlx4_dev *dev, struct mlx4_icm_table *table,
-                         int start, int end)
+                         u32 start, u32 end)
 {
-       int i;
+       u32 i;
 
        for (i = start; i <= end; i += MLX4_TABLE_CHUNK_SIZE / table->obj_size)
                mlx4_table_put(dev, table, i);
index a67744f53506af7f86c0de581445e1e67568de7f..dee67fa39107f890508e158e56b680af6496d74a 100644 (file)
@@ -71,17 +71,17 @@ struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages,
                                gfp_t gfp_mask, int coherent);
 void mlx4_free_icm(struct mlx4_dev *dev, struct mlx4_icm *icm, int coherent);
 
-int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj);
-void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj);
+int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj);
+void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj);
 int mlx4_table_get_range(struct mlx4_dev *dev, struct mlx4_icm_table *table,
-                        int start, int end);
+                        u32 start, u32 end);
 void mlx4_table_put_range(struct mlx4_dev *dev, struct mlx4_icm_table *table,
-                         int start, int end);
+                         u32 start, u32 end);
 int mlx4_init_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table,
                        u64 virt, int obj_size, u32 nobj, int reserved,
                        int use_lowmem, int use_coherent);
 void mlx4_cleanup_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table);
-void *mlx4_table_find(struct mlx4_icm_table *table, int obj, dma_addr_t *dma_handle);
+void *mlx4_table_find(struct mlx4_icm_table *table, u32 obj, dma_addr_t *dma_handle);
 
 static inline void mlx4_icm_first(struct mlx4_icm *icm,
                                  struct mlx4_icm_iter *iter)
index 827b72dfce99690093f0fe81ee3ccf804d59db85..2f816c6aed72da16bc6afc22c957f0b6c7a5eac0 100644 (file)
@@ -1234,13 +1234,13 @@ static int mlx4_init_hca(struct mlx4_dev *dev)
                                mlx4_info(dev, "non-primary physical function, skipping.\n");
                        else
                                mlx4_err(dev, "QUERY_FW command failed, aborting.\n");
-                       goto unmap_bf;
+                       return err;
                }
 
                err = mlx4_load_fw(dev);
                if (err) {
                        mlx4_err(dev, "Failed to start FW, aborting.\n");
-                       goto unmap_bf;
+                       return err;
                }
 
                mlx4_cfg.log_pg_sz_m = 1;
@@ -1304,7 +1304,7 @@ static int mlx4_init_hca(struct mlx4_dev *dev)
                err = mlx4_init_slave(dev);
                if (err) {
                        mlx4_err(dev, "Failed to initialize slave\n");
-                       goto unmap_bf;
+                       return err;
                }
 
                err = mlx4_slave_cap(dev);
@@ -1324,7 +1324,7 @@ static int mlx4_init_hca(struct mlx4_dev *dev)
        err = mlx4_QUERY_ADAPTER(dev, &adapter);
        if (err) {
                mlx4_err(dev, "QUERY_ADAPTER command failed, aborting.\n");
-               goto err_close;
+               goto unmap_bf;
        }
 
        priv->eq_table.inta_pin = adapter.inta_pin;
@@ -1332,6 +1332,9 @@ static int mlx4_init_hca(struct mlx4_dev *dev)
 
        return 0;
 
+unmap_bf:
+       unmap_bf_area(dev);
+
 err_close:
        mlx4_close_hca(dev);
 
@@ -1344,8 +1347,6 @@ err_stop_fw:
                mlx4_UNMAP_FA(dev);
                mlx4_free_icm(dev, priv->fw.fw_icm, 0);
        }
-unmap_bf:
-       unmap_bf_area(dev);
        return err;
 }
 
@@ -1996,7 +1997,8 @@ static int __mlx4_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
        }
 
 slave_start:
-       if (mlx4_cmd_init(dev)) {
+       err = mlx4_cmd_init(dev);
+       if (err) {
                mlx4_err(dev, "Failed to init command interface, aborting.\n");
                goto err_sriov;
        }
index a018ea2a43deb9c67e773032e62d8a83f54bb3d9..e151c21baf2baf5970c9c232c79d0c8650c0eb4e 100644 (file)
@@ -137,11 +137,11 @@ static int mlx4_GID_HASH(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox,
        return err;
 }
 
-static struct mlx4_promisc_qp *get_promisc_qp(struct mlx4_dev *dev, u8 pf_num,
+static struct mlx4_promisc_qp *get_promisc_qp(struct mlx4_dev *dev, u8 port,
                                              enum mlx4_steer_type steer,
                                              u32 qpn)
 {
-       struct mlx4_steer *s_steer = &mlx4_priv(dev)->steer[pf_num];
+       struct mlx4_steer *s_steer = &mlx4_priv(dev)->steer[port - 1];
        struct mlx4_promisc_qp *pqp;
 
        list_for_each_entry(pqp, &s_steer->promisc_qps[steer], list) {
@@ -182,7 +182,7 @@ static int new_steering_entry(struct mlx4_dev *dev, u8 port,
        /* If the given qpn is also a promisc qp,
         * it should be inserted to duplicates list
         */
-       pqp = get_promisc_qp(dev, 0, steer, qpn);
+       pqp = get_promisc_qp(dev, port, steer, qpn);
        if (pqp) {
                dqp = kmalloc(sizeof *dqp, GFP_KERNEL);
                if (!dqp) {
@@ -256,7 +256,7 @@ static int existing_steering_entry(struct mlx4_dev *dev, u8 port,
 
        s_steer = &mlx4_priv(dev)->steer[port - 1];
 
-       pqp = get_promisc_qp(dev, 0, steer, qpn);
+       pqp = get_promisc_qp(dev, port, steer, qpn);
        if (!pqp)
                return 0; /* nothing to do */
 
@@ -302,7 +302,7 @@ static bool check_duplicate_entry(struct mlx4_dev *dev, u8 port,
        s_steer = &mlx4_priv(dev)->steer[port - 1];
 
        /* if qp is not promisc, it cannot be duplicated */
-       if (!get_promisc_qp(dev, 0, steer, qpn))
+       if (!get_promisc_qp(dev, port, steer, qpn))
                return false;
 
        /* The qp is promisc qp so it is a duplicate on this index
@@ -352,7 +352,7 @@ static bool can_remove_steering_entry(struct mlx4_dev *dev, u8 port,
        members_count = be32_to_cpu(mgm->members_count) & 0xffffff;
        for (i = 0;  i < members_count; i++) {
                qpn = be32_to_cpu(mgm->qp[i]) & MGM_QPN_MASK;
-               if (!get_promisc_qp(dev, 0, steer, qpn) && qpn != tqpn) {
+               if (!get_promisc_qp(dev, port, steer, qpn) && qpn != tqpn) {
                        /* the qp is not promisc, the entry can't be removed */
                        goto out;
                }
@@ -398,7 +398,7 @@ static int add_promisc_qp(struct mlx4_dev *dev, u8 port,
 
        mutex_lock(&priv->mcg_table.mutex);
 
-       if (get_promisc_qp(dev, 0, steer, qpn)) {
+       if (get_promisc_qp(dev, port, steer, qpn)) {
                err = 0;  /* Noting to do, already exists */
                goto out_mutex;
        }
@@ -503,7 +503,7 @@ static int remove_promisc_qp(struct mlx4_dev *dev, u8 port,
        s_steer = &mlx4_priv(dev)->steer[port - 1];
        mutex_lock(&priv->mcg_table.mutex);
 
-       pqp = get_promisc_qp(dev, 0, steer, qpn);
+       pqp = get_promisc_qp(dev, port, steer, qpn);
        if (unlikely(!pqp)) {
                mlx4_warn(dev, "QP %x is not promiscuous QP\n", qpn);
                /* nothing to do */
@@ -650,13 +650,6 @@ static int find_entry(struct mlx4_dev *dev, u8 port,
        return err;
 }
 
-struct mlx4_net_trans_rule_hw_ctrl {
-       __be32 ctrl;
-       __be32 vf_vep_port;
-       __be32 qpn;
-       __be32 reserved;
-};
-
 static void trans_rule_ctrl_to_hw(struct mlx4_net_trans_rule *ctrl,
                                  struct mlx4_net_trans_rule_hw_ctrl *hw)
 {
@@ -680,87 +673,18 @@ static void trans_rule_ctrl_to_hw(struct mlx4_net_trans_rule *ctrl,
        hw->qpn = cpu_to_be32(ctrl->qpn);
 }
 
-struct mlx4_net_trans_rule_hw_ib {
-       u8      size;
-       u8      rsvd1;
-       __be16  id;
-       u32     rsvd2;
-       __be32  qpn;
-       __be32  qpn_mask;
-       u8      dst_gid[16];
-       u8      dst_gid_msk[16];
-} __packed;
-
-struct mlx4_net_trans_rule_hw_eth {
-       u8      size;
-       u8      rsvd;
-       __be16  id;
-       u8      rsvd1[6];
-       u8      dst_mac[6];
-       u16     rsvd2;
-       u8      dst_mac_msk[6];
-       u16     rsvd3;
-       u8      src_mac[6];
-       u16     rsvd4;
-       u8      src_mac_msk[6];
-       u8      rsvd5;
-       u8      ether_type_enable;
-       __be16  ether_type;
-       __be16  vlan_id_msk;
-       __be16  vlan_id;
-} __packed;
-
-struct mlx4_net_trans_rule_hw_tcp_udp {
-       u8      size;
-       u8      rsvd;
-       __be16  id;
-       __be16  rsvd1[3];
-       __be16  dst_port;
-       __be16  rsvd2;
-       __be16  dst_port_msk;
-       __be16  rsvd3;
-       __be16  src_port;
-       __be16  rsvd4;
-       __be16  src_port_msk;
-} __packed;
-
-struct mlx4_net_trans_rule_hw_ipv4 {
-       u8      size;
-       u8      rsvd;
-       __be16  id;
-       __be32  rsvd1;
-       __be32  dst_ip;
-       __be32  dst_ip_msk;
-       __be32  src_ip;
-       __be32  src_ip_msk;
-} __packed;
-
-struct _rule_hw {
-       union {
-               struct {
-                       u8 size;
-                       u8 rsvd;
-                       __be16 id;
-               };
-               struct mlx4_net_trans_rule_hw_eth eth;
-               struct mlx4_net_trans_rule_hw_ib ib;
-               struct mlx4_net_trans_rule_hw_ipv4 ipv4;
-               struct mlx4_net_trans_rule_hw_tcp_udp tcp_udp;
-       };
+const u16 __sw_id_hw[] = {
+       [MLX4_NET_TRANS_RULE_ID_ETH]     = 0xE001,
+       [MLX4_NET_TRANS_RULE_ID_IB]      = 0xE005,
+       [MLX4_NET_TRANS_RULE_ID_IPV6]    = 0xE003,
+       [MLX4_NET_TRANS_RULE_ID_IPV4]    = 0xE002,
+       [MLX4_NET_TRANS_RULE_ID_TCP]     = 0xE004,
+       [MLX4_NET_TRANS_RULE_ID_UDP]     = 0xE006
 };
 
 static int parse_trans_rule(struct mlx4_dev *dev, struct mlx4_spec_list *spec,
                            struct _rule_hw *rule_hw)
 {
-       static const u16 __sw_id_hw[] = {
-               [MLX4_NET_TRANS_RULE_ID_ETH]     = 0xE001,
-               [MLX4_NET_TRANS_RULE_ID_IB]      = 0xE005,
-               [MLX4_NET_TRANS_RULE_ID_IPV6]    = 0xE003,
-               [MLX4_NET_TRANS_RULE_ID_IPV4]    = 0xE002,
-               [MLX4_NET_TRANS_RULE_ID_TCP]     = 0xE004,
-               [MLX4_NET_TRANS_RULE_ID_UDP]     = 0xE006
-       };
-
        static const size_t __rule_hw_sz[] = {
                [MLX4_NET_TRANS_RULE_ID_ETH] =
                        sizeof(struct mlx4_net_trans_rule_hw_eth),
index 4d9df8f2a12617047355fc9988d5187af80a95f5..dba69d98734a29b9a10038e22eff8e93714147ed 100644 (file)
@@ -690,6 +690,82 @@ struct mlx4_steer {
        struct list_head steer_entries[MLX4_NUM_STEERS];
 };
 
+struct mlx4_net_trans_rule_hw_ctrl {
+       __be32 ctrl;
+       __be32 vf_vep_port;
+       __be32 qpn;
+       __be32 reserved;
+};
+
+struct mlx4_net_trans_rule_hw_ib {
+       u8 size;
+       u8 rsvd1;
+       __be16 id;
+       u32 rsvd2;
+       __be32 qpn;
+       __be32 qpn_mask;
+       u8 dst_gid[16];
+       u8 dst_gid_msk[16];
+} __packed;
+
+struct mlx4_net_trans_rule_hw_eth {
+       u8      size;
+       u8      rsvd;
+       __be16  id;
+       u8      rsvd1[6];
+       u8      dst_mac[6];
+       u16     rsvd2;
+       u8      dst_mac_msk[6];
+       u16     rsvd3;
+       u8      src_mac[6];
+       u16     rsvd4;
+       u8      src_mac_msk[6];
+       u8      rsvd5;
+       u8      ether_type_enable;
+       __be16  ether_type;
+       __be16  vlan_id_msk;
+       __be16  vlan_id;
+} __packed;
+
+struct mlx4_net_trans_rule_hw_tcp_udp {
+       u8      size;
+       u8      rsvd;
+       __be16  id;
+       __be16  rsvd1[3];
+       __be16  dst_port;
+       __be16  rsvd2;
+       __be16  dst_port_msk;
+       __be16  rsvd3;
+       __be16  src_port;
+       __be16  rsvd4;
+       __be16  src_port_msk;
+} __packed;
+
+struct mlx4_net_trans_rule_hw_ipv4 {
+       u8      size;
+       u8      rsvd;
+       __be16  id;
+       __be32  rsvd1;
+       __be32  dst_ip;
+       __be32  dst_ip_msk;
+       __be32  src_ip;
+       __be32  src_ip_msk;
+} __packed;
+
+struct _rule_hw {
+       union {
+               struct {
+                       u8 size;
+                       u8 rsvd;
+                       __be16 id;
+               };
+               struct mlx4_net_trans_rule_hw_eth eth;
+               struct mlx4_net_trans_rule_hw_ib ib;
+               struct mlx4_net_trans_rule_hw_ipv4 ipv4;
+               struct mlx4_net_trans_rule_hw_tcp_udp tcp_udp;
+       };
+};
+
 struct mlx4_priv {
        struct mlx4_dev         dev;
 
index 94ceddd17ab28a3ea13a15ea4c7ec1211042278f..293c9e820c49b5d470dce7eda95f2252b94d4251 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/mlx4/cmd.h>
 #include <linux/mlx4/qp.h>
 #include <linux/if_ether.h>
+#include <linux/etherdevice.h>
 
 #include "mlx4.h"
 #include "fw.h"
@@ -2776,18 +2777,133 @@ ex_put:
        return err;
 }
 
+/*
+ * MAC validation for Flow Steering rules.
+ * VF can attach rules only with a mac address which is assigned to it.
+ */
+static int validate_eth_header_mac(int slave, struct _rule_hw *eth_header,
+                                  struct list_head *rlist)
+{
+       struct mac_res *res, *tmp;
+       __be64 be_mac;
+
+       /* make sure it isn't multicast or broadcast mac*/
+       if (!is_multicast_ether_addr(eth_header->eth.dst_mac) &&
+           !is_broadcast_ether_addr(eth_header->eth.dst_mac)) {
+               list_for_each_entry_safe(res, tmp, rlist, list) {
+                       be_mac = cpu_to_be64(res->mac << 16);
+                       if (!memcmp(&be_mac, eth_header->eth.dst_mac, ETH_ALEN))
+                               return 0;
+               }
+               pr_err("MAC %pM doesn't belong to VF %d, Steering rule rejected\n",
+                      eth_header->eth.dst_mac, slave);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/*
+ * In case of missing eth header, append eth header with a MAC address
+ * assigned to the VF.
+ */
+static int add_eth_header(struct mlx4_dev *dev, int slave,
+                         struct mlx4_cmd_mailbox *inbox,
+                         struct list_head *rlist, int header_id)
+{
+       struct mac_res *res, *tmp;
+       u8 port;
+       struct mlx4_net_trans_rule_hw_ctrl *ctrl;
+       struct mlx4_net_trans_rule_hw_eth *eth_header;
+       struct mlx4_net_trans_rule_hw_ipv4 *ip_header;
+       struct mlx4_net_trans_rule_hw_tcp_udp *l4_header;
+       __be64 be_mac = 0;
+       __be64 mac_msk = cpu_to_be64(MLX4_MAC_MASK << 16);
+
+       ctrl = (struct mlx4_net_trans_rule_hw_ctrl *)inbox->buf;
+       port = be32_to_cpu(ctrl->vf_vep_port) & 0xff;
+       eth_header = (struct mlx4_net_trans_rule_hw_eth *)(ctrl + 1);
+
+       /* Clear a space in the inbox for eth header */
+       switch (header_id) {
+       case MLX4_NET_TRANS_RULE_ID_IPV4:
+               ip_header =
+                       (struct mlx4_net_trans_rule_hw_ipv4 *)(eth_header + 1);
+               memmove(ip_header, eth_header,
+                       sizeof(*ip_header) + sizeof(*l4_header));
+               break;
+       case MLX4_NET_TRANS_RULE_ID_TCP:
+       case MLX4_NET_TRANS_RULE_ID_UDP:
+               l4_header = (struct mlx4_net_trans_rule_hw_tcp_udp *)
+                           (eth_header + 1);
+               memmove(l4_header, eth_header, sizeof(*l4_header));
+               break;
+       default:
+               return -EINVAL;
+       }
+       list_for_each_entry_safe(res, tmp, rlist, list) {
+               if (port == res->port) {
+                       be_mac = cpu_to_be64(res->mac << 16);
+                       break;
+               }
+       }
+       if (!be_mac) {
+               pr_err("Failed adding eth header to FS rule, Can't find matching MAC for port %d .\n",
+                      port);
+               return -EINVAL;
+       }
+
+       memset(eth_header, 0, sizeof(*eth_header));
+       eth_header->size = sizeof(*eth_header) >> 2;
+       eth_header->id = cpu_to_be16(__sw_id_hw[MLX4_NET_TRANS_RULE_ID_ETH]);
+       memcpy(eth_header->dst_mac, &be_mac, ETH_ALEN);
+       memcpy(eth_header->dst_mac_msk, &mac_msk, ETH_ALEN);
+
+       return 0;
+
+}
+
 int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
                                         struct mlx4_vhcr *vhcr,
                                         struct mlx4_cmd_mailbox *inbox,
                                         struct mlx4_cmd_mailbox *outbox,
                                         struct mlx4_cmd_info *cmd)
 {
+
+       struct mlx4_priv *priv = mlx4_priv(dev);
+       struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker;
+       struct list_head *rlist = &tracker->slave_list[slave].res_list[RES_MAC];
        int err;
+       struct mlx4_net_trans_rule_hw_ctrl *ctrl;
+       struct _rule_hw  *rule_header;
+       int header_id;
 
        if (dev->caps.steering_mode !=
            MLX4_STEERING_MODE_DEVICE_MANAGED)
                return -EOPNOTSUPP;
 
+       ctrl = (struct mlx4_net_trans_rule_hw_ctrl *)inbox->buf;
+       rule_header = (struct _rule_hw *)(ctrl + 1);
+       header_id = map_hw_to_sw_id(be16_to_cpu(rule_header->id));
+
+       switch (header_id) {
+       case MLX4_NET_TRANS_RULE_ID_ETH:
+               if (validate_eth_header_mac(slave, rule_header, rlist))
+                       return -EINVAL;
+               break;
+       case MLX4_NET_TRANS_RULE_ID_IPV4:
+       case MLX4_NET_TRANS_RULE_ID_TCP:
+       case MLX4_NET_TRANS_RULE_ID_UDP:
+               pr_warn("Can't attach FS rule without L2 headers, adding L2 header.\n");
+               if (add_eth_header(dev, slave, inbox, rlist, header_id))
+                       return -EINVAL;
+               vhcr->in_modifier +=
+                       sizeof(struct mlx4_net_trans_rule_hw_eth) >> 2;
+               break;
+       default:
+               pr_err("Corrupted mailbox.\n");
+               return -EINVAL;
+       }
+
        err = mlx4_cmd_imm(dev, inbox->dma, &vhcr->out_param,
                           vhcr->in_modifier, 0,
                           MLX4_QP_FLOW_STEERING_ATTACH, MLX4_CMD_TIME_CLASS_A,
index 342b3a79bd0fd7a6a6bde8860446c20c78595ef2..a77c558d8f4078e7cc4747a5308dc315e34d99c1 100644 (file)
@@ -1378,6 +1378,10 @@ static void netxen_mask_aer_correctable(struct netxen_adapter *adapter)
        struct pci_dev *root = pdev->bus->self;
        u32 aer_pos;
 
+       /* root bus? */
+       if (!root)
+               return;
+
        if (adapter->ahw.board_type != NETXEN_BRDTYPE_P3_4_GB_MM &&
                adapter->ahw.board_type != NETXEN_BRDTYPE_P3_10G_TP)
                return;
index bb8c8222122b920511f729463a04b97395724c7d..4d15bf413bdc89f060964c8114d4486be337ce46 100644 (file)
@@ -751,6 +751,7 @@ static int __devinit sgiseeq_probe(struct platform_device *pdev)
        sp->srings = sr;
        sp->rx_desc = sp->srings->rxvector;
        sp->tx_desc = sp->srings->txvector;
+       spin_lock_init(&sp->tx_lock);
 
        /* A couple calculations now, saves many cycles later. */
        setup_rx_ring(dev, sp->rx_desc, SEEQ_RX_BUFFERS);
index c136162e6473e1746b48f50a0c7cc0adc57986d6..3be88331d17ab0e690b71f06b2ac30f50d7a7f77 100644 (file)
@@ -1066,7 +1066,7 @@ static int stmmac_open(struct net_device *dev)
        } else
                priv->tm->enable = 1;
 #endif
-       clk_enable(priv->stmmac_clk);
+       clk_prepare_enable(priv->stmmac_clk);
 
        stmmac_check_ether_addr(priv);
 
@@ -1188,7 +1188,7 @@ open_error:
        if (priv->phydev)
                phy_disconnect(priv->phydev);
 
-       clk_disable(priv->stmmac_clk);
+       clk_disable_unprepare(priv->stmmac_clk);
 
        return ret;
 }
@@ -1246,7 +1246,7 @@ static int stmmac_release(struct net_device *dev)
 #ifdef CONFIG_STMMAC_DEBUG_FS
        stmmac_exit_fs();
 #endif
-       clk_disable(priv->stmmac_clk);
+       clk_disable_unprepare(priv->stmmac_clk);
 
        return 0;
 }
@@ -2178,7 +2178,7 @@ int stmmac_suspend(struct net_device *ndev)
        else {
                stmmac_set_mac(priv->ioaddr, false);
                /* Disable clock in case of PWM is off */
-               clk_disable(priv->stmmac_clk);
+               clk_disable_unprepare(priv->stmmac_clk);
        }
        spin_unlock_irqrestore(&priv->lock, flags);
        return 0;
@@ -2203,7 +2203,7 @@ int stmmac_resume(struct net_device *ndev)
                priv->hw->mac->pmt(priv->ioaddr, 0);
        else
                /* enable the clk prevously disabled */
-               clk_enable(priv->stmmac_clk);
+               clk_prepare_enable(priv->stmmac_clk);
 
        netif_device_attach(ndev);
 
index 2a0e1abde7e73eeea25c9c457999765b5315a01f..4ccd4e2977b73a0d441f6f0dbb99ea0636c8dae0 100644 (file)
@@ -97,19 +97,19 @@ static struct clk *timer_clock;
 static void stmmac_tmu_start(unsigned int new_freq)
 {
        clk_set_rate(timer_clock, new_freq);
-       clk_enable(timer_clock);
+       clk_prepare_enable(timer_clock);
 }
 
 static void stmmac_tmu_stop(void)
 {
-       clk_disable(timer_clock);
+       clk_disable_unprepare(timer_clock);
 }
 
 int stmmac_open_ext_timer(struct net_device *dev, struct stmmac_timer *tm)
 {
        timer_clock = clk_get(NULL, TMU_CHANNEL);
 
-       if (timer_clock == NULL)
+       if (IS_ERR(timer_clock))
                return -1;
 
        if (tmu2_register_user(stmmac_timer_handler, (void *)dev) < 0) {
@@ -126,7 +126,7 @@ int stmmac_open_ext_timer(struct net_device *dev, struct stmmac_timer *tm)
 
 int stmmac_close_ext_timer(void)
 {
-       clk_disable(timer_clock);
+       clk_disable_unprepare(timer_clock);
        tmu2_unregister_user();
        clk_put(timer_clock);
        return 0;
index 256eddf1f75a1913576067742f8fc1cb3dce3e7a..795109425568623977428b1a62bffc7d11c6fa14 100644 (file)
@@ -280,7 +280,7 @@ static int sh_sir_set_baudrate(struct sh_sir_self *self, u32 baudrate)
        }
 
        clk = clk_get(NULL, "irda_clk");
-       if (!clk) {
+       if (IS_ERR(clk)) {
                dev_err(dev, "can not get irda_clk\n");
                return -EIO;
        }
index 4fd48df6b989eee8aff62ee1d5edb5014bae5e1d..32e31c5c5dc6bfe95b4d38e377a508448d85c890 100644 (file)
@@ -961,6 +961,10 @@ static const struct usb_device_id  products [] = {
        // DLink DUB-E100 H/W Ver B1 Alternate
        USB_DEVICE (0x2001, 0x3c05),
        .driver_info = (unsigned long) &ax88772_info,
+}, {
+       // DLink DUB-E100 H/W Ver C1
+       USB_DEVICE (0x2001, 0x1a02),
+       .driver_info = (unsigned long) &ax88772_info,
 }, {
        // Linksys USB1000
        USB_DEVICE (0x1737, 0x0039),
index adfab3fc5478cb6815129e3b966dbe904141bad3..3543c9e578247e6ee550ef7f18f680b680be3587 100644 (file)
@@ -297,7 +297,7 @@ static int qmi_wwan_suspend(struct usb_interface *intf, pm_message_t message)
        if (ret < 0)
                goto err;
 
-       if (info->subdriver && info->subdriver->suspend)
+       if (intf == info->control && info->subdriver && info->subdriver->suspend)
                ret = info->subdriver->suspend(intf, message);
        if (ret < 0)
                usbnet_resume(intf);
@@ -310,13 +310,14 @@ static int qmi_wwan_resume(struct usb_interface *intf)
        struct usbnet *dev = usb_get_intfdata(intf);
        struct qmi_wwan_state *info = (void *)&dev->data;
        int ret = 0;
+       bool callsub = (intf == info->control && info->subdriver && info->subdriver->resume);
 
-       if (info->subdriver && info->subdriver->resume)
+       if (callsub)
                ret = info->subdriver->resume(intf);
        if (ret < 0)
                goto err;
        ret = usbnet_resume(intf);
-       if (ret < 0 && info->subdriver && info->subdriver->resume && info->subdriver->suspend)
+       if (ret < 0 && callsub && info->subdriver->suspend)
                info->subdriver->suspend(intf, PMSG_SUSPEND);
 err:
        return ret;
@@ -365,16 +366,20 @@ static const struct usb_device_id products[] = {
        },
 
        /* 2. Combined interface devices matching on class+protocol */
+       {       /* Huawei E367 and possibly others in "Windows mode" */
+               USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 7),
+               .driver_info        = (unsigned long)&qmi_wwan_info,
+       },
        {       /* Huawei E392, E398 and possibly others in "Windows mode" */
                USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 17),
                .driver_info        = (unsigned long)&qmi_wwan_shared,
        },
-       {       /* Pantech UML290 */
-               USB_DEVICE_AND_INTERFACE_INFO(0x106c, 0x3718, USB_CLASS_VENDOR_SPEC, 0xf0, 0xff),
+       {       /* Pantech UML290, P4200 and more */
+               USB_VENDOR_AND_INTERFACE_INFO(0x106c, USB_CLASS_VENDOR_SPEC, 0xf0, 0xff),
                .driver_info        = (unsigned long)&qmi_wwan_shared,
        },
        {       /* Pantech UML290 - newer firmware */
-               USB_DEVICE_AND_INTERFACE_INFO(0x106c, 0x3718, USB_CLASS_VENDOR_SPEC, 0xf1, 0xff),
+               USB_VENDOR_AND_INTERFACE_INFO(0x106c, USB_CLASS_VENDOR_SPEC, 0xf1, 0xff),
                .driver_info        = (unsigned long)&qmi_wwan_shared,
        },
 
@@ -382,6 +387,7 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x19d2, 0x0055, 1)},    /* ZTE (Vodafone) K3520-Z */
        {QMI_FIXED_INTF(0x19d2, 0x0063, 4)},    /* ZTE (Vodafone) K3565-Z */
        {QMI_FIXED_INTF(0x19d2, 0x0104, 4)},    /* ZTE (Vodafone) K4505-Z */
+       {QMI_FIXED_INTF(0x19d2, 0x0157, 5)},    /* ZTE MF683 */
        {QMI_FIXED_INTF(0x19d2, 0x0167, 4)},    /* ZTE MF820D */
        {QMI_FIXED_INTF(0x19d2, 0x0326, 4)},    /* ZTE MF821D */
        {QMI_FIXED_INTF(0x19d2, 0x1008, 4)},    /* ZTE (Vodafone) K3570-Z */
@@ -398,7 +404,6 @@ static const struct usb_device_id products[] = {
        /* 4. Gobi 1000 devices */
        {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)},    /* Acer Gobi Modem Device */
        {QMI_GOBI1K_DEVICE(0x03f0, 0x1f1d)},    /* HP un2400 Gobi Modem Device */
-       {QMI_GOBI1K_DEVICE(0x03f0, 0x371d)},    /* HP un2430 Mobile Broadband Module */
        {QMI_GOBI1K_DEVICE(0x04da, 0x250d)},    /* Panasonic Gobi Modem device */
        {QMI_GOBI1K_DEVICE(0x413c, 0x8172)},    /* Dell Gobi Modem device */
        {QMI_GOBI1K_DEVICE(0x1410, 0xa001)},    /* Novatel Gobi Modem device */
@@ -440,6 +445,7 @@ static const struct usb_device_id products[] = {
        {QMI_GOBI_DEVICE(0x16d8, 0x8002)},      /* CMDTech Gobi 2000 Modem device (VU922) */
        {QMI_GOBI_DEVICE(0x05c6, 0x9205)},      /* Gobi 2000 Modem device */
        {QMI_GOBI_DEVICE(0x1199, 0x9013)},      /* Sierra Wireless Gobi 3000 Modem device (MC8355) */
+       {QMI_GOBI_DEVICE(0x03f0, 0x371d)},      /* HP un2430 Mobile Broadband Module */
        {QMI_GOBI_DEVICE(0x1199, 0x9015)},      /* Sierra Wireless Gobi 3000 Modem device */
        {QMI_GOBI_DEVICE(0x1199, 0x9019)},      /* Sierra Wireless Gobi 3000 Modem device */
        {QMI_GOBI_DEVICE(0x1199, 0x901b)},      /* Sierra Wireless MC7770 */
index 7be49ea60b6d8f9353720d757f47c2c61b16fc60..8e22417fa6c11b5d41845bda1ff4c84e9d369cda 100644 (file)
@@ -656,7 +656,7 @@ static int sierra_net_get_fw_attr(struct usbnet *dev, u16 *datap)
                return -EIO;
        }
 
-       *datap = *attrdata;
+       *datap = le16_to_cpu(*attrdata);
 
        kfree(attrdata);
        return result;
index fd4b26d46fd5d2f2c8ccb68cd55844a3a5c9d336..fc9f578a1e253a781b9406b1e4a43ed2de2688d1 100644 (file)
@@ -1201,19 +1201,26 @@ deferred:
 }
 EXPORT_SYMBOL_GPL(usbnet_start_xmit);
 
-static void rx_alloc_submit(struct usbnet *dev, gfp_t flags)
+static int rx_alloc_submit(struct usbnet *dev, gfp_t flags)
 {
        struct urb      *urb;
        int             i;
+       int             ret = 0;
 
        /* don't refill the queue all at once */
        for (i = 0; i < 10 && dev->rxq.qlen < RX_QLEN(dev); i++) {
                urb = usb_alloc_urb(0, flags);
                if (urb != NULL) {
-                       if (rx_submit(dev, urb, flags) == -ENOLINK)
-                               return;
+                       ret = rx_submit(dev, urb, flags);
+                       if (ret)
+                               goto err;
+               } else {
+                       ret = -ENOMEM;
+                       goto err;
                }
        }
+err:
+       return ret;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -1257,7 +1264,8 @@ static void usbnet_bh (unsigned long param)
                int     temp = dev->rxq.qlen;
 
                if (temp < RX_QLEN(dev)) {
-                       rx_alloc_submit(dev, GFP_ATOMIC);
+                       if (rx_alloc_submit(dev, GFP_ATOMIC) == -ENOLINK)
+                               return;
                        if (temp != dev->rxq.qlen)
                                netif_dbg(dev, link, dev->net,
                                          "rxqlen %d --> %d\n",
index aaaca9aa2293dcf4fc991cb1645056ebfa3d0da1..3f575afd8cfcb03f283df0932f6fb33bb1495cca 100644 (file)
@@ -10,6 +10,7 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include <linux/module.h>
 #include <linux/bitops.h>
 #include <linux/cdev.h>
 #include <linux/dma-mapping.h>
index 2588848f4a822a1bf879ce447f9f146c5e186873..d066f2516e4753617aa55f1522427eb96f4a1115 100644 (file)
@@ -2982,6 +2982,10 @@ static u32 ath9k_hw_ar9300_get_eeprom(struct ath_hw *ah,
        case EEP_RX_MASK:
                return pBase->txrxMask & 0xf;
        case EEP_PAPRD:
+               if (AR_SREV_9462(ah))
+                       return false;
+               if (!ah->config.enable_paprd);
+                       return false;
                return !!(pBase->featureEnable & BIT(5));
        case EEP_CHAIN_MASK_REDUCE:
                return (pBase->miscConfiguration >> 0x3) & 0x1;
index 2c9f7d7ed4cc2557a86cb5256afc213a4c16f5c5..0ed3846f9cbb36e8aa1f67eab09dc9e5f219b0c8 100644 (file)
@@ -142,6 +142,7 @@ static int ar9003_paprd_setup_single_table(struct ath_hw *ah)
        };
        int training_power;
        int i, val;
+       u32 am2pm_mask = ah->paprd_ratemask;
 
        if (IS_CHAN_2GHZ(ah->curchan))
                training_power = ar9003_get_training_power_2g(ah);
@@ -158,10 +159,13 @@ static int ar9003_paprd_setup_single_table(struct ath_hw *ah)
        }
        ah->paprd_training_power = training_power;
 
+       if (AR_SREV_9330(ah))
+               am2pm_mask = 0;
+
        REG_RMW_FIELD(ah, AR_PHY_PAPRD_AM2AM, AR_PHY_PAPRD_AM2AM_MASK,
                      ah->paprd_ratemask);
        REG_RMW_FIELD(ah, AR_PHY_PAPRD_AM2PM, AR_PHY_PAPRD_AM2PM_MASK,
-                     ah->paprd_ratemask);
+                     am2pm_mask);
        REG_RMW_FIELD(ah, AR_PHY_PAPRD_HT40, AR_PHY_PAPRD_HT40_MASK,
                      ah->paprd_ratemask_ht40);
 
@@ -782,6 +786,102 @@ int ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain)
 }
 EXPORT_SYMBOL(ar9003_paprd_setup_gain_table);
 
+static bool ar9003_paprd_retrain_pa_in(struct ath_hw *ah,
+                                      struct ath9k_hw_cal_data *caldata,
+                                      int chain)
+{
+       u32 *pa_in = caldata->pa_table[chain];
+       int capdiv_offset, quick_drop_offset;
+       int capdiv2g, quick_drop;
+       int count = 0;
+       int i;
+
+       if (!AR_SREV_9485(ah) && !AR_SREV_9330(ah))
+               return false;
+
+       capdiv2g = REG_READ_FIELD(ah, AR_PHY_65NM_CH0_TXRF3,
+                                 AR_PHY_65NM_CH0_TXRF3_CAPDIV2G);
+
+       quick_drop = REG_READ_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
+                                   AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP);
+
+       if (quick_drop)
+               quick_drop -= 0x40;
+
+       for (i = 0; i < NUM_BIN + 1; i++) {
+               if (pa_in[i] == 1400)
+                       count++;
+       }
+
+       if (AR_SREV_9485(ah)) {
+               if (pa_in[23] < 800) {
+                       capdiv_offset = (int)((1000 - pa_in[23] + 75) / 150);
+                       capdiv2g += capdiv_offset;
+                       if (capdiv2g > 7) {
+                               capdiv2g = 7;
+                               if (pa_in[23] < 600) {
+                                       quick_drop++;
+                                       if (quick_drop > 0)
+                                               quick_drop = 0;
+                               }
+                       }
+               } else if (pa_in[23] == 1400) {
+                       quick_drop_offset = min_t(int, count / 3, 2);
+                       quick_drop += quick_drop_offset;
+                       capdiv2g += quick_drop_offset / 2;
+
+                       if (capdiv2g > 7)
+                               capdiv2g = 7;
+
+                       if (quick_drop > 0) {
+                               quick_drop = 0;
+                               capdiv2g -= quick_drop_offset;
+                               if (capdiv2g < 0)
+                                       capdiv2g = 0;
+                       }
+               } else {
+                       return false;
+               }
+       } else if (AR_SREV_9330(ah)) {
+               if (pa_in[23] < 1000) {
+                       capdiv_offset = (1000 - pa_in[23]) / 100;
+                       capdiv2g += capdiv_offset;
+                       if (capdiv_offset > 3) {
+                               capdiv_offset = 1;
+                               quick_drop--;
+                       }
+
+                       capdiv2g += capdiv_offset;
+                       if (capdiv2g > 6)
+                               capdiv2g = 6;
+                       if (quick_drop < -4)
+                               quick_drop = -4;
+               } else if (pa_in[23] == 1400) {
+                       if (count > 3) {
+                               quick_drop++;
+                               capdiv2g -= count / 4;
+                               if (quick_drop > -2)
+                                       quick_drop = -2;
+                       } else {
+                               capdiv2g--;
+                       }
+
+                       if (capdiv2g < 0)
+                               capdiv2g = 0;
+               } else {
+                       return false;
+               }
+       }
+
+       REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_TXRF3,
+                     AR_PHY_65NM_CH0_TXRF3_CAPDIV2G, capdiv2g);
+       REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
+                     AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP,
+                     quick_drop);
+
+       return true;
+}
+
 int ar9003_paprd_create_curve(struct ath_hw *ah,
                              struct ath9k_hw_cal_data *caldata, int chain)
 {
@@ -817,6 +917,9 @@ int ar9003_paprd_create_curve(struct ath_hw *ah,
        if (!create_pa_curve(data_L, data_U, pa_table, small_signal_gain))
                status = -2;
 
+       if (ar9003_paprd_retrain_pa_in(ah, caldata, chain))
+               status = -EINPROGRESS;
+
        REG_CLR_BIT(ah, AR_PHY_PAPRD_TRAINER_STAT1,
                    AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE);
 
index 7bfbaf065a4332c89ac5568c0cde81ed42d9313e..84d3d49568616c5452692b1660253f6ae70468cf 100644 (file)
 #define AR_PHY_AIC_CTRL_4_B0   (AR_SM_BASE + 0x4c0)
 #define AR_PHY_AIC_STAT_2_B0   (AR_SM_BASE + 0x4cc)
 
+#define AR_PHY_65NM_CH0_TXRF3       0x16048
+#define AR_PHY_65NM_CH0_TXRF3_CAPDIV2G         0x0000001e
+#define AR_PHY_65NM_CH0_TXRF3_CAPDIV2G_S       1
+
 #define AR_PHY_65NM_CH0_SYNTH4      0x1608c
 #define AR_PHY_SYNTH4_LONG_SHIFT_SELECT   (AR_SREV_9462(ah) ? 0x00000001 : 0x00000002)
 #define AR_PHY_SYNTH4_LONG_SHIFT_SELECT_S (AR_SREV_9462(ah) ? 0 : 1)
index 68b643c8943c4c2db074e4ada4eeb2fda397eccc..c8ef30127adb53da590bf4d6350798fa261dffa6 100644 (file)
@@ -1577,6 +1577,8 @@ int ath9k_init_debug(struct ath_hw *ah)
                            sc->debug.debugfs_phy, sc, &fops_tx_chainmask);
        debugfs_create_file("disable_ani", S_IRUSR | S_IWUSR,
                            sc->debug.debugfs_phy, sc, &fops_disable_ani);
+       debugfs_create_bool("paprd", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
+                           &sc->sc_ah->config.enable_paprd);
        debugfs_create_file("regidx", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
                            sc, &fops_regidx);
        debugfs_create_file("regval", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
index bacdb8fb4ef453dda48d5a9394c0030cb7866596..9f83f71742a5ecb774f95c3d563f2e0dc7d37ab7 100644 (file)
@@ -341,7 +341,8 @@ void ath9k_btcoex_stop_gen_timer(struct ath_softc *sc)
 {
        struct ath_btcoex *btcoex = &sc->btcoex;
 
-       ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer);
+       if (btcoex->hw_timer_enabled)
+               ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer);
 }
 
 u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen)
index 60b6a9daff7e21cde68fb6eda800e4ca065c62aa..4faf0a3958765bd07ae683fdd6dceb879a5adbfd 100644 (file)
@@ -463,9 +463,6 @@ static void ath9k_hw_init_config(struct ath_hw *ah)
                ah->config.spurchans[i][1] = AR_NO_SPUR;
        }
 
-       /* PAPRD needs some more work to be enabled */
-       ah->config.paprd_disable = 1;
-
        ah->config.rx_intr_mitigation = true;
        ah->config.pcieSerDesWrite = true;
 
@@ -978,9 +975,6 @@ static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah,
        else
                imr_reg |= AR_IMR_TXOK;
 
-       if (opmode == NL80211_IFTYPE_AP)
-               imr_reg |= AR_IMR_MIB;
-
        ENABLE_REGWRITE_BUFFER(ah);
 
        REG_WRITE(ah, AR_IMR, imr_reg);
@@ -1778,6 +1772,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
                /* Operating channel changed, reset channel calibration data */
                memset(caldata, 0, sizeof(*caldata));
                ath9k_init_nfcal_hist_buffer(ah, chan);
+       } else if (caldata) {
+               caldata->paprd_packet_sent = false;
        }
        ah->noise = ath9k_hw_getchan_noise(ah, chan);
 
@@ -2501,9 +2497,6 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
                pCap->rx_status_len = sizeof(struct ar9003_rxs);
                pCap->tx_desc_len = sizeof(struct ar9003_txc);
                pCap->txs_len = sizeof(struct ar9003_txs);
-               if (!ah->config.paprd_disable &&
-                   ah->eep_ops->get_eeprom(ah, EEP_PAPRD))
-                       pCap->hw_caps |= ATH9K_HW_CAP_PAPRD;
        } else {
                pCap->tx_desc_len = sizeof(struct ath_desc);
                if (AR_SREV_9280_20(ah))
index ce7332c64efb2c5b417cd40fec2428522186142a..de6968fc64f42920304fe52dd93a493c3ce679c6 100644 (file)
@@ -236,7 +236,6 @@ enum ath9k_hw_caps {
        ATH9K_HW_CAP_LDPC                       = BIT(6),
        ATH9K_HW_CAP_FASTCLOCK                  = BIT(7),
        ATH9K_HW_CAP_SGI_20                     = BIT(8),
-       ATH9K_HW_CAP_PAPRD                      = BIT(9),
        ATH9K_HW_CAP_ANT_DIV_COMB               = BIT(10),
        ATH9K_HW_CAP_2GHZ                       = BIT(11),
        ATH9K_HW_CAP_5GHZ                       = BIT(12),
@@ -287,12 +286,12 @@ struct ath9k_ops_config {
        u8 pcie_clock_req;
        u32 pcie_waen;
        u8 analog_shiftreg;
-       u8 paprd_disable;
        u32 ofdm_trig_low;
        u32 ofdm_trig_high;
        u32 cck_trig_high;
        u32 cck_trig_low;
        u32 enable_ani;
+       u32 enable_paprd;
        int serialize_regmode;
        bool rx_intr_mitigation;
        bool tx_intr_mitigation;
@@ -405,6 +404,7 @@ struct ath9k_hw_cal_data {
        int8_t iCoff;
        int8_t qCoff;
        bool rtt_done;
+       bool paprd_packet_sent;
        bool paprd_done;
        bool nfcal_pending;
        bool nfcal_interference;
index d4549e9aac5c5f1f30ace855d6c94dafe98f4d38..7b88b9c39ccddc4ef4e3aeafb53e437d0f35a0ce 100644 (file)
@@ -254,8 +254,9 @@ void ath_paprd_calibrate(struct work_struct *work)
        int chain_ok = 0;
        int chain;
        int len = 1800;
+       int ret;
 
-       if (!caldata)
+       if (!caldata || !caldata->paprd_packet_sent || caldata->paprd_done)
                return;
 
        ath9k_ps_wakeup(sc);
@@ -282,13 +283,6 @@ void ath_paprd_calibrate(struct work_struct *work)
                        continue;
 
                chain_ok = 0;
-
-               ath_dbg(common, CALIBRATE,
-                       "Sending PAPRD frame for thermal measurement on chain %d\n",
-                       chain);
-               if (!ath_paprd_send_frame(sc, skb, chain))
-                       goto fail_paprd;
-
                ar9003_paprd_setup_gain_table(ah, chain);
 
                ath_dbg(common, CALIBRATE,
@@ -302,7 +296,13 @@ void ath_paprd_calibrate(struct work_struct *work)
                        break;
                }
 
-               if (ar9003_paprd_create_curve(ah, caldata, chain)) {
+               ret = ar9003_paprd_create_curve(ah, caldata, chain);
+               if (ret == -EINPROGRESS) {
+                       ath_dbg(common, CALIBRATE,
+                               "PAPRD curve on chain %d needs to be re-trained\n",
+                               chain);
+                       break;
+               } else if (ret) {
                        ath_dbg(common, CALIBRATE,
                                "PAPRD create curve failed on chain %d\n",
                                chain);
@@ -423,7 +423,7 @@ set_timer:
                cal_interval = min(cal_interval, (u32)short_cal_interval);
 
        mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval));
-       if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_PAPRD) && ah->caldata) {
+       if (ah->eep_ops->get_eeprom(ah, EEP_PAPRD) && ah->caldata) {
                if (!ah->caldata->paprd_done)
                        ieee80211_queue_work(sc->hw, &sc->paprd_work);
                else if (!ah->paprd_table_write_done)
index 2c9da6b2ecb1b7b1141770f1240188bf2af50277..0d4155aec48d72196d5c64eee5c2517766760632 100644 (file)
@@ -2018,6 +2018,9 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
 
        ath_dbg(common, XMIT, "TX complete: skb: %p\n", skb);
 
+       if (sc->sc_ah->caldata)
+               sc->sc_ah->caldata->paprd_packet_sent = true;
+
        if (!(tx_flags & ATH_TX_ERROR))
                /* Frame was ACKed */
                tx_info->flags |= IEEE80211_TX_STAT_ACK;
index 49765d34b4e0b43ac947aa360fb58d1fff88aa90..7c4ee72f9d56006cb7e069a9472a0524bbebb9c9 100644 (file)
@@ -638,6 +638,8 @@ static int brcmf_sdio_pd_probe(struct platform_device *pdev)
 
                oobirq_entry = kzalloc(sizeof(struct brcmf_sdio_oobirq),
                                       GFP_KERNEL);
+               if (!oobirq_entry)
+                       return -ENOMEM;
                oobirq_entry->irq = res->start;
                oobirq_entry->flags = res->flags & IRQF_TRIGGER_MASK;
                list_add_tail(&oobirq_entry->list, &oobirq_lh);
index 2621dd3d7dcd9d3b6ac5ca05db587cdd94454011..6f70953f0bade06ef046a845829a887611c0a8b8 100644 (file)
@@ -764,8 +764,11 @@ static void brcmf_c_arp_offload_set(struct brcmf_pub *drvr, int arp_mode)
 {
        char iovbuf[32];
        int retcode;
+       __le32 arp_mode_le;
 
-       brcmf_c_mkiovar("arp_ol", (char *)&arp_mode, 4, iovbuf, sizeof(iovbuf));
+       arp_mode_le = cpu_to_le32(arp_mode);
+       brcmf_c_mkiovar("arp_ol", (char *)&arp_mode_le, 4, iovbuf,
+                       sizeof(iovbuf));
        retcode = brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR,
                                   iovbuf, sizeof(iovbuf));
        retcode = retcode >= 0 ? 0 : retcode;
@@ -781,8 +784,11 @@ static void brcmf_c_arp_offload_enable(struct brcmf_pub *drvr, int arp_enable)
 {
        char iovbuf[32];
        int retcode;
+       __le32 arp_enable_le;
 
-       brcmf_c_mkiovar("arpoe", (char *)&arp_enable, 4,
+       arp_enable_le = cpu_to_le32(arp_enable);
+
+       brcmf_c_mkiovar("arpoe", (char *)&arp_enable_le, 4,
                        iovbuf, sizeof(iovbuf));
        retcode = brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR,
                                   iovbuf, sizeof(iovbuf));
@@ -800,10 +806,10 @@ int brcmf_c_preinit_dcmds(struct brcmf_pub *drvr)
        char iovbuf[BRCMF_EVENTING_MASK_LEN + 12];      /*  Room for
                                 "event_msgs" + '\0' + bitvec  */
        char buf[128], *ptr;
-       u32 roaming = 1;
-       uint bcn_timeout = 3;
-       int scan_assoc_time = 40;
-       int scan_unassoc_time = 40;
+       __le32 roaming_le = cpu_to_le32(1);
+       __le32 bcn_timeout_le = cpu_to_le32(3);
+       __le32 scan_assoc_time_le = cpu_to_le32(40);
+       __le32 scan_unassoc_time_le = cpu_to_le32(40);
        int i;
        struct brcmf_bus_dcmd *cmdlst;
        struct list_head *cur, *q;
@@ -829,14 +835,14 @@ int brcmf_c_preinit_dcmds(struct brcmf_pub *drvr)
 
        /* Setup timeout if Beacons are lost and roam is off to report
                 link down */
-       brcmf_c_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf,
+       brcmf_c_mkiovar("bcn_timeout", (char *)&bcn_timeout_le, 4, iovbuf,
                    sizeof(iovbuf));
        brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, iovbuf,
                                  sizeof(iovbuf));
 
        /* Enable/Disable build-in roaming to allowed ext supplicant to take
                 of romaing */
-       brcmf_c_mkiovar("roam_off", (char *)&roaming, 4,
+       brcmf_c_mkiovar("roam_off", (char *)&roaming_le, 4,
                      iovbuf, sizeof(iovbuf));
        brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, iovbuf,
                                  sizeof(iovbuf));
@@ -848,9 +854,9 @@ int brcmf_c_preinit_dcmds(struct brcmf_pub *drvr)
                                  sizeof(iovbuf));
 
        brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_SCAN_CHANNEL_TIME,
-                        (char *)&scan_assoc_time, sizeof(scan_assoc_time));
+                (char *)&scan_assoc_time_le, sizeof(scan_assoc_time_le));
        brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_SCAN_UNASSOC_TIME,
-                        (char *)&scan_unassoc_time, sizeof(scan_unassoc_time));
+                (char *)&scan_unassoc_time_le, sizeof(scan_unassoc_time_le));
 
        /* Set and enable ARP offload feature */
        brcmf_c_arp_offload_set(drvr, BRCMF_ARPOL_MODE);
index a299d42da8e74a358939b8fa5da8a32a01fd312b..58f89fa9c9f8a218ed29cfb93ad253c41471c648 100644 (file)
@@ -519,7 +519,7 @@ static void brcmf_usb_tx_complete(struct urb *urb)
        else
                devinfo->bus_pub.bus->dstats.tx_errors++;
 
-       dev_kfree_skb(req->skb);
+       brcmu_pkt_buf_free_skb(req->skb);
        req->skb = NULL;
        brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req);
 
@@ -540,7 +540,7 @@ static void brcmf_usb_rx_complete(struct urb *urb)
                devinfo->bus_pub.bus->dstats.rx_packets++;
        } else {
                devinfo->bus_pub.bus->dstats.rx_errors++;
-               dev_kfree_skb(skb);
+               brcmu_pkt_buf_free_skb(skb);
                brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req);
                return;
        }
@@ -550,13 +550,15 @@ static void brcmf_usb_rx_complete(struct urb *urb)
                if (brcmf_proto_hdrpull(devinfo->dev, &ifidx, skb) != 0) {
                        brcmf_dbg(ERROR, "rx protocol error\n");
                        brcmu_pkt_buf_free_skb(skb);
+                       brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req);
                        devinfo->bus_pub.bus->dstats.rx_errors++;
                } else {
                        brcmf_rx_packet(devinfo->dev, ifidx, skb);
                        brcmf_usb_rx_refill(devinfo, req);
                }
        } else {
-               dev_kfree_skb(skb);
+               brcmu_pkt_buf_free_skb(skb);
+               brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req);
        }
        return;
 
@@ -581,14 +583,13 @@ static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo,
        usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->rx_pipe,
                          skb->data, skb_tailroom(skb), brcmf_usb_rx_complete,
                          req);
-       req->urb->transfer_flags |= URB_ZERO_PACKET;
        req->devinfo = devinfo;
+       brcmf_usb_enq(devinfo, &devinfo->rx_postq, req);
 
        ret = usb_submit_urb(req->urb, GFP_ATOMIC);
-       if (ret == 0) {
-               brcmf_usb_enq(devinfo, &devinfo->rx_postq, req);
-       } else {
-               dev_kfree_skb(req->skb);
+       if (ret) {
+               brcmf_usb_del_fromq(devinfo, req);
+               brcmu_pkt_buf_free_skb(req->skb);
                req->skb = NULL;
                brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req);
        }
@@ -683,23 +684,22 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
 
        req = brcmf_usb_deq(devinfo, &devinfo->tx_freeq);
        if (!req) {
+               brcmu_pkt_buf_free_skb(skb);
                brcmf_dbg(ERROR, "no req to send\n");
                return -ENOMEM;
        }
-       if (!req->urb) {
-               brcmf_dbg(ERROR, "no urb for req %p\n", req);
-               return -ENOBUFS;
-       }
 
        req->skb = skb;
        req->devinfo = devinfo;
        usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->tx_pipe,
                          skb->data, skb->len, brcmf_usb_tx_complete, req);
        req->urb->transfer_flags |= URB_ZERO_PACKET;
+       brcmf_usb_enq(devinfo, &devinfo->tx_postq, req);
        ret = usb_submit_urb(req->urb, GFP_ATOMIC);
-       if (!ret) {
-               brcmf_usb_enq(devinfo, &devinfo->tx_postq, req);
-       } else {
+       if (ret) {
+               brcmf_dbg(ERROR, "brcmf_usb_tx usb_submit_urb FAILED\n");
+               brcmf_usb_del_fromq(devinfo, req);
+               brcmu_pkt_buf_free_skb(req->skb);
                req->skb = NULL;
                brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req);
        }
index 28c5fbb4af267b59ef1c8bab67f2d4ac2d7e9485..50b5553b6964b95dffecf40f4d28fdb91796efc8 100644 (file)
@@ -500,8 +500,10 @@ static void wl_iscan_prep(struct brcmf_scan_params_le *params_le,
        params_le->active_time = cpu_to_le32(-1);
        params_le->passive_time = cpu_to_le32(-1);
        params_le->home_time = cpu_to_le32(-1);
-       if (ssid && ssid->SSID_len)
-               memcpy(&params_le->ssid_le, ssid, sizeof(struct brcmf_ssid));
+       if (ssid && ssid->SSID_len) {
+               params_le->ssid_le.SSID_len = cpu_to_le32(ssid->SSID_len);
+               memcpy(&params_le->ssid_le.SSID, ssid->SSID, ssid->SSID_len);
+       }
 }
 
 static s32
@@ -1876,16 +1878,17 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
        }
 
        if (test_bit(WL_STATUS_CONNECTED, &cfg_priv->status)) {
-               scb_val.val = cpu_to_le32(0);
+               memset(&scb_val, 0, sizeof(scb_val));
                err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_RSSI, &scb_val,
                                      sizeof(struct brcmf_scb_val_le));
-               if (err)
+               if (err) {
                        WL_ERR("Could not get rssi (%d)\n", err);
-
-               rssi = le32_to_cpu(scb_val.val);
-               sinfo->filled |= STATION_INFO_SIGNAL;
-               sinfo->signal = rssi;
-               WL_CONN("RSSI %d dBm\n", rssi);
+               } else {
+                       rssi = le32_to_cpu(scb_val.val);
+                       sinfo->filled |= STATION_INFO_SIGNAL;
+                       sinfo->signal = rssi;
+                       WL_CONN("RSSI %d dBm\n", rssi);
+               }
        }
 
 done:
index 7ed7d7577024628d228a78f6e94433c810295bc2..64a48f06d68bfa124c1687bc1fa81a799fb5be82 100644 (file)
@@ -77,7 +77,7 @@
                                         NL80211_RRF_NO_IBSS)
 
 static const struct ieee80211_regdomain brcms_regdom_x2 = {
-       .n_reg_rules = 7,
+       .n_reg_rules = 6,
        .alpha2 = "X2",
        .reg_rules = {
                BRCM_2GHZ_2412_2462,
index e970897f6ab52370a632a64a62cc10bb3c39a3c6..4cb234349fbfacc305b1565ed5f1a30ba16aa108 100644 (file)
@@ -1326,6 +1326,11 @@ static int if_sdio_suspend(struct device *dev)
 
        mmc_pm_flag_t flags = sdio_get_host_pm_caps(func);
 
+       /* If we're powered off anyway, just let the mmc layer remove the
+        * card. */
+       if (!lbs_iface_active(card->priv))
+               return -ENOSYS;
+
        dev_info(dev, "%s: suspend: PM flags = 0x%x\n",
                 sdio_func_id(func), flags);
 
index c68adec3cc8b6522678c98582c7781b885193849..565527aee0ea3f73caa832f336c0ded06a3b22d9 100644 (file)
@@ -170,7 +170,20 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,
        cmd_code = le16_to_cpu(host_cmd->command);
        cmd_size = le16_to_cpu(host_cmd->size);
 
-       skb_trim(cmd_node->cmd_skb, cmd_size);
+       /* Adjust skb length */
+       if (cmd_node->cmd_skb->len > cmd_size)
+               /*
+                * cmd_size is less than sizeof(struct host_cmd_ds_command).
+                * Trim off the unused portion.
+                */
+               skb_trim(cmd_node->cmd_skb, cmd_size);
+       else if (cmd_node->cmd_skb->len < cmd_size)
+               /*
+                * cmd_size is larger than sizeof(struct host_cmd_ds_command)
+                * because we have appended custom IE TLV. Increase skb length
+                * accordingly.
+                */
+               skb_put(cmd_node->cmd_skb, cmd_size - cmd_node->cmd_skb->len);
 
        do_gettimeofday(&tstamp);
        dev_dbg(adapter->dev, "cmd: DNLD_CMD: (%lu.%lu): %#x, act %#x, len %d,"
index 8b9dbd76a25255634ad79338223739c939429d26..64328af496f598bb3280784b6d2adfd25ec5cc70 100644 (file)
@@ -1611,6 +1611,7 @@ static int rt2400pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
 static int rt2400pci_probe_hw(struct rt2x00_dev *rt2x00dev)
 {
        int retval;
+       u32 reg;
 
        /*
         * Allocate eeprom data.
@@ -1623,6 +1624,14 @@ static int rt2400pci_probe_hw(struct rt2x00_dev *rt2x00dev)
        if (retval)
                return retval;
 
+       /*
+        * Enable rfkill polling by setting GPIO direction of the
+        * rfkill switch GPIO pin correctly.
+        */
+       rt2x00pci_register_read(rt2x00dev, GPIOCSR, &reg);
+       rt2x00_set_field32(&reg, GPIOCSR_BIT8, 1);
+       rt2x00pci_register_write(rt2x00dev, GPIOCSR, reg);
+
        /*
         * Initialize hw specifications.
         */
index d3a4a68cc439b22faea7f6e9d4899e9313e5a7e2..7564ae992b735179b15e24a3d616c5a71acb1aeb 100644 (file)
 #define GPIOCSR_BIT5                   FIELD32(0x00000020)
 #define GPIOCSR_BIT6                   FIELD32(0x00000040)
 #define GPIOCSR_BIT7                   FIELD32(0x00000080)
+#define GPIOCSR_BIT8                   FIELD32(0x00000100)
 
 /*
  * BBPPCSR: BBP Pin control register.
index d2cf8a4bc8b52fd985f4720df6bc20a9269069c3..3de0406735f6b7347b46cdf2305e413aaa17256d 100644 (file)
@@ -1929,6 +1929,7 @@ static int rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
 static int rt2500pci_probe_hw(struct rt2x00_dev *rt2x00dev)
 {
        int retval;
+       u32 reg;
 
        /*
         * Allocate eeprom data.
@@ -1941,6 +1942,14 @@ static int rt2500pci_probe_hw(struct rt2x00_dev *rt2x00dev)
        if (retval)
                return retval;
 
+       /*
+        * Enable rfkill polling by setting GPIO direction of the
+        * rfkill switch GPIO pin correctly.
+        */
+       rt2x00pci_register_read(rt2x00dev, GPIOCSR, &reg);
+       rt2x00_set_field32(&reg, GPIOCSR_DIR0, 1);
+       rt2x00pci_register_write(rt2x00dev, GPIOCSR, reg);
+
        /*
         * Initialize hw specifications.
         */
index 3aae36bb0a9e9f99cf89705bc2dca5022109824a..89fee311d8fda5ad07ae5ecd50fae567232aa35d 100644 (file)
@@ -283,7 +283,7 @@ static int rt2500usb_rfkill_poll(struct rt2x00_dev *rt2x00dev)
        u16 reg;
 
        rt2500usb_register_read(rt2x00dev, MAC_CSR19, &reg);
-       return rt2x00_get_field32(reg, MAC_CSR19_BIT7);
+       return rt2x00_get_field16(reg, MAC_CSR19_BIT7);
 }
 
 #ifdef CONFIG_RT2X00_LIB_LEDS
@@ -1768,6 +1768,7 @@ static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
 static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev)
 {
        int retval;
+       u16 reg;
 
        /*
         * Allocate eeprom data.
@@ -1780,6 +1781,14 @@ static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev)
        if (retval)
                return retval;
 
+       /*
+        * Enable rfkill polling by setting GPIO direction of the
+        * rfkill switch GPIO pin correctly.
+        */
+       rt2500usb_register_read(rt2x00dev, MAC_CSR19, &reg);
+       rt2x00_set_field16(&reg, MAC_CSR19_BIT8, 0);
+       rt2500usb_register_write(rt2x00dev, MAC_CSR19, reg);
+
        /*
         * Initialize hw specifications.
         */
index b493306a7eede0888af2cccef613c6612b579514..196bd5103e4f5450483ce1e60449021bf6eafd2c 100644 (file)
  * MAC_CSR19: GPIO control register.
  */
 #define MAC_CSR19                      0x0426
-#define MAC_CSR19_BIT0                 FIELD32(0x0001)
-#define MAC_CSR19_BIT1                 FIELD32(0x0002)
-#define MAC_CSR19_BIT2                 FIELD32(0x0004)
-#define MAC_CSR19_BIT3                 FIELD32(0x0008)
-#define MAC_CSR19_BIT4                 FIELD32(0x0010)
-#define MAC_CSR19_BIT5                 FIELD32(0x0020)
-#define MAC_CSR19_BIT6                 FIELD32(0x0040)
-#define MAC_CSR19_BIT7                 FIELD32(0x0080)
+#define MAC_CSR19_BIT0                 FIELD16(0x0001)
+#define MAC_CSR19_BIT1                 FIELD16(0x0002)
+#define MAC_CSR19_BIT2                 FIELD16(0x0004)
+#define MAC_CSR19_BIT3                 FIELD16(0x0008)
+#define MAC_CSR19_BIT4                 FIELD16(0x0010)
+#define MAC_CSR19_BIT5                 FIELD16(0x0020)
+#define MAC_CSR19_BIT6                 FIELD16(0x0040)
+#define MAC_CSR19_BIT7                 FIELD16(0x0080)
+#define MAC_CSR19_BIT8                 FIELD16(0x0100)
 
 /*
  * MAC_CSR20: LED control register.
index cb8c2aca54e4dfdac4a7e223a8070f4e4543399e..b93516d832fb5603e4bb3d287a4770c0c8de06ad 100644 (file)
@@ -4089,6 +4089,7 @@ static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
                rt2800_register_write(rt2x00dev, LDO_CFG0, reg);
                msleep(1);
                rt2800_register_read(rt2x00dev, LDO_CFG0, &reg);
+               rt2x00_set_field32(&reg, LDO_CFG0_LDO_CORE_VLEVEL, 0);
                rt2x00_set_field32(&reg, LDO_CFG0_BGSEL, 1);
                rt2800_register_write(rt2x00dev, LDO_CFG0, reg);
        }
index 98aa426a35649828e3c70c0f2bab0acf24e49381..4765bbd654cdcfeea617c84f9c755db05409600d 100644 (file)
@@ -983,6 +983,7 @@ static int rt2800pci_validate_eeprom(struct rt2x00_dev *rt2x00dev)
 static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev)
 {
        int retval;
+       u32 reg;
 
        /*
         * Allocate eeprom data.
@@ -995,6 +996,14 @@ static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev)
        if (retval)
                return retval;
 
+       /*
+        * Enable rfkill polling by setting GPIO direction of the
+        * rfkill switch GPIO pin correctly.
+        */
+       rt2x00pci_register_read(rt2x00dev, GPIO_CTRL_CFG, &reg);
+       rt2x00_set_field32(&reg, GPIO_CTRL_CFG_GPIOD_BIT2, 1);
+       rt2x00pci_register_write(rt2x00dev, GPIO_CTRL_CFG, reg);
+
        /*
         * Initialize hw specifications.
         */
index 6cf336595e2544a5703612e5156af0559b5cc8ab..6b4226b716187ea037d2a1c84e012806649e8816 100644 (file)
@@ -667,8 +667,16 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry,
        skb_pull(entry->skb, RXINFO_DESC_SIZE);
 
        /*
-        * FIXME: we need to check for rx_pkt_len validity
+        * Check for rx_pkt_len validity. Return if invalid, leaving
+        * rxdesc->size zeroed out by the upper level.
         */
+       if (unlikely(rx_pkt_len == 0 ||
+                       rx_pkt_len > entry->queue->data_size)) {
+               ERROR(entry->queue->rt2x00dev,
+                       "Bad frame size %d, forcing to 0\n", rx_pkt_len);
+               return;
+       }
+
        rxd = (__le32 *)(entry->skb->data + rx_pkt_len);
 
        /*
@@ -736,6 +744,7 @@ static int rt2800usb_validate_eeprom(struct rt2x00_dev *rt2x00dev)
 static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
 {
        int retval;
+       u32 reg;
 
        /*
         * Allocate eeprom data.
@@ -748,6 +757,14 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
        if (retval)
                return retval;
 
+       /*
+        * Enable rfkill polling by setting GPIO direction of the
+        * rfkill switch GPIO pin correctly.
+        */
+       rt2x00usb_register_read(rt2x00dev, GPIO_CTRL_CFG, &reg);
+       rt2x00_set_field32(&reg, GPIO_CTRL_CFG_GPIOD_BIT2, 1);
+       rt2x00usb_register_write(rt2x00dev, GPIO_CTRL_CFG, reg);
+
        /*
         * Initialize hw specifications.
         */
@@ -1157,6 +1174,8 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x1690, 0x0744) },
        { USB_DEVICE(0x1690, 0x0761) },
        { USB_DEVICE(0x1690, 0x0764) },
+       /* ASUS */
+       { USB_DEVICE(0x0b05, 0x179d) },
        /* Cisco */
        { USB_DEVICE(0x167b, 0x4001) },
        /* EnGenius */
@@ -1222,7 +1241,6 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x0b05, 0x1760) },
        { USB_DEVICE(0x0b05, 0x1761) },
        { USB_DEVICE(0x0b05, 0x1790) },
-       { USB_DEVICE(0x0b05, 0x179d) },
        /* AzureWave */
        { USB_DEVICE(0x13d3, 0x3262) },
        { USB_DEVICE(0x13d3, 0x3284) },
index a6b88bd4a1a57d7f904c75faa62c95ea219be029..3f07e36f462b384565884580170faf7c3be2f25f 100644 (file)
@@ -629,7 +629,7 @@ void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp)
         */
        if (unlikely(rxdesc.size == 0 ||
                     rxdesc.size > entry->queue->data_size)) {
-               WARNING(rt2x00dev, "Wrong frame size %d max %d.\n",
+               ERROR(rt2x00dev, "Wrong frame size %d max %d.\n",
                        rxdesc.size, entry->queue->data_size);
                dev_kfree_skb(entry->skb);
                goto renew_skb;
index 3f7bc5cadf9a8a7a433688e8d480d76de3c1ab82..b8ec96163922a11711a3d6800b9556052c1386fc 100644 (file)
@@ -2832,6 +2832,7 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
 static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev)
 {
        int retval;
+       u32 reg;
 
        /*
         * Disable power saving.
@@ -2849,6 +2850,14 @@ static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev)
        if (retval)
                return retval;
 
+       /*
+        * Enable rfkill polling by setting GPIO direction of the
+        * rfkill switch GPIO pin correctly.
+        */
+       rt2x00pci_register_read(rt2x00dev, MAC_CSR13, &reg);
+       rt2x00_set_field32(&reg, MAC_CSR13_BIT13, 1);
+       rt2x00pci_register_write(rt2x00dev, MAC_CSR13, reg);
+
        /*
         * Initialize hw specifications.
         */
index e3cd6db76b0e561d481873d22c5637d3d1732446..8f3da5a56766f4c3293825c2d649078f4cb5455f 100644 (file)
@@ -372,6 +372,7 @@ struct hw_pairwise_ta_entry {
 #define MAC_CSR13_BIT10                        FIELD32(0x00000400)
 #define MAC_CSR13_BIT11                        FIELD32(0x00000800)
 #define MAC_CSR13_BIT12                        FIELD32(0x00001000)
+#define MAC_CSR13_BIT13                        FIELD32(0x00002000)
 
 /*
  * MAC_CSR14: LED control register.
index ba6e434b859d66506c26c5f05b7d5a569403c9d9..248436c13ce04ae1f79312c6cbb1e16d8a4b5fc9 100644 (file)
@@ -2177,6 +2177,7 @@ static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
 static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev)
 {
        int retval;
+       u32 reg;
 
        /*
         * Allocate eeprom data.
@@ -2189,6 +2190,14 @@ static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev)
        if (retval)
                return retval;
 
+       /*
+        * Enable rfkill polling by setting GPIO direction of the
+        * rfkill switch GPIO pin correctly.
+        */
+       rt2x00usb_register_read(rt2x00dev, MAC_CSR13, &reg);
+       rt2x00_set_field32(&reg, MAC_CSR13_BIT15, 0);
+       rt2x00usb_register_write(rt2x00dev, MAC_CSR13, reg);
+
        /*
         * Initialize hw specifications.
         */
index 9f6b470414d33a687c8795b380add18cd8efaeb4..df1cc116b83be891ee2ff20702260f5949d3d983 100644 (file)
@@ -282,6 +282,9 @@ struct hw_pairwise_ta_entry {
 #define MAC_CSR13_BIT10                        FIELD32(0x00000400)
 #define MAC_CSR13_BIT11                        FIELD32(0x00000800)
 #define MAC_CSR13_BIT12                        FIELD32(0x00001000)
+#define MAC_CSR13_BIT13                        FIELD32(0x00002000)
+#define MAC_CSR13_BIT14                        FIELD32(0x00004000)
+#define MAC_CSR13_BIT15                        FIELD32(0x00008000)
 
 /*
  * MAC_CSR14: LED control register.
index 04c3aef8a4f62e178fbac155a956c64c529b97c8..2925094b2d912529fb32dc5202487d1def6d9910 100644 (file)
 
 #define CHIP_VER_B                     BIT(4)
 #define CHIP_92C_BITMASK               BIT(0)
+#define CHIP_UNKNOWN                   BIT(7)
 #define CHIP_92C_1T2R                  0x03
 #define CHIP_92C                       0x01
 #define CHIP_88C                       0x00
index bd0da7ef290b3cf5cafafe4d56ec4c3d7cf33611..dd4bb0950a575ddd37c31489f10d2bacdaaab940 100644 (file)
@@ -994,8 +994,16 @@ static enum version_8192c _rtl92ce_read_chip_version(struct ieee80211_hw *hw)
                version = (value32 & TYPE_ID) ? VERSION_A_CHIP_92C :
                           VERSION_A_CHIP_88C;
        } else {
-               version = (value32 & TYPE_ID) ? VERSION_B_CHIP_92C :
-                          VERSION_B_CHIP_88C;
+               version = (enum version_8192c) (CHIP_VER_B |
+                               ((value32 & TYPE_ID) ? CHIP_92C_BITMASK : 0) |
+                               ((value32 & VENDOR_ID) ? CHIP_VENDOR_UMC : 0));
+               if ((!IS_CHIP_VENDOR_UMC(version)) && (value32 &
+                    CHIP_VER_RTL_MASK)) {
+                       version = (enum version_8192c)(version |
+                                  ((((value32 & CHIP_VER_RTL_MASK) == BIT(12))
+                                  ? CHIP_VENDOR_UMC_B_CUT : CHIP_UNKNOWN) |
+                                  CHIP_VENDOR_UMC));
+               }
        }
 
        switch (version) {
index 3aa927f8b9b93bfbf0b12d9d0194966cd24900c8..7d8f96405f42068bafa9c08d7ec6a382f2e2c7f0 100644 (file)
@@ -162,10 +162,12 @@ int rtl92c_init_sw_vars(struct ieee80211_hw *hw)
 
        /* request fw */
        if (IS_VENDOR_UMC_A_CUT(rtlhal->version) &&
-           !IS_92C_SERIAL(rtlhal->version))
+           !IS_92C_SERIAL(rtlhal->version)) {
                rtlpriv->cfg->fw_name = "rtlwifi/rtl8192cfwU.bin";
-       else if (IS_81xxC_VENDOR_UMC_B_CUT(rtlhal->version))
+       } else if (IS_81xxC_VENDOR_UMC_B_CUT(rtlhal->version)) {
                rtlpriv->cfg->fw_name = "rtlwifi/rtl8192cfwU_B.bin";
+               pr_info("****** This B_CUT device may not work with kernels 3.6 and earlier\n");
+       }
 
        rtlpriv->max_fw_size = 0x4000;
        pr_info("Using firmware %s\n", rtlpriv->cfg->fw_name);
index 3782e1cd3697020219d34b81aa039a8c767be486..934d861a32359bb553ce17966d152f089b31c04d 100644 (file)
@@ -2196,10 +2196,8 @@ static int __init acer_wmi_init(void)
                interface->capability &= ~ACER_CAP_BRIGHTNESS;
                pr_info("Brightness must be controlled by acpi video driver\n");
        } else {
-#ifdef CONFIG_ACPI_VIDEO
                pr_info("Disabling ACPI video driver\n");
                acpi_video_unregister();
-#endif
        }
 
        if (wmi_has_guid(WMID_GUID3)) {
index dfb1a92ce9497cb49e913fa23d8f8819300243e9..db8f63841b4265922b5a630c020e00cb4f490ad3 100644 (file)
@@ -101,7 +101,7 @@ static void gmux_pio_write32(struct apple_gmux_data *gmux_data, int port,
 
        for (i = 0; i < 4; i++) {
                tmpval = (val >> (i * 8)) & 0xff;
-               outb(tmpval, port + i);
+               outb(tmpval, gmux_data->iostart + port + i);
        }
 }
 
@@ -142,8 +142,9 @@ static u8 gmux_index_read8(struct apple_gmux_data *gmux_data, int port)
        u8 val;
 
        mutex_lock(&gmux_data->index_lock);
-       outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ);
        gmux_index_wait_ready(gmux_data);
+       outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ);
+       gmux_index_wait_complete(gmux_data);
        val = inb(gmux_data->iostart + GMUX_PORT_VALUE);
        mutex_unlock(&gmux_data->index_lock);
 
@@ -166,8 +167,9 @@ static u32 gmux_index_read32(struct apple_gmux_data *gmux_data, int port)
        u32 val;
 
        mutex_lock(&gmux_data->index_lock);
-       outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ);
        gmux_index_wait_ready(gmux_data);
+       outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ);
+       gmux_index_wait_complete(gmux_data);
        val = inl(gmux_data->iostart + GMUX_PORT_VALUE);
        mutex_unlock(&gmux_data->index_lock);
 
@@ -461,18 +463,22 @@ static int __devinit gmux_probe(struct pnp_dev *pnp,
        ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE);
        if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) {
                if (gmux_is_indexed(gmux_data)) {
+                       u32 version;
                        mutex_init(&gmux_data->index_lock);
                        gmux_data->indexed = true;
+                       version = gmux_read32(gmux_data,
+                               GMUX_PORT_VERSION_MAJOR);
+                       ver_major = (version >> 24) & 0xff;
+                       ver_minor = (version >> 16) & 0xff;
+                       ver_release = (version >> 8) & 0xff;
                } else {
                        pr_info("gmux device not present\n");
                        ret = -ENODEV;
                        goto err_release;
                }
-               pr_info("Found indexed gmux\n");
-       } else {
-               pr_info("Found gmux version %d.%d.%d\n", ver_major, ver_minor,
-                       ver_release);
        }
+       pr_info("Found gmux version %d.%d.%d [%s]\n", ver_major, ver_minor,
+               ver_release, (gmux_data->indexed ? "indexed" : "classic"));
 
        memset(&props, 0, sizeof(props));
        props.type = BACKLIGHT_PLATFORM;
@@ -505,9 +511,7 @@ static int __devinit gmux_probe(struct pnp_dev *pnp,
         * Disable the other backlight choices.
         */
        acpi_video_dmi_promote_vendor();
-#if defined (CONFIG_ACPI_VIDEO) || defined (CONFIG_ACPI_VIDEO_MODULE)
        acpi_video_unregister();
-#endif
        apple_bl_unregister();
 
        gmux_data->power_state = VGA_SWITCHEROO_ON;
@@ -593,9 +597,7 @@ static void __devexit gmux_remove(struct pnp_dev *pnp)
        kfree(gmux_data);
 
        acpi_video_dmi_demote_vendor();
-#if defined (CONFIG_ACPI_VIDEO) || defined (CONFIG_ACPI_VIDEO_MODULE)
        acpi_video_register();
-#endif
        apple_bl_register();
 }
 
index e38f91be0b10964c12f6a9b8eb4347908fc54d94..4b568df56643f846de78a3228582342fb3a89c67 100644 (file)
@@ -85,7 +85,7 @@ static char *wled_type = "unknown";
 static char *bled_type = "unknown";
 
 module_param(wled_type, charp, 0444);
-MODULE_PARM_DESC(wlan_status, "Set the wled type on boot "
+MODULE_PARM_DESC(wled_type, "Set the wled type on boot "
                 "(unknown, led or rfkill). "
                 "default is unknown");
 
@@ -863,9 +863,9 @@ static ssize_t show_infos(struct device *dev,
         * The significance of others is yet to be found.
         * If we don't find the method, we assume the device are present.
         */
-       rv = acpi_evaluate_integer(asus->handle, "HRWS", NULL, &temp);
+       rv = acpi_evaluate_integer(asus->handle, "HWRS", NULL, &temp);
        if (!ACPI_FAILURE(rv))
-               len += sprintf(page + len, "HRWS value         : %#x\n",
+               len += sprintf(page + len, "HWRS value         : %#x\n",
                               (uint) temp);
        /*
         * Another value for userspace: the ASYM method returns 0x02 for
@@ -1751,9 +1751,9 @@ static int asus_laptop_get_info(struct asus_laptop *asus)
         * The significance of others is yet to be found.
         */
        status =
-           acpi_evaluate_integer(asus->handle, "HRWS", NULL, &hwrs_result);
+           acpi_evaluate_integer(asus->handle, "HWRS", NULL, &hwrs_result);
        if (!ACPI_FAILURE(status))
-               pr_notice("  HRWS returned %x", (int)hwrs_result);
+               pr_notice("  HWRS returned %x", (int)hwrs_result);
 
        if (!acpi_check_handle(asus->handle, METHOD_WL_STATUS, NULL))
                asus->have_rsts = true;
index 2eb9fe8e8efd038c7bb1d25ad4fbde2961ca8e1a..c0e9ff489b2417f2469e7abc7d2289e4b971c8a2 100644 (file)
@@ -47,9 +47,7 @@
 #include <linux/thermal.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
-#ifdef CONFIG_ACPI_VIDEO
 #include <acpi/video.h>
-#endif
 
 #include "asus-wmi.h"
 
@@ -1704,10 +1702,8 @@ static int asus_wmi_add(struct platform_device *pdev)
        if (asus->driver->quirks->wmi_backlight_power)
                acpi_video_dmi_promote_vendor();
        if (!acpi_video_backlight_support()) {
-#ifdef CONFIG_ACPI_VIDEO
                pr_info("Disabling ACPI video driver\n");
                acpi_video_unregister();
-#endif
                err = asus_wmi_backlight_init(asus);
                if (err && err != -ENODEV)
                        goto fail_backlight;
index dab91b48d22cf5fbb955efd7ecb5fc0f6fa318c1..5ca264179f4e32a758102b0a05f865164250534b 100644 (file)
@@ -610,12 +610,12 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
 
                if (!bus) {
                        pr_warn("Unable to find PCI bus 1?\n");
-                       goto out_unlock;
+                       goto out_put_dev;
                }
 
                if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
                        pr_err("Unable to read PCI config space?\n");
-                       goto out_unlock;
+                       goto out_put_dev;
                }
 
                absent = (l == 0xffffffff);
@@ -627,7 +627,7 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
                                absent ? "absent" : "present");
                        pr_warn("skipped wireless hotplug as probably "
                                "inappropriate for this model\n");
-                       goto out_unlock;
+                       goto out_put_dev;
                }
 
                if (!blocked) {
@@ -635,7 +635,7 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
                        if (dev) {
                                /* Device already present */
                                pci_dev_put(dev);
-                               goto out_unlock;
+                               goto out_put_dev;
                        }
                        dev = pci_scan_single_device(bus, 0);
                        if (dev) {
@@ -650,6 +650,8 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
                                pci_dev_put(dev);
                        }
                }
+out_put_dev:
+               pci_dev_put(port);
        }
 
 out_unlock:
index c1ca7bcebb66b52bfb033fc24cc0bc2856bd313d..dd90d15f52101e24296b30523d68056e01bac7df 100644 (file)
@@ -26,9 +26,7 @@
 #include <linux/seq_file.h>
 #include <linux/debugfs.h>
 #include <linux/ctype.h>
-#ifdef CONFIG_ACPI_VIDEO
 #include <acpi/video.h>
-#endif
 
 /*
  * This driver is needed because a number of Samsung laptops do not hook
@@ -1558,9 +1556,7 @@ static int __init samsung_init(void)
                samsung->handle_backlight = false;
        } else if (samsung->quirks->broken_acpi_video) {
                pr_info("Disabling ACPI video driver\n");
-#ifdef CONFIG_ACPI_VIDEO
                acpi_video_unregister();
-#endif
        }
 #endif
 
index 80e377949314ba37b3f00f09655290ed062349ed..52daaa816e53691792b6071955dbe680c4721b91 100644 (file)
@@ -545,7 +545,7 @@ TPACPI_HANDLE(hkey, ec, "\\_SB.HKEY",       /* 600e/x, 770e, 770x */
  */
 
 static int acpi_evalf(acpi_handle handle,
-                     void *res, char *method, char *fmt, ...)
+                     int *res, char *method, char *fmt, ...)
 {
        char *fmt0 = fmt;
        struct acpi_object_list params;
@@ -606,7 +606,7 @@ static int acpi_evalf(acpi_handle handle,
                success = (status == AE_OK &&
                           out_obj.type == ACPI_TYPE_INTEGER);
                if (success && res)
-                       *(int *)res = out_obj.integer.value;
+                       *res = out_obj.integer.value;
                break;
        case 'v':               /* void */
                success = status == AE_OK;
@@ -7386,17 +7386,18 @@ static int fan_get_status(u8 *status)
         * Add TPACPI_FAN_RD_ACPI_FANS ? */
 
        switch (fan_status_access_mode) {
-       case TPACPI_FAN_RD_ACPI_GFAN:
+       case TPACPI_FAN_RD_ACPI_GFAN: {
                /* 570, 600e/x, 770e, 770x */
+               int res;
 
-               if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d")))
+               if (unlikely(!acpi_evalf(gfan_handle, &res, NULL, "d")))
                        return -EIO;
 
                if (likely(status))
-                       *status = s & 0x07;
+                       *status = res & 0x07;
 
                break;
-
+       }
        case TPACPI_FAN_RD_TPEC:
                /* all except 570, 600e/x, 770e, 770x */
                if (unlikely(!acpi_ec_read(fan_status_offset, &s)))
index 0b66d0f259224f9e7ca679a2759a473708cd3be6..4b6688909fee076c5573e086b972b7b3cf5ebffc 100644 (file)
@@ -100,6 +100,13 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
                writel(period_cycles, pc->mmio_base + CAP3);
        }
 
+       if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
+               reg_val = readw(pc->mmio_base + ECCTL2);
+               /* Disable APWM mode to put APWM output Low */
+               reg_val &= ~ECCTL2_APWM_MODE;
+               writew(reg_val, pc->mmio_base + ECCTL2);
+       }
+
        pm_runtime_put_sync(pc->chip.dev);
        return 0;
 }
index c3756d1be19496fdf3dd163787b7e289e9ad70bd..b1996bcd5b788fd8923ef646b828d71dbc769265 100644 (file)
@@ -104,6 +104,7 @@ struct ehrpwm_pwm_chip {
        struct pwm_chip chip;
        unsigned int    clk_rate;
        void __iomem    *mmio_base;
+       unsigned long period_cycles[NUM_PWM_CHANNEL];
 };
 
 static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip)
@@ -210,6 +211,7 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
        unsigned long long c;
        unsigned long period_cycles, duty_cycles;
        unsigned short ps_divval, tb_divval;
+       int i;
 
        if (period_ns < 0 || duty_ns < 0 || period_ns > NSEC_PER_SEC)
                return -ERANGE;
@@ -229,6 +231,28 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
                duty_cycles = (unsigned long)c;
        }
 
+       /*
+        * Period values should be same for multiple PWM channels as IP uses
+        * same period register for multiple channels.
+        */
+       for (i = 0; i < NUM_PWM_CHANNEL; i++) {
+               if (pc->period_cycles[i] &&
+                               (pc->period_cycles[i] != period_cycles)) {
+                       /*
+                        * Allow channel to reconfigure period if no other
+                        * channels being configured.
+                        */
+                       if (i == pwm->hwpwm)
+                               continue;
+
+                       dev_err(chip->dev, "Period value conflicts with channel %d\n",
+                                       i);
+                       return -EINVAL;
+               }
+       }
+
+       pc->period_cycles[pwm->hwpwm] = period_cycles;
+
        /* Configure clock prescaler to support Low frequency PWM wave */
        if (set_prescale_div(period_cycles/PERIOD_MAX, &ps_divval,
                                &tb_divval)) {
@@ -320,10 +344,15 @@ static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 
 static void ehrpwm_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
 {
+       struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
+
        if (test_bit(PWMF_ENABLED, &pwm->flags)) {
                dev_warn(chip->dev, "Removing PWM device without disabling\n");
                pm_runtime_put_sync(chip->dev);
        }
+
+       /* set period value to zero on free */
+       pc->period_cycles[pwm->hwpwm] = 0;
 }
 
 static const struct pwm_ops ehrpwm_pwm_ops = {
index 6caa222af77a284f09f1e8a5f634da794dd84838..ab00cab905b730459c6e752907f1139aaec9ee57 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/err.h>
 #include <linux/platform_device.h>
 
+#include <linux/regulator/of_regulator.h>
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
 #include <linux/mfd/tps65217.h>
@@ -281,37 +282,130 @@ static const struct regulator_desc regulators[] = {
                           NULL),
 };
 
+#ifdef CONFIG_OF
+static struct of_regulator_match reg_matches[] = {
+       { .name = "dcdc1", .driver_data = (void *)TPS65217_DCDC_1 },
+       { .name = "dcdc2", .driver_data = (void *)TPS65217_DCDC_2 },
+       { .name = "dcdc3", .driver_data = (void *)TPS65217_DCDC_3 },
+       { .name = "ldo1", .driver_data = (void *)TPS65217_LDO_1 },
+       { .name = "ldo2", .driver_data = (void *)TPS65217_LDO_2 },
+       { .name = "ldo3", .driver_data = (void *)TPS65217_LDO_3 },
+       { .name = "ldo4", .driver_data = (void *)TPS65217_LDO_4 },
+};
+
+static struct tps65217_board *tps65217_parse_dt(struct platform_device *pdev)
+{
+       struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
+       struct device_node *node = tps->dev->of_node;
+       struct tps65217_board *pdata;
+       struct device_node *regs;
+       int i, count;
+
+       regs = of_find_node_by_name(node, "regulators");
+       if (!regs)
+               return NULL;
+
+       count = of_regulator_match(pdev->dev.parent, regs,
+                               reg_matches, TPS65217_NUM_REGULATOR);
+       of_node_put(regs);
+       if ((count < 0) || (count > TPS65217_NUM_REGULATOR))
+               return NULL;
+
+       pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return NULL;
+
+       for (i = 0; i < count; i++) {
+               if (!reg_matches[i].init_data || !reg_matches[i].of_node)
+                       continue;
+
+               pdata->tps65217_init_data[i] = reg_matches[i].init_data;
+               pdata->of_node[i] = reg_matches[i].of_node;
+       }
+
+       return pdata;
+}
+#else
+static struct tps65217_board *tps65217_parse_dt(struct platform_device *pdev)
+{
+       return NULL;
+}
+#endif
+
 static int __devinit tps65217_regulator_probe(struct platform_device *pdev)
 {
+       struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
+       struct tps65217_board *pdata = dev_get_platdata(tps->dev);
+       struct regulator_init_data *reg_data;
        struct regulator_dev *rdev;
-       struct tps65217 *tps;
-       struct tps_info *info = &tps65217_pmic_regs[pdev->id];
        struct regulator_config config = { };
+       int i, ret;
 
-       /* Already set by core driver */
-       tps = dev_to_tps65217(pdev->dev.parent);
-       tps->info[pdev->id] = info;
+       if (tps->dev->of_node)
+               pdata = tps65217_parse_dt(pdev);
 
-       config.dev = &pdev->dev;
-       config.of_node = pdev->dev.of_node;
-       config.init_data = pdev->dev.platform_data;
-       config.driver_data = tps;
+       if (!pdata) {
+               dev_err(&pdev->dev, "Platform data not found\n");
+               return -EINVAL;
+       }
 
-       rdev = regulator_register(&regulators[pdev->id], &config);
-       if (IS_ERR(rdev))
-               return PTR_ERR(rdev);
+       if (tps65217_chip_id(tps) != TPS65217) {
+               dev_err(&pdev->dev, "Invalid tps chip version\n");
+               return -ENODEV;
+       }
 
-       platform_set_drvdata(pdev, rdev);
+       platform_set_drvdata(pdev, tps);
 
+       for (i = 0; i < TPS65217_NUM_REGULATOR; i++) {
+
+               reg_data = pdata->tps65217_init_data[i];
+
+               /*
+                * Regulator API handles empty constraints but not NULL
+                * constraints
+                */
+               if (!reg_data)
+                       continue;
+
+               /* Register the regulators */
+               tps->info[i] = &tps65217_pmic_regs[i];
+
+               config.dev = tps->dev;
+               config.init_data = reg_data;
+               config.driver_data = tps;
+               config.regmap = tps->regmap;
+               if (tps->dev->of_node)
+                       config.of_node = pdata->of_node[i];
+
+               rdev = regulator_register(&regulators[i], &config);
+               if (IS_ERR(rdev)) {
+                       dev_err(tps->dev, "failed to register %s regulator\n",
+                               pdev->name);
+                       ret = PTR_ERR(rdev);
+                       goto err_unregister_regulator;
+               }
+
+               /* Save regulator for cleanup */
+               tps->rdev[i] = rdev;
+       }
        return 0;
+
+err_unregister_regulator:
+       while (--i >= 0)
+               regulator_unregister(tps->rdev[i]);
+
+       return ret;
 }
 
 static int __devexit tps65217_regulator_remove(struct platform_device *pdev)
 {
-       struct regulator_dev *rdev = platform_get_drvdata(pdev);
+       struct tps65217 *tps = platform_get_drvdata(pdev);
+       unsigned int i;
+
+       for (i = 0; i < TPS65217_NUM_REGULATOR; i++)
+               regulator_unregister(tps->rdev[i]);
 
        platform_set_drvdata(pdev, NULL);
-       regulator_unregister(rdev);
 
        return 0;
 }
index 590cfafc7c172ce8a2d3230de12057a85dfaa7d0..1859f71372e24912203dca245f3d60c71c80a35d 100644 (file)
@@ -1008,8 +1008,8 @@ static int rpmsg_probe(struct virtio_device *vdev)
        return 0;
 
 free_coherent:
-       dma_free_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE, bufs_va,
-                                       vrp->bufs_dma);
+       dma_free_coherent(vdev->dev.parent->parent, RPMSG_TOTAL_BUF_SPACE,
+                                       bufs_va, vrp->bufs_dma);
 vqs_del:
        vdev->config->del_vqs(vrp->vdev);
 free_vrp:
@@ -1043,7 +1043,7 @@ static void __devexit rpmsg_remove(struct virtio_device *vdev)
 
        vdev->config->del_vqs(vrp->vdev);
 
-       dma_free_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE,
+       dma_free_coherent(vdev->dev.parent->parent, RPMSG_TOTAL_BUF_SPACE,
                                        vrp->rbufs, vrp->bufs_dma);
 
        kfree(vrp);
index c5d06fe83bba6274f2e6b944a43489395e04e01c..9277d945bf4853d136ec483036be8cd0cf6f06b3 100644 (file)
@@ -495,6 +495,11 @@ static int __devinit twl_rtc_probe(struct platform_device *pdev)
        if (ret < 0)
                goto out1;
 
+       /* ensure interrupts are disabled, bootloaders can be strange */
+       ret = twl_rtc_write_u8(0, REG_RTC_INTERRUPTS_REG);
+       if (ret < 0)
+               dev_warn(&pdev->dev, "unable to disable interrupt\n");
+
        /* init cached IRQ enable bits */
        ret = twl_rtc_read_u8(&rtc_irq_bits, REG_RTC_INTERRUPTS_REG);
        if (ret < 0)
index 15370a2c5ff0b29e5286a9ffb2660beefd045f2e..0595c763dafd687ac3938f803618eed679549d76 100644 (file)
@@ -534,11 +534,11 @@ static void dasd_change_state(struct dasd_device *device)
        if (rc)
                device->target = device->state;
 
-       if (device->state == device->target)
-               wake_up(&dasd_init_waitq);
-
        /* let user-space know that the device status changed */
        kobject_uevent(&device->cdev->dev.kobj, KOBJ_CHANGE);
+
+       if (device->state == device->target)
+               wake_up(&dasd_init_waitq);
 }
 
 /*
@@ -2157,6 +2157,7 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible)
                    test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) &&
                    (!dasd_eer_enabled(device))) {
                        cqr->status = DASD_CQR_FAILED;
+                       cqr->intrc = -EAGAIN;
                        continue;
                }
                /* Don't try to start requests if device is stopped */
@@ -3270,6 +3271,16 @@ void dasd_generic_path_event(struct ccw_device *cdev, int *path_event)
                        dasd_schedule_device_bh(device);
                }
                if (path_event[chp] & PE_PATHGROUP_ESTABLISHED) {
+                       if (!(device->path_data.opm & eventlpm) &&
+                           !(device->path_data.tbvpm & eventlpm)) {
+                               /*
+                                * we can not establish a pathgroup on an
+                                * unavailable path, so trigger a path
+                                * verification first
+                                */
+                               device->path_data.tbvpm |= eventlpm;
+                               dasd_schedule_device_bh(device);
+                       }
                        DBF_DEV_EVENT(DBF_WARNING, device, "%s",
                                      "Pathgroup re-established\n");
                        if (device->discipline->kick_validate)
index 157defe5e0693c06c6432ddfbd65d0f6632a6f90..6b556995bb33e9baf98663175973bbb69805522d 100644 (file)
@@ -384,6 +384,29 @@ static void _remove_device_from_lcu(struct alias_lcu *lcu,
                group->next = NULL;
 };
 
+static int
+suborder_not_supported(struct dasd_ccw_req *cqr)
+{
+       char *sense;
+       char reason;
+       char msg_format;
+       char msg_no;
+
+       sense = dasd_get_sense(&cqr->irb);
+       if (!sense)
+               return 0;
+
+       reason = sense[0];
+       msg_format = (sense[7] & 0xF0);
+       msg_no = (sense[7] & 0x0F);
+
+       /* command reject, Format 0 MSG 4 - invalid parameter */
+       if ((reason == 0x80) && (msg_format == 0x00) && (msg_no == 0x04))
+               return 1;
+
+       return 0;
+}
+
 static int read_unit_address_configuration(struct dasd_device *device,
                                           struct alias_lcu *lcu)
 {
@@ -435,6 +458,8 @@ static int read_unit_address_configuration(struct dasd_device *device,
 
        do {
                rc = dasd_sleep_on(cqr);
+               if (rc && suborder_not_supported(cqr))
+                       return -EOPNOTSUPP;
        } while (rc && (cqr->retries > 0));
        if (rc) {
                spin_lock_irqsave(&lcu->lock, flags);
@@ -521,7 +546,7 @@ static void lcu_update_work(struct work_struct *work)
         * processing the data
         */
        spin_lock_irqsave(&lcu->lock, flags);
-       if (rc || (lcu->flags & NEED_UAC_UPDATE)) {
+       if ((rc && (rc != -EOPNOTSUPP)) || (lcu->flags & NEED_UAC_UPDATE)) {
                DBF_DEV_EVENT(DBF_WARNING, device, "could not update"
                            " alias data in lcu (rc = %d), retry later", rc);
                schedule_delayed_work(&lcu->ruac_data.dwork, 30*HZ);
index 2fb2b9ea97ecc11d7e60af84d756b9eaad8dfbed..c48c72abbefc640671af73090536bd206b1c95dc 100644 (file)
@@ -1507,7 +1507,8 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device,
  * call might change behaviour of DASD devices.
  */
 static int
-dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav)
+dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav,
+                 unsigned long flags)
 {
        struct dasd_ccw_req *cqr;
        int rc;
@@ -1516,10 +1517,19 @@ dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav)
        if (IS_ERR(cqr))
                return PTR_ERR(cqr);
 
+       /*
+        * set flags e.g. turn on failfast, to prevent blocking
+        * the calling function should handle failed requests
+        */
+       cqr->flags |= flags;
+
        rc = dasd_sleep_on(cqr);
        if (!rc)
                /* trigger CIO to reprobe devices */
                css_schedule_reprobe();
+       else if (cqr->intrc == -EAGAIN)
+               rc = -EAGAIN;
+
        dasd_sfree_request(cqr, cqr->memdev);
        return rc;
 }
@@ -1527,7 +1537,8 @@ dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav)
 /*
  * Valide storage server of current device.
  */
-static void dasd_eckd_validate_server(struct dasd_device *device)
+static int dasd_eckd_validate_server(struct dasd_device *device,
+                                    unsigned long flags)
 {
        int rc;
        struct dasd_eckd_private *private;
@@ -1536,17 +1547,18 @@ static void dasd_eckd_validate_server(struct dasd_device *device)
        private = (struct dasd_eckd_private *) device->private;
        if (private->uid.type == UA_BASE_PAV_ALIAS ||
            private->uid.type == UA_HYPER_PAV_ALIAS)
-               return;
+               return 0;
        if (dasd_nopav || MACHINE_IS_VM)
                enable_pav = 0;
        else
                enable_pav = 1;
-       rc = dasd_eckd_psf_ssc(device, enable_pav);
+       rc = dasd_eckd_psf_ssc(device, enable_pav, flags);
 
        /* may be requested feature is not available on server,
         * therefore just report error and go ahead */
        DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "PSF-SSC for SSID %04x "
                        "returned rc=%d", private->uid.ssid, rc);
+       return rc;
 }
 
 /*
@@ -1556,7 +1568,13 @@ static void dasd_eckd_do_validate_server(struct work_struct *work)
 {
        struct dasd_device *device = container_of(work, struct dasd_device,
                                                  kick_validate);
-       dasd_eckd_validate_server(device);
+       if (dasd_eckd_validate_server(device, DASD_CQR_FLAGS_FAILFAST)
+           == -EAGAIN) {
+               /* schedule worker again if failed */
+               schedule_work(&device->kick_validate);
+               return;
+       }
+
        dasd_put_device(device);
 }
 
@@ -1685,7 +1703,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
        if (rc)
                goto out_err2;
 
-       dasd_eckd_validate_server(device);
+       dasd_eckd_validate_server(device, 0);
 
        /* device may report different configuration data after LCU setup */
        rc = dasd_eckd_read_conf(device);
@@ -4153,7 +4171,7 @@ static int dasd_eckd_restore_device(struct dasd_device *device)
        rc = dasd_alias_make_device_known_to_lcu(device);
        if (rc)
                return rc;
-       dasd_eckd_validate_server(device);
+       dasd_eckd_validate_server(device, DASD_CQR_FLAGS_FAILFAST);
 
        /* RE-Read Configuration Data */
        rc = dasd_eckd_read_conf(device);
index ed25c8740a9ce4b0bb8e8334a32b72abc08655ae..fc916f5d731412c7146465d3ff29277979b69189 100644 (file)
@@ -1426,6 +1426,8 @@ static enum io_sch_action sch_get_action(struct subchannel *sch)
                return IO_SCH_REPROBE;
        if (cdev->online)
                return IO_SCH_VERIFY;
+       if (cdev->private->state == DEV_STATE_NOT_OPER)
+               return IO_SCH_UNREG_ATTACH;
        return IO_SCH_NOP;
 }
 
@@ -1519,11 +1521,14 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process)
                        goto out;
                break;
        case IO_SCH_UNREG_ATTACH:
+               spin_lock_irqsave(sch->lock, flags);
                if (cdev->private->flags.resuming) {
                        /* Device will be handled later. */
                        rc = 0;
-                       goto out;
+                       goto out_unlock;
                }
+               sch_set_cdev(sch, NULL);
+               spin_unlock_irqrestore(sch->lock, flags);
                /* Unregister ccw device. */
                ccw_device_unregister(cdev);
                break;
index 33d6630529ded9b8db625a1ca4545c6fdb2472b3..91eec60252ee1c973198ae5c230762af37607073 100644 (file)
@@ -1264,6 +1264,9 @@ int bnx2i_send_fw_iscsi_init_msg(struct bnx2i_hba *hba)
        int rc = 0;
        u64 mask64;
 
+       memset(&iscsi_init, 0x00, sizeof(struct iscsi_kwqe_init1));
+       memset(&iscsi_init2, 0x00, sizeof(struct iscsi_kwqe_init2));
+
        bnx2i_adjust_qp_size(hba);
 
        iscsi_init.flags =
index 796482badf13acfe91f4391822d90cd6339d9ffc..2b4261cb77424b52046d6e4c255f32f3359bbf2a 100644 (file)
@@ -1315,8 +1315,9 @@ static void complete_scsi_command(struct CommandList *cp)
        }
                break;
        case CMD_PROTOCOL_ERR:
+               cmd->result = DID_ERROR << 16;
                dev_warn(&h->pdev->dev, "cp %p has "
-                       "protocol error \n", cp);
+                       "protocol error\n", cp);
                break;
        case CMD_HARDWARE_ERR:
                cmd->result = DID_ERROR << 16;
index b25757d1e91b5ee8ebf2964d3ee424105939d56b..9d5a56c4b3321deea9b9001468755d222fa13b51 100644 (file)
@@ -1209,6 +1209,13 @@ _base_check_enable_msix(struct MPT2SAS_ADAPTER *ioc)
        u16 message_control;
 
 
+       /* Check whether controller SAS2008 B0 controller,
+          if it is SAS2008 B0 controller use IO-APIC instead of MSIX */
+       if (ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2008 &&
+           ioc->pdev->revision == 0x01) {
+               return -EINVAL;
+       }
+
        base = pci_find_capability(ioc->pdev, PCI_CAP_ID_MSIX);
        if (!base) {
                dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "msix not "
index c7030fbee79c254ace6a41eed0a88e6b968e6bfb..3e79a2f0004279d0b2b1175281d003a664d5aeee 100644 (file)
@@ -331,7 +331,7 @@ static void virtscsi_map_sgl(struct scatterlist *sg, unsigned int *p_idx,
        int i;
 
        for_each_sg(table->sgl, sg_elem, table->nents, i)
-               sg_set_buf(&sg[idx++], sg_virt(sg_elem), sg_elem->length);
+               sg[idx++] = *sg_elem;
 
        *p_idx = idx;
 }
index 32c26d795ed06d3a58c43bf3d295d8f75dcf676f..8f32a1323a79dd40e6e91433fa38e15709a3ca02 100644 (file)
@@ -355,7 +355,7 @@ int __init register_intc_controller(struct intc_desc *desc)
                        if (unlikely(res)) {
                                if (res == -EEXIST) {
                                        res = irq_domain_associate(d->domain,
-                                                                  irq, irq);
+                                                                  irq2, irq2);
                                        if (unlikely(res)) {
                                                pr_err("domain association "
                                                       "failure\n");
index 2804eaae804e7279aedb64f52066e380212a2176..a3ac39b79192a4c8f2de0d063054f5477a1f257a 100644 (file)
@@ -211,7 +211,8 @@ static int sh_pfc_gpio_request_enable(struct pinctrl_dev *pctldev,
                break;
        default:
                pr_err("Unsupported mux type (%d), bailing...\n", pinmux_type);
-               return -ENOTSUPP;
+               ret = -ENOTSUPP;
+               goto err;
        }
 
        ret = 0;
index d0cafd6371996c75e3ee094b2be3168ad14d38b7..f2ffd963f1c348e3986f761b1d2d2b7b75b361e2 100644 (file)
@@ -51,10 +51,12 @@ enum android_alarm_return_flags {
 #define ANDROID_ALARM_WAIT                  _IO('a', 1)
 
 #define ALARM_IOW(c, type, size)            _IOW('a', (c) | ((type) << 4), size)
+#define ALARM_IOR(c, type, size)            _IOR('a', (c) | ((type) << 4), size)
+
 /* Set alarm */
 #define ANDROID_ALARM_SET(type)             ALARM_IOW(2, type, struct timespec)
 #define ANDROID_ALARM_SET_AND_WAIT(type)    ALARM_IOW(3, type, struct timespec)
-#define ANDROID_ALARM_GET_TIME(type)        ALARM_IOW(4, type, struct timespec)
+#define ANDROID_ALARM_GET_TIME(type)        ALARM_IOR(4, type, struct timespec)
 #define ANDROID_ALARM_SET_RTC               _IOW('a', 5, struct timespec)
 #define ANDROID_ALARM_BASE_CMD(cmd)         (cmd & ~(_IOC(0, 0, 0xf0, 0)))
 #define ANDROID_ALARM_IOCTL_TO_TYPE(cmd)    (_IOC_NR(cmd) >> 4)
index 6c81e377262c204ca8a9354743f344ff824267d5..cc8931fde839c491455ed10beaa56529480fbe2d 100644 (file)
@@ -1412,6 +1412,13 @@ static int __devinit dio200_attach_pci(struct comedi_device *dev,
                dev_err(dev->class_dev, "BUG! cannot determine board type!\n");
                return -EINVAL;
        }
+       /*
+        * Need to 'get' the PCI device to match the 'put' in dio200_detach().
+        * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
+        * support for manual attachment of PCI devices via dio200_attach()
+        * has been removed.
+        */
+       pci_dev_get(pci_dev);
        return dio200_pci_common_attach(dev, pci_dev);
 }
 
index aabba9886b7d9276eb1c4233c89ce894685fbd58..f50287903038bb22772c1c99522c69078fc638c0 100644 (file)
@@ -565,6 +565,13 @@ static int __devinit pc236_attach_pci(struct comedi_device *dev,
                dev_err(dev->class_dev, "BUG! cannot determine board type!\n");
                return -EINVAL;
        }
+       /*
+        * Need to 'get' the PCI device to match the 'put' in pc236_detach().
+        * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
+        * support for manual attachment of PCI devices via pc236_attach()
+        * has been removed.
+        */
+       pci_dev_get(pci_dev);
        return pc236_pci_common_attach(dev, pci_dev);
 }
 
index 40ec1ffebba651fda62f23af21ff7b0e3ef097a4..8191c4e28e0a6849fb53e7def7b4fc1639c79c09 100644 (file)
@@ -298,6 +298,13 @@ static int __devinit pc263_attach_pci(struct comedi_device *dev,
                dev_err(dev->class_dev, "BUG! cannot determine board type!\n");
                return -EINVAL;
        }
+       /*
+        * Need to 'get' the PCI device to match the 'put' in pc263_detach().
+        * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
+        * support for manual attachment of PCI devices via pc263_attach()
+        * has been removed.
+        */
+       pci_dev_get(pci_dev);
        return pc263_pci_common_attach(dev, pci_dev);
 }
 
index 4e17f13e57f6530b2bc3cfd1afc4485d0940d880..8bf109e7bb05cef4c9c50669314289cde734c47c 100644 (file)
@@ -1503,6 +1503,13 @@ pci224_attach_pci(struct comedi_device *dev, struct pci_dev *pci_dev)
                        DRIVER_NAME ": BUG! cannot determine board type!\n");
                return -EINVAL;
        }
+       /*
+        * Need to 'get' the PCI device to match the 'put' in pci224_detach().
+        * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
+        * support for manual attachment of PCI devices via pci224_attach()
+        * has been removed.
+        */
+       pci_dev_get(pci_dev);
        return pci224_attach_common(dev, pci_dev, NULL);
 }
 
index 1b67d0c61fa72ff782e02a09dc63b2c14ec5982c..66e74bd12267a565263556f8e6f3389b2c6c41b9 100644 (file)
@@ -2925,6 +2925,13 @@ static int __devinit pci230_attach_pci(struct comedi_device *dev,
                        "amplc_pci230: BUG! cannot determine board type!\n");
                return -EINVAL;
        }
+       /*
+        * Need to 'get' the PCI device to match the 'put' in pci230_detach().
+        * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
+        * support for manual attachment of PCI devices via pci230_attach()
+        * has been removed.
+        */
+       pci_dev_get(pci_dev);
        return pci230_attach_common(dev, pci_dev);
 }
 
index 874e02e47668e60a024b350a72822894be801f1a..67a914a10b55fb2333c9ec747ccd907084fc7b6d 100644 (file)
@@ -378,7 +378,7 @@ das08jr_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
        int chan;
 
        lsb = data[0] & 0xff;
-       msb = (data[0] >> 8) & 0xf;
+       msb = (data[0] >> 8) & 0xff;
 
        chan = CR_CHAN(insn->chanspec);
 
@@ -623,7 +623,7 @@ static const struct das08_board_struct das08_boards[] = {
                .ai = das08_ai_rinsn,
                .ai_nbits = 16,
                .ai_pg = das08_pg_none,
-               .ai_encoding = das08_encode12,
+               .ai_encoding = das08_encode16,
                .ao = das08jr_ao_winsn,
                .ao_nbits = 16,
                .di = das08jr_di_rbits,
@@ -922,6 +922,13 @@ das08_attach_pci(struct comedi_device *dev, struct pci_dev *pdev)
                dev_err(dev->class_dev, "BUG! cannot determine board type!\n");
                return -EINVAL;
        }
+       /*
+        * Need to 'get' the PCI device to match the 'put' in das08_detach().
+        * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
+        * support for manual attachment of PCI devices via das08_attach()
+        * has been removed.
+        */
+       pci_dev_get(pdev);
        return das08_pci_attach_common(dev, pdev);
 }
 
index 18d108fd967a908817efa70d1056fcba35d00458..f3da59063ed2e5a8c934ed201f1add435810028a 100644 (file)
@@ -121,8 +121,10 @@ static int lis3l02dq_get_buffer_element(struct iio_dev *indio_dev,
        if (rx_array == NULL)
                return -ENOMEM;
        ret = lis3l02dq_read_all(indio_dev, rx_array);
-       if (ret < 0)
+       if (ret < 0) {
+               kfree(rx_array);
                return ret;
+       }
        for (i = 0; i < scan_count; i++)
                data[i] = combine_8_to_16(rx_array[i*4+1],
                                        rx_array[i*4+3]);
index 095837285f4fb25732b8986f3d6d25458a9a11a2..19a064d649e3f72a783dd2b2ffc2e6a385175216 100644 (file)
@@ -647,6 +647,8 @@ static ssize_t ad7192_write_frequency(struct device *dev,
        ret = strict_strtoul(buf, 10, &lval);
        if (ret)
                return ret;
+       if (lval == 0)
+               return -EINVAL;
 
        mutex_lock(&indio_dev->mlock);
        if (iio_buffer_enabled(indio_dev)) {
index 93aa431287ac6efb17d9b5172c337f67be30365a..eb8e9d69efd3f39ab42a788f70c8eb1d4b105bf2 100644 (file)
@@ -195,6 +195,8 @@ static ssize_t adis16260_write_frequency(struct device *dev,
        ret = strict_strtol(buf, 10, &val);
        if (ret)
                return ret;
+       if (val == 0)
+               return -EINVAL;
 
        mutex_lock(&indio_dev->mlock);
        if (spi_get_device_id(st->us)) {
index 1f4c17779b5a64e18f48865aa6ecb0e6d49387f4..a618327e06edf2c3374b38b425771b4a823517f2 100644 (file)
@@ -234,6 +234,8 @@ static ssize_t adis16400_write_frequency(struct device *dev,
        ret = strict_strtol(buf, 10, &val);
        if (ret)
                return ret;
+       if (val == 0)
+               return -EINVAL;
 
        mutex_lock(&indio_dev->mlock);
 
index f04ece7fbc2fbe3d33b61dd3387e1cb0b53b23c5..3ccff189f258232cd4704acc2d8674029cf190b1 100644 (file)
@@ -425,6 +425,8 @@ static ssize_t ade7753_write_frequency(struct device *dev,
        ret = strict_strtol(buf, 10, &val);
        if (ret)
                return ret;
+       if (val == 0)
+               return -EINVAL;
 
        mutex_lock(&indio_dev->mlock);
 
index 6cee28a5e87731bee476ab4efc7c0501c89cff45..abb1e9c8d0947adcd1bc6d5567b9620496207c5e 100644 (file)
@@ -445,6 +445,8 @@ static ssize_t ade7754_write_frequency(struct device *dev,
        ret = strict_strtol(buf, 10, &val);
        if (ret)
                return ret;
+       if (val == 0)
+               return -EINVAL;
 
        mutex_lock(&indio_dev->mlock);
 
index b3f7e0fa96124b6ce8d719d63fce6e45df210d0f..eb0a2a98f3886afe1948b284c2ede98094a95754 100644 (file)
@@ -385,6 +385,8 @@ static ssize_t ade7759_write_frequency(struct device *dev,
        ret = strict_strtol(buf, 10, &val);
        if (ret)
                return ret;
+       if (val == 0)
+               return -EINVAL;
 
        mutex_lock(&indio_dev->mlock);
 
index 695ea35f75b0831cc8e2d28369d9ef124f8c2da9..d0a7e408efe93847606d1db21397fab44aba1ae6 100644 (file)
@@ -837,7 +837,7 @@ static int __devinit tegra_nvec_probe(struct platform_device *pdev)
        }
 
        ret = mfd_add_devices(nvec->dev, -1, nvec_devices,
-                             ARRAY_SIZE(nvec_devices), base, 0);
+                             ARRAY_SIZE(nvec_devices), base, 0, NULL);
        if (ret)
                dev_err(nvec->dev, "error adding subdevices\n");
 
index 5e2856c0e0bbf3ead714ca105d06e54e1dff1545..38be186c249a8ed8e3fbed927ec6ed17633ea2f1 100644 (file)
@@ -48,13 +48,20 @@ static inline void copy_timings_omap_to_drm(struct drm_display_mode *mode,
        mode->vsync_end = mode->vsync_start + timings->vsw;
        mode->vtotal = mode->vsync_end + timings->vbp;
 
-       /* note: whether or not it is interlaced, +/- h/vsync, etc,
-        * which should be set in the mode flags, is not exposed in
-        * the omap_video_timings struct.. but hdmi driver tracks
-        * those separately so all we have to have to set the mode
-        * is the way to recover these timings values, and the
-        * omap_dss_driver would do the rest.
-        */
+       mode->flags = 0;
+
+       if (timings->interlace)
+               mode->flags |= DRM_MODE_FLAG_INTERLACE;
+
+       if (timings->hsync_level == OMAPDSS_SIG_ACTIVE_HIGH)
+               mode->flags |= DRM_MODE_FLAG_PHSYNC;
+       else
+               mode->flags |= DRM_MODE_FLAG_NHSYNC;
+
+       if (timings->vsync_level == OMAPDSS_SIG_ACTIVE_HIGH)
+               mode->flags |= DRM_MODE_FLAG_PVSYNC;
+       else
+               mode->flags |= DRM_MODE_FLAG_NVSYNC;
 }
 
 static inline void copy_timings_drm_to_omap(struct omap_video_timings *timings,
@@ -71,6 +78,22 @@ static inline void copy_timings_drm_to_omap(struct omap_video_timings *timings,
        timings->vfp = mode->vsync_start - mode->vdisplay;
        timings->vsw = mode->vsync_end - mode->vsync_start;
        timings->vbp = mode->vtotal - mode->vsync_end;
+
+       timings->interlace = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
+
+       if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+               timings->hsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
+       else
+               timings->hsync_level = OMAPDSS_SIG_ACTIVE_LOW;
+
+       if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+               timings->vsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
+       else
+               timings->vsync_level = OMAPDSS_SIG_ACTIVE_LOW;
+
+       timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
+       timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH;
+       timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES;
 }
 
 static void omap_connector_dpms(struct drm_connector *connector, int mode)
@@ -177,17 +200,14 @@ static int omap_connector_get_modes(struct drm_connector *connector)
                        drm_mode_connector_update_edid_property(
                                        connector, edid);
                        n = drm_add_edid_modes(connector, edid);
-                       kfree(connector->display_info.raw_edid);
-                       connector->display_info.raw_edid = edid;
                } else {
                        drm_mode_connector_update_edid_property(
                                        connector, NULL);
-                       connector->display_info.raw_edid = NULL;
-                       kfree(edid);
                }
+               kfree(edid);
        } else {
                struct drm_display_mode *mode = drm_mode_create(dev);
-               struct omap_video_timings timings;
+               struct omap_video_timings timings = {0};
 
                dssdrv->get_timings(dssdev, &timings);
 
@@ -291,7 +311,7 @@ void omap_connector_mode_set(struct drm_connector *connector,
        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;
+       struct omap_video_timings timings = {0};
 
        copy_timings_drm_to_omap(&timings, mode);
 
index 8c6ed3b0c6f6c9e63c15d0c42ca1985878a2c270..8a027bb77d97c8c3fe04a6e0398c8e74aced86ae 100644 (file)
@@ -276,7 +276,7 @@ fail:
                if (fbi)
                        framebuffer_release(fbi);
                if (fb)
-                       fb->funcs->destroy(fb);
+                       drm_framebuffer_remove(fb);
        }
 
        return ret;
@@ -401,7 +401,7 @@ void omap_fbdev_free(struct drm_device *dev)
 
        /* this will free the backing object */
        if (fbdev->fb)
-               fbdev->fb->funcs->destroy(fbdev->fb);
+               drm_framebuffer_remove(fbdev->fb);
 
        kfree(fbdev);
 
index d98321945802c8daf8ec71cc3908ec0c44133c7a..758ce0a8d82e03c59d326b5ce0f6b8bdda425b25 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/cdev.h>
 #include <linux/uaccess.h>
 #include <linux/netdevice.h>
+#include <linux/etherdevice.h>
 #include <linux/poll.h>
 #include <linux/sched.h>
 #include "ozconfig.h"
@@ -213,7 +214,7 @@ static int oz_set_active_pd(u8 *addr)
                if (old_pd)
                        oz_pd_put(old_pd);
        } else {
-               if (!memcmp(addr, "\0\0\0\0\0\0", sizeof(addr))) {
+               if (is_zero_ether_addr(addr)) {
                        spin_lock_bh(&g_cdev.lock);
                        pd = g_cdev.active_pd;
                        g_cdev.active_pd = 0;
index 0e26d5f6cf2d57d64eca7d5440af7da0f77443cd..495ee1205e02a9c77aa59a1e7ec5da1cf255d534 100644 (file)
@@ -117,13 +117,8 @@ void r8712_recv_indicatepkt(struct _adapter *padapter,
        if (skb == NULL)
                goto _recv_indicatepkt_drop;
        skb->data = precv_frame->u.hdr.rx_data;
-#ifdef NET_SKBUFF_DATA_USES_OFFSET
-       skb->tail = (sk_buff_data_t)(precv_frame->u.hdr.rx_tail -
-                    precv_frame->u.hdr.rx_head);
-#else
-       skb->tail = (sk_buff_data_t)precv_frame->u.hdr.rx_tail;
-#endif
        skb->len = precv_frame->u.hdr.len;
+       skb_set_tail_pointer(skb, skb->len);
        if ((pattrib->tcpchk_valid == 1) && (pattrib->tcp_chkrpt == 1))
                skb->ip_summed = CHECKSUM_UNNECESSARY;
        else
index e4bdf2a2b5829292e23d8f4675fab023249e16d3..3aa895ec6507f7dde81f1440b4af349e792dc1cd 100644 (file)
@@ -200,7 +200,7 @@ s_vProcessRxMACHeader (
     } else if (!compare_ether_addr(pbyRxBuffer, &pDevice->abySNAP_RFC1042[0])) {
         cbHeaderSize += 6;
         pwType = (PWORD) (pbyRxBufferAddr + cbHeaderSize);
-       if ((*pwType == cpu_to_le16(ETH_P_IPX)) ||
+       if ((*pwType == cpu_to_be16(ETH_P_IPX)) ||
            (*pwType == cpu_to_le16(0xF380))) {
                cbHeaderSize -= 8;
             pwType = (PWORD) (pbyRxBufferAddr + cbHeaderSize);
index bb464527fc1b06e8814b05c23c97e89208cda92e..b6e04e7b629bdc0a63a04ac4f47698abed3cece8 100644 (file)
@@ -1699,7 +1699,7 @@ s_bPacketToWirelessUsb(
     // 802.1H
     if (ntohs(psEthHeader->wType) > ETH_DATA_LEN) {
        if (pDevice->dwDiagRefCount == 0) {
-               if ((psEthHeader->wType == cpu_to_le16(ETH_P_IPX)) ||
+               if ((psEthHeader->wType == cpu_to_be16(ETH_P_IPX)) ||
                    (psEthHeader->wType == cpu_to_le16(0xF380))) {
                        memcpy((PBYTE) (pbyPayloadHead),
                               abySNAP_Bridgetunnel, 6);
@@ -2838,10 +2838,10 @@ int nsDMA_tx_packet(PSDevice pDevice, unsigned int uDMAIdx, struct sk_buff *skb)
     Packet_Type = skb->data[ETH_HLEN+1];
     Descriptor_type = skb->data[ETH_HLEN+1+1+2];
     Key_info = (skb->data[ETH_HLEN+1+1+2+1] << 8)|(skb->data[ETH_HLEN+1+1+2+2]);
-    if (pDevice->sTxEthHeader.wType == cpu_to_le16(ETH_P_PAE)) {
-       /* 802.1x OR eapol-key challenge frame transfer */
-       if (((Protocol_Version == 1) || (Protocol_Version == 2)) &&
-               (Packet_Type == 3)) {
+       if (pDevice->sTxEthHeader.wType == cpu_to_be16(ETH_P_PAE)) {
+               /* 802.1x OR eapol-key challenge frame transfer */
+               if (((Protocol_Version == 1) || (Protocol_Version == 2)) &&
+                       (Packet_Type == 3)) {
                         bTxeapol_key = TRUE;
                        if(!(Key_info & BIT3) &&  //WPA or RSN group-key challenge
                           (Key_info & BIT8) && (Key_info & BIT9)) {    //send 2/2 key
@@ -2987,19 +2987,19 @@ int nsDMA_tx_packet(PSDevice pDevice, unsigned int uDMAIdx, struct sk_buff *skb)
         }
     }
 
-    if (pDevice->sTxEthHeader.wType == cpu_to_le16(ETH_P_PAE)) {
-        if (pDevice->byBBType != BB_TYPE_11A) {
-            pDevice->wCurrentRate = RATE_1M;
-            pDevice->byACKRate = RATE_1M;
-            pDevice->byTopCCKBasicRate = RATE_1M;
-            pDevice->byTopOFDMBasicRate = RATE_6M;
-        } else {
-            pDevice->wCurrentRate = RATE_6M;
-            pDevice->byACKRate = RATE_6M;
-            pDevice->byTopCCKBasicRate = RATE_1M;
-            pDevice->byTopOFDMBasicRate = RATE_6M;
-        }
-    }
+       if (pDevice->sTxEthHeader.wType == cpu_to_be16(ETH_P_PAE)) {
+               if (pDevice->byBBType != BB_TYPE_11A) {
+                       pDevice->wCurrentRate = RATE_1M;
+                       pDevice->byACKRate = RATE_1M;
+                       pDevice->byTopCCKBasicRate = RATE_1M;
+                       pDevice->byTopOFDMBasicRate = RATE_6M;
+               } else {
+                       pDevice->wCurrentRate = RATE_6M;
+                       pDevice->byACKRate = RATE_6M;
+                       pDevice->byTopCCKBasicRate = RATE_1M;
+                       pDevice->byTopOFDMBasicRate = RATE_6M;
+               }
+       }
 
     DBG_PRT(MSG_LEVEL_DEBUG,
            KERN_INFO "dma_tx: pDevice->wCurrentRate = %d\n",
@@ -3015,7 +3015,7 @@ int nsDMA_tx_packet(PSDevice pDevice, unsigned int uDMAIdx, struct sk_buff *skb)
 
     if (bNeedEncryption == TRUE) {
         DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"ntohs Pkt Type=%04x\n", ntohs(pDevice->sTxEthHeader.wType));
-       if ((pDevice->sTxEthHeader.wType) == cpu_to_le16(ETH_P_PAE)) {
+       if ((pDevice->sTxEthHeader.wType) == cpu_to_be16(ETH_P_PAE)) {
                bNeedEncryption = FALSE;
             DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"Pkt Type=%04x\n", (pDevice->sTxEthHeader.wType));
             if ((pMgmt->eCurrMode == WMAC_MODE_ESS_STA) && (pMgmt->eCurrState == WMAC_STATE_ASSOC)) {
index fabff4d650ef8c5645ed26dfeb5a46e452d26c05..0970127344e60828e569e1ca87ee6c82cb3f11eb 100644 (file)
@@ -327,9 +327,9 @@ int prism2_get_station(struct wiphy *wiphy, struct net_device *dev,
        return result;
 }
 
-int prism2_scan(struct wiphy *wiphy, struct net_device *dev,
-               struct cfg80211_scan_request *request)
+int prism2_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
 {
+       struct net_device *dev = request->wdev->netdev;
        struct prism2_wiphy_private *priv = wiphy_priv(wiphy);
        wlandevice_t *wlandev = dev->ml_priv;
        struct p80211msg_dot11req_scan msg1;
index c214977b4ab48adec84fc260df0116b12b498aa7..52b43b7b83d7b3c9d7e3acfc853912afc64c2b61 100644 (file)
@@ -1251,13 +1251,12 @@ static int zcache_pampd_get_data_and_free(char *data, size_t *bufsize, bool raw,
                                        void *pampd, struct tmem_pool *pool,
                                        struct tmem_oid *oid, uint32_t index)
 {
-       int ret = 0;
-
        BUG_ON(!is_ephemeral(pool));
-       zbud_decompress((struct page *)(data), pampd);
+       if (zbud_decompress((struct page *)(data), pampd) < 0)
+               return -EINVAL;
        zbud_free_and_delist((struct zbud_hdr *)pampd);
        atomic_dec(&zcache_curr_eph_pampd_count);
-       return ret;
+       return 0;
 }
 
 /*
index 0694d9b1bce6a4e066a7bba1b1ef5f870b4b51d7..6aba4395e8d8ffd5aa950c4dfc745791c3f65fc2 100644 (file)
@@ -221,6 +221,7 @@ static int iscsi_login_zero_tsih_s1(
 {
        struct iscsi_session *sess = NULL;
        struct iscsi_login_req *pdu = (struct iscsi_login_req *)buf;
+       int ret;
 
        sess = kzalloc(sizeof(struct iscsi_session), GFP_KERNEL);
        if (!sess) {
@@ -257,9 +258,17 @@ static int iscsi_login_zero_tsih_s1(
                return -ENOMEM;
        }
        spin_lock(&sess_idr_lock);
-       idr_get_new(&sess_idr, NULL, &sess->session_index);
+       ret = idr_get_new(&sess_idr, NULL, &sess->session_index);
        spin_unlock(&sess_idr_lock);
 
+       if (ret < 0) {
+               pr_err("idr_get_new() for sess_idr failed\n");
+               iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+                               ISCSI_LOGIN_STATUS_NO_RESOURCES);
+               kfree(sess);
+               return -ENOMEM;
+       }
+
        sess->creation_time = get_jiffies_64();
        spin_lock_init(&sess->session_stats_lock);
        /*
index 91799973081a3d907cd260792df3f573d1dbec82..41641ba548286e9dbf33f45a02ece5b20eec0d14 100644 (file)
@@ -218,6 +218,13 @@ int target_emulate_set_target_port_groups(struct se_cmd *cmd)
                cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
                return -EINVAL;
        }
+       if (cmd->data_length < 4) {
+               pr_warn("SET TARGET PORT GROUPS parameter list length %u too"
+                       " small\n", cmd->data_length);
+               cmd->scsi_sense_reason = TCM_INVALID_PARAMETER_LIST;
+               return -EINVAL;
+       }
+
        buf = transport_kmap_data_sg(cmd);
 
        /*
index cf2c66f3c11690c81ca23e9fd2ce286e76b56e62..9fc9a6006ca082076a6d235dfad676e1e1a11ea9 100644 (file)
@@ -669,6 +669,13 @@ int target_report_luns(struct se_cmd *se_cmd)
        unsigned char *buf;
        u32 lun_count = 0, offset = 8, i;
 
+       if (se_cmd->data_length < 16) {
+               pr_warn("REPORT LUNS allocation length %u too small\n",
+                       se_cmd->data_length);
+               se_cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD;
+               return -EINVAL;
+       }
+
        buf = transport_kmap_data_sg(se_cmd);
        if (!buf)
                return -ENOMEM;
index 76db75e836ede701c2aed6090a212fdf1a08ad10..9ba495477fd24f80bf6643cfae3707525b7c165e 100644 (file)
@@ -325,17 +325,30 @@ static int iblock_execute_unmap(struct se_cmd *cmd)
        struct iblock_dev *ibd = dev->dev_ptr;
        unsigned char *buf, *ptr = NULL;
        sector_t lba;
-       int size = cmd->data_length;
+       int size;
        u32 range;
        int ret = 0;
        int dl, bd_dl;
 
+       if (cmd->data_length < 8) {
+               pr_warn("UNMAP parameter list length %u too small\n",
+                       cmd->data_length);
+               cmd->scsi_sense_reason = TCM_INVALID_PARAMETER_LIST;
+               return -EINVAL;
+       }
+
        buf = transport_kmap_data_sg(cmd);
 
        dl = get_unaligned_be16(&buf[0]);
        bd_dl = get_unaligned_be16(&buf[2]);
 
-       size = min(size - 8, bd_dl);
+       size = cmd->data_length - 8;
+       if (bd_dl > size)
+               pr_warn("UNMAP parameter list length %u too small, ignoring bd_dl %u\n",
+                       cmd->data_length, bd_dl);
+       else
+               size = bd_dl;
+
        if (size / 16 > dev->se_sub_dev->se_dev_attrib.max_unmap_block_desc_count) {
                cmd->scsi_sense_reason = TCM_INVALID_PARAMETER_LIST;
                ret = -EINVAL;
index 1e946502c378886aa90bafc16239d2c34730b9fb..956c84c6b666498caabf7b60b7404413698c0b14 100644 (file)
@@ -1540,6 +1540,14 @@ static int core_scsi3_decode_spec_i_port(
        tidh_new->dest_local_nexus = 1;
        list_add_tail(&tidh_new->dest_list, &tid_dest_list);
 
+       if (cmd->data_length < 28) {
+               pr_warn("SPC-PR: Received PR OUT parameter list"
+                       " length too small: %u\n", cmd->data_length);
+               cmd->scsi_sense_reason = TCM_INVALID_PARAMETER_LIST;
+               ret = -EINVAL;
+               goto out;
+       }
+
        buf = transport_kmap_data_sg(cmd);
        /*
         * For a PERSISTENT RESERVE OUT specify initiator ports payload,
index 5552fa7426bc9b317da906dec2e395bbf854ae78..9d7ce3daa26275a7c08b831ae9b955382e0e9103 100644 (file)
@@ -667,7 +667,8 @@ static void pscsi_free_device(void *p)
        kfree(pdv);
 }
 
-static int pscsi_transport_complete(struct se_cmd *cmd, struct scatterlist *sg)
+static void pscsi_transport_complete(struct se_cmd *cmd, struct scatterlist *sg,
+                                    unsigned char *sense_buffer)
 {
        struct pscsi_dev_virt *pdv = cmd->se_dev->dev_ptr;
        struct scsi_device *sd = pdv->pdv_sd;
@@ -679,7 +680,7 @@ static int pscsi_transport_complete(struct se_cmd *cmd, struct scatterlist *sg)
         * not been allocated because TCM is handling the emulation directly.
         */
        if (!pt)
-               return 0;
+               return;
 
        cdb = &pt->pscsi_cdb[0];
        result = pt->pscsi_result;
@@ -687,11 +688,11 @@ static int pscsi_transport_complete(struct se_cmd *cmd, struct scatterlist *sg)
         * Hack to make sure that Write-Protect modepage is set if R/O mode is
         * forced.
         */
+       if (!cmd->se_deve || !cmd->data_length)
+               goto after_mode_sense;
+
        if (((cdb[0] == MODE_SENSE) || (cdb[0] == MODE_SENSE_10)) &&
             (status_byte(result) << 1) == SAM_STAT_GOOD) {
-               if (!cmd->se_deve)
-                       goto after_mode_sense;
-
                if (cmd->se_deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY) {
                        unsigned char *buf = transport_kmap_data_sg(cmd);
 
@@ -708,7 +709,7 @@ static int pscsi_transport_complete(struct se_cmd *cmd, struct scatterlist *sg)
        }
 after_mode_sense:
 
-       if (sd->type != TYPE_TAPE)
+       if (sd->type != TYPE_TAPE || !cmd->data_length)
                goto after_mode_select;
 
        /*
@@ -750,10 +751,10 @@ after_mode_sense:
        }
 after_mode_select:
 
-       if (status_byte(result) & CHECK_CONDITION)
-               return 1;
-
-       return 0;
+       if (sense_buffer && (status_byte(result) & CHECK_CONDITION)) {
+               memcpy(sense_buffer, pt->pscsi_sense, TRANSPORT_SENSE_BUFFER);
+               cmd->se_cmd_flags |= SCF_TRANSPORT_TASK_SENSE;
+       }
 }
 
 enum {
@@ -1184,13 +1185,6 @@ fail:
        return -ENOMEM;
 }
 
-static unsigned char *pscsi_get_sense_buffer(struct se_cmd *cmd)
-{
-       struct pscsi_plugin_task *pt = cmd->priv;
-
-       return pt->pscsi_sense;
-}
-
 /*     pscsi_get_device_rev():
  *
  *
@@ -1273,7 +1267,6 @@ static struct se_subsystem_api pscsi_template = {
        .check_configfs_dev_params = pscsi_check_configfs_dev_params,
        .set_configfs_dev_params = pscsi_set_configfs_dev_params,
        .show_configfs_dev_params = pscsi_show_configfs_dev_params,
-       .get_sense_buffer       = pscsi_get_sense_buffer,
        .get_device_rev         = pscsi_get_device_rev,
        .get_device_type        = pscsi_get_device_type,
        .get_blocks             = pscsi_get_blocks,
index 4c861de538c9ddb627356e8aa3ea992b164033cf..388a922c8f6de8f4f2a18f7bea85f5b3590b41a9 100644 (file)
@@ -877,9 +877,11 @@ static int spc_emulate_modesense(struct se_cmd *cmd)
 static int spc_emulate_request_sense(struct se_cmd *cmd)
 {
        unsigned char *cdb = cmd->t_task_cdb;
-       unsigned char *buf;
+       unsigned char *rbuf;
        u8 ua_asc = 0, ua_ascq = 0;
-       int err = 0;
+       unsigned char buf[SE_SENSE_BUF];
+
+       memset(buf, 0, SE_SENSE_BUF);
 
        if (cdb[1] & 0x01) {
                pr_err("REQUEST_SENSE description emulation not"
@@ -888,20 +890,21 @@ static int spc_emulate_request_sense(struct se_cmd *cmd)
                return -ENOSYS;
        }
 
-       buf = transport_kmap_data_sg(cmd);
-
-       if (!core_scsi3_ua_clear_for_request_sense(cmd, &ua_asc, &ua_ascq)) {
+       rbuf = transport_kmap_data_sg(cmd);
+       if (cmd->scsi_sense_reason != 0) {
+               /*
+                * Out of memory.  We will fail with CHECK CONDITION, so
+                * we must not clear the unit attention condition.
+                */
+               target_complete_cmd(cmd, CHECK_CONDITION);
+               return 0;
+       } else if (!core_scsi3_ua_clear_for_request_sense(cmd, &ua_asc, &ua_ascq)) {
                /*
                 * CURRENT ERROR, UNIT ATTENTION
                 */
                buf[0] = 0x70;
                buf[SPC_SENSE_KEY_OFFSET] = UNIT_ATTENTION;
 
-               if (cmd->data_length < 18) {
-                       buf[7] = 0x00;
-                       err = -EINVAL;
-                       goto end;
-               }
                /*
                 * The Additional Sense Code (ASC) from the UNIT ATTENTION
                 */
@@ -915,11 +918,6 @@ static int spc_emulate_request_sense(struct se_cmd *cmd)
                buf[0] = 0x70;
                buf[SPC_SENSE_KEY_OFFSET] = NO_SENSE;
 
-               if (cmd->data_length < 18) {
-                       buf[7] = 0x00;
-                       err = -EINVAL;
-                       goto end;
-               }
                /*
                 * NO ADDITIONAL SENSE INFORMATION
                 */
@@ -927,8 +925,11 @@ static int spc_emulate_request_sense(struct se_cmd *cmd)
                buf[7] = 0x0A;
        }
 
-end:
-       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 4de3186dc44e99672d3666a24ec145e058183691..269f54488397bd2193bb80869bf9de8ac4c73bf2 100644 (file)
@@ -567,6 +567,34 @@ static void target_complete_failure_work(struct work_struct *work)
        transport_generic_request_failure(cmd);
 }
 
+/*
+ * Used when asking transport to copy Sense Data from the underlying
+ * Linux/SCSI struct scsi_cmnd
+ */
+static unsigned char *transport_get_sense_buffer(struct se_cmd *cmd)
+{
+       unsigned char *buffer = cmd->sense_buffer;
+       struct se_device *dev = cmd->se_dev;
+       u32 offset = 0;
+
+       WARN_ON(!cmd->se_lun);
+
+       if (!dev)
+               return NULL;
+
+       if (cmd->se_cmd_flags & SCF_SENT_CHECK_CONDITION)
+               return NULL;
+
+       offset = cmd->se_tfo->set_fabric_sense_len(cmd, TRANSPORT_SENSE_BUFFER);
+
+       /* Automatically padded */
+       cmd->scsi_sense_length = TRANSPORT_SENSE_BUFFER + offset;
+
+       pr_debug("HBA_[%u]_PLUG[%s]: Requesting sense for SAM STATUS: 0x%02x\n",
+               dev->se_hba->hba_id, dev->transport->name, cmd->scsi_status);
+       return &buffer[offset];
+}
+
 void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)
 {
        struct se_device *dev = cmd->se_dev;
@@ -580,11 +608,11 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)
        cmd->transport_state &= ~CMD_T_BUSY;
 
        if (dev && dev->transport->transport_complete) {
-               if (dev->transport->transport_complete(cmd,
-                               cmd->t_data_sg) != 0) {
-                       cmd->se_cmd_flags |= SCF_TRANSPORT_TASK_SENSE;
+               dev->transport->transport_complete(cmd,
+                               cmd->t_data_sg,
+                               transport_get_sense_buffer(cmd));
+               if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE)
                        success = 1;
-               }
        }
 
        /*
@@ -1181,15 +1209,20 @@ int target_cmd_size_check(struct se_cmd *cmd, unsigned int size)
                        /* Returns CHECK_CONDITION + INVALID_CDB_FIELD */
                        goto out_invalid_cdb_field;
                }
-
+               /*
+                * For the overflow case keep the existing fabric provided
+                * ->data_length.  Otherwise for the underflow case, reset
+                * ->data_length to the smaller SCSI expected data transfer
+                * length.
+                */
                if (size > cmd->data_length) {
                        cmd->se_cmd_flags |= SCF_OVERFLOW_BIT;
                        cmd->residual_count = (size - cmd->data_length);
                } else {
                        cmd->se_cmd_flags |= SCF_UNDERFLOW_BIT;
                        cmd->residual_count = (cmd->data_length - size);
+                       cmd->data_length = size;
                }
-               cmd->data_length = size;
        }
 
        return 0;
@@ -1815,61 +1848,6 @@ execute:
 }
 EXPORT_SYMBOL(target_execute_cmd);
 
-/*
- * Used to obtain Sense Data from underlying Linux/SCSI struct scsi_cmnd
- */
-static int transport_get_sense_data(struct se_cmd *cmd)
-{
-       unsigned char *buffer = cmd->sense_buffer, *sense_buffer = NULL;
-       struct se_device *dev = cmd->se_dev;
-       unsigned long flags;
-       u32 offset = 0;
-
-       WARN_ON(!cmd->se_lun);
-
-       if (!dev)
-               return 0;
-
-       spin_lock_irqsave(&cmd->t_state_lock, flags);
-       if (cmd->se_cmd_flags & SCF_SENT_CHECK_CONDITION) {
-               spin_unlock_irqrestore(&cmd->t_state_lock, flags);
-               return 0;
-       }
-
-       if (!(cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE))
-               goto out;
-
-       if (!dev->transport->get_sense_buffer) {
-               pr_err("dev->transport->get_sense_buffer is NULL\n");
-               goto out;
-       }
-
-       sense_buffer = dev->transport->get_sense_buffer(cmd);
-       if (!sense_buffer) {
-               pr_err("ITT 0x%08x cmd %p: Unable to locate"
-                       " sense buffer for task with sense\n",
-                       cmd->se_tfo->get_task_tag(cmd), cmd);
-               goto out;
-       }
-
-       spin_unlock_irqrestore(&cmd->t_state_lock, flags);
-
-       offset = cmd->se_tfo->set_fabric_sense_len(cmd, TRANSPORT_SENSE_BUFFER);
-
-       memcpy(&buffer[offset], sense_buffer, TRANSPORT_SENSE_BUFFER);
-
-       /* Automatically padded */
-       cmd->scsi_sense_length = TRANSPORT_SENSE_BUFFER + offset;
-
-       pr_debug("HBA_[%u]_PLUG[%s]: Set SAM STATUS: 0x%02x and sense\n",
-               dev->se_hba->hba_id, dev->transport->name, cmd->scsi_status);
-       return 0;
-
-out:
-       spin_unlock_irqrestore(&cmd->t_state_lock, flags);
-       return -1;
-}
-
 /*
  * Process all commands up to the last received ORDERED task attribute which
  * requires another blocking boundary
@@ -1985,7 +1963,7 @@ static void transport_handle_queue_full(
 static void target_complete_ok_work(struct work_struct *work)
 {
        struct se_cmd *cmd = container_of(work, struct se_cmd, work);
-       int reason = 0, ret;
+       int ret;
 
        /*
         * Check if we need to move delayed/dormant tasks from cmds on the
@@ -2002,23 +1980,19 @@ static void target_complete_ok_work(struct work_struct *work)
                schedule_work(&cmd->se_dev->qf_work_queue);
 
        /*
-        * Check if we need to retrieve a sense buffer from
+        * Check if we need to send a sense buffer from
         * the struct se_cmd in question.
         */
        if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) {
-               if (transport_get_sense_data(cmd) < 0)
-                       reason = TCM_NON_EXISTENT_LUN;
-
-               if (cmd->scsi_status) {
-                       ret = transport_send_check_condition_and_sense(
-                                       cmd, reason, 1);
-                       if (ret == -EAGAIN || ret == -ENOMEM)
-                               goto queue_full;
+               WARN_ON(!cmd->scsi_status);
+               ret = transport_send_check_condition_and_sense(
+                                       cmd, 0, 1);
+               if (ret == -EAGAIN || ret == -ENOMEM)
+                       goto queue_full;
 
-                       transport_lun_remove_cmd(cmd);
-                       transport_cmd_check_stop_to_fabric(cmd);
-                       return;
-               }
+               transport_lun_remove_cmd(cmd);
+               transport_cmd_check_stop_to_fabric(cmd);
+               return;
        }
        /*
         * Check for a callback, used by amongst other things
@@ -2216,7 +2190,6 @@ void *transport_kmap_data_sg(struct se_cmd *cmd)
        struct page **pages;
        int i;
 
-       BUG_ON(!sg);
        /*
         * We need to take into account a possible offset here for fabrics like
         * tcm_loop who may be using a contig buffer from the SCSI midlayer for
@@ -2224,13 +2197,17 @@ void *transport_kmap_data_sg(struct se_cmd *cmd)
         */
        if (!cmd->t_data_nents)
                return NULL;
-       else if (cmd->t_data_nents == 1)
+
+       BUG_ON(!sg);
+       if (cmd->t_data_nents == 1)
                return kmap(sg_page(sg)) + sg->offset;
 
        /* >1 page. use vmap */
        pages = kmalloc(sizeof(*pages) * cmd->t_data_nents, GFP_KERNEL);
-       if (!pages)
+       if (!pages) {
+               cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
                return NULL;
+       }
 
        /* convert sg[] to pages[] */
        for_each_sg(cmd->t_data_sg, sg, cmd->t_data_nents, i) {
@@ -2239,8 +2216,10 @@ void *transport_kmap_data_sg(struct se_cmd *cmd)
 
        cmd->t_data_vmap = vmap(pages, cmd->t_data_nents,  VM_MAP, PAGE_KERNEL);
        kfree(pages);
-       if (!cmd->t_data_vmap)
+       if (!cmd->t_data_vmap) {
+               cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
                return NULL;
+       }
 
        return cmd->t_data_vmap + cmd->t_data_sg[0].offset;
 }
@@ -2326,19 +2305,14 @@ int transport_generic_new_cmd(struct se_cmd *cmd)
         * into the fabric for data transfers, go ahead and complete it right
         * away.
         */
-       if (!cmd->data_length) {
+       if (!cmd->data_length &&
+           cmd->t_task_cdb[0] != REQUEST_SENSE &&
+           cmd->se_dev->transport->transport_type != TRANSPORT_PLUGIN_PHBA_PDEV) {
                spin_lock_irq(&cmd->t_state_lock);
                cmd->t_state = TRANSPORT_COMPLETE;
                cmd->transport_state |= CMD_T_ACTIVE;
                spin_unlock_irq(&cmd->t_state_lock);
 
-               if (cmd->t_task_cdb[0] == REQUEST_SENSE) {
-                       u8 ua_asc = 0, ua_ascq = 0;
-
-                       core_scsi3_ua_clear_for_request_sense(cmd,
-                                       &ua_asc, &ua_ascq);
-               }
-
                INIT_WORK(&cmd->work, target_complete_ok_work);
                queue_work(target_completion_wq, &cmd->work);
                return 0;
index d5c689d6217e3a2eb46223fa1ffd01b77e15e0df..e309e8b0aaba0c10f0e45d72c57480000ed43d6b 100644 (file)
 #define  UCR4_OREN      (1<<1)  /* Receiver overrun interrupt enable */
 #define  UCR4_DREN      (1<<0)  /* Recv data ready interrupt enable */
 #define  UFCR_RXTL_SHF   0       /* Receiver trigger level shift */
+#define  UFCR_DCEDTE    (1<<6)  /* DCE/DTE mode select */
 #define  UFCR_RFDIV      (7<<7)  /* Reference freq divider mask */
 #define  UFCR_RFDIV_REG(x)     (((x) < 7 ? 6 - (x) : 6) << 7)
 #define  UFCR_TXTL_SHF   10      /* Transmitter trigger level shift */
@@ -667,22 +668,11 @@ static void imx_break_ctl(struct uart_port *port, int break_state)
 static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
 {
        unsigned int val;
-       unsigned int ufcr_rfdiv;
-
-       /* set receiver / transmitter trigger level.
-        * RFDIV is set such way to satisfy requested uartclk value
-        */
-       val = TXTL << 10 | RXTL;
-       ufcr_rfdiv = (clk_get_rate(sport->clk_per) + sport->port.uartclk / 2)
-                       / sport->port.uartclk;
-
-       if(!ufcr_rfdiv)
-               ufcr_rfdiv = 1;
-
-       val |= UFCR_RFDIV_REG(ufcr_rfdiv);
 
+       /* set receiver / transmitter trigger level */
+       val = readl(sport->port.membase + UFCR) & (UFCR_RFDIV | UFCR_DCEDTE);
+       val |= TXTL << UFCR_TXTL_SHF | RXTL;
        writel(val, sport->port.membase + UFCR);
-
        return 0;
 }
 
@@ -754,6 +744,7 @@ static int imx_startup(struct uart_port *port)
                }
        }
 
+       spin_lock_irqsave(&sport->port.lock, flags);
        /*
         * Finally, clear and enable interrupts
         */
@@ -807,7 +798,6 @@ static int imx_startup(struct uart_port *port)
        /*
         * Enable modem status interrupts
         */
-       spin_lock_irqsave(&sport->port.lock,flags);
        imx_enable_ms(&sport->port);
        spin_unlock_irqrestore(&sport->port.lock,flags);
 
@@ -837,10 +827,13 @@ static void imx_shutdown(struct uart_port *port)
 {
        struct imx_port *sport = (struct imx_port *)port;
        unsigned long temp;
+       unsigned long flags;
 
+       spin_lock_irqsave(&sport->port.lock, flags);
        temp = readl(sport->port.membase + UCR2);
        temp &= ~(UCR2_TXEN);
        writel(temp, sport->port.membase + UCR2);
+       spin_unlock_irqrestore(&sport->port.lock, flags);
 
        if (USE_IRDA(sport)) {
                struct imxuart_platform_data *pdata;
@@ -869,12 +862,14 @@ static void imx_shutdown(struct uart_port *port)
         * Disable all interrupts, port and break condition.
         */
 
+       spin_lock_irqsave(&sport->port.lock, flags);
        temp = readl(sport->port.membase + UCR1);
        temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
        if (USE_IRDA(sport))
                temp &= ~(UCR1_IREN);
 
        writel(temp, sport->port.membase + UCR1);
+       spin_unlock_irqrestore(&sport->port.lock, flags);
 }
 
 static void
@@ -1217,6 +1212,9 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
        struct imx_port *sport = imx_ports[co->index];
        struct imx_port_ucrs old_ucr;
        unsigned int ucr1;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sport->port.lock, flags);
 
        /*
         *      First, save UCR1/2/3 and then disable interrupts
@@ -1242,6 +1240,8 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
        while (!(readl(sport->port.membase + USR2) & USR2_TXDC));
 
        imx_port_ucrs_restore(&sport->port, &old_ucr);
+
+       spin_unlock_irqrestore(&sport->port.lock, flags);
 }
 
 /*
index c7a032a4f0c54b4aa975e4dbc3dba9ba43cabfc9..d214448b677e68d1eeff7847fea48bb46942f31f 100644 (file)
@@ -78,8 +78,7 @@ static inline int ep_to_bit(struct ci13xxx *ci, int n)
 }
 
 /**
- * hw_device_state: enables/disables interrupts & starts/stops device (execute
- *                  without interruption)
+ * hw_device_state: enables/disables interrupts (execute without interruption)
  * @dma: 0 => disable, !0 => enable and set dma engine
  *
  * This function returns an error code
@@ -91,9 +90,7 @@ static int hw_device_state(struct ci13xxx *ci, u32 dma)
                /* interrupt, error, port change, reset, sleep/suspend */
                hw_write(ci, OP_USBINTR, ~0,
                             USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI);
-               hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
        } else {
-               hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
                hw_write(ci, OP_USBINTR, ~0, 0);
        }
        return 0;
@@ -774,10 +771,7 @@ __acquires(mEp->lock)
 {
        struct ci13xxx_req *mReq, *mReqTemp;
        struct ci13xxx_ep *mEpTemp = mEp;
-       int uninitialized_var(retval);
-
-       if (list_empty(&mEp->qh.queue))
-               return -EINVAL;
+       int retval = 0;
 
        list_for_each_entry_safe(mReq, mReqTemp, &mEp->qh.queue,
                        queue) {
@@ -1420,6 +1414,21 @@ static int ci13xxx_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
        return -ENOTSUPP;
 }
 
+/* Change Data+ pullup status
+ * this func is used by usb_gadget_connect/disconnet
+ */
+static int ci13xxx_pullup(struct usb_gadget *_gadget, int is_on)
+{
+       struct ci13xxx *ci = container_of(_gadget, struct ci13xxx, gadget);
+
+       if (is_on)
+               hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
+       else
+               hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
+
+       return 0;
+}
+
 static int ci13xxx_start(struct usb_gadget *gadget,
                         struct usb_gadget_driver *driver);
 static int ci13xxx_stop(struct usb_gadget *gadget,
@@ -1432,6 +1441,7 @@ static int ci13xxx_stop(struct usb_gadget *gadget,
 static const struct usb_gadget_ops usb_gadget_ops = {
        .vbus_session   = ci13xxx_vbus_session,
        .wakeup         = ci13xxx_wakeup,
+       .pullup         = ci13xxx_pullup,
        .vbus_draw      = ci13xxx_vbus_draw,
        .udc_start      = ci13xxx_start,
        .udc_stop       = ci13xxx_stop,
@@ -1455,7 +1465,12 @@ static int init_eps(struct ci13xxx *ci)
 
                        mEp->ep.name      = mEp->name;
                        mEp->ep.ops       = &usb_ep_ops;
-                       mEp->ep.maxpacket = CTRL_PAYLOAD_MAX;
+                       /*
+                        * for ep0: maxP defined in desc, for other
+                        * eps, maxP is set by epautoconfig() called
+                        * by gadget layer
+                        */
+                       mEp->ep.maxpacket = (unsigned short)~0;
 
                        INIT_LIST_HEAD(&mEp->qh.queue);
                        mEp->qh.ptr = dma_pool_alloc(ci->qh_pool, GFP_KERNEL,
@@ -1475,6 +1490,7 @@ static int init_eps(struct ci13xxx *ci)
                                else
                                        ci->ep0in = mEp;
 
+                               mEp->ep.maxpacket = CTRL_PAYLOAD_MAX;
                                continue;
                        }
 
@@ -1484,6 +1500,17 @@ static int init_eps(struct ci13xxx *ci)
        return retval;
 }
 
+static void destroy_eps(struct ci13xxx *ci)
+{
+       int i;
+
+       for (i = 0; i < ci->hw_ep_max; i++) {
+               struct ci13xxx_ep *mEp = &ci->ci13xxx_ep[i];
+
+               dma_pool_free(ci->qh_pool, mEp->qh.ptr, mEp->qh.dma);
+       }
+}
+
 /**
  * ci13xxx_start: register a gadget driver
  * @gadget: our gadget
@@ -1691,7 +1718,7 @@ static int udc_start(struct ci13xxx *ci)
        if (ci->platdata->flags & CI13XXX_REQUIRE_TRANSCEIVER) {
                if (ci->transceiver == NULL) {
                        retval = -ENODEV;
-                       goto free_pools;
+                       goto destroy_eps;
                }
        }
 
@@ -1729,7 +1756,7 @@ static int udc_start(struct ci13xxx *ci)
 
 remove_trans:
        if (!IS_ERR_OR_NULL(ci->transceiver)) {
-               otg_set_peripheral(ci->transceiver->otg, &ci->gadget);
+               otg_set_peripheral(ci->transceiver->otg, NULL);
                if (ci->global_phy)
                        usb_put_phy(ci->transceiver);
        }
@@ -1742,6 +1769,8 @@ unreg_device:
 put_transceiver:
        if (!IS_ERR_OR_NULL(ci->transceiver) && ci->global_phy)
                usb_put_phy(ci->transceiver);
+destroy_eps:
+       destroy_eps(ci);
 free_pools:
        dma_pool_destroy(ci->td_pool);
 free_qh_pool:
@@ -1756,18 +1785,12 @@ free_qh_pool:
  */
 static void udc_stop(struct ci13xxx *ci)
 {
-       int i;
-
        if (ci == NULL)
                return;
 
        usb_del_gadget_udc(&ci->gadget);
 
-       for (i = 0; i < ci->hw_ep_max; i++) {
-               struct ci13xxx_ep *mEp = &ci->ci13xxx_ep[i];
-
-               dma_pool_free(ci->qh_pool, mEp->qh.ptr, mEp->qh.dma);
-       }
+       destroy_eps(ci);
 
        dma_pool_destroy(ci->td_pool);
        dma_pool_destroy(ci->qh_pool);
index 65a55abb791f53dd458f0b23c77af5497142ec6d..5f0cb417b736bb4b61c73afc52672029af59cbda 100644 (file)
@@ -109,12 +109,14 @@ static struct usb_driver wdm_driver;
 /* return intfdata if we own the interface, else look up intf in the list */
 static struct wdm_device *wdm_find_device(struct usb_interface *intf)
 {
-       struct wdm_device *desc = NULL;
+       struct wdm_device *desc;
 
        spin_lock(&wdm_device_list_lock);
        list_for_each_entry(desc, &wdm_device_list, device_list)
                if (desc->intf == intf)
-                       break;
+                       goto found;
+       desc = NULL;
+found:
        spin_unlock(&wdm_device_list_lock);
 
        return desc;
@@ -122,12 +124,14 @@ static struct wdm_device *wdm_find_device(struct usb_interface *intf)
 
 static struct wdm_device *wdm_find_device_by_minor(int minor)
 {
-       struct wdm_device *desc = NULL;
+       struct wdm_device *desc;
 
        spin_lock(&wdm_device_list_lock);
        list_for_each_entry(desc, &wdm_device_list, device_list)
                if (desc->intf->minor == minor)
-                       break;
+                       goto found;
+       desc = NULL;
+found:
        spin_unlock(&wdm_device_list_lock);
 
        return desc;
index f15501f4c585694c0c513eac78f692558ae8720f..e77a8e8eaa233b9b4febd38ef8c01ace8ac832a5 100644 (file)
@@ -71,6 +71,10 @@ static const struct usb_device_id usb_quirk_list[] = {
        { USB_DEVICE(0x04b4, 0x0526), .driver_info =
                        USB_QUIRK_CONFIG_INTF_STRINGS },
 
+       /* Microchip Joss Optical infrared touchboard device */
+       { USB_DEVICE(0x04d8, 0x000c), .driver_info =
+                       USB_QUIRK_CONFIG_INTF_STRINGS },
+
        /* Samsung Android phone modem - ID conflict with SPH-I500 */
        { USB_DEVICE(0x04e8, 0x6601), .driver_info =
                        USB_QUIRK_CONFIG_INTF_STRINGS },
index c34452a7304f9dec269ba64706ab999559e67eff..a68ff53124dc15e88ab3ceaa35e3fbf7e97cc978 100644 (file)
@@ -436,16 +436,21 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
                dev_err(dev, "missing IRQ\n");
                return -ENODEV;
        }
-       dwc->xhci_resources[1] = *res;
+       dwc->xhci_resources[1].start = res->start;
+       dwc->xhci_resources[1].end = res->end;
+       dwc->xhci_resources[1].flags = res->flags;
+       dwc->xhci_resources[1].name = res->name;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res) {
                dev_err(dev, "missing memory resource\n");
                return -ENODEV;
        }
-       dwc->xhci_resources[0] = *res;
+       dwc->xhci_resources[0].start = res->start;
        dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
                                        DWC3_XHCI_REGS_END;
+       dwc->xhci_resources[0].flags = res->flags;
+       dwc->xhci_resources[0].name = res->name;
 
         /*
          * Request memory region but exclude xHCI regs,
index 9b94886b66e589ee3040556bf284985a01a64bc6..e4d5ca86b9da5413d1c3c52079b4a2abcd9481ad 100644 (file)
@@ -720,7 +720,6 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
                transferred = min_t(u32, ur->length,
                                transfer_size - length);
                memcpy(ur->buf, dwc->ep0_bounce, transferred);
-               dwc->ep0_bounced = false;
        } else {
                transferred = ur->length - length;
        }
index 58fdfad96b4d61b2cc86de9763a135dc81fcd908..c2813c2b005a8e223f93ff50a3215e24172904c2 100644 (file)
@@ -263,8 +263,11 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
        if (req->request.status == -EINPROGRESS)
                req->request.status = status;
 
-       usb_gadget_unmap_request(&dwc->gadget, &req->request,
-                       req->direction);
+       if (dwc->ep0_bounced && dep->number == 0)
+               dwc->ep0_bounced = false;
+       else
+               usb_gadget_unmap_request(&dwc->gadget, &req->request,
+                               req->direction);
 
        dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n",
                        req, dep->name, req->request.actual,
@@ -1026,6 +1029,7 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
        if (list_empty(&dep->request_list)) {
                dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
                        dep->name);
+               dep->flags |= DWC3_EP_PENDING_REQUEST;
                return;
        }
 
@@ -1089,6 +1093,17 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
        if (dep->flags & DWC3_EP_PENDING_REQUEST) {
                int     ret;
 
+               /*
+                * If xfernotready is already elapsed and it is a case
+                * of isoc transfer, then issue END TRANSFER, so that
+                * you can receive xfernotready again and can have
+                * notion of current microframe.
+                */
+               if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+                       dwc3_stop_active_transfer(dwc, dep->number);
+                       return 0;
+               }
+
                ret = __dwc3_gadget_kick_transfer(dep, 0, true);
                if (ret && ret != -EBUSY) {
                        struct dwc3     *dwc = dep->dwc;
index c9e66dfb02e6642c1e88149884857948b44869e8..1e35963bd4edc29f735319a76e0a4eb7052b27f5 100644 (file)
@@ -475,8 +475,7 @@ static int at91_ep_enable(struct usb_ep *_ep,
        unsigned long   flags;
 
        if (!_ep || !ep
-                       || !desc || ep->ep.desc
-                       || _ep->name == ep0name
+                       || !desc || _ep->name == ep0name
                        || desc->bDescriptorType != USB_DT_ENDPOINT
                        || (maxpacket = usb_endpoint_maxp(desc)) == 0
                        || maxpacket > ep->maxpacket) {
@@ -530,7 +529,6 @@ ok:
        tmp |= AT91_UDP_EPEDS;
        __raw_writel(tmp, ep->creg);
 
-       ep->ep.desc = desc;
        ep->ep.maxpacket = maxpacket;
 
        /*
@@ -1635,7 +1633,6 @@ static int at91_start(struct usb_gadget *gadget,
        udc->driver = driver;
        udc->gadget.dev.driver = &driver->driver;
        udc->gadget.dev.of_node = udc->pdev->dev.of_node;
-       dev_set_drvdata(&udc->gadget.dev, &driver->driver);
        udc->enabled = 1;
        udc->selfpowered = 1;
 
@@ -1656,7 +1653,6 @@ static int at91_stop(struct usb_gadget *gadget,
        spin_unlock_irqrestore(&udc->lock, flags);
 
        udc->gadget.dev.driver = NULL;
-       dev_set_drvdata(&udc->gadget.dev, NULL);
        udc->driver = NULL;
 
        DBG("unbound from %s\n", driver->driver.name);
index b799106027adfc5d75a45244a47fbd015bedfd20..afdbb1cbf5d94d972c52f57f4099a1bb0202bef5 100644 (file)
@@ -1916,6 +1916,27 @@ done:
        return retval;
 }
 
+/* usb 3.0 root hub device descriptor */
+struct {
+       struct usb_bos_descriptor bos;
+       struct usb_ss_cap_descriptor ss_cap;
+} __packed usb3_bos_desc = {
+
+       .bos = {
+               .bLength                = USB_DT_BOS_SIZE,
+               .bDescriptorType        = USB_DT_BOS,
+               .wTotalLength           = cpu_to_le16(sizeof(usb3_bos_desc)),
+               .bNumDeviceCaps         = 1,
+       },
+       .ss_cap = {
+               .bLength                = USB_DT_USB_SS_CAP_SIZE,
+               .bDescriptorType        = USB_DT_DEVICE_CAPABILITY,
+               .bDevCapabilityType     = USB_SS_CAP_TYPE,
+               .wSpeedSupported        = cpu_to_le16(USB_5GBPS_OPERATION),
+               .bFunctionalitySupport  = ilog2(USB_5GBPS_OPERATION),
+       },
+};
+
 static inline void
 ss_hub_descriptor(struct usb_hub_descriptor *desc)
 {
@@ -2006,6 +2027,18 @@ static int dummy_hub_control(
                else
                        hub_descriptor((struct usb_hub_descriptor *) buf);
                break;
+
+       case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+               if (hcd->speed != HCD_USB3)
+                       goto error;
+
+               if ((wValue >> 8) != USB_DT_BOS)
+                       goto error;
+
+               memcpy(buf, &usb3_bos_desc, sizeof(usb3_bos_desc));
+               retval = sizeof(usb3_bos_desc);
+               break;
+
        case GetHubStatus:
                *(__le32 *) buf = cpu_to_le32(0);
                break;
@@ -2503,10 +2536,8 @@ static int dummy_hcd_probe(struct platform_device *pdev)
        hs_hcd->has_tt = 1;
 
        retval = usb_add_hcd(hs_hcd, 0, 0);
-       if (retval != 0) {
-               usb_put_hcd(hs_hcd);
-               return retval;
-       }
+       if (retval)
+               goto put_usb2_hcd;
 
        if (mod_data.is_super_speed) {
                ss_hcd = usb_create_shared_hcd(&dummy_hcd, &pdev->dev,
@@ -2525,6 +2556,8 @@ static int dummy_hcd_probe(struct platform_device *pdev)
 put_usb3_hcd:
        usb_put_hcd(ss_hcd);
 dealloc_usb2_hcd:
+       usb_remove_hcd(hs_hcd);
+put_usb2_hcd:
        usb_put_hcd(hs_hcd);
        the_controller.hs_hcd = the_controller.ss_hcd = NULL;
        return retval;
index 8adc79d1b40277ac15092a53bac28749627c60cf..829aba75a6dfef28f1ce79055f5b73d6df883c68 100644 (file)
 /* Debugging ****************************************************************/
 
 #ifdef VERBOSE_DEBUG
+#ifndef pr_vdebug
 #  define pr_vdebug pr_debug
+#endif /* pr_vdebug */
 #  define ffs_dump_mem(prefix, ptr, len) \
        print_hex_dump_bytes(pr_fmt(prefix ": "), DUMP_PREFIX_NONE, ptr, len)
 #else
+#ifndef pr_vdebug
 #  define pr_vdebug(...)                 do { } while (0)
+#endif /* pr_vdebug */
 #  define ffs_dump_mem(prefix, ptr, len) do { } while (0)
 #endif /* VERBOSE_DEBUG */
 
index b13e0bb5f5b8131de7cc26a928449215f2e0334c..0bb617e1dda2e0ecf2a440b6494761338f15004c 100644 (file)
@@ -3599,6 +3599,7 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
 
        if (hsotg->num_of_eps == 0) {
                dev_err(dev, "wrong number of EPs (zero)\n");
+               ret = -EINVAL;
                goto err_supplies;
        }
 
@@ -3606,6 +3607,7 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
                      GFP_KERNEL);
        if (!eps) {
                dev_err(dev, "cannot get memory\n");
+               ret = -ENOMEM;
                goto err_supplies;
        }
 
@@ -3622,6 +3624,7 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
                                                     GFP_KERNEL);
        if (!hsotg->ctrl_req) {
                dev_err(dev, "failed to allocate ctrl req\n");
+               ret = -ENOMEM;
                goto err_ep_mem;
        }
 
index 5b3f5fffea92d241b50587b885c24bf69f83e878..da6d479ff9a61e5f57950be3b78e052a029eaa32 100644 (file)
@@ -132,11 +132,15 @@ static unsigned   n_ports;
 
 
 #ifdef VERBOSE_DEBUG
+#ifndef pr_vdebug
 #define pr_vdebug(fmt, arg...) \
        pr_debug(fmt, ##arg)
+#endif /* pr_vdebug */
 #else
+#ifndef pr_vdebig
 #define pr_vdebug(fmt, arg...) \
        ({ if (0) pr_debug(fmt, ##arg); })
+#endif /* pr_vdebug */
 #endif
 
 /*-------------------------------------------------------------------------*/
index 9bc39ca460c80bdfe275b313a47f4683a81e4f3c..4b66374bdc8e33f74e20ff7bc30b7894ab24698e 100644 (file)
@@ -128,9 +128,17 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh)
        else {
                qtd = list_entry (qh->qtd_list.next,
                                struct ehci_qtd, qtd_list);
-               /* first qtd may already be partially processed */
-               if (cpu_to_hc32(ehci, qtd->qtd_dma) == qh->hw->hw_current)
+               /*
+                * first qtd may already be partially processed.
+                * If we come here during unlink, the QH overlay region
+                * might have reference to the just unlinked qtd. The
+                * qtd is updated in qh_completions(). Update the QH
+                * overlay here.
+                */
+               if (cpu_to_hc32(ehci, qtd->qtd_dma) == qh->hw->hw_current) {
+                       qh->hw->hw_qtd_next = qtd->hw_next;
                        qtd = NULL;
+               }
        }
 
        if (qtd)
index a665b3eaa74672f28e7d011b1ea4da265667187f..aaa8d2bce21702aa8d7844bb343de68bd30f0a3d 100644 (file)
@@ -570,6 +570,16 @@ static int __devinit ohci_hcd_at91_drv_probe(struct platform_device *pdev)
 
        if (pdata) {
                at91_for_each_port(i) {
+                       /*
+                        * do not configure PIO if not in relation with
+                        * real USB port on board
+                        */
+                       if (i >= pdata->ports) {
+                               pdata->vbus_pin[i] = -EINVAL;
+                               pdata->overcurrent_pin[i] = -EINVAL;
+                               break;
+                       }
+
                        if (!gpio_is_valid(pdata->vbus_pin[i]))
                                continue;
                        gpio = pdata->vbus_pin[i];
index c5e9e4a76f148d4eed0c4785cf46fb38d143a074..966d1484ee79a2db8c5e78e135c5e0760814d183 100644 (file)
@@ -75,7 +75,9 @@
 #define        NB_PIF0_PWRDOWN_1       0x01100013
 
 #define USB_INTEL_XUSB2PR      0xD0
+#define USB_INTEL_USB2PRM      0xD4
 #define USB_INTEL_USB3_PSSEN   0xD8
+#define USB_INTEL_USB3PRM      0xDC
 
 static struct amd_chipset_info {
        struct pci_dev  *nb_dev;
@@ -772,10 +774,18 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev)
                return;
        }
 
-       ports_available = 0xffffffff;
+       /* Read USB3PRM, the USB 3.0 Port Routing Mask Register
+        * Indicate the ports that can be changed from OS.
+        */
+       pci_read_config_dword(xhci_pdev, USB_INTEL_USB3PRM,
+                       &ports_available);
+
+       dev_dbg(&xhci_pdev->dev, "Configurable ports to enable SuperSpeed: 0x%x\n",
+                       ports_available);
+
        /* Write USB3_PSSEN, the USB 3.0 Port SuperSpeed Enable
-        * Register, to turn on SuperSpeed terminations for all
-        * available ports.
+        * Register, to turn on SuperSpeed terminations for the
+        * switchable ports.
         */
        pci_write_config_dword(xhci_pdev, USB_INTEL_USB3_PSSEN,
                        cpu_to_le32(ports_available));
@@ -785,7 +795,16 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev)
        dev_dbg(&xhci_pdev->dev, "USB 3.0 ports that are now enabled "
                        "under xHCI: 0x%x\n", ports_available);
 
-       ports_available = 0xffffffff;
+       /* Read XUSB2PRM, xHCI USB 2.0 Port Routing Mask Register
+        * Indicate the USB 2.0 ports to be controlled by the xHCI host.
+        */
+
+       pci_read_config_dword(xhci_pdev, USB_INTEL_USB2PRM,
+                       &ports_available);
+
+       dev_dbg(&xhci_pdev->dev, "Configurable USB 2.0 ports to hand over to xCHI: 0x%x\n",
+                       ports_available);
+
        /* Write XUSB2PR, the xHC USB 2.0 Port Routing Register, to
         * switch the USB 2.0 power and data lines over to the xHCI
         * host.
@@ -822,12 +841,12 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev)
        void __iomem *op_reg_base;
        u32 val;
        int timeout;
+       int len = pci_resource_len(pdev, 0);
 
        if (!mmio_resource_enabled(pdev, 0))
                return;
 
-       base = ioremap_nocache(pci_resource_start(pdev, 0),
-                               pci_resource_len(pdev, 0));
+       base = ioremap_nocache(pci_resource_start(pdev, 0), len);
        if (base == NULL)
                return;
 
@@ -837,9 +856,17 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev)
         */
        ext_cap_offset = xhci_find_next_cap_offset(base, XHCI_HCC_PARAMS_OFFSET);
        do {
+               if ((ext_cap_offset + sizeof(val)) > len) {
+                       /* We're reading garbage from the controller */
+                       dev_warn(&pdev->dev,
+                                "xHCI controller failing to respond");
+                       return;
+               }
+
                if (!ext_cap_offset)
                        /* We've reached the end of the extended capabilities */
                        goto hc_init;
+
                val = readl(base + ext_cap_offset);
                if (XHCI_EXT_CAPS_ID(val) == XHCI_EXT_CAPS_LEGACY)
                        break;
@@ -870,9 +897,10 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev)
        /* Disable any BIOS SMIs and clear all SMI events*/
        writel(val, base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET);
 
+hc_init:
        if (usb_is_intel_switchable_xhci(pdev))
                usb_enable_xhci_ports(pdev);
-hc_init:
+
        op_reg_base = base + XHCI_HC_LENGTH(readl(base));
 
        /* Wait for the host controller to be ready before writing any
index ef004a5de20f176c27801f83bd9ae2456570e6b1..7f69a39163ce3b5560f9e0e24a0a9d86e7f77cef 100644 (file)
@@ -15,6 +15,7 @@ void usb_disable_xhci_ports(struct pci_dev *xhci_pdev);
 static inline void usb_amd_quirk_pll_disable(void) {}
 static inline void usb_amd_quirk_pll_enable(void) {}
 static inline void usb_amd_dev_put(void) {}
+static inline void usb_disable_xhci_ports(struct pci_dev *xhci_pdev) {}
 #endif  /* CONFIG_PCI */
 
 #endif  /*  __LINUX_USB_PCI_QUIRKS_H  */
index 74bfc868b7ade609dc67cea66195bd499f92b067..d5eb357aa5c42cff5575bdf1297f3ebbafa49d85 100644 (file)
@@ -493,11 +493,48 @@ static void xhci_hub_report_link_state(u32 *status, u32 status_reg)
                 * when this bit is set.
                 */
                pls |= USB_PORT_STAT_CONNECTION;
+       } else {
+               /*
+                * If CAS bit isn't set but the Port is already at
+                * Compliance Mode, fake a connection so the USB core
+                * notices the Compliance state and resets the port.
+                * This resolves an issue generated by the SN65LVPE502CP
+                * in which sometimes the port enters compliance mode
+                * caused by a delay on the host-device negotiation.
+                */
+               if (pls == USB_SS_PORT_LS_COMP_MOD)
+                       pls |= USB_PORT_STAT_CONNECTION;
        }
+
        /* update status field */
        *status |= pls;
 }
 
+/*
+ * Function for Compliance Mode Quirk.
+ *
+ * This Function verifies if all xhc USB3 ports have entered U0, if so,
+ * the compliance mode timer is deleted. A port won't enter
+ * compliance mode if it has previously entered U0.
+ */
+void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, u16 wIndex)
+{
+       u32 all_ports_seen_u0 = ((1 << xhci->num_usb3_ports)-1);
+       bool port_in_u0 = ((status & PORT_PLS_MASK) == XDEV_U0);
+
+       if (!(xhci->quirks & XHCI_COMP_MODE_QUIRK))
+               return;
+
+       if ((xhci->port_status_u0 != all_ports_seen_u0) && port_in_u0) {
+               xhci->port_status_u0 |= 1 << wIndex;
+               if (xhci->port_status_u0 == all_ports_seen_u0) {
+                       del_timer_sync(&xhci->comp_mode_recovery_timer);
+                       xhci_dbg(xhci, "All USB3 ports have entered U0 already!\n");
+                       xhci_dbg(xhci, "Compliance Mode Recovery Timer Deleted.\n");
+               }
+       }
+}
+
 int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                u16 wIndex, char *buf, u16 wLength)
 {
@@ -651,6 +688,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                /* Update Port Link State for super speed ports*/
                if (hcd->speed == HCD_USB3) {
                        xhci_hub_report_link_state(&status, temp);
+                       /*
+                        * Verify if all USB3 Ports Have entered U0 already.
+                        * Delete Compliance Mode Timer if so.
+                        */
+                       xhci_del_comp_mod_timer(xhci, temp, wIndex);
                }
                if (bus_state->port_c_suspend & (1 << wIndex))
                        status |= 1 << USB_PORT_FEAT_C_SUSPEND;
index 689bc18b051d6c1d1ae8e381e887a01edbdfc9e9..df90fe51b4aa2d406b8b2f8bfa591c410df89a35 100644 (file)
@@ -118,7 +118,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
                goto put_hcd;
        }
 
-       hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+       hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
        if (!hcd->regs) {
                dev_dbg(&pdev->dev, "error mapping memory\n");
                ret = -EFAULT;
index c59d5b5b6c7d227a8005ca520e8b9badad5207b1..6ece0ed288d4da9398bb96ecc8223f31b6ce8e0c 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/slab.h>
+#include <linux/dmi.h>
 
 #include "xhci.h"
 
@@ -398,6 +399,95 @@ static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
 
 #endif
 
+static void compliance_mode_recovery(unsigned long arg)
+{
+       struct xhci_hcd *xhci;
+       struct usb_hcd *hcd;
+       u32 temp;
+       int i;
+
+       xhci = (struct xhci_hcd *)arg;
+
+       for (i = 0; i < xhci->num_usb3_ports; i++) {
+               temp = xhci_readl(xhci, xhci->usb3_ports[i]);
+               if ((temp & PORT_PLS_MASK) == USB_SS_PORT_LS_COMP_MOD) {
+                       /*
+                        * Compliance Mode Detected. Letting USB Core
+                        * handle the Warm Reset
+                        */
+                       xhci_dbg(xhci, "Compliance Mode Detected->Port %d!\n",
+                                       i + 1);
+                       xhci_dbg(xhci, "Attempting Recovery routine!\n");
+                       hcd = xhci->shared_hcd;
+
+                       if (hcd->state == HC_STATE_SUSPENDED)
+                               usb_hcd_resume_root_hub(hcd);
+
+                       usb_hcd_poll_rh_status(hcd);
+               }
+       }
+
+       if (xhci->port_status_u0 != ((1 << xhci->num_usb3_ports)-1))
+               mod_timer(&xhci->comp_mode_recovery_timer,
+                       jiffies + msecs_to_jiffies(COMP_MODE_RCVRY_MSECS));
+}
+
+/*
+ * Quirk to work around issue generated by the SN65LVPE502CP USB3.0 re-driver
+ * that causes ports behind that hardware to enter compliance mode sometimes.
+ * The quirk creates a timer that polls every 2 seconds the link state of
+ * each host controller's port and recovers it by issuing a Warm reset
+ * if Compliance mode is detected, otherwise the port will become "dead" (no
+ * device connections or disconnections will be detected anymore). Becasue no
+ * status event is generated when entering compliance mode (per xhci spec),
+ * this quirk is needed on systems that have the failing hardware installed.
+ */
+static void compliance_mode_recovery_timer_init(struct xhci_hcd *xhci)
+{
+       xhci->port_status_u0 = 0;
+       init_timer(&xhci->comp_mode_recovery_timer);
+
+       xhci->comp_mode_recovery_timer.data = (unsigned long) xhci;
+       xhci->comp_mode_recovery_timer.function = compliance_mode_recovery;
+       xhci->comp_mode_recovery_timer.expires = jiffies +
+                       msecs_to_jiffies(COMP_MODE_RCVRY_MSECS);
+
+       set_timer_slack(&xhci->comp_mode_recovery_timer,
+                       msecs_to_jiffies(COMP_MODE_RCVRY_MSECS));
+       add_timer(&xhci->comp_mode_recovery_timer);
+       xhci_dbg(xhci, "Compliance Mode Recovery Timer Initialized.\n");
+}
+
+/*
+ * This function identifies the systems that have installed the SN65LVPE502CP
+ * USB3.0 re-driver and that need the Compliance Mode Quirk.
+ * Systems:
+ * Vendor: Hewlett-Packard -> System Models: Z420, Z620 and Z820
+ */
+static bool compliance_mode_recovery_timer_quirk_check(void)
+{
+       const char *dmi_product_name, *dmi_sys_vendor;
+
+       dmi_product_name = dmi_get_system_info(DMI_PRODUCT_NAME);
+       dmi_sys_vendor = dmi_get_system_info(DMI_SYS_VENDOR);
+
+       if (!(strstr(dmi_sys_vendor, "Hewlett-Packard")))
+               return false;
+
+       if (strstr(dmi_product_name, "Z420") ||
+                       strstr(dmi_product_name, "Z620") ||
+                       strstr(dmi_product_name, "Z820"))
+               return true;
+
+       return false;
+}
+
+static int xhci_all_ports_seen_u0(struct xhci_hcd *xhci)
+{
+       return (xhci->port_status_u0 == ((1 << xhci->num_usb3_ports)-1));
+}
+
+
 /*
  * Initialize memory for HCD and xHC (one-time init).
  *
@@ -421,6 +511,12 @@ int xhci_init(struct usb_hcd *hcd)
        retval = xhci_mem_init(xhci, GFP_KERNEL);
        xhci_dbg(xhci, "Finished xhci_init\n");
 
+       /* Initializing Compliance Mode Recovery Data If Needed */
+       if (compliance_mode_recovery_timer_quirk_check()) {
+               xhci->quirks |= XHCI_COMP_MODE_QUIRK;
+               compliance_mode_recovery_timer_init(xhci);
+       }
+
        return retval;
 }
 
@@ -629,6 +725,11 @@ void xhci_stop(struct usb_hcd *hcd)
        del_timer_sync(&xhci->event_ring_timer);
 #endif
 
+       /* Deleting Compliance Mode Recovery Timer */
+       if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
+                       (!(xhci_all_ports_seen_u0(xhci))))
+               del_timer_sync(&xhci->comp_mode_recovery_timer);
+
        if (xhci->quirks & XHCI_AMD_PLL_FIX)
                usb_amd_dev_put();
 
@@ -659,7 +760,7 @@ void xhci_shutdown(struct usb_hcd *hcd)
 {
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
 
-       if (xhci->quirks && XHCI_SPURIOUS_REBOOT)
+       if (xhci->quirks & XHCI_SPURIOUS_REBOOT)
                usb_disable_xhci_ports(to_pci_dev(hcd->self.controller));
 
        spin_lock_irq(&xhci->lock);
@@ -806,6 +907,16 @@ int xhci_suspend(struct xhci_hcd *xhci)
        }
        spin_unlock_irq(&xhci->lock);
 
+       /*
+        * Deleting Compliance Mode Recovery Timer because the xHCI Host
+        * is about to be suspended.
+        */
+       if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
+                       (!(xhci_all_ports_seen_u0(xhci)))) {
+               del_timer_sync(&xhci->comp_mode_recovery_timer);
+               xhci_dbg(xhci, "Compliance Mode Recovery Timer Deleted!\n");
+       }
+
        /* step 5: remove core well power */
        /* synchronize irq when using MSI-X */
        xhci_msix_sync_irqs(xhci);
@@ -938,6 +1049,16 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
                usb_hcd_resume_root_hub(hcd);
                usb_hcd_resume_root_hub(xhci->shared_hcd);
        }
+
+       /*
+        * If system is subject to the Quirk, Compliance Mode Timer needs to
+        * be re-initialized Always after a system resume. Ports are subject
+        * to suffer the Compliance Mode issue again. It doesn't matter if
+        * ports have entered previously to U0 before system's suspension.
+        */
+       if (xhci->quirks & XHCI_COMP_MODE_QUIRK)
+               compliance_mode_recovery_timer_init(xhci);
+
        return retval;
 }
 #endif /* CONFIG_PM */
index c713256297acd073e7589f2fdfd936390fb8bbd0..1a05908c66737ce94a77f880d1bc14cb7e40134a 100644 (file)
@@ -1495,6 +1495,7 @@ struct xhci_hcd {
 #define XHCI_LPM_SUPPORT       (1 << 11)
 #define XHCI_INTEL_HOST                (1 << 12)
 #define XHCI_SPURIOUS_REBOOT   (1 << 13)
+#define XHCI_COMP_MODE_QUIRK   (1 << 14)
        unsigned int            num_active_eps;
        unsigned int            limit_active_eps;
        /* There are two roothubs to keep track of bus suspend info for */
@@ -1511,6 +1512,11 @@ struct xhci_hcd {
        unsigned                sw_lpm_support:1;
        /* support xHCI 1.0 spec USB2 hardware LPM */
        unsigned                hw_lpm_support:1;
+       /* Compliance Mode Recovery Data */
+       struct timer_list       comp_mode_recovery_timer;
+       u32                     port_status_u0;
+/* Compliance Mode Timer Triggered every 2 seconds */
+#define COMP_MODE_RCVRY_MSECS 2000
 };
 
 /* convert between an HCD pointer and the corresponding EHCI_HCD */
index 4bb717d0bd41b12a8b0f6fd879c987286a8059b5..1ae378d5fc6f25b5861eb2e3ef79b32a76a4885f 100644 (file)
@@ -2049,7 +2049,7 @@ static int musb_urb_enqueue(
         * we only have work to do in the former case.
         */
        spin_lock_irqsave(&musb->lock, flags);
-       if (hep->hcpriv) {
+       if (hep->hcpriv || !next_urb(qh)) {
                /* some concurrent activity submitted another urb to hep...
                 * odd, rare, error prone, but legal.
                 */
index 57a608584e160248708aca39570a7e30bf55d7a5..c1be687e00ec722524f8807ff8d628ec7a5d0798 100644 (file)
@@ -388,7 +388,7 @@ dma_controller_create(struct musb *musb, void __iomem *base)
        struct platform_device *pdev = to_platform_device(dev);
        int irq = platform_get_irq_byname(pdev, "dma");
 
-       if (irq == 0) {
+       if (irq <= 0) {
                dev_err(dev, "No DMA interrupt line!\n");
                return NULL;
        }
index 1a1bd9cf40c5ce7c1d6f7ef1ac46360d0b6884a4..341625442377ec6f1daac69450269c95f4148eb5 100644 (file)
@@ -1215,7 +1215,7 @@ static int __devinit tusb_probe(struct platform_device *pdev)
        ret = platform_device_add(musb);
        if (ret) {
                dev_err(&pdev->dev, "failed to register musb device\n");
-               goto err1;
+               goto err2;
        }
 
        return 0;
index ecd173032fd480cc5954c48f03aa61e7b5c439e8..143c4e9e1be45cc24c0a612d38e590a5ad136be9 100644 (file)
@@ -818,7 +818,7 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
            usbhs_pipe_is_dcp(pipe))
                goto usbhsf_pio_prepare_push;
 
-       if (len % 4) /* 32bit alignment */
+       if (len & 0x7) /* 8byte alignment */
                goto usbhsf_pio_prepare_push;
 
        if ((uintptr_t)(pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */
@@ -905,7 +905,7 @@ static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done)
        /* use PIO if packet is less than pio_dma_border */
        len = usbhsf_fifo_rcv_len(priv, fifo);
        len = min(pkt->length - pkt->actual, len);
-       if (len % 4) /* 32bit alignment */
+       if (len & 0x7) /* 8byte alignment */
                goto usbhsf_pio_prepare_pop_unselect;
 
        if (len < usbhs_get_dparam(priv, pio_dma_border))
index 5620db6469e586f85ea8eacfa79798b1b4a676d8..f906b3aec2179ceb708c9cce772cd85a4e1a1724 100644 (file)
@@ -704,6 +704,7 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_PCDJ_DAC2_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_RRCIRKITS_LOCOBUFFER_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_ASK_RDR400_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_NZR_SEM_USB_PID) },
        { USB_DEVICE(ICOM_VID, ICOM_ID_1_PID) },
        { USB_DEVICE(ICOM_VID, ICOM_OPC_U_UC_PID) },
        { USB_DEVICE(ICOM_VID, ICOM_ID_RP2C1_PID) },
@@ -804,13 +805,32 @@ static struct usb_device_id id_table_combined [] = {
                .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
        { USB_DEVICE(ADI_VID, ADI_GNICEPLUS_PID),
                .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
-       { USB_DEVICE(MICROCHIP_VID, MICROCHIP_USB_BOARD_PID) },
+       { USB_DEVICE_AND_INTERFACE_INFO(MICROCHIP_VID, MICROCHIP_USB_BOARD_PID,
+                                       USB_CLASS_VENDOR_SPEC,
+                                       USB_SUBCLASS_VENDOR_SPEC, 0x00) },
        { USB_DEVICE(JETI_VID, JETI_SPC1201_PID) },
        { USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID),
                .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
        { USB_DEVICE(LARSENBRUSGAARD_VID, LB_ALTITRACK_PID) },
        { USB_DEVICE(GN_OTOMETRICS_VID, AURICAL_USB_PID) },
+       { USB_DEVICE(FTDI_VID, PI_C865_PID) },
+       { USB_DEVICE(FTDI_VID, PI_C857_PID) },
+       { USB_DEVICE(PI_VID, PI_C866_PID) },
+       { USB_DEVICE(PI_VID, PI_C663_PID) },
+       { USB_DEVICE(PI_VID, PI_C725_PID) },
+       { USB_DEVICE(PI_VID, PI_E517_PID) },
+       { USB_DEVICE(PI_VID, PI_C863_PID) },
        { USB_DEVICE(PI_VID, PI_E861_PID) },
+       { USB_DEVICE(PI_VID, PI_C867_PID) },
+       { USB_DEVICE(PI_VID, PI_E609_PID) },
+       { USB_DEVICE(PI_VID, PI_E709_PID) },
+       { USB_DEVICE(PI_VID, PI_100F_PID) },
+       { USB_DEVICE(PI_VID, PI_1011_PID) },
+       { USB_DEVICE(PI_VID, PI_1012_PID) },
+       { USB_DEVICE(PI_VID, PI_1013_PID) },
+       { USB_DEVICE(PI_VID, PI_1014_PID) },
+       { USB_DEVICE(PI_VID, PI_1015_PID) },
+       { USB_DEVICE(PI_VID, PI_1016_PID) },
        { USB_DEVICE(KONDO_VID, KONDO_USB_SERIAL_PID) },
        { USB_DEVICE(BAYER_VID, BAYER_CONTOUR_CABLE_PID) },
        { USB_DEVICE(FTDI_VID, MARVELL_OPENRD_PID),
index 5dd96ca6c380a0971921d198e12399e1902663db..41fe5826100c0ad27a62d21d16e8d5845dfa25cc 100644 (file)
@@ -75,6 +75,9 @@
 #define FTDI_OPENDCC_GATEWAY_PID       0xBFDB
 #define FTDI_OPENDCC_GBM_PID   0xBFDC
 
+/* NZR SEM 16+ USB (http://www.nzr.de) */
+#define FTDI_NZR_SEM_USB_PID   0xC1E0  /* NZR SEM-LOG16+ */
+
 /*
  * RR-CirKits LocoBuffer USB (http://www.rr-cirkits.com)
  */
 /*
  * Microchip Technology, Inc.
  *
- * MICROCHIP_VID (0x04D8) and MICROCHIP_USB_BOARD_PID (0x000A) are also used by:
+ * MICROCHIP_VID (0x04D8) and MICROCHIP_USB_BOARD_PID (0x000A) are
+ * used by single function CDC ACM class based firmware demo
+ * applications.  The VID/PID has also been used in firmware
+ * emulating FTDI serial chips by:
  * Hornby Elite - Digital Command Control Console
  * http://www.hornby.com/hornby-dcc/controllers/
  */
  * Physik Instrumente
  * http://www.physikinstrumente.com/en/products/
  */
+/* These two devices use the VID of FTDI */
+#define PI_C865_PID    0xe0a0  /* PI C-865 Piezomotor Controller */
+#define PI_C857_PID    0xe0a1  /* PI Encoder Trigger Box */
+
 #define PI_VID              0x1a72  /* Vendor ID */
-#define PI_E861_PID         0x1008  /* E-861 piezo controller USB connection */
+#define PI_C866_PID    0x1000  /* PI C-866 Piezomotor Controller */
+#define PI_C663_PID    0x1001  /* PI C-663 Mercury-Step */
+#define PI_C725_PID    0x1002  /* PI C-725 Piezomotor Controller */
+#define PI_E517_PID    0x1005  /* PI E-517 Digital Piezo Controller Operation Module */
+#define PI_C863_PID    0x1007  /* PI C-863 */
+#define PI_E861_PID    0x1008  /* PI E-861 Piezomotor Controller */
+#define PI_C867_PID    0x1009  /* PI C-867 Piezomotor Controller */
+#define PI_E609_PID    0x100D  /* PI E-609 Digital Piezo Controller */
+#define PI_E709_PID    0x100E  /* PI E-709 Digital Piezo Controller */
+#define PI_100F_PID    0x100F  /* PI Digital Piezo Controller */
+#define PI_1011_PID    0x1011  /* PI Digital Piezo Controller */
+#define PI_1012_PID    0x1012  /* PI Motion Controller */
+#define PI_1013_PID    0x1013  /* PI Motion Controller */
+#define PI_1014_PID    0x1014  /* PI Device */
+#define PI_1015_PID    0x1015  /* PI Device */
+#define PI_1016_PID    0x1016  /* PI Digital Servo Module */
 
 /*
  * Kondo Kagaku Co.Ltd.
index cc40f47ecea13ff2041389d5aa7d5526159e58d8..5ce88d1bc6f1e3ac15ac91290ad8cb2fd5935361 100644 (file)
@@ -886,8 +886,6 @@ static const struct usb_device_id option_ids[] = {
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff),
          .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1012, 0xff, 0xff, 0xff) },
-       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1018, 0xff, 0xff, 0xff),
-         .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1057, 0xff, 0xff, 0xff) },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1058, 0xff, 0xff, 0xff) },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1059, 0xff, 0xff, 0xff) },
@@ -1092,6 +1090,10 @@ static const struct usb_device_id option_ids[] = {
         .driver_info = (kernel_ulong_t)&zte_ad3812_z_blacklist },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2716, 0xff, 0xff, 0xff),
         .driver_info = (kernel_ulong_t)&zte_mc2716_z_blacklist },
+       { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x01) },
+       { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x05) },
+       { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x86, 0x10) },
+
        { USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_H10) },
        { USB_DEVICE(DLINK_VENDOR_ID, DLINK_PRODUCT_DWM_652) },
        { USB_DEVICE(ALINK_VENDOR_ID, DLINK_PRODUCT_DWM_652_U5) }, /* Yes, ALINK_VENDOR_ID */
index 1eff743ec4970071f8451704fb51d4b06de70950..ae60406ea8a1878cefd03d29868eda31b955578f 100644 (file)
@@ -814,6 +814,9 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev,
        hpwdt_timer_reg = pci_mem_addr + 0x70;
        hpwdt_timer_con = pci_mem_addr + 0x72;
 
+       /* Make sure that timer is disabled until /dev/watchdog is opened */
+       hpwdt_stop();
+
        /* Make sure that we have a valid soft_margin */
        if (hpwdt_change_timer(soft_margin))
                hpwdt_change_timer(DEFAULT_MARGIN);
index 6aa46a90ff028691f97627765f593d864bc885a1..3796434991faa83466c85c8779a072db1278ef33 100644 (file)
@@ -128,11 +128,12 @@ EXPORT_SYMBOL_GPL(watchdog_register_device);
 void watchdog_unregister_device(struct watchdog_device *wdd)
 {
        int ret;
-       int devno = wdd->cdev.dev;
+       int devno;
 
        if (wdd == NULL)
                return;
 
+       devno = wdd->cdev.dev;
        ret = watchdog_dev_unregister(wdd);
        if (ret)
                pr_err("error unregistering /dev/watchdog (err=%d)\n", ret);
index 1ffd03bf8e10dcb6b015e3e0b94a2a460f0d0dcd..7f1241608489de05a33e957a77145f4faa605f0e 100644 (file)
@@ -314,8 +314,9 @@ static int __unmap_grant_pages(struct grant_map *map, int offset, int pages)
                }
        }
 
-       err = gnttab_unmap_refs(map->unmap_ops + offset, map->pages + offset,
-                               pages, true);
+       err = gnttab_unmap_refs(map->unmap_ops + offset,
+                       use_ptemod ? map->kmap_ops + offset : NULL, map->pages + offset,
+                       pages);
        if (err)
                return err;
 
index 0bfc1ef11259eccaa937eb8d0913e4e14e04832e..006726688baf4f0a535896dd38c9a3ea2198c617 100644 (file)
@@ -870,7 +870,8 @@ int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops,
 EXPORT_SYMBOL_GPL(gnttab_map_refs);
 
 int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops,
-                     struct page **pages, unsigned int count, bool clear_pte)
+                     struct gnttab_map_grant_ref *kmap_ops,
+                     struct page **pages, unsigned int count)
 {
        int i, ret;
        bool lazy = false;
@@ -888,7 +889,8 @@ int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops,
        }
 
        for (i = 0; i < count; i++) {
-               ret = m2p_remove_override(pages[i], clear_pte);
+               ret = m2p_remove_override(pages[i], kmap_ops ?
+                                      &kmap_ops[i] : NULL);
                if (ret)
                        return ret;
        }
index 38b42e7bc91d0e258a7fd086d793aae3d43ac4c1..b65015581744a6eefb9b3ed720478673b888d336 100644 (file)
@@ -1371,10 +1371,8 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
 
        if (srcid) {
                srcgroup = find_qgroup_rb(fs_info, srcid);
-               if (!srcgroup) {
-                       ret = -EINVAL;
+               if (!srcgroup)
                        goto unlock;
-               }
                dstgroup->rfer = srcgroup->rfer - level_size;
                dstgroup->rfer_cmpr = srcgroup->rfer_cmpr - level_size;
                srcgroup->excl = level_size;
@@ -1383,10 +1381,8 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
                qgroup_dirty(fs_info, srcgroup);
        }
 
-       if (!inherit) {
-               ret = -EINVAL;
+       if (!inherit)
                goto unlock;
-       }
 
        i_qgroups = (u64 *)(inherit + 1);
        for (i = 0; i < inherit->num_qgroups; ++i) {
index 7dab9c04ad524117a386c08fb21d7a886e9ca82d..53cf2aabce877ce2a8e15844ab667cf6f26a2d63 100644 (file)
@@ -328,7 +328,7 @@ cifsConvertToUTF16(__le16 *target, const char *source, int srclen,
        }
 
 ctoUTF16_out:
-       return i;
+       return j;
 }
 
 #ifdef CONFIG_CIFS_SMB2
index 8086636bf796ab328219d4a898608af1be2fdb2b..16521a9f203859a28a8804d3fb2f5f4cb894ed7f 100644 (file)
@@ -389,7 +389,7 @@ static struct dentry *d_kill(struct dentry *dentry, struct dentry *parent)
         * Inform try_to_ascend() that we are no longer attached to the
         * dentry tree
         */
-       dentry->d_flags |= DCACHE_DISCONNECTED;
+       dentry->d_flags |= DCACHE_DENTRY_KILLED;
        if (parent)
                spin_unlock(&parent->d_lock);
        dentry_iput(dentry);
@@ -1048,7 +1048,7 @@ static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq
         * or deletion
         */
        if (new != old->d_parent ||
-                (old->d_flags & DCACHE_DISCONNECTED) ||
+                (old->d_flags & DCACHE_DENTRY_KILLED) ||
                 (!locked && read_seqretry(&rename_lock, seq))) {
                spin_unlock(&new->d_lock);
                new = NULL;
index 2340f6978d6e29e01c9b90ddaa9abd33beb0ee11..c5ca6ae5a30cc750e5a33b42ce746da6cf2f4591 100644 (file)
@@ -526,73 +526,51 @@ struct array_data {
        u32 elements;
 };
 
-static int u32_array_open(struct inode *inode, struct file *file)
-{
-       file->private_data = NULL;
-       return nonseekable_open(inode, file);
-}
-
-static size_t format_array(char *buf, size_t bufsize, const char *fmt,
-                          u32 *array, u32 array_size)
+static size_t u32_format_array(char *buf, size_t bufsize,
+                              u32 *array, int array_size)
 {
        size_t ret = 0;
-       u32 i;
 
-       for (i = 0; i < array_size; i++) {
+       while (--array_size >= 0) {
                size_t len;
+               char term = array_size ? ' ' : '\n';
 
-               len = snprintf(buf, bufsize, fmt, array[i]);
-               len++;  /* ' ' or '\n' */
+               len = snprintf(buf, bufsize, "%u%c", *array++, term);
                ret += len;
 
-               if (buf) {
-                       buf += len;
-                       bufsize -= len;
-                       buf[-1] = (i == array_size-1) ? '\n' : ' ';
-               }
+               buf += len;
+               bufsize -= len;
        }
-
-       ret++;          /* \0 */
-       if (buf)
-               *buf = '\0';
-
        return ret;
 }
 
-static char *format_array_alloc(const char *fmt, u32 *array,
-                                               u32 array_size)
+static int u32_array_open(struct inode *inode, struct file *file)
 {
-       size_t len = format_array(NULL, 0, fmt, array, array_size);
-       char *ret;
-
-       ret = kmalloc(len, GFP_KERNEL);
-       if (ret == NULL)
-               return NULL;
+       struct array_data *data = inode->i_private;
+       int size, elements = data->elements;
+       char *buf;
+
+       /*
+        * Max size:
+        *  - 10 digits + ' '/'\n' = 11 bytes per number
+        *  - terminating NUL character
+        */
+       size = elements*11;
+       buf = kmalloc(size+1, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+       buf[size] = 0;
+
+       file->private_data = buf;
+       u32_format_array(buf, size, data->array, data->elements);
 
-       format_array(ret, len, fmt, array, array_size);
-       return ret;
+       return nonseekable_open(inode, file);
 }
 
 static ssize_t u32_array_read(struct file *file, char __user *buf, size_t len,
                              loff_t *ppos)
 {
-       struct inode *inode = file->f_path.dentry->d_inode;
-       struct array_data *data = inode->i_private;
-       size_t size;
-
-       if (*ppos == 0) {
-               if (file->private_data) {
-                       kfree(file->private_data);
-                       file->private_data = NULL;
-               }
-
-               file->private_data = format_array_alloc("%u", data->array,
-                                                             data->elements);
-       }
-
-       size = 0;
-       if (file->private_data)
-               size = strlen(file->private_data);
+       size_t size = strlen(file->private_data);
 
        return simple_read_from_buffer(buf, len, ppos,
                                        file->private_data, size);
index 44ce5c6a541d65b7ceae1e7d67655a2e3f0f109a..d45ba4568128eb17baf60535d6dc00e663196afa 100644 (file)
@@ -275,8 +275,14 @@ out:
 
 static int ecryptfs_flush(struct file *file, fl_owner_t td)
 {
-       return file->f_mode & FMODE_WRITE
-              ? filemap_write_and_wait(file->f_mapping) : 0;
+       struct file *lower_file = ecryptfs_file_to_lower(file);
+
+       if (lower_file->f_op && lower_file->f_op->flush) {
+               filemap_write_and_wait(file->f_mapping);
+               return lower_file->f_op->flush(lower_file, td);
+       }
+
+       return 0;
 }
 
 static int ecryptfs_release(struct inode *inode, struct file *file)
index 534b129ea676500c4df149d95cb9bd5be517dc54..cc7709e7c508d81a1429ffa25bace9c7a101a832 100644 (file)
@@ -619,6 +619,7 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        struct dentry *lower_old_dir_dentry;
        struct dentry *lower_new_dir_dentry;
        struct dentry *trap = NULL;
+       struct inode *target_inode;
 
        lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry);
        lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry);
@@ -626,6 +627,7 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        dget(lower_new_dentry);
        lower_old_dir_dentry = dget_parent(lower_old_dentry);
        lower_new_dir_dentry = dget_parent(lower_new_dentry);
+       target_inode = new_dentry->d_inode;
        trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
        /* source should not be ancestor of target */
        if (trap == lower_old_dentry) {
@@ -641,6 +643,9 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                        lower_new_dir_dentry->d_inode, lower_new_dentry);
        if (rc)
                goto out_lock;
+       if (target_inode)
+               fsstack_copy_attr_all(target_inode,
+                                     ecryptfs_inode_to_lower(target_inode));
        fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode);
        if (new_dir != old_dir)
                fsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode);
index 2768138eefeef85707f9ee29652532b25d50dfcb..9b627c15010a3af35e1f2ec85ccafc2b18d97d44 100644 (file)
@@ -162,6 +162,7 @@ void ecryptfs_put_lower_file(struct inode *inode)
        inode_info = ecryptfs_inode_to_private(inode);
        if (atomic_dec_and_mutex_lock(&inode_info->lower_file_count,
                                      &inode_info->lower_file_mutex)) {
+               filemap_write_and_wait(inode->i_mapping);
                fput(inode_info->lower_file);
                inode_info->lower_file = NULL;
                mutex_unlock(&inode_info->lower_file_mutex);
index d1d791ef38de2188852551254a63f974a605feff..382000ffac1f7e892163665a27982e587b9d83e7 100644 (file)
@@ -322,6 +322,29 @@ static long gfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
        return -ENOTTY;
 }
 
+/**
+ * gfs2_size_hint - Give a hint to the size of a write request
+ * @file: The struct file
+ * @offset: The file offset of the write
+ * @size: The length of the write
+ *
+ * When we are about to do a write, this function records the total
+ * write size in order to provide a suitable hint to the lower layers
+ * about how many blocks will be required.
+ *
+ */
+
+static void gfs2_size_hint(struct file *filep, loff_t offset, size_t size)
+{
+       struct inode *inode = filep->f_dentry->d_inode;
+       struct gfs2_sbd *sdp = GFS2_SB(inode);
+       struct gfs2_inode *ip = GFS2_I(inode);
+       size_t blks = (size + sdp->sd_sb.sb_bsize - 1) >> sdp->sd_sb.sb_bsize_shift;
+       int hint = min_t(size_t, INT_MAX, blks);
+
+       atomic_set(&ip->i_res->rs_sizehint, hint);
+}
+
 /**
  * gfs2_allocate_page_backing - Use bmap to allocate blocks
  * @page: The (locked) page to allocate backing for
@@ -382,8 +405,7 @@ static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
        if (ret)
                return ret;
 
-       atomic_set(&ip->i_res->rs_sizehint,
-                  PAGE_CACHE_SIZE >> sdp->sd_sb.sb_bsize_shift);
+       gfs2_size_hint(vma->vm_file, pos, PAGE_CACHE_SIZE);
 
        gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
        ret = gfs2_glock_nq(&gh);
@@ -663,7 +685,8 @@ static ssize_t gfs2_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
        if (ret)
                return ret;
 
-       atomic_set(&ip->i_res->rs_sizehint, writesize >> sdp->sd_sb.sb_bsize_shift);
+       gfs2_size_hint(file, pos, writesize);
+
        if (file->f_flags & O_APPEND) {
                struct gfs2_holder gh;
 
@@ -789,7 +812,7 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset,
        if (unlikely(error))
                goto out_uninit;
 
-       atomic_set(&ip->i_res->rs_sizehint, len >> sdp->sd_sb.sb_bsize_shift);
+       gfs2_size_hint(file, offset, len);
 
        while (len > 0) {
                if (len < bytes)
index 4ce22e54730806e02ed0ba70f7e7fc847d513142..753af3d86bbcecaa76f5c01cb54d81b0006c4fac 100644 (file)
@@ -1722,7 +1722,9 @@ static int gfs2_setxattr(struct dentry *dentry, const char *name,
        gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
        ret = gfs2_glock_nq(&gh);
        if (ret == 0) {
-               ret = generic_setxattr(dentry, name, data, size, flags);
+               ret = gfs2_rs_alloc(ip);
+               if (ret == 0)
+                       ret = generic_setxattr(dentry, name, data, size, flags);
                gfs2_glock_dq(&gh);
        }
        gfs2_holder_uninit(&gh);
@@ -1757,7 +1759,9 @@ static int gfs2_removexattr(struct dentry *dentry, const char *name)
        gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
        ret = gfs2_glock_nq(&gh);
        if (ret == 0) {
-               ret = generic_removexattr(dentry, name);
+               ret = gfs2_rs_alloc(ip);
+               if (ret == 0)
+                       ret = generic_removexattr(dentry, name);
                gfs2_glock_dq(&gh);
        }
        gfs2_holder_uninit(&gh);
index 4d34887a601d966660549b0d0a27517353c1ed5e..c9ed814eeb6f9652eaa927e2ab13fc42c906da14 100644 (file)
@@ -1961,7 +1961,7 @@ static void gfs2_rgrp_error(struct gfs2_rgrpd *rgd)
  * @dinode: 1 if this block is a dinode block, otherwise data block
  * @nblocks: desired extent length
  *
- * Lay claim to previously allocated block reservation blocks.
+ * Lay claim to previously reserved blocks.
  * Returns: Starting block number of the blocks claimed.
  * Sets *nblocks to the actual extent length allocated.
  */
@@ -1970,19 +1970,17 @@ static u64 claim_reserved_blks(struct gfs2_inode *ip, bool dinode,
 {
        struct gfs2_blkreserv *rs = ip->i_res;
        struct gfs2_rgrpd *rgd = rs->rs_rgd;
-       struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
        struct gfs2_bitmap *bi;
        u64 start_block = gfs2_rs_startblk(rs);
        const unsigned int elen = *nblocks;
 
-       /*BUG_ON(!gfs2_glock_is_locked_by_me(ip->i_gl));*/
-       gfs2_assert_withdraw(sdp, rgd);
-       /*BUG_ON(!gfs2_glock_is_locked_by_me(rgd->rd_gl));*/
        bi = rs->rs_bi;
        gfs2_trans_add_bh(rgd->rd_gl, bi->bi_bh, 1);
 
        for (*nblocks = 0; *nblocks < elen && rs->rs_free; (*nblocks)++) {
-               /* Make sure the bitmap hasn't changed */
+               if (gfs2_testbit(rgd, bi->bi_bh->b_data + bi->bi_offset,
+                                bi->bi_len, rs->rs_biblk) != GFS2_BLKST_FREE)
+                       break;
                gfs2_setbit(rgd, bi->bi_clone, bi, rs->rs_biblk,
                            dinode ? GFS2_BLKST_DINODE : GFS2_BLKST_USED);
                rs->rs_biblk++;
@@ -1991,20 +1989,12 @@ static u64 claim_reserved_blks(struct gfs2_inode *ip, bool dinode,
                BUG_ON(!rgd->rd_reserved);
                rgd->rd_reserved--;
                dinode = false;
-               trace_gfs2_rs(ip, rs, TRACE_RS_CLAIM);
        }
 
-       if (!rs->rs_free) {
-               struct gfs2_rgrpd *rgd = ip->i_res->rs_rgd;
-
+       trace_gfs2_rs(ip, rs, TRACE_RS_CLAIM);
+       if (!rs->rs_free || *nblocks != elen)
                gfs2_rs_deltree(rs);
-               /* -nblocks because we haven't returned to do the math yet.
-                  I'm doing the math backwards to prevent negative numbers,
-                  but think of it as:
-                  if (unclaimed_blocks(rgd) - *nblocks >= RGRP_RSRV_MINBLKS */
-               if (unclaimed_blocks(rgd) >= RGRP_RSRV_MINBLKS + *nblocks)
-                       rg_mblk_search(rgd, ip);
-       }
+
        return start_block;
 }
 
@@ -2037,34 +2027,34 @@ int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, unsigned int *nblocks,
        if (ip->i_res->rs_requested == 0)
                return -ECANCELED;
 
-       /* Check if we have a multi-block reservation, and if so, claim the
-          next free block from it. */
+       /* If we have a reservation, claim blocks from it. */
        if (gfs2_rs_active(ip->i_res)) {
                BUG_ON(!ip->i_res->rs_free);
                rgd = ip->i_res->rs_rgd;
                block = claim_reserved_blks(ip, dinode, nblocks);
-       } else {
-               rgd = ip->i_rgd;
+               if (*nblocks)
+                       goto found_blocks;
+       }
 
-               if (!dinode && rgrp_contains_block(rgd, ip->i_goal))
-                       goal = ip->i_goal - rgd->rd_data0;
-               else
-                       goal = rgd->rd_last_alloc;
-
-               blk = rgblk_search(rgd, goal, GFS2_BLKST_FREE, &bi);
-
-               /* Since all blocks are reserved in advance, this shouldn't
-                  happen */
-               if (blk == BFITNOENT) {
-                       printk(KERN_WARNING "BFITNOENT, nblocks=%u\n",
-                              *nblocks);
-                       printk(KERN_WARNING "FULL=%d\n",
-                              test_bit(GBF_FULL, &rgd->rd_bits->bi_flags));
-                       goto rgrp_error;
-               }
+       rgd = ip->i_rgd;
 
-               block = gfs2_alloc_extent(rgd, bi, blk, dinode, nblocks);
+       if (!dinode && rgrp_contains_block(rgd, ip->i_goal))
+               goal = ip->i_goal - rgd->rd_data0;
+       else
+               goal = rgd->rd_last_alloc;
+
+       blk = rgblk_search(rgd, goal, GFS2_BLKST_FREE, &bi);
+
+       /* Since all blocks are reserved in advance, this shouldn't happen */
+       if (blk == BFITNOENT) {
+               printk(KERN_WARNING "BFITNOENT, nblocks=%u\n", *nblocks);
+               printk(KERN_WARNING "FULL=%d\n",
+                      test_bit(GBF_FULL, &rgd->rd_bits->bi_flags));
+               goto rgrp_error;
        }
+
+       block = gfs2_alloc_extent(rgd, bi, blk, dinode, nblocks);
+found_blocks:
        ndata = *nblocks;
        if (dinode)
                ndata--;
index 75d6d0a3d32e2685bbd43f791b1f32775c87ce59..6a7fcab7ecb3115c7630573c17f4d8285a418591 100644 (file)
@@ -287,10 +287,12 @@ nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
        struct inode *inode = file->f_path.dentry->d_inode;
 
        ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
+       if (ret != 0)
+               goto out;
        mutex_lock(&inode->i_mutex);
        ret = nfs_file_fsync_commit(file, start, end, datasync);
        mutex_unlock(&inode->i_mutex);
-
+out:
        return ret;
 }
 
index c6e895f0fbf36eee681a5cb5d8e4a92bfd8c0d35..9b47610338f59f03f6b4fdc0280d6aa61c266d4f 100644 (file)
@@ -154,7 +154,7 @@ static void nfs_zap_caches_locked(struct inode *inode)
        nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
        nfsi->attrtimeo_timestamp = jiffies;
 
-       memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
+       memset(NFS_I(inode)->cookieverf, 0, sizeof(NFS_I(inode)->cookieverf));
        if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))
                nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
        else
index d6b3b5f2d779acd1ce7e0324e8c52e388c7a273a..69322096c32569d4674517f7121e4fc272206ba8 100644 (file)
@@ -643,7 +643,7 @@ nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
                  u64 cookie, struct page **pages, unsigned int count, int plus)
 {
        struct inode            *dir = dentry->d_inode;
-       __be32                  *verf = NFS_COOKIEVERF(dir);
+       __be32                  *verf = NFS_I(dir)->cookieverf;
        struct nfs3_readdirargs arg = {
                .fh             = NFS_FH(dir),
                .cookie         = cookie,
index acb65e7887f8437b8aac391255f4be1218524a27..eb5eb8eef4d34db3c7bafe3c84c1db0bf43e8974 100644 (file)
@@ -96,13 +96,15 @@ nfs4_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
        struct inode *inode = file->f_path.dentry->d_inode;
 
        ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
+       if (ret != 0)
+               goto out;
        mutex_lock(&inode->i_mutex);
        ret = nfs_file_fsync_commit(file, start, end, datasync);
        if (!ret && !datasync)
                /* application has asked for meta-data sync */
                ret = pnfs_layoutcommit_inode(inode, true);
        mutex_unlock(&inode->i_mutex);
-
+out:
        return ret;
 }
 
index 635274140b180287668dbaa7540bd84852051181..1e50326d00ddd1f7ef8931470bc1cd0ad32b4015 100644 (file)
@@ -3215,11 +3215,11 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
                        dentry->d_parent->d_name.name,
                        dentry->d_name.name,
                        (unsigned long long)cookie);
-       nfs4_setup_readdir(cookie, NFS_COOKIEVERF(dir), dentry, &args);
+       nfs4_setup_readdir(cookie, NFS_I(dir)->cookieverf, dentry, &args);
        res.pgbase = args.pgbase;
        status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &msg, &args.seq_args, &res.seq_res, 0);
        if (status >= 0) {
-               memcpy(NFS_COOKIEVERF(dir), res.verifier.data, NFS4_VERIFIER_SIZE);
+               memcpy(NFS_I(dir)->cookieverf, res.verifier.data, NFS4_VERIFIER_SIZE);
                status += args.pgbase;
        }
 
@@ -3653,11 +3653,11 @@ static inline int nfs4_server_supports_acls(struct nfs_server *server)
                && (server->acl_bitmask & ACL4_SUPPORT_DENY_ACL);
 }
 
-/* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_CACHE_SIZE, and that
- * it's OK to put sizeof(void) * (XATTR_SIZE_MAX/PAGE_CACHE_SIZE) bytes on
+/* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_SIZE, and that
+ * it's OK to put sizeof(void) * (XATTR_SIZE_MAX/PAGE_SIZE) bytes on
  * the stack.
  */
-#define NFS4ACL_MAXPAGES (XATTR_SIZE_MAX >> PAGE_CACHE_SHIFT)
+#define NFS4ACL_MAXPAGES DIV_ROUND_UP(XATTR_SIZE_MAX, PAGE_SIZE)
 
 static int buf_to_pages_noslab(const void *buf, size_t buflen,
                struct page **pages, unsigned int *pgbase)
@@ -3668,7 +3668,7 @@ static int buf_to_pages_noslab(const void *buf, size_t buflen,
        spages = pages;
 
        do {
-               len = min_t(size_t, PAGE_CACHE_SIZE, buflen);
+               len = min_t(size_t, PAGE_SIZE, buflen);
                newpage = alloc_page(GFP_KERNEL);
 
                if (newpage == NULL)
@@ -3739,7 +3739,7 @@ static void nfs4_write_cached_acl(struct inode *inode, struct page **pages, size
        struct nfs4_cached_acl *acl;
        size_t buflen = sizeof(*acl) + acl_len;
 
-       if (pages && buflen <= PAGE_SIZE) {
+       if (buflen <= PAGE_SIZE) {
                acl = kmalloc(buflen, GFP_KERNEL);
                if (acl == NULL)
                        goto out;
@@ -3782,17 +3782,15 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
                .rpc_argp = &args,
                .rpc_resp = &res,
        };
-       int ret = -ENOMEM, npages, i;
-       size_t acl_len = 0;
+       unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE);
+       int ret = -ENOMEM, i;
 
-       npages = (buflen + PAGE_SIZE - 1) >> PAGE_SHIFT;
        /* As long as we're doing a round trip to the server anyway,
         * let's be prepared for a page of acl data. */
        if (npages == 0)
                npages = 1;
-
-       /* Add an extra page to handle the bitmap returned */
-       npages++;
+       if (npages > ARRAY_SIZE(pages))
+               return -ERANGE;
 
        for (i = 0; i < npages; i++) {
                pages[i] = alloc_page(GFP_KERNEL);
@@ -3808,11 +3806,6 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
        args.acl_len = npages * PAGE_SIZE;
        args.acl_pgbase = 0;
 
-       /* Let decode_getfacl know not to fail if the ACL data is larger than
-        * the page we send as a guess */
-       if (buf == NULL)
-               res.acl_flags |= NFS4_ACL_LEN_REQUEST;
-
        dprintk("%s  buf %p buflen %zu npages %d args.acl_len %zu\n",
                __func__, buf, buflen, npages, args.acl_len);
        ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode),
@@ -3820,20 +3813,19 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
        if (ret)
                goto out_free;
 
-       acl_len = res.acl_len;
-       if (acl_len > args.acl_len)
-               nfs4_write_cached_acl(inode, NULL, 0, acl_len);
-       else
-               nfs4_write_cached_acl(inode, pages, res.acl_data_offset,
-                                     acl_len);
-       if (buf) {
+       /* Handle the case where the passed-in buffer is too short */
+       if (res.acl_flags & NFS4_ACL_TRUNC) {
+               /* Did the user only issue a request for the acl length? */
+               if (buf == NULL)
+                       goto out_ok;
                ret = -ERANGE;
-               if (acl_len > buflen)
-                       goto out_free;
-               _copy_from_pages(buf, pages, res.acl_data_offset,
-                               acl_len);
+               goto out_free;
        }
-       ret = acl_len;
+       nfs4_write_cached_acl(inode, pages, res.acl_data_offset, res.acl_len);
+       if (buf)
+               _copy_from_pages(buf, pages, res.acl_data_offset, res.acl_len);
+out_ok:
+       ret = res.acl_len;
 out_free:
        for (i = 0; i < npages; i++)
                if (pages[i])
@@ -3891,10 +3883,13 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
                .rpc_argp       = &arg,
                .rpc_resp       = &res,
        };
+       unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE);
        int ret, i;
 
        if (!nfs4_server_supports_acls(server))
                return -EOPNOTSUPP;
+       if (npages > ARRAY_SIZE(pages))
+               return -ERANGE;
        i = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
        if (i < 0)
                return i;
index 1bfbd67c556d753a21f046c87edc3c9b07b0b8f0..8dba6bd485578695fb791f8aa548bc8ac99b4391 100644 (file)
@@ -5072,18 +5072,14 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
                 * are stored with the acl data to handle the problem of
                 * variable length bitmaps.*/
                res->acl_data_offset = xdr_stream_pos(xdr) - pg_offset;
-
-               /* We ignore &savep and don't do consistency checks on
-                * the attr length.  Let userspace figure it out.... */
                res->acl_len = attrlen;
-               if (attrlen > (xdr->nwords << 2)) {
-                       if (res->acl_flags & NFS4_ACL_LEN_REQUEST) {
-                               /* getxattr interface called with a NULL buf */
-                               goto out;
-                       }
+
+               /* Check for receive buffer overflow */
+               if (res->acl_len > (xdr->nwords << 2) ||
+                   res->acl_len + res->acl_data_offset > xdr->buf->page_len) {
+                       res->acl_flags |= NFS4_ACL_TRUNC;
                        dprintk("NFS: acl reply: attrlen %u > page_len %u\n",
                                        attrlen, xdr->nwords << 2);
-                       return -EINVAL;
                }
        } else
                status = -EOPNOTSUPP;
@@ -6229,7 +6225,8 @@ static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        status = decode_open(xdr, res);
        if (status)
                goto out;
-       if (decode_getfh(xdr, &res->fh) != 0)
+       status = decode_getfh(xdr, &res->fh);
+       if (status)
                goto out;
        decode_getfattr(xdr, res->f_attr, res->server);
 out:
index 239aff7338eb89ee8c0d4080694178317d84929e..b8eda700584bfbd25086d898a21b3fd03959030d 100644 (file)
@@ -1867,6 +1867,7 @@ static int nfs23_validate_mount_data(void *options,
 
                memcpy(sap, &data->addr, sizeof(data->addr));
                args->nfs_server.addrlen = sizeof(data->addr);
+               args->nfs_server.port = ntohs(data->addr.sin_port);
                if (!nfs_verify_server_address(sap))
                        goto out_no_address;
 
@@ -2564,6 +2565,7 @@ static int nfs4_validate_mount_data(void *options,
                        return -EFAULT;
                if (!nfs_verify_server_address(sap))
                        goto out_no_address;
+               args->nfs_server.port = ntohs(((struct sockaddr_in *)sap)->sin_port);
 
                if (data->auth_flavourlen) {
                        if (data->auth_flavourlen > 1)
index dfafeb2b05a0e2ddc5481b2b2a4f445ec8086a0e..eb7cc91b7258870f89b77a60de4172dd6f1502b6 100644 (file)
@@ -462,9 +462,6 @@ static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
 
        err = ERR_PTR(-ENOMEM);
        inode = proc_sys_make_inode(dir->i_sb, h ? h : head, p);
-       if (h)
-               sysctl_head_finish(h);
-
        if (!inode)
                goto out;
 
@@ -473,6 +470,8 @@ static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
        d_add(dentry, inode);
 
 out:
+       if (h)
+               sysctl_head_finish(h);
        sysctl_head_finish(head);
        return err;
 }
index b6ff11825fc8a9c37f8d45ccf01e1fbdc1115868..40780229a03281376d4d449e896745f3f169a0d3 100644 (file)
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -58,7 +58,7 @@ EXPORT_SYMBOL(vfs_getattr);
 int vfs_fstat(unsigned int fd, struct kstat *stat)
 {
        int fput_needed;
-       struct file *f = fget_light(fd, &fput_needed);
+       struct file *f = fget_raw_light(fd, &fput_needed);
        int error = -EBADF;
 
        if (f) {
index d7a9dd735e1e429a1787d8b0e8e1c9ddde5eff10..933b7930b8636da970a627d571388b11e8dabde7 100644 (file)
@@ -96,6 +96,7 @@ xfs_buf_lru_add(
                atomic_inc(&bp->b_hold);
                list_add_tail(&bp->b_lru, &btp->bt_lru);
                btp->bt_lru_nr++;
+               bp->b_lru_flags &= ~_XBF_LRU_DISPOSE;
        }
        spin_unlock(&btp->bt_lru_lock);
 }
@@ -154,7 +155,8 @@ xfs_buf_stale(
                struct xfs_buftarg *btp = bp->b_target;
 
                spin_lock(&btp->bt_lru_lock);
-               if (!list_empty(&bp->b_lru)) {
+               if (!list_empty(&bp->b_lru) &&
+                   !(bp->b_lru_flags & _XBF_LRU_DISPOSE)) {
                        list_del_init(&bp->b_lru);
                        btp->bt_lru_nr--;
                        atomic_dec(&bp->b_hold);
@@ -1501,6 +1503,7 @@ xfs_buftarg_shrink(
                 */
                list_move(&bp->b_lru, &dispose);
                btp->bt_lru_nr--;
+               bp->b_lru_flags |= _XBF_LRU_DISPOSE;
        }
        spin_unlock(&btp->bt_lru_lock);
 
index d03b73b9604e3d55d93f1fdc08201512b01449dd..7c0b6a0a1557c0d30db4b94e4bc27879d2614537 100644 (file)
@@ -38,27 +38,28 @@ typedef enum {
        XBRW_ZERO = 3,                  /* Zero target memory */
 } xfs_buf_rw_t;
 
-#define XBF_READ       (1 << 0) /* buffer intended for reading from device */
-#define XBF_WRITE      (1 << 1) /* buffer intended for writing to device */
-#define XBF_READ_AHEAD (1 << 2) /* asynchronous read-ahead */
-#define XBF_ASYNC      (1 << 4) /* initiator will not wait for completion */
-#define XBF_DONE       (1 << 5) /* all pages in the buffer uptodate */
-#define XBF_STALE      (1 << 6) /* buffer has been staled, do not find it */
+#define XBF_READ        (1 << 0) /* buffer intended for reading from device */
+#define XBF_WRITE       (1 << 1) /* buffer intended for writing to device */
+#define XBF_READ_AHEAD  (1 << 2) /* asynchronous read-ahead */
+#define XBF_ASYNC       (1 << 4) /* initiator will not wait for completion */
+#define XBF_DONE        (1 << 5) /* all pages in the buffer uptodate */
+#define XBF_STALE       (1 << 6) /* buffer has been staled, do not find it */
 
 /* I/O hints for the BIO layer */
-#define XBF_SYNCIO     (1 << 10)/* treat this buffer as synchronous I/O */
-#define XBF_FUA                (1 << 11)/* force cache write through mode */
-#define XBF_FLUSH      (1 << 12)/* flush the disk cache before a write */
+#define XBF_SYNCIO      (1 << 10)/* treat this buffer as synchronous I/O */
+#define XBF_FUA                 (1 << 11)/* force cache write through mode */
+#define XBF_FLUSH       (1 << 12)/* flush the disk cache before a write */
 
 /* flags used only as arguments to access routines */
-#define XBF_TRYLOCK    (1 << 16)/* lock requested, but do not wait */
-#define XBF_UNMAPPED   (1 << 17)/* do not map the buffer */
+#define XBF_TRYLOCK     (1 << 16)/* lock requested, but do not wait */
+#define XBF_UNMAPPED    (1 << 17)/* do not map the buffer */
 
 /* flags used only internally */
-#define _XBF_PAGES     (1 << 20)/* backed by refcounted pages */
-#define _XBF_KMEM      (1 << 21)/* backed by heap memory */
-#define _XBF_DELWRI_Q  (1 << 22)/* buffer on a delwri queue */
-#define _XBF_COMPOUND  (1 << 23)/* compound buffer */
+#define _XBF_PAGES      (1 << 20)/* backed by refcounted pages */
+#define _XBF_KMEM       (1 << 21)/* backed by heap memory */
+#define _XBF_DELWRI_Q   (1 << 22)/* buffer on a delwri queue */
+#define _XBF_COMPOUND   (1 << 23)/* compound buffer */
+#define _XBF_LRU_DISPOSE (1 << 24)/* buffer being discarded */
 
 typedef unsigned int xfs_buf_flags_t;
 
@@ -72,12 +73,13 @@ typedef unsigned int xfs_buf_flags_t;
        { XBF_SYNCIO,           "SYNCIO" }, \
        { XBF_FUA,              "FUA" }, \
        { XBF_FLUSH,            "FLUSH" }, \
-       { XBF_TRYLOCK,          "TRYLOCK" },    /* should never be set */\
+       { XBF_TRYLOCK,          "TRYLOCK" },    /* should never be set */\
        { XBF_UNMAPPED,         "UNMAPPED" },   /* ditto */\
        { _XBF_PAGES,           "PAGES" }, \
        { _XBF_KMEM,            "KMEM" }, \
        { _XBF_DELWRI_Q,        "DELWRI_Q" }, \
-       { _XBF_COMPOUND,        "COMPOUND" }
+       { _XBF_COMPOUND,        "COMPOUND" }, \
+       { _XBF_LRU_DISPOSE,     "LRU_DISPOSE" }
 
 typedef struct xfs_buftarg {
        dev_t                   bt_dev;
@@ -124,7 +126,12 @@ typedef struct xfs_buf {
        xfs_buf_flags_t         b_flags;        /* status flags */
        struct semaphore        b_sema;         /* semaphore for lockables */
 
+       /*
+        * concurrent access to b_lru and b_lru_flags are protected by
+        * bt_lru_lock and not by b_sema
+        */
        struct list_head        b_lru;          /* lru list */
+       xfs_buf_flags_t         b_lru_flags;    /* internal lru status flags */
        wait_queue_head_t       b_waiters;      /* unpin waiters */
        struct list_head        b_list;
        struct xfs_perag        *b_pag;         /* contains rbtree root */
index bdaf4cb9f4a2d19fc8b781f232f61d88c529ac2a..19e2380fb8679d2e8641e691eaeb0b200456bfb4 100644 (file)
@@ -919,6 +919,7 @@ xfs_fs_put_super(
        struct xfs_mount        *mp = XFS_M(sb);
 
        xfs_filestream_unmount(mp);
+       cancel_delayed_work_sync(&mp->m_sync_work);
        xfs_unmountfs(mp);
        xfs_syncd_stop(mp);
        xfs_freesb(mp);
index d6b67bb9075f21b5782013b500ed6de52cdf4f68..d5f0c163eef1e18361740de9744dcb956af2aad4 100644 (file)
@@ -1367,6 +1367,7 @@ extern int drm_remove_magic(struct drm_master *master, drm_magic_t magic);
 
 /* Cache management (drm_cache.c) */
 void drm_clflush_pages(struct page *pages[], unsigned long num_pages);
+void drm_clflush_sg(struct sg_table *st);
 void drm_clflush_virt_range(char *addr, unsigned long length);
 
                                /* Locking IOCTL support (drm_lock.h) */
index bfacf0d5a225fd0dd4c9a52d42496f3e7049a030..8e405b82707d8c335a70fe7968c00272e30be73e 100644 (file)
@@ -215,11 +215,10 @@ struct drm_display_info {
        u32 color_formats;
 
        u8 cea_rev;
-
-       char *raw_edid; /* if any */
 };
 
 struct drm_framebuffer_funcs {
+       /* note: use drm_framebuffer_remove() */
        void (*destroy)(struct drm_framebuffer *framebuffer);
        int (*create_handle)(struct drm_framebuffer *fb,
                             struct drm_file *file_priv,
@@ -244,6 +243,16 @@ struct drm_framebuffer_funcs {
 
 struct drm_framebuffer {
        struct drm_device *dev;
+       /*
+        * Note that the fb is refcounted for the benefit of driver internals,
+        * for example some hw, disabling a CRTC/plane is asynchronous, and
+        * scanout does not actually complete until the next vblank.  So some
+        * cleanup (like releasing the reference(s) on the backing GEM bo(s))
+        * should be deferred.  In cases like this, the driver would like to
+        * hold a ref to the fb even though it has already been removed from
+        * userspace perspective.
+        */
+       struct kref refcount;
        struct list_head head;
        struct drm_mode_object base;
        const struct drm_framebuffer_funcs *funcs;
@@ -359,6 +368,9 @@ struct drm_crtc_funcs {
  * @enabled: is this CRTC enabled?
  * @mode: current mode timings
  * @hwmode: mode timings as programmed to hw regs
+ * @invert_dimensions: for purposes of error checking crtc vs fb sizes,
+ *    invert the width/height of the crtc.  This is used if the driver
+ *    is performing 90 or 270 degree rotated scanout
  * @x: x position on screen
  * @y: y position on screen
  * @funcs: CRTC control functions
@@ -392,6 +404,8 @@ struct drm_crtc {
         */
        struct drm_display_mode hwmode;
 
+       bool invert_dimensions;
+
        int x, y;
        const struct drm_crtc_funcs *funcs;
 
@@ -593,6 +607,7 @@ struct drm_connector {
        int video_latency[2];   /* [0]: progressive, [1]: interlaced */
        int audio_latency[2];
        int null_edid_counter; /* needed to workaround some HW bugs where we get all 0s */
+       unsigned bad_edid_counter;
 };
 
 /**
@@ -920,6 +935,9 @@ 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 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 int drmfb_probe(struct drm_device *dev, struct drm_crtc *crtc);
 extern int drmfb_remove(struct drm_device *dev, struct drm_framebuffer *fb);
@@ -1035,7 +1053,7 @@ extern int drm_add_modes_noedid(struct drm_connector *connector,
                                int hdisplay, int vdisplay);
 
 extern int drm_edid_header_is_valid(const u8 *raw_edid);
-extern bool drm_edid_block_valid(u8 *raw_edid, int block);
+extern bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid);
 extern bool drm_edid_is_valid(struct edid *edid);
 struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
                                           int hsize, int vsize, int fresh,
diff --git a/include/drm/drm_fb_cma_helper.h b/include/drm/drm_fb_cma_helper.h
new file mode 100644 (file)
index 0000000..76c7098
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef __DRM_FB_CMA_HELPER_H__
+#define __DRM_FB_CMA_HELPER_H__
+
+struct drm_fbdev_cma;
+struct drm_gem_cma_object;
+
+struct drm_framebuffer;
+struct drm_device;
+struct drm_file;
+struct drm_mode_fb_cmd2;
+
+struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
+       unsigned int preferred_bpp, unsigned int num_crtc,
+       unsigned int max_conn_count);
+void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma);
+
+void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma);
+void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma);
+
+struct drm_framebuffer *drm_fb_cma_create(struct drm_device *dev,
+       struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd);
+
+struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb,
+       unsigned int plane);
+
+#endif
+
index f4621184a9b404f8f7a81dfb130258def67cbd58..646ae5f39f42a75b7ac5dcf2cf7cafd51cf3341b 100644 (file)
 #define DRM_FORMAT_NV21                fourcc_code('N', 'V', '2', '1') /* 2x2 subsampled Cb:Cr plane */
 #define DRM_FORMAT_NV16                fourcc_code('N', 'V', '1', '6') /* 2x1 subsampled Cr:Cb plane */
 #define DRM_FORMAT_NV61                fourcc_code('N', 'V', '6', '1') /* 2x1 subsampled Cb:Cr plane */
+#define DRM_FORMAT_NV24                fourcc_code('N', 'V', '2', '4') /* non-subsampled Cr:Cb plane */
+#define DRM_FORMAT_NV42                fourcc_code('N', 'V', '4', '2') /* non-subsampled Cb:Cr plane */
 
 /* special NV12 tiled format */
 #define DRM_FORMAT_NV12MT      fourcc_code('T', 'M', '1', '2') /* 2x2 subsampled Cr:Cb plane 64x32 macroblocks */
diff --git a/include/drm/drm_gem_cma_helper.h b/include/drm/drm_gem_cma_helper.h
new file mode 100644 (file)
index 0000000..f0f6b1a
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef __DRM_GEM_CMA_HELPER_H__
+#define __DRM_GEM_CMA_HELPER_H__
+
+struct drm_gem_cma_object {
+       struct drm_gem_object base;
+       dma_addr_t paddr;
+       void *vaddr;
+};
+
+static inline struct drm_gem_cma_object *
+to_drm_gem_cma_obj(struct drm_gem_object *gem_obj)
+{
+       return container_of(gem_obj, struct drm_gem_cma_object, base);
+}
+
+/* free gem object. */
+void drm_gem_cma_free_object(struct drm_gem_object *gem_obj);
+
+/* create memory region for drm framebuffer. */
+int drm_gem_cma_dumb_create(struct drm_file *file_priv,
+               struct drm_device *drm, struct drm_mode_create_dumb *args);
+
+/* map memory region for drm framebuffer to user space. */
+int drm_gem_cma_dumb_map_offset(struct drm_file *file_priv,
+               struct drm_device *drm, uint32_t handle, uint64_t *offset);
+
+/* set vm_flags and we can change the vm attribute to other one at here. */
+int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma);
+
+/*
+ * destroy memory region allocated.
+ *     - a gem handle and physical memory region pointed by a gem object
+ *     would be released by drm_gem_handle_delete().
+ */
+int drm_gem_cma_dumb_destroy(struct drm_file *file_priv,
+               struct drm_device *drm, unsigned int handle);
+
+/* allocate physical memory. */
+struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm,
+               unsigned int size);
+
+extern const struct vm_operations_struct drm_gem_cma_vm_ops;
+
+#endif /* __DRM_GEM_CMA_HELPER_H__ */
index ee5389d22c64745d72cc985d6024a5eec55176d4..1d1a858a203d87f44feb10fa64a9524f00893b86 100644 (file)
@@ -37,6 +37,8 @@
 /* SAREA area needs to be at least a page */
 #if defined(__alpha__)
 #define SAREA_MAX                       0x2000U
+#elif defined(__mips__)
+#define SAREA_MAX                       0x4000U
 #elif defined(__ia64__)
 #define SAREA_MAX                       0x10000U       /* 64kB */
 #else
index 8cc70837f929b7362ade2738cd707cbad01cef0a..e737607e055e983a520b4d617ed9f7b2a74db4bf 100644 (file)
@@ -203,6 +203,9 @@ typedef struct _drm_i915_sarea {
 #define DRM_I915_GEM_WAIT      0x2c
 #define DRM_I915_GEM_CONTEXT_CREATE    0x2d
 #define DRM_I915_GEM_CONTEXT_DESTROY   0x2e
+#define DRM_I915_GEM_SET_CACHING       0x2f
+#define DRM_I915_GEM_GET_CACHING       0x30
+#define DRM_I915_REG_READ              0x31
 
 #define DRM_IOCTL_I915_INIT            DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
 #define DRM_IOCTL_I915_FLUSH           DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH)
@@ -227,6 +230,8 @@ typedef struct _drm_i915_sarea {
 #define DRM_IOCTL_I915_GEM_PIN         DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_PIN, struct drm_i915_gem_pin)
 #define DRM_IOCTL_I915_GEM_UNPIN       DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_UNPIN, struct drm_i915_gem_unpin)
 #define DRM_IOCTL_I915_GEM_BUSY                DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_BUSY, struct drm_i915_gem_busy)
+#define DRM_IOCTL_I915_GEM_SET_CACHING         DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_SET_CACHING, struct drm_i915_gem_caching)
+#define DRM_IOCTL_I915_GEM_GET_CACHING         DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_GET_CACHING, struct drm_i915_gem_caching)
 #define DRM_IOCTL_I915_GEM_THROTTLE    DRM_IO ( DRM_COMMAND_BASE + DRM_I915_GEM_THROTTLE)
 #define DRM_IOCTL_I915_GEM_ENTERVT     DRM_IO(DRM_COMMAND_BASE + DRM_I915_GEM_ENTERVT)
 #define DRM_IOCTL_I915_GEM_LEAVEVT     DRM_IO(DRM_COMMAND_BASE + DRM_I915_GEM_LEAVEVT)
@@ -249,6 +254,7 @@ typedef struct _drm_i915_sarea {
 #define DRM_IOCTL_I915_GEM_WAIT                DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_WAIT, struct drm_i915_gem_wait)
 #define DRM_IOCTL_I915_GEM_CONTEXT_CREATE      DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_CREATE, struct drm_i915_gem_context_create)
 #define DRM_IOCTL_I915_GEM_CONTEXT_DESTROY     DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_DESTROY, struct drm_i915_gem_context_destroy)
+#define DRM_IOCTL_I915_REG_READ                        DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_REG_READ, struct drm_i915_reg_read)
 
 /* Allow drivers to submit batchbuffers directly to hardware, relying
  * on the security mechanisms provided by hardware.
@@ -305,6 +311,9 @@ typedef struct drm_i915_irq_wait {
 #define I915_PARAM_HAS_LLC                      17
 #define I915_PARAM_HAS_ALIASING_PPGTT   18
 #define I915_PARAM_HAS_WAIT_TIMEOUT     19
+#define I915_PARAM_HAS_SEMAPHORES       20
+#define I915_PARAM_HAS_PRIME_VMAP_FLUSH         21
+#define I915_PARAM_RSVD_FOR_FUTURE_USE  22
 
 typedef struct drm_i915_getparam {
        int param;
@@ -698,10 +707,31 @@ struct drm_i915_gem_busy {
        /** Handle of the buffer to check for busy */
        __u32 handle;
 
-       /** Return busy status (1 if busy, 0 if idle) */
+       /** Return busy status (1 if busy, 0 if idle).
+        * The high word is used to indicate on which rings the object
+        * currently resides:
+        *  16:31 - busy (r or r/w) rings (16 render, 17 bsd, 18 blt, etc)
+        */
        __u32 busy;
 };
 
+#define I915_CACHING_NONE              0
+#define I915_CACHING_CACHED            1
+
+struct drm_i915_gem_caching {
+       /**
+        * Handle of the buffer to set/get the caching level of. */
+       __u32 handle;
+
+       /**
+        * Cacheing level to apply or return value
+        *
+        * bits0-15 are for generic caching control (i.e. the above defined
+        * values). bits16-31 are reserved for platform-specific variations
+        * (e.g. l3$ caching on gen7). */
+       __u32 caching;
+};
+
 #define I915_TILING_NONE       0
 #define I915_TILING_X          1
 #define I915_TILING_Y          2
@@ -918,4 +948,8 @@ struct drm_i915_gem_context_destroy {
        __u32 pad;
 };
 
+struct drm_i915_reg_read {
+       __u64 offset;
+       __u64 val; /* Return value */
+};
 #endif                         /* _I915_DRM_H_ */
index 8e29d551bb3c692da6d54d874474d189e00c8a59..2e37e9f02e7141429af5860144dde1f863342b86 100644 (file)
@@ -30,16 +30,10 @@ void intel_gmch_remove(void);
 bool intel_enable_gtt(void);
 
 void intel_gtt_chipset_flush(void);
-void intel_gtt_unmap_memory(struct scatterlist *sg_list, int num_sg);
-void intel_gtt_clear_range(unsigned int first_entry, unsigned int num_entries);
-int intel_gtt_map_memory(struct page **pages, unsigned int num_entries,
-                        struct scatterlist **sg_list, int *num_sg);
-void intel_gtt_insert_sg_entries(struct scatterlist *sg_list,
-                                unsigned int sg_len,
+void intel_gtt_insert_sg_entries(struct sg_table *st,
                                 unsigned int pg_start,
                                 unsigned int flags);
-void intel_gtt_insert_pages(unsigned int first_entry, unsigned int num_entries,
-                           struct page **pages, unsigned int flags);
+void intel_gtt_clear_range(unsigned int first_entry, unsigned int num_entries);
 
 /* Special gtt memory types */
 #define AGP_DCACHE_MEMORY      1
index 06023393fba97cfc069ae119f66d84b1bdb0b256..4eb31752e2b77592e8a2fdbf3fde779d4851504d 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <linux/platform_device.h>
 #include <linux/list.h>
+#include <linux/io.h>
 
 struct ssc_device {
        struct list_head        list;
index 2f4079175afb81f80f641e28da9b1145b5415bae..934bc34d5f993892f75c3b803d412b43e62dc072 100644 (file)
 #endif
 #endif
 
+#if __GNUC_MINOR__ >= 6
+/*
+ * Tell the optimizer that something else uses this function or variable.
+ */
+#define __visible __attribute__((externally_visible))
+#endif
+
 #if __GNUC_MINOR__ > 0
 #define __compiletime_object_size(obj) __builtin_object_size(obj, 0)
 #endif
index 923d093c9ceac685f199868a8b49fa58349aa554..f430e4162f41ea188fdb5168df28b4304b7c14c3 100644 (file)
@@ -278,6 +278,10 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect);
 # define __section(S) __attribute__ ((__section__(#S)))
 #endif
 
+#ifndef __visible
+#define __visible
+#endif
+
 /* Are two types/vars the same type (ignoring qualifiers)? */
 #ifndef __same_type
 # define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
index caa34e50537e895a7a4b5149148f03157e253129..59200795482eebccde8497372c82d1aca0cadd40 100644 (file)
@@ -206,6 +206,8 @@ struct dentry_operations {
 #define DCACHE_MANAGED_DENTRY \
        (DCACHE_MOUNTED|DCACHE_NEED_AUTOMOUNT|DCACHE_MANAGE_TRANSIT)
 
+#define DCACHE_DENTRY_KILLED   0x100000
+
 extern seqlock_t rename_lock;
 
 static inline int dname_external(struct dentry *dentry)
index 1bc74afe7a35c7ea248510874910525d0206c9c3..49ed17fdf0556436cd626e52e0a9cd9809bc610a 100644 (file)
@@ -22,6 +22,7 @@ struct i2c_pnx_mif {
        struct timer_list       timer;          /* Timeout */
        u8 *                    buf;            /* Data buffer */
        int                     len;            /* Length of data buffer */
+       int                     order;          /* RX Bytes to order via TX */
 };
 
 struct i2c_pnx_algo_data {
index 594b419b7d20229cf79715b1250124491abe4ca6..2451f1f7a1d969f29bb2a3137c846566bfaf614a 100644 (file)
@@ -91,7 +91,7 @@
 {                                                      \
        typeof(x) __x = x;                              \
        typeof(divisor) __d = divisor;                  \
-       (((typeof(x))-1) >= 0 || (__x) >= 0) ?          \
+       (((typeof(x))-1) > 0 || (__x) > 0) ?            \
                (((__x) + ((__d) / 2)) / (__d)) :       \
                (((__x) - ((__d) / 2)) / (__d));        \
 }                                                      \
index fc615a97e2d363df686f6111988cf686d8e03dfa..1e57449395b16db43ecfb638b71acc533e9ec73b 100644 (file)
@@ -224,7 +224,7 @@ static inline int kobject_uevent_env(struct kobject *kobj,
 
 static inline __printf(2, 3)
 int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
-{ return 0; }
+{ return -ENOMEM; }
 
 static inline int kobject_action_type(const char *buf, size_t count,
                                      enum kobject_action *type)
index d0752eca9b4495011f6f82b20ba4e487b06ad6da..9d96d5d4dfed30f19c5ddeb04bacacd44f08bd45 100644 (file)
@@ -183,7 +183,7 @@ extern int  mISDN_initbchannel(struct bchannel *, unsigned short,
                                   unsigned short);
 extern int     mISDN_freedchannel(struct dchannel *);
 extern void    mISDN_clear_bchannel(struct bchannel *);
-extern int     mISDN_freebchannel(struct bchannel *);
+extern void    mISDN_freebchannel(struct bchannel *);
 extern int     mISDN_ctrl_bchannel(struct bchannel *, struct mISDN_ctrl_req *);
 extern void    queue_ch_frame(struct mISDNchannel *, u_int,
                        int, struct sk_buff *);
index 1ac7f6e405f9e2fe3556cb02135393b9b818af24..ff9a9f8e0ed9deb1573c087475511b269b7e17e2 100644 (file)
@@ -19,7 +19,7 @@
 #include <linux/compiler.h>
 #include <linux/mutex.h>
 
-#define MIN_MEMORY_BLOCK_SIZE     (1 << SECTION_SIZE_BITS)
+#define MIN_MEMORY_BLOCK_SIZE     (1UL << SECTION_SIZE_BITS)
 
 struct memory_block {
        unsigned long start_section_nr;
index 3a8435a8058f1cec9357b3f980efb0eae18f5569..cebe97ee98b86e1d6f6ef478ddf84693d8486d6b 100644 (file)
@@ -16,6 +16,8 @@
 
 #include <linux/platform_device.h>
 
+struct irq_domain;
+
 /*
  * This struct describes the MFD part ("cell").
  * After registration the copy of this structure will become the platform data
@@ -98,7 +100,7 @@ static inline const struct mfd_cell *mfd_get_cell(struct platform_device *pdev)
 extern int mfd_add_devices(struct device *parent, int id,
                           struct mfd_cell *cells, int n_devs,
                           struct resource *mem_base,
-                          int irq_base);
+                          int irq_base, struct irq_domain *irq_domain);
 
 extern void mfd_remove_devices(struct device *parent);
 
index 12c06870829af2add4caed14a9ea57f069b7479f..7cd83d826ed82285099c55cc7008dc558feddcb2 100644 (file)
@@ -22,6 +22,9 @@
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
 
+/* TPS chip id list */
+#define TPS65217                       0xF0
+
 /* I2C ID for TPS65217 part */
 #define TPS65217_I2C_ID                        0x24
 
@@ -248,13 +251,11 @@ struct tps_info {
 struct tps65217 {
        struct device *dev;
        struct tps65217_board *pdata;
+       unsigned int id;
        struct regulator_desc desc[TPS65217_NUM_REGULATOR];
        struct regulator_dev *rdev[TPS65217_NUM_REGULATOR];
        struct tps_info *info[TPS65217_NUM_REGULATOR];
        struct regmap *regmap;
-
-       /* Client devices */
-       struct platform_device *regulator_pdev[TPS65217_NUM_REGULATOR];
 };
 
 static inline struct tps65217 *dev_to_tps65217(struct device *dev)
@@ -262,6 +263,11 @@ static inline struct tps65217 *dev_to_tps65217(struct device *dev)
        return dev_get_drvdata(dev);
 }
 
+static inline int tps65217_chip_id(struct tps65217 *tps65217)
+{
+       return tps65217->id;
+}
+
 int tps65217_reg_read(struct tps65217 *tps, unsigned int reg,
                                        unsigned int *val);
 int tps65217_reg_write(struct tps65217 *tps, unsigned int reg,
index bd6c9fcdf2dd30c29b582a38e5f5c3f1eb320b62..6e1b0f973a03511b398154a5d42f3a9174b9268a 100644 (file)
@@ -796,6 +796,19 @@ enum mlx4_net_trans_rule_id {
        MLX4_NET_TRANS_RULE_NUM, /* should be last */
 };
 
+extern const u16 __sw_id_hw[];
+
+static inline int map_hw_to_sw_id(u16 header_id)
+{
+
+       int i;
+       for (i = 0; i < MLX4_NET_TRANS_RULE_NUM; i++) {
+               if (header_id == __sw_id_hw[i])
+                       return i;
+       }
+       return -EINVAL;
+}
+
 enum mlx4_net_trans_promisc_mode {
        MLX4_FS_PROMISC_NONE = 0,
        MLX4_FS_PROMISC_UPLINK,
index 1f8fc7f9bcd8b8eb0d07588ba671b9327e53fe90..4b03f56e280eb9e59f236806ce24ce36e435c9c7 100644 (file)
@@ -265,11 +265,6 @@ static inline const struct nfs_rpc_ops *NFS_PROTO(const struct inode *inode)
        return NFS_SERVER(inode)->nfs_client->rpc_ops;
 }
 
-static inline __be32 *NFS_COOKIEVERF(const struct inode *inode)
-{
-       return NFS_I(inode)->cookieverf;
-}
-
 static inline unsigned NFS_MINATTRTIMEO(const struct inode *inode)
 {
        struct nfs_server *nfss = NFS_SERVER(inode);
index ac7c8ae254f251933e48f04d5c877eaaa3ec6e09..be9cf3c7e79ec0afcc0e024f6b95da5ab2bbd97e 100644 (file)
@@ -652,7 +652,7 @@ struct nfs_getaclargs {
 };
 
 /* getxattr ACL interface flags */
-#define NFS4_ACL_LEN_REQUEST   0x0001  /* zero length getxattr buffer */
+#define NFS4_ACL_TRUNC         0x0001  /* ACL was truncated */
 struct nfs_getaclres {
        size_t                          acl_len;
        size_t                          acl_data_offset;
index 7602ccb3f40ec672001be2eae9be3395604493f2..bdb41612bfec621e1aaba5e2468c5b01c6131770 100644 (file)
@@ -274,6 +274,8 @@ struct perf_event_attr {
        __u64   branch_sample_type; /* enum branch_sample_type */
 };
 
+#define perf_flags(attr)       (*(&(attr)->read_format + 1))
+
 /*
  * Ioctls that can be done on a perf event fd:
  */
@@ -926,7 +928,7 @@ struct perf_event {
        struct hw_perf_event            hw;
 
        struct perf_event_context       *ctx;
-       struct file                     *filp;
+       atomic_long_t                   refcount;
 
        /*
         * These accumulate total time (in nanoseconds) that children
@@ -1296,6 +1298,7 @@ extern int perf_swevent_get_recursion_context(void);
 extern void perf_swevent_put_recursion_context(int rctx);
 extern void perf_event_enable(struct perf_event *event);
 extern void perf_event_disable(struct perf_event *event);
+extern int __perf_event_disable(void *info);
 extern void perf_event_task_tick(void);
 #else
 static inline void
@@ -1334,6 +1337,7 @@ static inline int  perf_swevent_get_recursion_context(void)               { return -1; }
 static inline void perf_swevent_put_recursion_context(int rctx)                { }
 static inline void perf_event_enable(struct perf_event *event)         { }
 static inline void perf_event_disable(struct perf_event *event)                { }
+static inline int __perf_event_disable(void *info)                     { return -1; }
 static inline void perf_event_task_tick(void)                          { }
 #endif
 
diff --git a/include/linux/platform_data/shmob_drm.h b/include/linux/platform_data/shmob_drm.h
new file mode 100644 (file)
index 0000000..7c686d3
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * shmob_drm.h  --  SH Mobile DRM driver
+ *
+ * Copyright (C) 2012 Renesas Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __SHMOB_DRM_H__
+#define __SHMOB_DRM_H__
+
+#include <linux/kernel.h>
+
+#include <drm/drm_mode.h>
+
+struct sh_mobile_meram_cfg;
+struct sh_mobile_meram_info;
+
+enum shmob_drm_clk_source {
+       SHMOB_DRM_CLK_BUS,
+       SHMOB_DRM_CLK_PERIPHERAL,
+       SHMOB_DRM_CLK_EXTERNAL,
+};
+
+enum shmob_drm_interface {
+       SHMOB_DRM_IFACE_RGB8,           /* 24bpp, 8:8:8 */
+       SHMOB_DRM_IFACE_RGB9,           /* 18bpp, 9:9 */
+       SHMOB_DRM_IFACE_RGB12A,         /* 24bpp, 12:12 */
+       SHMOB_DRM_IFACE_RGB12B,         /* 12bpp */
+       SHMOB_DRM_IFACE_RGB16,          /* 16bpp */
+       SHMOB_DRM_IFACE_RGB18,          /* 18bpp */
+       SHMOB_DRM_IFACE_RGB24,          /* 24bpp */
+       SHMOB_DRM_IFACE_YUV422,         /* 16bpp */
+       SHMOB_DRM_IFACE_SYS8A,          /* 24bpp, 8:8:8 */
+       SHMOB_DRM_IFACE_SYS8B,          /* 18bpp, 8:8:2 */
+       SHMOB_DRM_IFACE_SYS8C,          /* 18bpp, 2:8:8 */
+       SHMOB_DRM_IFACE_SYS8D,          /* 16bpp, 8:8 */
+       SHMOB_DRM_IFACE_SYS9,           /* 18bpp, 9:9 */
+       SHMOB_DRM_IFACE_SYS12,          /* 24bpp, 12:12 */
+       SHMOB_DRM_IFACE_SYS16A,         /* 16bpp */
+       SHMOB_DRM_IFACE_SYS16B,         /* 18bpp, 16:2 */
+       SHMOB_DRM_IFACE_SYS16C,         /* 18bpp, 2:16 */
+       SHMOB_DRM_IFACE_SYS18,          /* 18bpp */
+       SHMOB_DRM_IFACE_SYS24,          /* 24bpp */
+};
+
+struct shmob_drm_backlight_data {
+       const char *name;
+       int max_brightness;
+       int (*get_brightness)(void);
+       int (*set_brightness)(int brightness);
+};
+
+struct shmob_drm_panel_data {
+       unsigned int width_mm;          /* Panel width in mm */
+       unsigned int height_mm;         /* Panel height in mm */
+       struct drm_mode_modeinfo mode;
+};
+
+struct shmob_drm_sys_interface_data {
+       unsigned int read_latch:6;
+       unsigned int read_setup:8;
+       unsigned int read_cycle:8;
+       unsigned int read_strobe:8;
+       unsigned int write_setup:8;
+       unsigned int write_cycle:8;
+       unsigned int write_strobe:8;
+       unsigned int cs_setup:3;
+       unsigned int vsync_active_high:1;
+       unsigned int vsync_dir_input:1;
+};
+
+#define SHMOB_DRM_IFACE_FL_DWPOL (1 << 0) /* Rising edge dot clock data latch */
+#define SHMOB_DRM_IFACE_FL_DIPOL (1 << 1) /* Active low display enable */
+#define SHMOB_DRM_IFACE_FL_DAPOL (1 << 2) /* Active low display data */
+#define SHMOB_DRM_IFACE_FL_HSCNT (1 << 3) /* Disable HSYNC during VBLANK */
+#define SHMOB_DRM_IFACE_FL_DWCNT (1 << 4) /* Disable dotclock during blanking */
+
+struct shmob_drm_interface_data {
+       enum shmob_drm_interface interface;
+       struct shmob_drm_sys_interface_data sys;
+       unsigned int clk_div;
+       unsigned int flags;
+};
+
+struct shmob_drm_platform_data {
+       enum shmob_drm_clk_source clk_source;
+       struct shmob_drm_interface_data iface;
+       struct shmob_drm_panel_data panel;
+       struct shmob_drm_backlight_data backlight;
+       const struct sh_mobile_meram_cfg *meram;
+};
+
+#endif /* __SHMOB_DRM_H__ */
index b8c86648a2f95dc6f83aab668fd9a7e07f277b4e..23bddac4bad8d08f3781d1e8a453aa41edb28632 100644 (file)
@@ -954,7 +954,6 @@ struct sched_domain {
        unsigned int smt_gain;
        int flags;                      /* See SD_* */
        int level;
-       int idle_buddy;                 /* cpu assigned to select_idle_sibling() */
 
        /* Runtime fields. */
        unsigned long last_balance;     /* init to jiffies. units in jiffies */
index cff40aa7db625bbb9dafd6b5842d3dc70276c682..bf8c49ff7530c7ee8b85d4a8b3ccef5de86ff308 100644 (file)
@@ -114,6 +114,7 @@ struct rpc_xprt_ops {
        void            (*set_buffer_size)(struct rpc_xprt *xprt, size_t sndsize, size_t rcvsize);
        int             (*reserve_xprt)(struct rpc_xprt *xprt, struct rpc_task *task);
        void            (*release_xprt)(struct rpc_xprt *xprt, struct rpc_task *task);
+       void            (*alloc_slot)(struct rpc_xprt *xprt, struct rpc_task *task);
        void            (*rpcbind)(struct rpc_task *task);
        void            (*set_port)(struct rpc_xprt *xprt, unsigned short port);
        void            (*connect)(struct rpc_task *task);
@@ -281,6 +282,8 @@ void                        xprt_connect(struct rpc_task *task);
 void                   xprt_reserve(struct rpc_task *task);
 int                    xprt_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task);
 int                    xprt_reserve_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task);
+void                   xprt_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task);
+void                   xprt_lock_and_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task);
 int                    xprt_prepare_transmit(struct rpc_task *task);
 void                   xprt_transmit(struct rpc_task *task);
 void                   xprt_end_transmit(struct rpc_task *task);
index 22e61fdf75a2bd46a656e164e046a006acd43343..28e493b5b94c9ba6d946d728a0908c84e34294db 100644 (file)
@@ -84,6 +84,8 @@ struct xfrm_replay_state {
        __u32   bitmap;
 };
 
+#define XFRMA_REPLAY_ESN_MAX   4096
+
 struct xfrm_replay_state_esn {
        unsigned int    bmp_len;
        __u32           oseq;
index ca356a7349202272236b9d7db421f6d8804d89a5..8b27927b2a55de3dfd5f94c5a40ef3c3b886eb06 100644 (file)
@@ -136,7 +136,7 @@ struct smp_chan {
 };
 
 /* SMP Commands */
-int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level);
+int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
 int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb);
 int smp_distribute_keys(struct l2cap_conn *conn, __u8 force);
 int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);
index 0fedbd8d747a049db1810fdc325b032d62782b1b..9fc7114159e885031550abedebf67c74f08bde76 100644 (file)
@@ -111,9 +111,8 @@ struct rt6_info {
        struct inet6_dev                *rt6i_idev;
        unsigned long                   _rt6i_peer;
 
-#ifdef CONFIG_XFRM
-       u32                             rt6i_flow_cache_genid;
-#endif
+       u32                             rt6i_genid;
+
        /* more non-fragment space at head required */
        unsigned short                  rt6i_nfheader_len;
 
index ae1cd6c9ba521bb6ca9a57706b4ca77180bdcc7a..fd87963a0ea5878cecae892541b9fc9f79e64814 100644 (file)
@@ -102,6 +102,7 @@ struct net {
 #endif
        struct netns_ipvs       *ipvs;
        struct sock             *diag_nlsk;
+       atomic_t                rt_genid;
 };
 
 
@@ -300,5 +301,14 @@ static inline void unregister_net_sysctl_table(struct ctl_table_header *header)
 }
 #endif
 
+static inline int rt_genid(struct net *net)
+{
+       return atomic_read(&net->rt_genid);
+}
+
+static inline void rt_genid_bump(struct net *net)
+{
+       atomic_inc(&net->rt_genid);
+}
 
 #endif /* __NET_NET_NAMESPACE_H */
index 1474dd65c66f558999a70f38c081dea247fbee5a..eb24dbccd81e81aff5fac76c8792e93df66e7bb6 100644 (file)
@@ -65,7 +65,6 @@ struct netns_ipv4 {
        unsigned int sysctl_ping_group_range[2];
        long sysctl_tcp_mem[3];
 
-       atomic_t rt_genid;
        atomic_t dev_addr_genid;
 
 #ifdef CONFIG_IP_MROUTE
index 776a27f1ab78ef5a385ef8b8d2f21b9e0101e115..da22243d27600cd66b5fb70da61f5cadc05d5468 100644 (file)
@@ -108,7 +108,7 @@ extern struct ip_rt_acct __percpu *ip_rt_acct;
 
 struct in_device;
 extern int             ip_rt_init(void);
-extern void            rt_cache_flush(struct net *net, int how);
+extern void            rt_cache_flush(struct net *net);
 extern void            rt_flush_dev(struct net_device *dev);
 extern struct rtable *__ip_route_output_key(struct net *, struct flowi4 *flp);
 extern struct rtable *ip_route_output_flow(struct net *, struct flowi4 *flp,
index 72132aef53fc61a721ce23c8fbc4fe17cb7a9699..adb7da20b5a10b2679d59734f6f46b971acf0094 100644 (file)
@@ -1332,7 +1332,7 @@ static inline bool sk_wmem_schedule(struct sock *sk, int size)
 }
 
 static inline bool
-sk_rmem_schedule(struct sock *sk, struct sk_buff *skb, unsigned int size)
+sk_rmem_schedule(struct sock *sk, struct sk_buff *skb, int size)
 {
        if (!sk_has_account(sk))
                return true;
index 976a81abe1a231de348085a92c8f3110a21fc3fa..639dd1316d375aeb2802c73032cc4cae6dcb8b0c 100644 (file)
@@ -273,6 +273,9 @@ struct xfrm_replay {
        int     (*check)(struct xfrm_state *x,
                         struct sk_buff *skb,
                         __be32 net_seq);
+       int     (*recheck)(struct xfrm_state *x,
+                          struct sk_buff *skb,
+                          __be32 net_seq);
        void    (*notify)(struct xfrm_state *x, int event);
        int     (*overflow)(struct xfrm_state *x, struct sk_buff *skb);
 };
index f1405d335a968d093aadafee06ab4be105970f88..941c84bf1065f406cfbc3eeb5f4b09e4fdcd58ec 100644 (file)
@@ -23,7 +23,9 @@ struct se_subsystem_api {
        struct se_device *(*create_virtdevice)(struct se_hba *,
                                struct se_subsystem_dev *, void *);
        void (*free_device)(void *);
-       int (*transport_complete)(struct se_cmd *cmd, struct scatterlist *);
+       void (*transport_complete)(struct se_cmd *cmd,
+                                  struct scatterlist *,
+                                  unsigned char *);
 
        int (*parse_cdb)(struct se_cmd *cmd);
        ssize_t (*check_configfs_dev_params)(struct se_hba *,
index 015cea01ae39bedd1a1bf796e23a8f90f061e5e2..5be89373ceac659c92e671a463c80f146656c416 100644 (file)
 
 #define SE_INQUIRY_BUF                         512
 #define SE_MODE_PAGE_BUF                       512
+#define SE_SENSE_BUF                           96
 
 /* struct se_hba->hba_flags */
 enum hba_flags_table {
index 5f889f16b0c891dbedafd65686c48688aa7110da..08fa27244da740787539bd391ef3d61dc8060158 100644 (file)
@@ -214,7 +214,7 @@ TRACE_EVENT(mm_page_alloc,
 
        TP_printk("page=%p pfn=%lu order=%d migratetype=%d gfp_flags=%s",
                __entry->page,
-               page_to_pfn(__entry->page),
+               __entry->page ? page_to_pfn(__entry->page) : 0,
                __entry->order,
                __entry->migratetype,
                show_gfp_flags(__entry->gfp_flags))
@@ -240,7 +240,7 @@ DECLARE_EVENT_CLASS(mm_page,
 
        TP_printk("page=%p pfn=%lu order=%u migratetype=%d percpu_refill=%d",
                __entry->page,
-               page_to_pfn(__entry->page),
+               __entry->page ? page_to_pfn(__entry->page) : 0,
                __entry->order,
                __entry->migratetype,
                __entry->order == 0)
index 11e27c3af3cb45c63d9c689442096b4afd66c55f..f19fff8650e93075aae1717034f29898f1199646 100644 (file)
@@ -187,6 +187,7 @@ int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops,
                    struct gnttab_map_grant_ref *kmap_ops,
                    struct page **pages, unsigned int count);
 int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops,
-                     struct page **pages, unsigned int count, bool clear_pte);
+                     struct gnttab_map_grant_ref *kunmap_ops,
+                     struct page **pages, unsigned int count);
 
 #endif /* __ASM_GNTTAB_H__ */
index b7935fcec7d923b0b0b89fe0fe7dfdf61967b447..7fee567153f022cc2a097135a524bf393887e1a2 100644 (file)
@@ -1253,7 +1253,7 @@ retry:
 /*
  * Cross CPU call to disable a performance event
  */
-static int __perf_event_disable(void *info)
+int __perf_event_disable(void *info)
 {
        struct perf_event *event = info;
        struct perf_event_context *ctx = event->ctx;
@@ -2935,12 +2935,12 @@ EXPORT_SYMBOL_GPL(perf_event_release_kernel);
 /*
  * Called when the last reference to the file is gone.
  */
-static int perf_release(struct inode *inode, struct file *file)
+static void put_event(struct perf_event *event)
 {
-       struct perf_event *event = file->private_data;
        struct task_struct *owner;
 
-       file->private_data = NULL;
+       if (!atomic_long_dec_and_test(&event->refcount))
+               return;
 
        rcu_read_lock();
        owner = ACCESS_ONCE(event->owner);
@@ -2975,7 +2975,13 @@ static int perf_release(struct inode *inode, struct file *file)
                put_task_struct(owner);
        }
 
-       return perf_event_release_kernel(event);
+       perf_event_release_kernel(event);
+}
+
+static int perf_release(struct inode *inode, struct file *file)
+{
+       put_event(file->private_data);
+       return 0;
 }
 
 u64 perf_event_read_value(struct perf_event *event, u64 *enabled, u64 *running)
@@ -3227,7 +3233,7 @@ unlock:
 
 static const struct file_operations perf_fops;
 
-static struct perf_event *perf_fget_light(int fd, int *fput_needed)
+static struct file *perf_fget_light(int fd, int *fput_needed)
 {
        struct file *file;
 
@@ -3241,7 +3247,7 @@ static struct perf_event *perf_fget_light(int fd, int *fput_needed)
                return ERR_PTR(-EBADF);
        }
 
-       return file->private_data;
+       return file;
 }
 
 static int perf_event_set_output(struct perf_event *event,
@@ -3273,19 +3279,21 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
        case PERF_EVENT_IOC_SET_OUTPUT:
        {
+               struct file *output_file = NULL;
                struct perf_event *output_event = NULL;
                int fput_needed = 0;
                int ret;
 
                if (arg != -1) {
-                       output_event = perf_fget_light(arg, &fput_needed);
-                       if (IS_ERR(output_event))
-                               return PTR_ERR(output_event);
+                       output_file = perf_fget_light(arg, &fput_needed);
+                       if (IS_ERR(output_file))
+                               return PTR_ERR(output_file);
+                       output_event = output_file->private_data;
                }
 
                ret = perf_event_set_output(event, output_event);
                if (output_event)
-                       fput_light(output_event->filp, fput_needed);
+                       fput_light(output_file, fput_needed);
 
                return ret;
        }
@@ -5950,6 +5958,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
 
        mutex_init(&event->mmap_mutex);
 
+       atomic_long_set(&event->refcount, 1);
        event->cpu              = cpu;
        event->attr             = *attr;
        event->group_leader     = group_leader;
@@ -6260,12 +6269,12 @@ SYSCALL_DEFINE5(perf_event_open,
                return event_fd;
 
        if (group_fd != -1) {
-               group_leader = perf_fget_light(group_fd, &fput_needed);
-               if (IS_ERR(group_leader)) {
-                       err = PTR_ERR(group_leader);
+               group_file = perf_fget_light(group_fd, &fput_needed);
+               if (IS_ERR(group_file)) {
+                       err = PTR_ERR(group_file);
                        goto err_fd;
                }
-               group_file = group_leader->filp;
+               group_leader = group_file->private_data;
                if (flags & PERF_FLAG_FD_OUTPUT)
                        output_event = group_leader;
                if (flags & PERF_FLAG_FD_NO_GROUP)
@@ -6402,7 +6411,6 @@ SYSCALL_DEFINE5(perf_event_open,
                put_ctx(gctx);
        }
 
-       event->filp = event_file;
        WARN_ON_ONCE(ctx->parent_ctx);
        mutex_lock(&ctx->mutex);
 
@@ -6496,7 +6504,6 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu,
                goto err_free;
        }
 
-       event->filp = NULL;
        WARN_ON_ONCE(ctx->parent_ctx);
        mutex_lock(&ctx->mutex);
        perf_install_in_context(ctx, event, cpu);
@@ -6578,7 +6585,7 @@ static void sync_child_event(struct perf_event *child_event,
         * Release the parent event, if this was the last
         * reference to it.
         */
-       fput(parent_event->filp);
+       put_event(parent_event);
 }
 
 static void
@@ -6654,9 +6661,8 @@ static void perf_event_exit_task_context(struct task_struct *child, int ctxn)
         *
         *   __perf_event_exit_task()
         *     sync_child_event()
-        *       fput(parent_event->filp)
-        *         perf_release()
-        *           mutex_lock(&ctx->mutex)
+        *       put_event()
+        *         mutex_lock(&ctx->mutex)
         *
         * But since its the parent context it won't be the same instance.
         */
@@ -6724,7 +6730,7 @@ static void perf_free_event(struct perf_event *event,
        list_del_init(&event->child_list);
        mutex_unlock(&parent->child_mutex);
 
-       fput(parent->filp);
+       put_event(parent);
 
        perf_group_detach(event);
        list_del_event(event, ctx);
@@ -6804,6 +6810,12 @@ inherit_event(struct perf_event *parent_event,
                                           NULL, NULL);
        if (IS_ERR(child_event))
                return child_event;
+
+       if (!atomic_long_inc_not_zero(&parent_event->refcount)) {
+               free_event(child_event);
+               return NULL;
+       }
+
        get_ctx(child_ctx);
 
        /*
@@ -6844,14 +6856,6 @@ inherit_event(struct perf_event *parent_event,
        add_event_to_ctx(child_event, child_ctx);
        raw_spin_unlock_irqrestore(&child_ctx->lock, flags);
 
-       /*
-        * Get a reference to the parent filp - we will fput it
-        * when the child event exits. This is safe to do because
-        * we are in the parent and we know that the filp still
-        * exists and has a nonzero count:
-        */
-       atomic_long_inc(&parent_event->filp->f_count);
-
        /*
         * Link this into the parent event's child list
         */
index bb38c4d3ee129ab06c1b46dc295ca6864c39c416..9a7b487c6fe240c1a2e4f5c70ef68da6370ebf78 100644 (file)
@@ -453,7 +453,16 @@ int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *att
        int old_type = bp->attr.bp_type;
        int err = 0;
 
-       perf_event_disable(bp);
+       /*
+        * modify_user_hw_breakpoint can be invoked with IRQs disabled and hence it
+        * will not be possible to raise IPIs that invoke __perf_event_disable.
+        * So call the function directly after making sure we are targeting the
+        * current task.
+        */
+       if (irqs_disabled() && bp->ctx && bp->ctx->task == current)
+               __perf_event_disable(bp);
+       else
+               perf_event_disable(bp);
 
        bp->attr.bp_addr = attr->bp_addr;
        bp->attr.bp_type = attr->bp_type;
index b3c7fd5542500ab13940814211692bbe3b7ef8d3..6144bab8fd8eeed14327c116bea39ba0061cbdb5 100644 (file)
@@ -232,15 +232,19 @@ static int pid_ns_ctl_handler(struct ctl_table *table, int write,
         */
 
        tmp.data = &current->nsproxy->pid_ns->last_pid;
-       return proc_dointvec(&tmp, write, buffer, lenp, ppos);
+       return proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
 }
 
+extern int pid_max;
+static int zero = 0;
 static struct ctl_table pid_ns_ctl_table[] = {
        {
                .procname = "ns_last_pid",
                .maxlen = sizeof(int),
                .mode = 0666, /* permissions are checked in the handler */
                .proc_handler = pid_ns_ctl_handler,
+               .extra1 = &zero,
+               .extra2 = &pid_max,
        },
        { }
 };
index fbf1fd098dc6cca687f0e9296a931aa0425c6fee..649c9f876cb164b0e16683479b59bf4d4f3b794b 100644 (file)
@@ -5304,27 +5304,17 @@ void idle_task_exit(void)
 }
 
 /*
- * While a dead CPU has no uninterruptible tasks queued at this point,
- * it might still have a nonzero ->nr_uninterruptible counter, because
- * for performance reasons the counter is not stricly tracking tasks to
- * their home CPUs. So we just add the counter to another CPU's counter,
- * to keep the global sum constant after CPU-down:
- */
-static void migrate_nr_uninterruptible(struct rq *rq_src)
-{
-       struct rq *rq_dest = cpu_rq(cpumask_any(cpu_active_mask));
-
-       rq_dest->nr_uninterruptible += rq_src->nr_uninterruptible;
-       rq_src->nr_uninterruptible = 0;
-}
-
-/*
- * remove the tasks which were accounted by rq from calc_load_tasks.
+ * Since this CPU is going 'away' for a while, fold any nr_active delta
+ * we might have. Assumes we're called after migrate_tasks() so that the
+ * nr_active count is stable.
+ *
+ * Also see the comment "Global load-average calculations".
  */
-static void calc_global_load_remove(struct rq *rq)
+static void calc_load_migrate(struct rq *rq)
 {
-       atomic_long_sub(rq->calc_load_active, &calc_load_tasks);
-       rq->calc_load_active = 0;
+       long delta = calc_load_fold_active(rq);
+       if (delta)
+               atomic_long_add(delta, &calc_load_tasks);
 }
 
 /*
@@ -5352,9 +5342,6 @@ static void migrate_tasks(unsigned int dead_cpu)
         */
        rq->stop = NULL;
 
-       /* Ensure any throttled groups are reachable by pick_next_task */
-       unthrottle_offline_cfs_rqs(rq);
-
        for ( ; ; ) {
                /*
                 * There's this thread running, bail when that's the only
@@ -5618,8 +5605,7 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu)
                BUG_ON(rq->nr_running != 1); /* the migration thread */
                raw_spin_unlock_irqrestore(&rq->lock, flags);
 
-               migrate_nr_uninterruptible(rq);
-               calc_global_load_remove(rq);
+               calc_load_migrate(rq);
                break;
 #endif
        }
@@ -6028,11 +6014,6 @@ static void destroy_sched_domains(struct sched_domain *sd, int cpu)
  * SD_SHARE_PKG_RESOURCE set (Last Level Cache Domain) for this
  * allows us to avoid some pointer chasing select_idle_sibling().
  *
- * Iterate domains and sched_groups downward, assigning CPUs to be
- * select_idle_sibling() hw buddy.  Cross-wiring hw makes bouncing
- * due to random perturbation self canceling, ie sw buddies pull
- * their counterpart to their CPU's hw counterpart.
- *
  * Also keep a unique ID per domain (we use the first cpu number in
  * the cpumask of the domain), this allows us to quickly tell if
  * two cpus are in the same cache domain, see cpus_share_cache().
@@ -6046,40 +6027,8 @@ static void update_top_cache_domain(int cpu)
        int id = cpu;
 
        sd = highest_flag_domain(cpu, SD_SHARE_PKG_RESOURCES);
-       if (sd) {
-               struct sched_domain *tmp = sd;
-               struct sched_group *sg, *prev;
-               bool right;
-
-               /*
-                * Traverse to first CPU in group, and count hops
-                * to cpu from there, switching direction on each
-                * hop, never ever pointing the last CPU rightward.
-                */
-               do {
-                       id = cpumask_first(sched_domain_span(tmp));
-                       prev = sg = tmp->groups;
-                       right = 1;
-
-                       while (cpumask_first(sched_group_cpus(sg)) != id)
-                               sg = sg->next;
-
-                       while (!cpumask_test_cpu(cpu, sched_group_cpus(sg))) {
-                               prev = sg;
-                               sg = sg->next;
-                               right = !right;
-                       }
-
-                       /* A CPU went down, never point back to domain start. */
-                       if (right && cpumask_first(sched_group_cpus(sg->next)) == id)
-                               right = false;
-
-                       sg = right ? sg->next : prev;
-                       tmp->idle_buddy = cpumask_first(sched_group_cpus(sg));
-               } while ((tmp = tmp->child));
-
+       if (sd)
                id = cpumask_first(sched_domain_span(sd));
-       }
 
        rcu_assign_pointer(per_cpu(sd_llc, cpu), sd);
        per_cpu(sd_llc_id, cpu) = id;
index c219bf8d704c5460291abee8416264ee32d36e22..96e2b18b628312dd69e8ad2ee6e3a6e08f33cce7 100644 (file)
@@ -2052,7 +2052,7 @@ static void destroy_cfs_bandwidth(struct cfs_bandwidth *cfs_b)
        hrtimer_cancel(&cfs_b->slack_timer);
 }
 
-void unthrottle_offline_cfs_rqs(struct rq *rq)
+static void unthrottle_offline_cfs_rqs(struct rq *rq)
 {
        struct cfs_rq *cfs_rq;
 
@@ -2106,7 +2106,7 @@ static inline struct cfs_bandwidth *tg_cfs_bandwidth(struct task_group *tg)
        return NULL;
 }
 static inline void destroy_cfs_bandwidth(struct cfs_bandwidth *cfs_b) {}
-void unthrottle_offline_cfs_rqs(struct rq *rq) {}
+static inline void unthrottle_offline_cfs_rqs(struct rq *rq) {}
 
 #endif /* CONFIG_CFS_BANDWIDTH */
 
@@ -2637,6 +2637,8 @@ static int select_idle_sibling(struct task_struct *p, int target)
        int cpu = smp_processor_id();
        int prev_cpu = task_cpu(p);
        struct sched_domain *sd;
+       struct sched_group *sg;
+       int i;
 
        /*
         * If the task is going to be woken-up on this cpu and if it is
@@ -2653,17 +2655,29 @@ static int select_idle_sibling(struct task_struct *p, int target)
                return prev_cpu;
 
        /*
-        * Otherwise, check assigned siblings to find an elegible idle cpu.
+        * Otherwise, iterate the domains and find an elegible idle cpu.
         */
        sd = rcu_dereference(per_cpu(sd_llc, target));
-
        for_each_lower_domain(sd) {
-               if (!cpumask_test_cpu(sd->idle_buddy, tsk_cpus_allowed(p)))
-                       continue;
-               if (idle_cpu(sd->idle_buddy))
-                       return sd->idle_buddy;
-       }
+               sg = sd->groups;
+               do {
+                       if (!cpumask_intersects(sched_group_cpus(sg),
+                                               tsk_cpus_allowed(p)))
+                               goto next;
 
+                       for_each_cpu(i, sched_group_cpus(sg)) {
+                               if (!idle_cpu(i))
+                                       goto next;
+                       }
+
+                       target = cpumask_first_and(sched_group_cpus(sg),
+                                       tsk_cpus_allowed(p));
+                       goto done;
+next:
+                       sg = sg->next;
+               } while (sg != sd->groups);
+       }
+done:
        return target;
 }
 
@@ -3658,7 +3672,6 @@ fix_small_capacity(struct sched_domain *sd, struct sched_group *group)
  * @group: sched_group whose statistics are to be updated.
  * @load_idx: Load index of sched_domain of this_cpu for load calc.
  * @local_group: Does group contain this_cpu.
- * @cpus: Set of cpus considered for load balancing.
  * @balance: Should we balance.
  * @sgs: variable to hold the statistics for this group.
  */
@@ -3805,7 +3818,6 @@ static bool update_sd_pick_busiest(struct lb_env *env,
 /**
  * update_sd_lb_stats - Update sched_domain's statistics for load balancing.
  * @env: The load balancing environment.
- * @cpus: Set of cpus considered for load balancing.
  * @balance: Should we balance.
  * @sds: variable to hold the statistics for this sched_domain.
  */
@@ -4956,6 +4968,9 @@ static void rq_online_fair(struct rq *rq)
 static void rq_offline_fair(struct rq *rq)
 {
        update_sysctl();
+
+       /* Ensure any throttled groups are reachable by pick_next_task */
+       unthrottle_offline_cfs_rqs(rq);
 }
 
 #endif /* CONFIG_SMP */
index 944cb68420e957cbde71f9cacaaa4e81c4b1de20..e0b7ba9c040f74b22bb63e0d957b672dac4adce0 100644 (file)
@@ -691,6 +691,7 @@ balanced:
                 * runtime - in which case borrowing doesn't make sense.
                 */
                rt_rq->rt_runtime = RUNTIME_INF;
+               rt_rq->rt_throttled = 0;
                raw_spin_unlock(&rt_rq->rt_runtime_lock);
                raw_spin_unlock(&rt_b->rt_runtime_lock);
        }
index f6714d009e779a225ef295d33ceff7f2f8573be8..0848fa36c383e940a1e4245c611312dbdb7459d3 100644 (file)
@@ -1144,7 +1144,6 @@ extern void print_rt_stats(struct seq_file *m, int cpu);
 
 extern void init_cfs_rq(struct cfs_rq *cfs_rq);
 extern void init_rt_rq(struct rt_rq *rt_rq, struct rq *rq);
-extern void unthrottle_offline_cfs_rqs(struct rq *rq);
 
 extern void account_cfs_bandwidth_used(int enabled, int was_enabled);
 
index 024540f97f74c3e94205826f33d3968ea765f626..3a9e5d5c10916a7e67c131df489617a485a39bfc 100644 (file)
@@ -573,6 +573,7 @@ static void tick_nohz_restart_sched_tick(struct tick_sched *ts, ktime_t now)
        tick_do_update_jiffies64(now);
        update_cpu_load_nohz();
 
+       calc_load_exit_idle();
        touch_softlockup_watchdog();
        /*
         * Cancel the scheduled timer and restore the tick
index 34e5eac81424d98738246415a85d1a98a042bef1..d3b91e75cecd0df7895033aff87f588c6ba2d4df 100644 (file)
@@ -303,10 +303,11 @@ void getnstimeofday(struct timespec *ts)
                seq = read_seqbegin(&tk->lock);
 
                ts->tv_sec = tk->xtime_sec;
-               ts->tv_nsec = timekeeping_get_ns(tk);
+               nsecs = timekeeping_get_ns(tk);
 
        } while (read_seqretry(&tk->lock, seq));
 
+       ts->tv_nsec = 0;
        timespec_add_ns(ts, nsecs);
 }
 EXPORT_SYMBOL(getnstimeofday);
@@ -345,6 +346,7 @@ void ktime_get_ts(struct timespec *ts)
 {
        struct timekeeper *tk = &timekeeper;
        struct timespec tomono;
+       s64 nsec;
        unsigned int seq;
 
        WARN_ON(timekeeping_suspended);
@@ -352,13 +354,14 @@ void ktime_get_ts(struct timespec *ts)
        do {
                seq = read_seqbegin(&tk->lock);
                ts->tv_sec = tk->xtime_sec;
-               ts->tv_nsec = timekeeping_get_ns(tk);
+               nsec = timekeeping_get_ns(tk);
                tomono = tk->wall_to_monotonic;
 
        } while (read_seqretry(&tk->lock, seq));
 
-       set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec,
-                               ts->tv_nsec + tomono.tv_nsec);
+       ts->tv_sec += tomono.tv_sec;
+       ts->tv_nsec = 0;
+       timespec_add_ns(ts, nsec + tomono.tv_nsec);
 }
 EXPORT_SYMBOL_GPL(ktime_get_ts);
 
@@ -1244,6 +1247,7 @@ void get_monotonic_boottime(struct timespec *ts)
 {
        struct timekeeper *tk = &timekeeper;
        struct timespec tomono, sleep;
+       s64 nsec;
        unsigned int seq;
 
        WARN_ON(timekeeping_suspended);
@@ -1251,14 +1255,15 @@ void get_monotonic_boottime(struct timespec *ts)
        do {
                seq = read_seqbegin(&tk->lock);
                ts->tv_sec = tk->xtime_sec;
-               ts->tv_nsec = timekeeping_get_ns(tk);
+               nsec = timekeeping_get_ns(tk);
                tomono = tk->wall_to_monotonic;
                sleep = tk->total_sleep_time;
 
        } while (read_seqretry(&tk->lock, seq));
 
-       set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec + sleep.tv_sec,
-                       ts->tv_nsec + tomono.tv_nsec + sleep.tv_nsec);
+       ts->tv_sec += tomono.tv_sec + sleep.tv_sec;
+       ts->tv_nsec = 0;
+       timespec_add_ns(ts, nsec + tomono.tv_nsec + sleep.tv_nsec);
 }
 EXPORT_SYMBOL_GPL(get_monotonic_boottime);
 
index 1e1373bcb3e3125f72baf77cf396689d18de9bdd..3c5a79e2134cac6f404673093a84df5c8b371d80 100644 (file)
@@ -1349,8 +1349,16 @@ static void busy_worker_rebind_fn(struct work_struct *work)
        struct worker *worker = container_of(work, struct worker, rebind_work);
        struct global_cwq *gcwq = worker->pool->gcwq;
 
-       if (worker_maybe_bind_and_lock(worker))
-               worker_clr_flags(worker, WORKER_REBIND);
+       worker_maybe_bind_and_lock(worker);
+
+       /*
+        * %WORKER_REBIND must be cleared even if the above binding failed;
+        * otherwise, we may confuse the next CPU_UP cycle or oops / get
+        * stuck by calling idle_worker_rebind() prematurely.  If CPU went
+        * down again inbetween, %WORKER_UNBOUND would be set, so clearing
+        * %WORKER_REBIND is always safe.
+        */
+       worker_clr_flags(worker, WORKER_REBIND);
 
        spin_unlock_irq(&gcwq->lock);
 }
@@ -3568,18 +3576,17 @@ static int __devinit workqueue_cpu_down_callback(struct notifier_block *nfb,
 #ifdef CONFIG_SMP
 
 struct work_for_cpu {
-       struct completion completion;
+       struct work_struct work;
        long (*fn)(void *);
        void *arg;
        long ret;
 };
 
-static int do_work_for_cpu(void *_wfc)
+static void work_for_cpu_fn(struct work_struct *work)
 {
-       struct work_for_cpu *wfc = _wfc;
+       struct work_for_cpu *wfc = container_of(work, struct work_for_cpu, work);
+
        wfc->ret = wfc->fn(wfc->arg);
-       complete(&wfc->completion);
-       return 0;
 }
 
 /**
@@ -3594,19 +3601,11 @@ static int do_work_for_cpu(void *_wfc)
  */
 long work_on_cpu(unsigned int cpu, long (*fn)(void *), void *arg)
 {
-       struct task_struct *sub_thread;
-       struct work_for_cpu wfc = {
-               .completion = COMPLETION_INITIALIZER_ONSTACK(wfc.completion),
-               .fn = fn,
-               .arg = arg,
-       };
+       struct work_for_cpu wfc = { .fn = fn, .arg = arg };
 
-       sub_thread = kthread_create(do_work_for_cpu, &wfc, "work_for_cpu");
-       if (IS_ERR(sub_thread))
-               return PTR_ERR(sub_thread);
-       kthread_bind(sub_thread, cpu);
-       wake_up_process(sub_thread);
-       wait_for_completion(&wfc.completion);
+       INIT_WORK_ONSTACK(&wfc.work, work_for_cpu_fn);
+       schedule_work_on(cpu, &wfc.work);
+       flush_work(&wfc.work);
        return wfc.ret;
 }
 EXPORT_SYMBOL_GPL(work_on_cpu);
index 286d558033e270524ff3fdeac9c96393b81170a6..8c0e62975c88d49a09c9c29ab9e7a2b1334a6587 100644 (file)
@@ -163,9 +163,11 @@ static int digsig_verify_rsa(struct key *key,
        memcpy(out1 + head, p, l);
 
        err = pkcs_1_v1_5_decode_emsa(out1, len, mblen, out2, &len);
+       if (err)
+               goto err;
 
-       if (!err && len == hlen)
-               err = memcmp(out2, h, hlen);
+       if (len != hlen || memcmp(out2, h, hlen))
+               err = -EINVAL;
 
 err:
        mpi_free(in);
index 4d9393c7edc9072ff929175eec6e611788da124b..82aa349d2f7a040b489bee441bf848b61119f788 100644 (file)
@@ -246,7 +246,7 @@ static int __init_memblock memblock_double_array(struct memblock_type *type,
                                min(new_area_start, memblock.current_limit),
                                new_alloc_size, PAGE_SIZE);
 
-               new_array = addr ? __va(addr) : 0;
+               new_array = addr ? __va(addr) : NULL;
        }
        if (!addr) {
                pr_err("memblock: Failed to double %s array from %ld to %ld entries !\n",
index 3ad25f9d1fc134dc09e9672fad40f508e12e9502..6a5b90d0cfd7cbd61f4bc679bb4313e7a374ac0d 100644 (file)
@@ -126,9 +126,6 @@ static void register_page_bootmem_info_section(unsigned long start_pfn)
        struct mem_section *ms;
        struct page *page, *memmap;
 
-       if (!pfn_valid(start_pfn))
-               return;
-
        section_nr = pfn_to_section_nr(start_pfn);
        ms = __nr_to_section(section_nr);
 
@@ -187,9 +184,16 @@ void register_page_bootmem_info_node(struct pglist_data *pgdat)
        end_pfn = pfn + pgdat->node_spanned_pages;
 
        /* register_section info */
-       for (; pfn < end_pfn; pfn += PAGES_PER_SECTION)
-               register_page_bootmem_info_section(pfn);
-
+       for (; pfn < end_pfn; pfn += PAGES_PER_SECTION) {
+               /*
+                * Some platforms can assign the same pfn to multiple nodes - on
+                * node0 as well as nodeN.  To avoid registering a pfn against
+                * multiple nodes we check that this pfn does not already
+                * reside in some other node.
+                */
+               if (pfn_valid(pfn) && (pfn_to_nid(pfn) == node))
+                       register_page_bootmem_info_section(pfn);
+       }
 }
 #endif /* !CONFIG_SPARSEMEM_VMEMMAP */
 
index c66fb875104ab99feccb0e116b48b9d54a6011e4..c13ea7538891d85988b029236f11e233632b58ec 100644 (file)
@@ -584,7 +584,7 @@ static inline void __free_one_page(struct page *page,
                combined_idx = buddy_idx & page_idx;
                higher_page = page + (combined_idx - page_idx);
                buddy_idx = __find_buddy_index(combined_idx, order + 1);
-               higher_buddy = page + (buddy_idx - combined_idx);
+               higher_buddy = higher_page + (buddy_idx - combined_idx);
                if (page_is_buddy(higher_page, higher_buddy, order + 1)) {
                        list_add_tail(&page->lru,
                                &zone->free_area[order].free_list[migratetype]);
index 811af03a14ef168ca8e6ba90ec0af60b2dffa305..c6854759bcf1e041d7a21781921ac45cfd2f5484 100644 (file)
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -983,7 +983,7 @@ static void *__ac_get_obj(struct kmem_cache *cachep, struct array_cache *ac,
                }
 
                /* The caller cannot use PFMEMALLOC objects, find another one */
-               for (i = 1; i < ac->avail; i++) {
+               for (i = 0; i < ac->avail; i++) {
                        /* If a !PFMEMALLOC object is found, swap them */
                        if (!is_obj_pfmemalloc(ac->entry[i])) {
                                objp = ac->entry[i];
@@ -1000,7 +1000,7 @@ static void *__ac_get_obj(struct kmem_cache *cachep, struct array_cache *ac,
                l3 = cachep->nodelists[numa_mem_id()];
                if (!list_empty(&l3->slabs_free) && force_refill) {
                        struct slab *slabp = virt_to_slab(objp);
-                       ClearPageSlabPfmemalloc(virt_to_page(slabp->s_mem));
+                       ClearPageSlabPfmemalloc(virt_to_head_page(slabp->s_mem));
                        clear_obj_pfmemalloc(&objp);
                        recheck_pfmemalloc_active(cachep, ac);
                        return objp;
@@ -1032,7 +1032,7 @@ static void *__ac_put_obj(struct kmem_cache *cachep, struct array_cache *ac,
 {
        if (unlikely(pfmemalloc_active)) {
                /* Some pfmemalloc slabs exist, check if this is one */
-               struct page *page = virt_to_page(objp);
+               struct page *page = virt_to_head_page(objp);
                if (PageSlabPfmemalloc(page))
                        set_obj_pfmemalloc(&objp);
        }
index 8f78e25770317e63a37c741d66c2761f71afa2e9..2fdd96f9e9986b4c1244c13fde954d586afa7952 100644 (file)
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -1524,12 +1524,13 @@ static inline void *acquire_slab(struct kmem_cache *s,
 }
 
 static int put_cpu_partial(struct kmem_cache *s, struct page *page, int drain);
+static inline bool pfmemalloc_match(struct page *page, gfp_t gfpflags);
 
 /*
  * Try to allocate a partial slab from a specific node.
  */
-static void *get_partial_node(struct kmem_cache *s,
-               struct kmem_cache_node *n, struct kmem_cache_cpu *c)
+static void *get_partial_node(struct kmem_cache *s, struct kmem_cache_node *n,
+                               struct kmem_cache_cpu *c, gfp_t flags)
 {
        struct page *page, *page2;
        void *object = NULL;
@@ -1545,9 +1546,13 @@ static void *get_partial_node(struct kmem_cache *s,
 
        spin_lock(&n->list_lock);
        list_for_each_entry_safe(page, page2, &n->partial, lru) {
-               void *t = acquire_slab(s, n, page, object == NULL);
+               void *t;
                int available;
 
+               if (!pfmemalloc_match(page, flags))
+                       continue;
+
+               t = acquire_slab(s, n, page, object == NULL);
                if (!t)
                        break;
 
@@ -1614,7 +1619,7 @@ static void *get_any_partial(struct kmem_cache *s, gfp_t flags,
 
                        if (n && cpuset_zone_allowed_hardwall(zone, flags) &&
                                        n->nr_partial > s->min_partial) {
-                               object = get_partial_node(s, n, c);
+                               object = get_partial_node(s, n, c, flags);
                                if (object) {
                                        /*
                                         * Return the object even if
@@ -1643,7 +1648,7 @@ static void *get_partial(struct kmem_cache *s, gfp_t flags, int node,
        void *object;
        int searchnode = (node == NUMA_NO_NODE) ? numa_node_id() : node;
 
-       object = get_partial_node(s, get_node(s, searchnode), c);
+       object = get_partial_node(s, get_node(s, searchnode), c, flags);
        if (object || node != NUMA_NO_NODE)
                return object;
 
index 8d01243d9560e0ea8d8a04cf51cd4087ed8b280d..99b434b674c02b4099bcfe58632ad56e183801fd 100644 (file)
@@ -3102,6 +3102,7 @@ int kswapd_run(int nid)
                /* failure at boot is fatal */
                BUG_ON(system_state == SYSTEM_BOOTING);
                printk("Failed to start kswapd on node %d\n",nid);
+               pgdat->kswapd = NULL;
                ret = -1;
        }
        return ret;
index a081ce1c05140d43473dbc02518bfc51f913f6eb..cebaae7e148b7f8a88f8b265bb625256363cde5a 100644 (file)
@@ -20,8 +20,8 @@
 #ifndef _NET_BATMAN_ADV_BITARRAY_H_
 #define _NET_BATMAN_ADV_BITARRAY_H_
 
-/* returns true if the corresponding bit in the given seq_bits indicates true
- * and curr_seqno is within range of last_seqno
+/* Returns 1 if the corresponding bit in the given seq_bits indicates true
+ * and curr_seqno is within range of last_seqno. Otherwise returns 0.
  */
 static inline int batadv_test_bit(const unsigned long *seq_bits,
                                  uint32_t last_seqno, uint32_t curr_seqno)
@@ -32,7 +32,7 @@ static inline int batadv_test_bit(const unsigned long *seq_bits,
        if (diff < 0 || diff >= BATADV_TQ_LOCAL_WINDOW_SIZE)
                return 0;
        else
-               return  test_bit(diff, seq_bits);
+               return test_bit(diff, seq_bits) != 0;
 }
 
 /* turn corresponding bit on, so we can remember that we got the packet */
index 5e5f5b410e0b24f751eb158ad235fe16bfa3ee6f..1eaacf10d19d9ea078f49f225423dff96ebee8a2 100644 (file)
@@ -58,7 +58,7 @@ static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
        switch (cmd) {
        case BNEPCONNADD:
                if (!capable(CAP_NET_ADMIN))
-                       return -EACCES;
+                       return -EPERM;
 
                if (copy_from_user(&ca, argp, sizeof(ca)))
                        return -EFAULT;
@@ -84,7 +84,7 @@ static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
 
        case BNEPCONNDEL:
                if (!capable(CAP_NET_ADMIN))
-                       return -EACCES;
+                       return -EPERM;
 
                if (copy_from_user(&cd, argp, sizeof(cd)))
                        return -EFAULT;
index 311668d14571626dac778201a65ed21f05c72cbe..32dc83dcb6b2edd669d7a9ce2fc00480d249b4be 100644 (file)
@@ -72,7 +72,7 @@ static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
        switch (cmd) {
        case CMTPCONNADD:
                if (!capable(CAP_NET_ADMIN))
-                       return -EACCES;
+                       return -EPERM;
 
                if (copy_from_user(&ca, argp, sizeof(ca)))
                        return -EFAULT;
@@ -97,7 +97,7 @@ static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
 
        case CMTPCONNDEL:
                if (!capable(CAP_NET_ADMIN))
-                       return -EACCES;
+                       return -EPERM;
 
                if (copy_from_user(&cd, argp, sizeof(cd)))
                        return -EFAULT;
index 5ad7da21747413f50ba0106041c6e73d6ce727d4..3c094e78dde98cafed3ac893abd3b2fa86b76a92 100644 (file)
@@ -29,6 +29,7 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/a2mp.h>
+#include <net/bluetooth/smp.h>
 
 static void hci_le_connect(struct hci_conn *conn)
 {
@@ -619,6 +620,9 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
 {
        BT_DBG("hcon %p", conn);
 
+       if (conn->type == LE_LINK)
+               return smp_conn_security(conn, sec_level);
+
        /* For sdp we don't need the link key. */
        if (sec_level == BT_SECURITY_SDP)
                return 1;
index 19fdac78e555b97c44dd6826bc60ef89a888395b..d5ace1eda3ed8c3fd06f422bb7cbf340d3187c6c 100644 (file)
@@ -490,7 +490,7 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
        switch (cmd) {
        case HCISETRAW:
                if (!capable(CAP_NET_ADMIN))
-                       return -EACCES;
+                       return -EPERM;
 
                if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
                        return -EPERM;
@@ -510,12 +510,12 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
 
        case HCIBLOCKADDR:
                if (!capable(CAP_NET_ADMIN))
-                       return -EACCES;
+                       return -EPERM;
                return hci_sock_blacklist_add(hdev, (void __user *) arg);
 
        case HCIUNBLOCKADDR:
                if (!capable(CAP_NET_ADMIN))
-                       return -EACCES;
+                       return -EPERM;
                return hci_sock_blacklist_del(hdev, (void __user *) arg);
 
        default:
@@ -546,22 +546,22 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
 
        case HCIDEVUP:
                if (!capable(CAP_NET_ADMIN))
-                       return -EACCES;
+                       return -EPERM;
                return hci_dev_open(arg);
 
        case HCIDEVDOWN:
                if (!capable(CAP_NET_ADMIN))
-                       return -EACCES;
+                       return -EPERM;
                return hci_dev_close(arg);
 
        case HCIDEVRESET:
                if (!capable(CAP_NET_ADMIN))
-                       return -EACCES;
+                       return -EPERM;
                return hci_dev_reset(arg);
 
        case HCIDEVRESTAT:
                if (!capable(CAP_NET_ADMIN))
-                       return -EACCES;
+                       return -EPERM;
                return hci_dev_reset_stat(arg);
 
        case HCISETSCAN:
@@ -573,7 +573,7 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
        case HCISETACLMTU:
        case HCISETSCOMTU:
                if (!capable(CAP_NET_ADMIN))
-                       return -EACCES;
+                       return -EPERM;
                return hci_dev_cmd(cmd, argp);
 
        case HCIINQUIRY:
index 18b3f6892a36847de621954cbae7358ae719c0be..b24fb3bd862555c81746fe664e7f90270cc6a2df 100644 (file)
@@ -56,7 +56,7 @@ static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
        switch (cmd) {
        case HIDPCONNADD:
                if (!capable(CAP_NET_ADMIN))
-                       return -EACCES;
+                       return -EPERM;
 
                if (copy_from_user(&ca, argp, sizeof(ca)))
                        return -EFAULT;
@@ -91,7 +91,7 @@ static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
 
        case HIDPCONNDEL:
                if (!capable(CAP_NET_ADMIN))
-                       return -EACCES;
+                       return -EPERM;
 
                if (copy_from_user(&cd, argp, sizeof(cd)))
                        return -EFAULT;
index daa149b7003cdd659120e79225a50c6216a6dce4..4ea1710a478329a5d4219ce686a2ef4450ca30b8 100644 (file)
@@ -1199,14 +1199,15 @@ clean:
 static void l2cap_conn_ready(struct l2cap_conn *conn)
 {
        struct l2cap_chan *chan;
+       struct hci_conn *hcon = conn->hcon;
 
        BT_DBG("conn %p", conn);
 
-       if (!conn->hcon->out && conn->hcon->type == LE_LINK)
+       if (!hcon->out && hcon->type == LE_LINK)
                l2cap_le_conn_ready(conn);
 
-       if (conn->hcon->out && conn->hcon->type == LE_LINK)
-               smp_conn_security(conn, conn->hcon->pending_sec_level);
+       if (hcon->out && hcon->type == LE_LINK)
+               smp_conn_security(hcon, hcon->pending_sec_level);
 
        mutex_lock(&conn->chan_lock);
 
@@ -1219,8 +1220,8 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
                        continue;
                }
 
-               if (conn->hcon->type == LE_LINK) {
-                       if (smp_conn_security(conn, chan->sec_level))
+               if (hcon->type == LE_LINK) {
+                       if (smp_conn_security(hcon, chan->sec_level))
                                l2cap_chan_ready(chan);
 
                } else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
index 1497edd191a2e04ee3121624db92547059f24369..34bbe1c5e389500f080e15b30c194e95ea36f189 100644 (file)
@@ -616,7 +616,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
                                break;
                        }
 
-                       if (smp_conn_security(conn, sec.level))
+                       if (smp_conn_security(conn->hcon, sec.level))
                                break;
                        sk->sk_state = BT_CONFIG;
                        chan->state = BT_CONFIG;
index 901a616c8083e22f5163f8bbd1613b1529c63519..8c225ef349cd733614dfeaca0f2f1bceccdae064 100644 (file)
@@ -267,10 +267,10 @@ static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send)
        mgmt_auth_failed(conn->hcon->hdev, conn->dst, hcon->type,
                         hcon->dst_type, reason);
 
-       if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) {
-               cancel_delayed_work_sync(&conn->security_timer);
+       cancel_delayed_work_sync(&conn->security_timer);
+
+       if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags))
                smp_chan_destroy(conn);
-       }
 }
 
 #define JUST_WORKS     0x00
@@ -760,9 +760,9 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
        return 0;
 }
 
-int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
+int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
 {
-       struct hci_conn *hcon = conn->hcon;
+       struct l2cap_conn *conn = hcon->l2cap_data;
        struct smp_chan *smp = conn->smp_chan;
        __u8 authreq;
 
index f88ee537fb2b811347c109cfa19dbdd34c2923c0..92de5e5f9db211fb004a5b8f095c2bd215276682 100644 (file)
@@ -80,7 +80,7 @@ ebt_log_packet(u_int8_t pf, unsigned int hooknum,
        unsigned int bitmask;
 
        spin_lock_bh(&ebt_log_lock);
-       printk("<%c>%s IN=%s OUT=%s MAC source = %pM MAC dest = %pM proto = 0x%04x",
+       printk(KERN_SOH "%c%s IN=%s OUT=%s MAC source = %pM MAC dest = %pM proto = 0x%04x",
               '0' + loginfo->u.log.level, prefix,
               in ? in->name : "", out ? out->name : "",
               eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest,
index dd485f6128e81df5f8b1b454f57dac3b3871158a..ba217e90765e11024251feae5bbcb46450101ea7 100644 (file)
@@ -211,9 +211,10 @@ void caif_client_register_refcnt(struct cflayer *adapt_layer,
                                        void (*put)(struct cflayer *lyr))
 {
        struct cfsrvl *service;
-       service = container_of(adapt_layer->dn, struct cfsrvl, layer);
 
-       WARN_ON(adapt_layer == NULL || adapt_layer->dn == NULL);
+       if (WARN_ON(adapt_layer == NULL || adapt_layer->dn == NULL))
+               return;
+       service = container_of(adapt_layer->dn, struct cfsrvl, layer);
        service->hold = hold;
        service->put = put;
 }
index 83988362805ef1453efb37bfcb3692f59f4edd21..89e33a5d4d932c54af74a08cb080809affc7dd67 100644 (file)
@@ -2134,7 +2134,8 @@ static bool can_checksum_protocol(netdev_features_t features, __be16 protocol)
 static netdev_features_t harmonize_features(struct sk_buff *skb,
        __be16 protocol, netdev_features_t features)
 {
-       if (!can_checksum_protocol(features, protocol)) {
+       if (skb->ip_summed != CHECKSUM_NONE &&
+           !can_checksum_protocol(features, protocol)) {
                features &= ~NETIF_F_ALL_CSUM;
                features &= ~NETIF_F_SG;
        } else if (illegal_highdma(skb->dev, skb)) {
@@ -2647,15 +2648,16 @@ void __skb_get_rxhash(struct sk_buff *skb)
        if (!skb_flow_dissect(skb, &keys))
                return;
 
-       if (keys.ports) {
-               if ((__force u16)keys.port16[1] < (__force u16)keys.port16[0])
-                       swap(keys.port16[0], keys.port16[1]);
+       if (keys.ports)
                skb->l4_rxhash = 1;
-       }
 
        /* get a consistent hash (same value on both flow directions) */
-       if ((__force u32)keys.dst < (__force u32)keys.src)
+       if (((__force u32)keys.dst < (__force u32)keys.src) ||
+           (((__force u32)keys.dst == (__force u32)keys.src) &&
+            ((__force u16)keys.port16[1] < (__force u16)keys.port16[0]))) {
                swap(keys.dst, keys.src);
+               swap(keys.port16[0], keys.port16[1]);
+       }
 
        hash = jhash_3words((__force u32)keys.dst,
                            (__force u32)keys.src,
@@ -3321,7 +3323,7 @@ ncls:
 
        if (pt_prev) {
                if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC)))
-                       ret = -ENOMEM;
+                       goto drop;
                else
                        ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
        } else {
index cce9e53528b169a67a8b5cd8bf0e568460587e24..148e73d2c4515d777d577733f32205c38d03932e 100644 (file)
@@ -2721,7 +2721,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
        /* Eth + IPh + UDPh + mpls */
        datalen = pkt_dev->cur_pkt_size - 14 - 20 - 8 -
                  pkt_dev->pkt_overhead;
-       if (datalen < sizeof(struct pktgen_hdr))
+       if (datalen < 0 || datalen < sizeof(struct pktgen_hdr))
                datalen = sizeof(struct pktgen_hdr);
 
        udph->source = htons(pkt_dev->cur_udp_src);
index fe00d12081671a22c65d05d290069d7358dda1d6..e33ebae519c8c3283dadde917cdf46d413229a54 100644 (file)
@@ -3502,7 +3502,9 @@ bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from,
        if (!skb_cloned(from))
                skb_shinfo(from)->nr_frags = 0;
 
-       /* if the skb is cloned this does nothing since we set nr_frags to 0 */
+       /* if the skb is not cloned this does nothing
+        * since we set nr_frags to 0.
+        */
        for (i = 0; i < skb_shinfo(from)->nr_frags; i++)
                skb_frag_ref(from, i);
 
index 8f67ced8d6a808689255435dd412df132138af65..30579207612175f0a65b19310368079c54ce4bc9 100644 (file)
@@ -1523,7 +1523,14 @@ EXPORT_SYMBOL(sock_rfree);
 
 void sock_edemux(struct sk_buff *skb)
 {
-       sock_put(skb->sk);
+       struct sock *sk = skb->sk;
+
+#ifdef CONFIG_INET
+       if (sk->sk_state == TCP_TIME_WAIT)
+               inet_twsk_put(inet_twsk(sk));
+       else
+#endif
+               sock_put(sk);
 }
 EXPORT_SYMBOL(sock_edemux);
 
index 77e87aff419ab105ef3ad3b5f852ef676b18a827..47800459e4cb341c395f54f2d161c300a112c1cf 100644 (file)
@@ -1225,7 +1225,7 @@ static int arp_netdev_event(struct notifier_block *this, unsigned long event,
        switch (event) {
        case NETDEV_CHANGEADDR:
                neigh_changeaddr(&arp_tbl, dev);
-               rt_cache_flush(dev_net(dev), 0);
+               rt_cache_flush(dev_net(dev));
                break;
        default:
                break;
index 44bf82e3aef7d6d4b5afc39a357b7da6d85a44e7..e12fad773852b5271afafa414ffc1f2706ff4428 100644 (file)
@@ -725,7 +725,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
                break;
 
        case SIOCSIFFLAGS:
-               ret = -EACCES;
+               ret = -EPERM;
                if (!capable(CAP_NET_ADMIN))
                        goto out;
                break;
@@ -733,7 +733,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
        case SIOCSIFBRDADDR:    /* Set the broadcast address */
        case SIOCSIFDSTADDR:    /* Set the destination address */
        case SIOCSIFNETMASK:    /* Set the netmask for the interface */
-               ret = -EACCES;
+               ret = -EPERM;
                if (!capable(CAP_NET_ADMIN))
                        goto out;
                ret = -EINVAL;
@@ -1503,7 +1503,7 @@ static int devinet_conf_proc(ctl_table *ctl, int write,
                if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
                    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
                        if ((new_value == 0) && (old_value != 0))
-                               rt_cache_flush(net, 0);
+                               rt_cache_flush(net);
        }
 
        return ret;
@@ -1537,7 +1537,7 @@ static int devinet_sysctl_forward(ctl_table *ctl, int write,
                                dev_disable_lro(idev->dev);
                        }
                        rtnl_unlock();
-                       rt_cache_flush(net, 0);
+                       rt_cache_flush(net);
                }
        }
 
@@ -1554,7 +1554,7 @@ static int ipv4_doint_and_flush(ctl_table *ctl, int write,
        struct net *net = ctl->extra2;
 
        if (write && *valp != val)
-               rt_cache_flush(net, 0);
+               rt_cache_flush(net);
 
        return ret;
 }
index c43ae3fba7921e590859cea407ba9b9e030b16be..8e2b475da9faca9f7e27482f7e416908e7cf66fa 100644 (file)
@@ -148,7 +148,7 @@ static void fib_flush(struct net *net)
        }
 
        if (flushed)
-               rt_cache_flush(net, -1);
+               rt_cache_flush(net);
 }
 
 /*
@@ -999,11 +999,11 @@ static void nl_fib_lookup_exit(struct net *net)
        net->ipv4.fibnl = NULL;
 }
 
-static void fib_disable_ip(struct net_device *dev, int force, int delay)
+static void fib_disable_ip(struct net_device *dev, int force)
 {
        if (fib_sync_down_dev(dev, force))
                fib_flush(dev_net(dev));
-       rt_cache_flush(dev_net(dev), delay);
+       rt_cache_flush(dev_net(dev));
        arp_ifdown(dev);
 }
 
@@ -1020,7 +1020,7 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event,
                fib_sync_up(dev);
 #endif
                atomic_inc(&net->ipv4.dev_addr_genid);
-               rt_cache_flush(dev_net(dev), -1);
+               rt_cache_flush(dev_net(dev));
                break;
        case NETDEV_DOWN:
                fib_del_ifaddr(ifa, NULL);
@@ -1029,9 +1029,9 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event,
                        /* Last address was deleted from this interface.
                         * Disable IP.
                         */
-                       fib_disable_ip(dev, 1, 0);
+                       fib_disable_ip(dev, 1);
                } else {
-                       rt_cache_flush(dev_net(dev), -1);
+                       rt_cache_flush(dev_net(dev));
                }
                break;
        }
@@ -1045,7 +1045,7 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo
        struct net *net = dev_net(dev);
 
        if (event == NETDEV_UNREGISTER) {
-               fib_disable_ip(dev, 2, -1);
+               fib_disable_ip(dev, 2);
                rt_flush_dev(dev);
                return NOTIFY_DONE;
        }
@@ -1062,14 +1062,14 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo
                fib_sync_up(dev);
 #endif
                atomic_inc(&net->ipv4.dev_addr_genid);
-               rt_cache_flush(dev_net(dev), -1);
+               rt_cache_flush(dev_net(dev));
                break;
        case NETDEV_DOWN:
-               fib_disable_ip(dev, 0, 0);
+               fib_disable_ip(dev, 0);
                break;
        case NETDEV_CHANGEMTU:
        case NETDEV_CHANGE:
-               rt_cache_flush(dev_net(dev), 0);
+               rt_cache_flush(dev_net(dev));
                break;
        case NETDEV_UNREGISTER_BATCH:
                break;
index a83d74e498d23af8c104bc3d68eca5ca4a8ac228..274309d3aded0ffbf351dbf5f4ad128d87ff5a0b 100644 (file)
@@ -259,7 +259,7 @@ static size_t fib4_rule_nlmsg_payload(struct fib_rule *rule)
 
 static void fib4_rule_flush_cache(struct fib_rules_ops *ops)
 {
-       rt_cache_flush(ops->fro_net, -1);
+       rt_cache_flush(ops->fro_net);
 }
 
 static const struct fib_rules_ops __net_initdata fib4_rules_ops_template = {
index 57bd978483e1fe47a96c964f356432e51e62d6b7..d1b93595b4a7dce1e7701bb713bd5f0bc5adf1f6 100644 (file)
@@ -1286,7 +1286,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
 
                        fib_release_info(fi_drop);
                        if (state & FA_S_ACCESSED)
-                               rt_cache_flush(cfg->fc_nlinfo.nl_net, -1);
+                               rt_cache_flush(cfg->fc_nlinfo.nl_net);
                        rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen,
                                tb->tb_id, &cfg->fc_nlinfo, NLM_F_REPLACE);
 
@@ -1333,7 +1333,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
        list_add_tail_rcu(&new_fa->fa_list,
                          (fa ? &fa->fa_list : fa_head));
 
-       rt_cache_flush(cfg->fc_nlinfo.nl_net, -1);
+       rt_cache_flush(cfg->fc_nlinfo.nl_net);
        rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, tb->tb_id,
                  &cfg->fc_nlinfo, 0);
 succeeded:
@@ -1708,7 +1708,7 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg)
                trie_leaf_remove(t, l);
 
        if (fa->fa_state & FA_S_ACCESSED)
-               rt_cache_flush(cfg->fc_nlinfo.nl_net, -1);
+               rt_cache_flush(cfg->fc_nlinfo.nl_net);
 
        fib_release_info(fa->fa_info);
        alias_free_mem_rcu(fa);
index 82cf2a722b2310803ec603f60d59cdf819481a4a..fd9af60397b590dd817e02a52a33fdf99bb24c8e 100644 (file)
@@ -202,11 +202,6 @@ EXPORT_SYMBOL(ip_tos2prio);
 static DEFINE_PER_CPU(struct rt_cache_stat, rt_cache_stat);
 #define RT_CACHE_STAT_INC(field) __this_cpu_inc(rt_cache_stat.field)
 
-static inline int rt_genid(struct net *net)
-{
-       return atomic_read(&net->ipv4.rt_genid);
-}
-
 #ifdef CONFIG_PROC_FS
 static void *rt_cache_seq_start(struct seq_file *seq, loff_t *pos)
 {
@@ -447,27 +442,9 @@ static inline bool rt_is_expired(const struct rtable *rth)
        return rth->rt_genid != rt_genid(dev_net(rth->dst.dev));
 }
 
-/*
- * Perturbation of rt_genid by a small quantity [1..256]
- * Using 8 bits of shuffling ensure we can call rt_cache_invalidate()
- * many times (2^24) without giving recent rt_genid.
- * Jenkins hash is strong enough that litle changes of rt_genid are OK.
- */
-static void rt_cache_invalidate(struct net *net)
+void rt_cache_flush(struct net *net)
 {
-       unsigned char shuffle;
-
-       get_random_bytes(&shuffle, sizeof(shuffle));
-       atomic_add(shuffle + 1U, &net->ipv4.rt_genid);
-}
-
-/*
- * delay < 0  : invalidate cache (fast : entries will be deleted later)
- * delay >= 0 : invalidate & flush cache (can be long)
- */
-void rt_cache_flush(struct net *net, int delay)
-{
-       rt_cache_invalidate(net);
+       rt_genid_bump(net);
 }
 
 static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst,
@@ -2345,7 +2322,7 @@ int ip_rt_dump(struct sk_buff *skb,  struct netlink_callback *cb)
 
 void ip_rt_multicast_event(struct in_device *in_dev)
 {
-       rt_cache_flush(dev_net(in_dev->dev), 0);
+       rt_cache_flush(dev_net(in_dev->dev));
 }
 
 #ifdef CONFIG_SYSCTL
@@ -2354,16 +2331,7 @@ static int ipv4_sysctl_rtcache_flush(ctl_table *__ctl, int write,
                                        size_t *lenp, loff_t *ppos)
 {
        if (write) {
-               int flush_delay;
-               ctl_table ctl;
-               struct net *net;
-
-               memcpy(&ctl, __ctl, sizeof(ctl));
-               ctl.data = &flush_delay;
-               proc_dointvec(&ctl, write, buffer, lenp, ppos);
-
-               net = (struct net *)__ctl->extra1;
-               rt_cache_flush(net, flush_delay);
+               rt_cache_flush((struct net *)__ctl->extra1);
                return 0;
        }
 
@@ -2533,8 +2501,7 @@ static __net_initdata struct pernet_operations sysctl_route_ops = {
 
 static __net_init int rt_genid_init(struct net *net)
 {
-       get_random_bytes(&net->ipv4.rt_genid,
-                        sizeof(net->ipv4.rt_genid));
+       atomic_set(&net->rt_genid, 0);
        get_random_bytes(&net->ipv4.dev_addr_genid,
                         sizeof(net->ipv4.dev_addr_genid));
        return 0;
index 2109ff4a1dafd489fbe0e2240075432df4517374..5f64193418216393448ec9d8e839f83e85636716 100644 (file)
@@ -1762,8 +1762,14 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                }
 
 #ifdef CONFIG_NET_DMA
-               if (tp->ucopy.dma_chan)
-                       dma_async_memcpy_issue_pending(tp->ucopy.dma_chan);
+               if (tp->ucopy.dma_chan) {
+                       if (tp->rcv_wnd == 0 &&
+                           !skb_queue_empty(&sk->sk_async_wait_queue)) {
+                               tcp_service_net_dma(sk, true);
+                               tcp_cleanup_rbuf(sk, copied);
+                       } else
+                               dma_async_memcpy_issue_pending(tp->ucopy.dma_chan);
+               }
 #endif
                if (copied >= target) {
                        /* Do not sleep, just process backlog. */
@@ -2325,10 +2331,17 @@ static int tcp_repair_options_est(struct tcp_sock *tp,
                        tp->rx_opt.mss_clamp = opt.opt_val;
                        break;
                case TCPOPT_WINDOW:
-                       if (opt.opt_val > 14)
-                               return -EFBIG;
+                       {
+                               u16 snd_wscale = opt.opt_val & 0xFFFF;
+                               u16 rcv_wscale = opt.opt_val >> 16;
+
+                               if (snd_wscale > 14 || rcv_wscale > 14)
+                                       return -EFBIG;
 
-                       tp->rx_opt.snd_wscale = opt.opt_val;
+                               tp->rx_opt.snd_wscale = snd_wscale;
+                               tp->rx_opt.rcv_wscale = rcv_wscale;
+                               tp->rx_opt.wscale_ok = 1;
+                       }
                        break;
                case TCPOPT_SACK_PERM:
                        if (opt.opt_val != 0)
index 6e38c6c23caa69601216e193507460663c1926ec..d377f4854cb853c454541062fb6f8438608d470c 100644 (file)
@@ -4661,7 +4661,7 @@ queue_and_out:
 
                if (eaten > 0)
                        kfree_skb_partial(skb, fragstolen);
-               else if (!sock_flag(sk, SOCK_DEAD))
+               if (!sock_flag(sk, SOCK_DEAD))
                        sk->sk_data_ready(sk, 0);
                return;
        }
@@ -5556,8 +5556,7 @@ no_ack:
 #endif
                        if (eaten)
                                kfree_skb_partial(skb, fragstolen);
-                       else
-                               sk->sk_data_ready(sk, 0);
+                       sk->sk_data_ready(sk, 0);
                        return 0;
                }
        }
index 6f6d1aca3c3de0e21036c075c0a13c429ef4f8ae..2814f66dac64cf5775806138c91903c7a02eeae3 100644 (file)
@@ -1226,6 +1226,11 @@ try_again:
 
        if (unlikely(err)) {
                trace_kfree_skb(skb, udp_recvmsg);
+               if (!peeked) {
+                       atomic_inc(&sk->sk_drops);
+                       UDP_INC_STATS_USER(sock_net(sk),
+                                          UDP_MIB_INERRORS, is_udplite);
+               }
                goto out_free;
        }
 
index 0251a6005be8ff8d35cffe7fb0e2611b43296158..c4f934176cabd92ebfb4bba774335427b9f8932a 100644 (file)
@@ -175,33 +175,12 @@ void __inet6_csk_dst_store(struct sock *sk, struct dst_entry *dst,
                           const struct in6_addr *saddr)
 {
        __ip6_dst_store(sk, dst, daddr, saddr);
-
-#ifdef CONFIG_XFRM
-       {
-               struct rt6_info *rt = (struct rt6_info  *)dst;
-               rt->rt6i_flow_cache_genid = atomic_read(&flow_cache_genid);
-       }
-#endif
 }
 
 static inline
 struct dst_entry *__inet6_csk_dst_check(struct sock *sk, u32 cookie)
 {
-       struct dst_entry *dst;
-
-       dst = __sk_dst_check(sk, cookie);
-
-#ifdef CONFIG_XFRM
-       if (dst) {
-               struct rt6_info *rt = (struct rt6_info *)dst;
-               if (rt->rt6i_flow_cache_genid != atomic_read(&flow_cache_genid)) {
-                       __sk_dst_reset(sk);
-                       dst = NULL;
-               }
-       }
-#endif
-
-       return dst;
+       return __sk_dst_check(sk, cookie);
 }
 
 static struct dst_entry *inet6_csk_route_socket(struct sock *sk,
index 13690d650c3e227de6579ed6ee489fbff1cbddef..286acfc21250cd760565b182cbc2d3d4490463dd 100644 (file)
@@ -819,6 +819,10 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
                                        offsetof(struct rt6_info, rt6i_src),
                                        allow_create, replace_required);
 
+                       if (IS_ERR(sn)) {
+                               err = PTR_ERR(sn);
+                               sn = NULL;
+                       }
                        if (!sn) {
                                /* If it is failed, discard just allocated
                                   root, and then (in st_failure) stale node
index 8e80fd279100d7d18d111eff1be73f10fdd6eeaf..854e4018d205c826032633ce2641cbc1af2c057a 100644 (file)
@@ -226,7 +226,7 @@ static struct rt6_info ip6_null_entry_template = {
        .dst = {
                .__refcnt       = ATOMIC_INIT(1),
                .__use          = 1,
-               .obsolete       = -1,
+               .obsolete       = DST_OBSOLETE_FORCE_CHK,
                .error          = -ENETUNREACH,
                .input          = ip6_pkt_discard,
                .output         = ip6_pkt_discard_out,
@@ -246,7 +246,7 @@ static struct rt6_info ip6_prohibit_entry_template = {
        .dst = {
                .__refcnt       = ATOMIC_INIT(1),
                .__use          = 1,
-               .obsolete       = -1,
+               .obsolete       = DST_OBSOLETE_FORCE_CHK,
                .error          = -EACCES,
                .input          = ip6_pkt_prohibit,
                .output         = ip6_pkt_prohibit_out,
@@ -261,7 +261,7 @@ static struct rt6_info ip6_blk_hole_entry_template = {
        .dst = {
                .__refcnt       = ATOMIC_INIT(1),
                .__use          = 1,
-               .obsolete       = -1,
+               .obsolete       = DST_OBSOLETE_FORCE_CHK,
                .error          = -EINVAL,
                .input          = dst_discard,
                .output         = dst_discard,
@@ -281,13 +281,14 @@ static inline struct rt6_info *ip6_dst_alloc(struct net *net,
                                             struct fib6_table *table)
 {
        struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
-                                       0, DST_OBSOLETE_NONE, flags);
+                                       0, DST_OBSOLETE_FORCE_CHK, flags);
 
        if (rt) {
                struct dst_entry *dst = &rt->dst;
 
                memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
                rt6_init_peer(rt, table ? &table->tb6_peers : net->ipv6.peers);
+               rt->rt6i_genid = rt_genid(net);
        }
        return rt;
 }
@@ -1031,6 +1032,13 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
 
        rt = (struct rt6_info *) dst;
 
+       /* All IPV6 dsts are created with ->obsolete set to the value
+        * DST_OBSOLETE_FORCE_CHK which forces validation calls down
+        * into this function always.
+        */
+       if (rt->rt6i_genid != rt_genid(dev_net(rt->dst.dev)))
+               return NULL;
+
        if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
                if (rt->rt6i_peer_genid != rt6_peer_genid()) {
                        if (!rt6_has_peer(rt))
@@ -1397,8 +1405,6 @@ int ip6_route_add(struct fib6_config *cfg)
                goto out;
        }
 
-       rt->dst.obsolete = -1;
-
        if (cfg->fc_flags & RTF_EXPIRES)
                rt6_set_expires(rt, jiffies +
                                clock_t_to_jiffies(cfg->fc_expires));
@@ -2080,7 +2086,6 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
        rt->dst.input = ip6_input;
        rt->dst.output = ip6_output;
        rt->rt6i_idev = idev;
-       rt->dst.obsolete = -1;
 
        rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
        if (anycast)
index a3e60cc04a8a17e229afb44f38643ed86ac0c63e..acd32e3f1b68e7c11fd211383e05b50dfd07ee6a 100644 (file)
@@ -403,8 +403,9 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                tp->mtu_info = ntohl(info);
                if (!sock_owned_by_user(sk))
                        tcp_v6_mtu_reduced(sk);
-               else
-                       set_bit(TCP_MTU_REDUCED_DEFERRED, &tp->tsq_flags);
+               else if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED,
+                                          &tp->tsq_flags))
+                       sock_hold(sk);
                goto out;
        }
 
index 99d0077b56b86f088a4a2fd2819a7b03e0eabd49..07e2bfef6845429ee7e359a6c21141db0a0219de 100644 (file)
@@ -394,6 +394,17 @@ try_again:
        }
        if (unlikely(err)) {
                trace_kfree_skb(skb, udpv6_recvmsg);
+               if (!peeked) {
+                       atomic_inc(&sk->sk_drops);
+                       if (is_udp4)
+                               UDP_INC_STATS_USER(sock_net(sk),
+                                                  UDP_MIB_INERRORS,
+                                                  is_udplite);
+                       else
+                               UDP6_INC_STATS_USER(sock_net(sk),
+                                                   UDP_MIB_INERRORS,
+                                                   is_udplite);
+               }
                goto out_free;
        }
        if (!peeked) {
index 513cab08a9863c0080d510ae5a2a4d927ad2ccdd..1a9f3723c13cb45b608bbc07fe4a9803df926523 100644 (file)
@@ -1501,6 +1501,8 @@ out:
        return err;
 }
 
+static struct lock_class_key l2tp_socket_class;
+
 int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, struct l2tp_tunnel **tunnelp)
 {
        struct l2tp_tunnel *tunnel = NULL;
@@ -1605,6 +1607,8 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
        tunnel->old_sk_destruct = sk->sk_destruct;
        sk->sk_destruct = &l2tp_tunnel_destruct;
        tunnel->sock = sk;
+       lockdep_set_class_and_name(&sk->sk_lock.slock, &l2tp_socket_class, "l2tp_sock");
+
        sk->sk_allocation = GFP_ATOMIC;
 
        /* Add tunnel to our list */
index f9ee74deeac26f5469271a0800669cc82ffddc63..3bfb34aaee293cb697f36ae88a060f3329571214 100644 (file)
@@ -153,7 +153,7 @@ static void l2tp_eth_dev_recv(struct l2tp_session *session, struct sk_buff *skb,
                print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, skb->data, length);
        }
 
-       if (!pskb_may_pull(skb, sizeof(ETH_HLEN)))
+       if (!pskb_may_pull(skb, ETH_HLEN))
                goto error;
 
        secpath_reset(skb);
index d41974aacf5168597fd559f1c976252f9e36ffd9..a58c0b649ba137b09214c031bf3508b5fe2974eb 100644 (file)
@@ -1378,6 +1378,8 @@ static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
        else
                memset(next_hop, 0, ETH_ALEN);
 
+       memset(pinfo, 0, sizeof(*pinfo));
+
        pinfo->generation = mesh_paths_generation;
 
        pinfo->filled = MPATH_INFO_FRAME_QLEN |
@@ -1396,7 +1398,6 @@ static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
        pinfo->discovery_timeout =
                        jiffies_to_msecs(mpath->discovery_timeout);
        pinfo->discovery_retries = mpath->discovery_retries;
-       pinfo->flags = 0;
        if (mpath->flags & MESH_PATH_ACTIVE)
                pinfo->flags |= NL80211_MPATH_FLAG_ACTIVE;
        if (mpath->flags & MESH_PATH_RESOLVING)
@@ -1405,10 +1406,8 @@ static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
                pinfo->flags |= NL80211_MPATH_FLAG_SN_VALID;
        if (mpath->flags & MESH_PATH_FIXED)
                pinfo->flags |= NL80211_MPATH_FLAG_FIXED;
-       if (mpath->flags & MESH_PATH_RESOLVING)
-               pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING;
-
-       pinfo->flags = mpath->flags;
+       if (mpath->flags & MESH_PATH_RESOLVED)
+               pinfo->flags |= NL80211_MPATH_FLAG_RESOLVED;
 }
 
 static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
index a4a5acdbaa4dd3ac5e2fb8c5f0ff1d1b97553d6a..f76b83341cf9a39db0e14092a85f2e576245a306 100644 (file)
@@ -3248,6 +3248,8 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
        goto out_unlock;
 
  err_clear:
+       memset(ifmgd->bssid, 0, ETH_ALEN);
+       ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
        ifmgd->auth_data = NULL;
  err_free:
        kfree(auth_data);
@@ -3439,6 +3441,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        err = 0;
        goto out;
  err_clear:
+       memset(ifmgd->bssid, 0, ETH_ALEN);
+       ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
        ifmgd->assoc_data = NULL;
  err_free:
        kfree(assoc_data);
index a5ac11ebef331895f39af5e86ca4406e56562ceb..e046b3756aab755080d3edced132c459b7c8d4c4 100644 (file)
@@ -158,21 +158,18 @@ static const u8 tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = {
  *     sCL -> sSS
  */
 /*          sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sS2   */
-/*synack*/ { sIV, sIV, sIG, sIG, sIG, sIG, sIG, sIG, sIG, sSR },
+/*synack*/ { sIV, sIV, sSR, sIV, sIV, sIV, sIV, sIV, sIV, sSR },
 /*
  *     sNO -> sIV      Too late and no reason to do anything
  *     sSS -> sIV      Client can't send SYN and then SYN/ACK
  *     sS2 -> sSR      SYN/ACK sent to SYN2 in simultaneous open
- *     sSR -> sIG
- *     sES -> sIG      Error: SYNs in window outside the SYN_SENT state
- *                     are errors. Receiver will reply with RST
- *                     and close the connection.
- *                     Or we are not in sync and hold a dead connection.
- *     sFW -> sIG
- *     sCW -> sIG
- *     sLA -> sIG
- *     sTW -> sIG
- *     sCL -> sIG
+ *     sSR -> sSR      Late retransmitted SYN/ACK in simultaneous open
+ *     sES -> sIV      Invalid SYN/ACK packets sent by the client
+ *     sFW -> sIV
+ *     sCW -> sIV
+ *     sLA -> sIV
+ *     sTW -> sIV
+ *     sCL -> sIV
  */
 /*          sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sS2   */
 /*fin*/    { sIV, sIV, sFW, sFW, sLA, sLA, sLA, sTW, sCL, sIV },
@@ -633,15 +630,9 @@ static bool tcp_in_window(const struct nf_conn *ct,
                ack = sack = receiver->td_end;
        }
 
-       if (seq == end
-           && (!tcph->rst
-               || (seq == 0 && state->state == TCP_CONNTRACK_SYN_SENT)))
+       if (tcph->rst && seq == 0 && state->state == TCP_CONNTRACK_SYN_SENT)
                /*
-                * Packets contains no data: we assume it is valid
-                * and check the ack value only.
-                * However RST segments are always validated by their
-                * SEQ number, except when seq == 0 (reset sent answering
-                * SYN.
+                * RST sent answering SYN.
                 */
                seq = end = sender->td_end;
 
index 14e2f3903142e322d1ce72a3eba13441004eef16..5cfb5bedb2b8e8f2fa44ed936a7cab265b5878e6 100644 (file)
@@ -381,6 +381,7 @@ __build_packet_message(struct nfulnl_instance *inst,
        struct nlmsghdr *nlh;
        struct nfgenmsg *nfmsg;
        sk_buff_data_t old_tail = inst->skb->tail;
+       struct sock *sk;
 
        nlh = nlmsg_put(inst->skb, 0, 0,
                        NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET,
@@ -499,18 +500,19 @@ __build_packet_message(struct nfulnl_instance *inst,
        }
 
        /* UID */
-       if (skb->sk) {
-               read_lock_bh(&skb->sk->sk_callback_lock);
-               if (skb->sk->sk_socket && skb->sk->sk_socket->file) {
-                       struct file *file = skb->sk->sk_socket->file;
+       sk = skb->sk;
+       if (sk && sk->sk_state != TCP_TIME_WAIT) {
+               read_lock_bh(&sk->sk_callback_lock);
+               if (sk->sk_socket && sk->sk_socket->file) {
+                       struct file *file = sk->sk_socket->file;
                        __be32 uid = htonl(file->f_cred->fsuid);
                        __be32 gid = htonl(file->f_cred->fsgid);
-                       read_unlock_bh(&skb->sk->sk_callback_lock);
+                       read_unlock_bh(&sk->sk_callback_lock);
                        if (nla_put_be32(inst->skb, NFULA_UID, uid) ||
                            nla_put_be32(inst->skb, NFULA_GID, gid))
                                goto nla_put_failure;
                } else
-                       read_unlock_bh(&skb->sk->sk_callback_lock);
+                       read_unlock_bh(&sk->sk_callback_lock);
        }
 
        /* local sequence number */
index ff5f75fddb15175c408a1e0a1cf8a656453aff80..91e9af4d1f42c3baef9af1261c9464c70cd1bac0 100644 (file)
@@ -145,6 +145,19 @@ static int dump_tcp_header(struct sbuff *m, const struct sk_buff *skb,
        return 0;
 }
 
+static void dump_sk_uid_gid(struct sbuff *m, struct sock *sk)
+{
+       if (!sk || sk->sk_state == TCP_TIME_WAIT)
+               return;
+
+       read_lock_bh(&sk->sk_callback_lock);
+       if (sk->sk_socket && sk->sk_socket->file)
+               sb_add(m, "UID=%u GID=%u ",
+                       sk->sk_socket->file->f_cred->fsuid,
+                       sk->sk_socket->file->f_cred->fsgid);
+       read_unlock_bh(&sk->sk_callback_lock);
+}
+
 /* One level of recursion won't kill us */
 static void dump_ipv4_packet(struct sbuff *m,
                        const struct nf_loginfo *info,
@@ -361,14 +374,8 @@ static void dump_ipv4_packet(struct sbuff *m,
        }
 
        /* Max length: 15 "UID=4294967295 " */
-       if ((logflags & XT_LOG_UID) && !iphoff && skb->sk) {
-               read_lock_bh(&skb->sk->sk_callback_lock);
-               if (skb->sk->sk_socket && skb->sk->sk_socket->file)
-                       sb_add(m, "UID=%u GID=%u ",
-                               skb->sk->sk_socket->file->f_cred->fsuid,
-                               skb->sk->sk_socket->file->f_cred->fsgid);
-               read_unlock_bh(&skb->sk->sk_callback_lock);
-       }
+       if ((logflags & XT_LOG_UID) && !iphoff)
+               dump_sk_uid_gid(m, skb->sk);
 
        /* Max length: 16 "MARK=0xFFFFFFFF " */
        if (!iphoff && skb->mark)
@@ -436,8 +443,8 @@ log_packet_common(struct sbuff *m,
                  const struct nf_loginfo *loginfo,
                  const char *prefix)
 {
-       sb_add(m, "<%d>%sIN=%s OUT=%s ", loginfo->u.log.level,
-              prefix,
+       sb_add(m, KERN_SOH "%c%sIN=%s OUT=%s ",
+              '0' + loginfo->u.log.level, prefix,
               in ? in->name : "",
               out ? out->name : "");
 #ifdef CONFIG_BRIDGE_NETFILTER
@@ -717,14 +724,8 @@ static void dump_ipv6_packet(struct sbuff *m,
        }
 
        /* Max length: 15 "UID=4294967295 " */
-       if ((logflags & XT_LOG_UID) && recurse && skb->sk) {
-               read_lock_bh(&skb->sk->sk_callback_lock);
-               if (skb->sk->sk_socket && skb->sk->sk_socket->file)
-                       sb_add(m, "UID=%u GID=%u ",
-                               skb->sk->sk_socket->file->f_cred->fsuid,
-                               skb->sk->sk_socket->file->f_cred->fsgid);
-               read_unlock_bh(&skb->sk->sk_callback_lock);
-       }
+       if ((logflags & XT_LOG_UID) && recurse)
+               dump_sk_uid_gid(m, skb->sk);
 
        /* Max length: 16 "MARK=0xFFFFFFFF " */
        if (!recurse && skb->mark)
index 06592d8b4a2b4eba33d9e71e44fc75ca206b38a8..7261eb81974ff9f3761976372c9272db04a62abd 100644 (file)
@@ -601,7 +601,7 @@ static int nr_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
                if (!capable(CAP_NET_BIND_SERVICE)) {
                        dev_put(dev);
                        release_sock(sk);
-                       return -EACCES;
+                       return -EPERM;
                }
                nr->user_addr   = addr->fsa_digipeater[0];
                nr->source_addr = addr->fsa_ax25.sax25_call;
@@ -1169,7 +1169,12 @@ static int nr_recvmsg(struct kiocb *iocb, struct socket *sock,
                msg->msg_flags |= MSG_TRUNC;
        }
 
-       skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+       er = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+       if (er < 0) {
+               skb_free_datagram(sk, skb);
+               release_sock(sk);
+               return er;
+       }
 
        if (sax != NULL) {
                sax->sax25_family = AF_NETROM;
index f3f96badf5aac0202a2bd54155d595b0373a18df..954405ceae9ed5141293d3f47ce7c784aeee3c31 100644 (file)
@@ -45,7 +45,7 @@ static int make_writable(struct sk_buff *skb, int write_len)
        return pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
 }
 
-/* remove VLAN header from packet and update csum accrodingly. */
+/* remove VLAN header from packet and update csum accordingly. */
 static int __pop_vlan_tci(struct sk_buff *skb, __be16 *current_tci)
 {
        struct vlan_hdr *vhdr;
index d8277d29e7102caf343d78b802f3371283c40de1..cf58cedad0833f9e9e704401fdecb5480c121caf 100644 (file)
@@ -425,10 +425,10 @@ static int validate_sample(const struct nlattr *attr,
 static int validate_tp_port(const struct sw_flow_key *flow_key)
 {
        if (flow_key->eth.type == htons(ETH_P_IP)) {
-               if (flow_key->ipv4.tp.src && flow_key->ipv4.tp.dst)
+               if (flow_key->ipv4.tp.src || flow_key->ipv4.tp.dst)
                        return 0;
        } else if (flow_key->eth.type == htons(ETH_P_IPV6)) {
-               if (flow_key->ipv6.tp.src && flow_key->ipv6.tp.dst)
+               if (flow_key->ipv6.tp.src || flow_key->ipv6.tp.dst)
                        return 0;
        }
 
@@ -460,7 +460,7 @@ static int validate_set(const struct nlattr *a,
                if (flow_key->eth.type != htons(ETH_P_IP))
                        return -EINVAL;
 
-               if (!flow_key->ipv4.addr.src || !flow_key->ipv4.addr.dst)
+               if (!flow_key->ip.proto)
                        return -EINVAL;
 
                ipv4_key = nla_data(ovs_key);
index 9b75617ca4e031db60ddbeb609bb658af7f8de0c..c30df1a10c670ad01b7b8b88c49434c95c0c659e 100644 (file)
@@ -145,15 +145,17 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies);
  *  OVS_KEY_ATTR_PRIORITY      4    --     4      8
  *  OVS_KEY_ATTR_IN_PORT       4    --     4      8
  *  OVS_KEY_ATTR_ETHERNET     12    --     4     16
+ *  OVS_KEY_ATTR_ETHERTYPE     2     2     4      8  (outer VLAN ethertype)
  *  OVS_KEY_ATTR_8021Q         4    --     4      8
- *  OVS_KEY_ATTR_ETHERTYPE     2     2     4      8
+ *  OVS_KEY_ATTR_ENCAP         0    --     4      4  (VLAN encapsulation)
+ *  OVS_KEY_ATTR_ETHERTYPE     2     2     4      8  (inner VLAN ethertype)
  *  OVS_KEY_ATTR_IPV6         40    --     4     44
  *  OVS_KEY_ATTR_ICMPV6        2     2     4      8
  *  OVS_KEY_ATTR_ND           28    --     4     32
  *  -------------------------------------------------
- *  total                                       132
+ *  total                                       144
  */
-#define FLOW_BUFSIZE 132
+#define FLOW_BUFSIZE 144
 
 int ovs_flow_to_nlattrs(const struct sw_flow_key *, struct sk_buff *);
 int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
index 6aabd77d1cfdd5cddd34b55dc69699cae9956e57..564b9fc8efd3c8778ef8ba155cf17f79d92a9a80 100644 (file)
@@ -250,10 +250,11 @@ cbq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
                        else if ((cl = defmap[res.classid & TC_PRIO_MAX]) == NULL)
                                cl = defmap[TC_PRIO_BESTEFFORT];
 
-                       if (cl == NULL || cl->level >= head->level)
+                       if (cl == NULL)
                                goto fallback;
                }
-
+               if (cl->level >= head->level)
+                       goto fallback;
 #ifdef CONFIG_NET_CLS_ACT
                switch (result) {
                case TC_ACT_QUEUED:
index 9fc1c62ec80e1ad56b760b2820c0c4f2e495d4a0..4e606fcb2534929a2470e807816ac1089d81b7b2 100644 (file)
@@ -191,7 +191,6 @@ static int fq_codel_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 
        if (list_empty(&flow->flowchain)) {
                list_add_tail(&flow->flowchain, &q->new_flows);
-               codel_vars_init(&flow->cvars);
                q->new_flow_count++;
                flow->deficit = q->quantum;
                flow->dropped = 0;
@@ -418,6 +417,7 @@ static int fq_codel_init(struct Qdisc *sch, struct nlattr *opt)
                        struct fq_codel_flow *flow = q->flows + i;
 
                        INIT_LIST_HEAD(&flow->flowchain);
+                       codel_vars_init(&flow->cvars);
                }
        }
        if (sch->limit >= 1)
index e901583e4ea533581f13e0fb39599f72e57b3e4c..d42234c0f13bf4d4829930e0f6bcb30681ad4782 100644 (file)
@@ -102,9 +102,8 @@ static inline int gred_wred_mode_check(struct Qdisc *sch)
                if (q == NULL)
                        continue;
 
-               for (n = 0; n < table->DPs; n++)
-                       if (table->tab[n] && table->tab[n] != q &&
-                           table->tab[n]->prio == q->prio)
+               for (n = i + 1; n < table->DPs; n++)
+                       if (table->tab[n] && table->tab[n]->prio == q->prio)
                                return 1;
        }
 
@@ -137,6 +136,7 @@ static inline void gred_store_wred_set(struct gred_sched *table,
                                       struct gred_sched_data *q)
 {
        table->wred_set.qavg = q->vars.qavg;
+       table->wred_set.qidlestart = q->vars.qidlestart;
 }
 
 static inline int gred_use_ecn(struct gred_sched *t)
@@ -176,7 +176,7 @@ static int gred_enqueue(struct sk_buff *skb, struct Qdisc *sch)
                skb->tc_index = (skb->tc_index & ~GRED_VQ_MASK) | dp;
        }
 
-       /* sum up all the qaves of prios <= to ours to get the new qave */
+       /* sum up all the qaves of prios < ours to get the new qave */
        if (!gred_wred_mode(t) && gred_rio_mode(t)) {
                int i;
 
@@ -260,16 +260,18 @@ static struct sk_buff *gred_dequeue(struct Qdisc *sch)
                } else {
                        q->backlog -= qdisc_pkt_len(skb);
 
-                       if (!q->backlog && !gred_wred_mode(t))
-                               red_start_of_idle_period(&q->vars);
+                       if (gred_wred_mode(t)) {
+                               if (!sch->qstats.backlog)
+                                       red_start_of_idle_period(&t->wred_set);
+                       } else {
+                               if (!q->backlog)
+                                       red_start_of_idle_period(&q->vars);
+                       }
                }
 
                return skb;
        }
 
-       if (gred_wred_mode(t) && !red_is_idling(&t->wred_set))
-               red_start_of_idle_period(&t->wred_set);
-
        return NULL;
 }
 
@@ -291,19 +293,20 @@ static unsigned int gred_drop(struct Qdisc *sch)
                        q->backlog -= len;
                        q->stats.other++;
 
-                       if (!q->backlog && !gred_wred_mode(t))
-                               red_start_of_idle_period(&q->vars);
+                       if (gred_wred_mode(t)) {
+                               if (!sch->qstats.backlog)
+                                       red_start_of_idle_period(&t->wred_set);
+                       } else {
+                               if (!q->backlog)
+                                       red_start_of_idle_period(&q->vars);
+                       }
                }
 
                qdisc_drop(skb, sch);
                return len;
        }
 
-       if (gred_wred_mode(t) && !red_is_idling(&t->wred_set))
-               red_start_of_idle_period(&t->wred_set);
-
        return 0;
-
 }
 
 static void gred_reset(struct Qdisc *sch)
@@ -535,6 +538,7 @@ static int gred_dump(struct Qdisc *sch, struct sk_buff *skb)
        for (i = 0; i < MAX_DPs; i++) {
                struct gred_sched_data *q = table->tab[i];
                struct tc_gred_qopt opt;
+               unsigned long qavg;
 
                memset(&opt, 0, sizeof(opt));
 
@@ -566,7 +570,9 @@ static int gred_dump(struct Qdisc *sch, struct sk_buff *skb)
                if (gred_wred_mode(table))
                        gred_load_wred_set(table, q);
 
-               opt.qave = red_calc_qavg(&q->parms, &q->vars, q->vars.qavg);
+               qavg = red_calc_qavg(&q->parms, &q->vars,
+                                    q->vars.qavg >> q->parms.Wlog);
+               opt.qave = qavg >> q->parms.Wlog;
 
 append_opt:
                if (nla_append(skb, sizeof(opt), &opt) < 0)
index e4723d31fdd56d6a46b2519afd19d612c7336749..211a212170451c41f352f087dc9e0b3af9e8ae2c 100644 (file)
@@ -865,7 +865,10 @@ static void qfq_update_start(struct qfq_sched *q, struct qfq_class *cl)
                if (mask) {
                        struct qfq_group *next = qfq_ffs(q, mask);
                        if (qfq_gt(roundedF, next->F)) {
-                               cl->S = next->F;
+                               if (qfq_gt(limit, next->F))
+                                       cl->S = next->F;
+                               else /* preserve timestamp correctness */
+                                       cl->S = limit;
                                return;
                        }
                }
index 838e18b4d7ea62cfd4d57e9d64a86630a0100f67..be50aa234dcdea30a5c7986eaeaae3570f64a6e3 100644 (file)
@@ -364,6 +364,25 @@ finish:
        return retval;
 }
 
+static void sctp_packet_release_owner(struct sk_buff *skb)
+{
+       sk_free(skb->sk);
+}
+
+static void sctp_packet_set_owner_w(struct sk_buff *skb, struct sock *sk)
+{
+       skb_orphan(skb);
+       skb->sk = sk;
+       skb->destructor = sctp_packet_release_owner;
+
+       /*
+        * The data chunks have already been accounted for in sctp_sendmsg(),
+        * therefore only reserve a single byte to keep socket around until
+        * the packet has been transmitted.
+        */
+       atomic_inc(&sk->sk_wmem_alloc);
+}
+
 /* All packets are sent to the network through this function from
  * sctp_outq_tail().
  *
@@ -405,7 +424,7 @@ int sctp_packet_transmit(struct sctp_packet *packet)
        /* Set the owning socket so that we know where to get the
         * destination IP address.
         */
-       skb_set_owner_w(nskb, sk);
+       sctp_packet_set_owner_w(nskb, sk);
 
        if (!sctp_transport_dst_check(tp)) {
                sctp_transport_route(tp, NULL, sctp_sk(sk));
index a5a402a7d21f9e888b1c3f45b3bed09e8baf57e8..5d7f61d7559c9753c9bff0f29b371b5e62b5f0d9 100644 (file)
@@ -969,11 +969,11 @@ static bool xprt_dynamic_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req)
        return false;
 }
 
-static void xprt_alloc_slot(struct rpc_task *task)
+void xprt_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task)
 {
-       struct rpc_xprt *xprt = task->tk_xprt;
        struct rpc_rqst *req;
 
+       spin_lock(&xprt->reserve_lock);
        if (!list_empty(&xprt->free)) {
                req = list_entry(xprt->free.next, struct rpc_rqst, rq_list);
                list_del(&req->rq_list);
@@ -994,12 +994,29 @@ static void xprt_alloc_slot(struct rpc_task *task)
        default:
                task->tk_status = -EAGAIN;
        }
+       spin_unlock(&xprt->reserve_lock);
        return;
 out_init_req:
        task->tk_status = 0;
        task->tk_rqstp = req;
        xprt_request_init(task, xprt);
+       spin_unlock(&xprt->reserve_lock);
+}
+EXPORT_SYMBOL_GPL(xprt_alloc_slot);
+
+void xprt_lock_and_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task)
+{
+       /* Note: grabbing the xprt_lock_write() ensures that we throttle
+        * new slot allocation if the transport is congested (i.e. when
+        * reconnecting a stream transport or when out of socket write
+        * buffer space).
+        */
+       if (xprt_lock_write(xprt, task)) {
+               xprt_alloc_slot(xprt, task);
+               xprt_release_write(xprt, task);
+       }
 }
+EXPORT_SYMBOL_GPL(xprt_lock_and_alloc_slot);
 
 static void xprt_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req)
 {
@@ -1083,20 +1100,9 @@ void xprt_reserve(struct rpc_task *task)
        if (task->tk_rqstp != NULL)
                return;
 
-       /* Note: grabbing the xprt_lock_write() here is not strictly needed,
-        * but ensures that we throttle new slot allocation if the transport
-        * is congested (e.g. if reconnecting or if we're out of socket
-        * write buffer space).
-        */
        task->tk_timeout = 0;
        task->tk_status = -EAGAIN;
-       if (!xprt_lock_write(xprt, task))
-               return;
-
-       spin_lock(&xprt->reserve_lock);
-       xprt_alloc_slot(task);
-       spin_unlock(&xprt->reserve_lock);
-       xprt_release_write(xprt, task);
+       xprt->ops->alloc_slot(xprt, task);
 }
 
 static inline __be32 xprt_alloc_xid(struct rpc_xprt *xprt)
index 06cdbff79e4af433d5a4ea3a2766d6d09a9b5a01..5d9202dc7cb127f5158a2a6e275fa0656c18f0a6 100644 (file)
@@ -713,6 +713,7 @@ static void xprt_rdma_print_stats(struct rpc_xprt *xprt, struct seq_file *seq)
 static struct rpc_xprt_ops xprt_rdma_procs = {
        .reserve_xprt           = xprt_rdma_reserve_xprt,
        .release_xprt           = xprt_release_xprt_cong, /* sunrpc/xprt.c */
+       .alloc_slot             = xprt_alloc_slot,
        .release_request        = xprt_release_rqst_cong,       /* ditto */
        .set_retrans_timeout    = xprt_set_retrans_timeout_def, /* ditto */
        .rpcbind                = rpcb_getport_async,   /* sunrpc/rpcb_clnt.c */
index 400567243f84ba95e8f04d8a631a3b74539c53ea..a35b8e52e551d4b931110a78727604c8d6af8283 100644 (file)
@@ -2473,6 +2473,7 @@ static void bc_destroy(struct rpc_xprt *xprt)
 static struct rpc_xprt_ops xs_local_ops = {
        .reserve_xprt           = xprt_reserve_xprt,
        .release_xprt           = xs_tcp_release_xprt,
+       .alloc_slot             = xprt_alloc_slot,
        .rpcbind                = xs_local_rpcbind,
        .set_port               = xs_local_set_port,
        .connect                = xs_connect,
@@ -2489,6 +2490,7 @@ static struct rpc_xprt_ops xs_udp_ops = {
        .set_buffer_size        = xs_udp_set_buffer_size,
        .reserve_xprt           = xprt_reserve_xprt_cong,
        .release_xprt           = xprt_release_xprt_cong,
+       .alloc_slot             = xprt_alloc_slot,
        .rpcbind                = rpcb_getport_async,
        .set_port               = xs_set_port,
        .connect                = xs_connect,
@@ -2506,6 +2508,7 @@ static struct rpc_xprt_ops xs_udp_ops = {
 static struct rpc_xprt_ops xs_tcp_ops = {
        .reserve_xprt           = xprt_reserve_xprt,
        .release_xprt           = xs_tcp_release_xprt,
+       .alloc_slot             = xprt_lock_and_alloc_slot,
        .rpcbind                = rpcb_getport_async,
        .set_port               = xs_set_port,
        .connect                = xs_connect,
index 97026f3b215a1c85b3ddf0f0f6cf71636e76a586..1e37dbf00cb3f3850d3785827f896ca09339873b 100644 (file)
@@ -5633,8 +5633,10 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
                       sizeof(connect.ht_capa_mask));
 
        if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
-               if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+               if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) {
+                       kfree(connkeys);
                        return -EINVAL;
+               }
                memcpy(&connect.ht_capa,
                       nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
                       sizeof(connect.ht_capa));
index 54a0dc2e2f8d45d7a842be98882969f696c07ec2..ab2bb42fe094b7390d5135ec6e37b9113ea8219b 100644 (file)
@@ -212,7 +212,7 @@ resume:
                /* only the first xfrm gets the encap type */
                encap_type = 0;
 
-               if (async && x->repl->check(x, skb, seq)) {
+               if (async && x->repl->recheck(x, skb, seq)) {
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATESEQERROR);
                        goto drop_unlock;
                }
index 5a2aa17e4d3c4a3667783266ddbb8992a3eff6bd..387848e900783f6e6862c6d2c0b52ab45bbcfe50 100644 (file)
@@ -585,6 +585,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
        xfrm_pol_hold(policy);
        net->xfrm.policy_count[dir]++;
        atomic_inc(&flow_cache_genid);
+       rt_genid_bump(net);
        if (delpol)
                __xfrm_policy_unlink(delpol, dir);
        policy->index = delpol ? delpol->index : xfrm_gen_index(net, dir);
@@ -1763,7 +1764,7 @@ static struct dst_entry *make_blackhole(struct net *net, u16 family,
 
        if (!afinfo) {
                dst_release(dst_orig);
-               ret = ERR_PTR(-EINVAL);
+               return ERR_PTR(-EINVAL);
        } else {
                ret = afinfo->blackhole_route(net, dst_orig);
        }
index 2f6d11d04a2b29910a1f284d3e3af8b0db1bfcce..3efb07d3eb27425c8b9b5114c925eb9e7f402c9e 100644 (file)
@@ -420,6 +420,18 @@ err:
        return -EINVAL;
 }
 
+static int xfrm_replay_recheck_esn(struct xfrm_state *x,
+                                  struct sk_buff *skb, __be32 net_seq)
+{
+       if (unlikely(XFRM_SKB_CB(skb)->seq.input.hi !=
+                    htonl(xfrm_replay_seqhi(x, net_seq)))) {
+                       x->stats.replay_window++;
+                       return -EINVAL;
+       }
+
+       return xfrm_replay_check_esn(x, skb, net_seq);
+}
+
 static void xfrm_replay_advance_esn(struct xfrm_state *x, __be32 net_seq)
 {
        unsigned int bitnr, nr, i;
@@ -479,6 +491,7 @@ static void xfrm_replay_advance_esn(struct xfrm_state *x, __be32 net_seq)
 static struct xfrm_replay xfrm_replay_legacy = {
        .advance        = xfrm_replay_advance,
        .check          = xfrm_replay_check,
+       .recheck        = xfrm_replay_check,
        .notify         = xfrm_replay_notify,
        .overflow       = xfrm_replay_overflow,
 };
@@ -486,6 +499,7 @@ static struct xfrm_replay xfrm_replay_legacy = {
 static struct xfrm_replay xfrm_replay_bmp = {
        .advance        = xfrm_replay_advance_bmp,
        .check          = xfrm_replay_check_bmp,
+       .recheck        = xfrm_replay_check_bmp,
        .notify         = xfrm_replay_notify_bmp,
        .overflow       = xfrm_replay_overflow_bmp,
 };
@@ -493,6 +507,7 @@ static struct xfrm_replay xfrm_replay_bmp = {
 static struct xfrm_replay xfrm_replay_esn = {
        .advance        = xfrm_replay_advance_esn,
        .check          = xfrm_replay_check_esn,
+       .recheck        = xfrm_replay_recheck_esn,
        .notify         = xfrm_replay_notify_bmp,
        .overflow       = xfrm_replay_overflow_esn,
 };
index e75d8e47f35cab2bdddd1051e6da556ef20eb26c..289f4bf18ff05751c5938c8722b098f6a560ee00 100644 (file)
@@ -123,9 +123,21 @@ static inline int verify_replay(struct xfrm_usersa_info *p,
                                struct nlattr **attrs)
 {
        struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL];
+       struct xfrm_replay_state_esn *rs;
 
-       if ((p->flags & XFRM_STATE_ESN) && !rt)
-               return -EINVAL;
+       if (p->flags & XFRM_STATE_ESN) {
+               if (!rt)
+                       return -EINVAL;
+
+               rs = nla_data(rt);
+
+               if (rs->bmp_len > XFRMA_REPLAY_ESN_MAX / sizeof(rs->bmp[0]) / 8)
+                       return -EINVAL;
+
+               if (nla_len(rt) < xfrm_replay_state_esn_len(rs) &&
+                   nla_len(rt) != sizeof(*rs))
+                       return -EINVAL;
+       }
 
        if (!rt)
                return 0;
@@ -370,14 +382,15 @@ static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_es
                                         struct nlattr *rp)
 {
        struct xfrm_replay_state_esn *up;
+       int ulen;
 
        if (!replay_esn || !rp)
                return 0;
 
        up = nla_data(rp);
+       ulen = xfrm_replay_state_esn_len(up);
 
-       if (xfrm_replay_state_esn_len(replay_esn) !=
-                       xfrm_replay_state_esn_len(up))
+       if (nla_len(rp) < ulen || xfrm_replay_state_esn_len(replay_esn) != ulen)
                return -EINVAL;
 
        return 0;
@@ -388,22 +401,28 @@ static int xfrm_alloc_replay_state_esn(struct xfrm_replay_state_esn **replay_esn
                                       struct nlattr *rta)
 {
        struct xfrm_replay_state_esn *p, *pp, *up;
+       int klen, ulen;
 
        if (!rta)
                return 0;
 
        up = nla_data(rta);
+       klen = xfrm_replay_state_esn_len(up);
+       ulen = nla_len(rta) >= klen ? klen : sizeof(*up);
 
-       p = kmemdup(up, xfrm_replay_state_esn_len(up), GFP_KERNEL);
+       p = kzalloc(klen, GFP_KERNEL);
        if (!p)
                return -ENOMEM;
 
-       pp = kmemdup(up, xfrm_replay_state_esn_len(up), GFP_KERNEL);
+       pp = kzalloc(klen, GFP_KERNEL);
        if (!pp) {
                kfree(p);
                return -ENOMEM;
        }
 
+       memcpy(p, up, ulen);
+       memcpy(pp, up, ulen);
+
        *replay_esn = p;
        *preplay_esn = pp;
 
@@ -442,10 +461,11 @@ static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *
  * somehow made shareable and move it to xfrm_state.c - JHS
  *
 */
-static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs)
+static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs,
+                                 int update_esn)
 {
        struct nlattr *rp = attrs[XFRMA_REPLAY_VAL];
-       struct nlattr *re = attrs[XFRMA_REPLAY_ESN_VAL];
+       struct nlattr *re = update_esn ? attrs[XFRMA_REPLAY_ESN_VAL] : NULL;
        struct nlattr *lt = attrs[XFRMA_LTIME_VAL];
        struct nlattr *et = attrs[XFRMA_ETIMER_THRESH];
        struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH];
@@ -555,7 +575,7 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
                goto error;
 
        /* override default values from above */
-       xfrm_update_ae_params(x, attrs);
+       xfrm_update_ae_params(x, attrs, 0);
 
        return x;
 
@@ -689,6 +709,7 @@ out:
 
 static void copy_to_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p)
 {
+       memset(p, 0, sizeof(*p));
        memcpy(&p->id, &x->id, sizeof(p->id));
        memcpy(&p->sel, &x->sel, sizeof(p->sel));
        memcpy(&p->lft, &x->lft, sizeof(p->lft));
@@ -742,7 +763,7 @@ static int copy_to_user_auth(struct xfrm_algo_auth *auth, struct sk_buff *skb)
                return -EMSGSIZE;
 
        algo = nla_data(nla);
-       strcpy(algo->alg_name, auth->alg_name);
+       strncpy(algo->alg_name, auth->alg_name, sizeof(algo->alg_name));
        memcpy(algo->alg_key, auth->alg_key, (auth->alg_key_len + 7) / 8);
        algo->alg_key_len = auth->alg_key_len;
 
@@ -878,6 +899,7 @@ static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb,
 {
        struct xfrm_dump_info info;
        struct sk_buff *skb;
+       int err;
 
        skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
        if (!skb)
@@ -888,9 +910,10 @@ static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb,
        info.nlmsg_seq = seq;
        info.nlmsg_flags = 0;
 
-       if (dump_one_state(x, 0, &info)) {
+       err = dump_one_state(x, 0, &info);
+       if (err) {
                kfree_skb(skb);
-               return NULL;
+               return ERR_PTR(err);
        }
 
        return skb;
@@ -1317,6 +1340,7 @@ static void copy_from_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy
 
 static void copy_to_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p, int dir)
 {
+       memset(p, 0, sizeof(*p));
        memcpy(&p->sel, &xp->selector, sizeof(p->sel));
        memcpy(&p->lft, &xp->lft, sizeof(p->lft));
        memcpy(&p->curlft, &xp->curlft, sizeof(p->curlft));
@@ -1421,6 +1445,7 @@ static int copy_to_user_tmpl(struct xfrm_policy *xp, struct sk_buff *skb)
                struct xfrm_user_tmpl *up = &vec[i];
                struct xfrm_tmpl *kp = &xp->xfrm_vec[i];
 
+               memset(up, 0, sizeof(*up));
                memcpy(&up->id, &kp->id, sizeof(up->id));
                up->family = kp->encap_family;
                memcpy(&up->saddr, &kp->saddr, sizeof(up->saddr));
@@ -1546,6 +1571,7 @@ static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb,
 {
        struct xfrm_dump_info info;
        struct sk_buff *skb;
+       int err;
 
        skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!skb)
@@ -1556,9 +1582,10 @@ static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb,
        info.nlmsg_seq = seq;
        info.nlmsg_flags = 0;
 
-       if (dump_one_policy(xp, dir, 0, &info) < 0) {
+       err = dump_one_policy(xp, dir, 0, &info);
+       if (err) {
                kfree_skb(skb);
-               return NULL;
+               return ERR_PTR(err);
        }
 
        return skb;
@@ -1822,7 +1849,7 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
                goto out;
 
        spin_lock_bh(&x->lock);
-       xfrm_update_ae_params(x, attrs);
+       xfrm_update_ae_params(x, attrs, 1);
        spin_unlock_bh(&x->lock);
 
        c.event = nlh->nlmsg_type;
index c3f69ae275d1f7710b5f38c02e9daf6b45b947a4..4d908d16c035c4e6c582e254e531998e7f6156ae 100644 (file)
@@ -27,7 +27,7 @@ endif
 installed-mod-fw := $(addprefix $(INSTALL_FW_PATH)/,$(mod-fw))
 
 installed-fw := $(addprefix $(INSTALL_FW_PATH)/,$(fw-shipped-all))
-installed-fw-dirs := $(sort $(dir $(installed-fw))) $(INSTALL_FW_PATH)/.
+installed-fw-dirs := $(sort $(dir $(installed-fw))) $(INSTALL_FW_PATH)/./
 
 # Workaround for make < 3.81, where .SECONDEXPANSION doesn't work.
 PHONY += $(INSTALL_FW_PATH)/$$(%) install-all-dirs
@@ -42,7 +42,7 @@ quiet_cmd_install = INSTALL $(subst $(srctree)/,,$@)
 $(installed-fw-dirs):
        $(call cmd,mkdir)
 
-$(installed-fw): $(INSTALL_FW_PATH)/%: $(obj)/% | $$(dir $(INSTALL_FW_PATH)/%)
+$(installed-fw): $(INSTALL_FW_PATH)/%: $(obj)/% | $(INSTALL_FW_PATH)/$$(dir %)
        $(call cmd,install)
 
 PHONY +=  __fw_install __fw_modinst FORCE
index c220f314709cc1345792e4a4ddab28cd63a23177..65f67cb0aefb22f323d8048c140417555c273c9d 100644 (file)
@@ -51,6 +51,7 @@ int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall);
 static inline void selinux_xfrm_notify_policyload(void)
 {
        atomic_inc(&flow_cache_genid);
+       rt_genid_bump(&init_net);
 }
 #else
 static inline int selinux_xfrm_enabled(void)
index ec2118d0e27aca3f5fef6c2ddd72f8b166ce98ca..eb60cb8dbb8a6f12d965912e923197cdd2f4e8b5 100644 (file)
@@ -80,14 +80,12 @@ static int snd_compr_open(struct inode *inode, struct file *f)
        int maj = imajor(inode);
        int ret;
 
-       if (f->f_flags & O_WRONLY)
+       if ((f->f_flags & O_ACCMODE) == O_WRONLY)
                dirn = SND_COMPRESS_PLAYBACK;
-       else if (f->f_flags & O_RDONLY)
+       else if ((f->f_flags & O_ACCMODE) == O_RDONLY)
                dirn = SND_COMPRESS_CAPTURE;
-       else {
-               pr_err("invalid direction\n");
+       else
                return -EINVAL;
-       }
 
        if (maj == snd_major)
                compr = snd_lookup_minor_data(iminor(inode),
index f25c24c743f9d7dedd551d2196c2f1e441076a07..1c65cc5e3a31101d098d6cdb48a2772d1adc38f9 100644 (file)
@@ -2353,6 +2353,7 @@ int snd_hda_codec_reset(struct hda_codec *codec)
        }
        if (codec->patch_ops.free)
                codec->patch_ops.free(codec);
+       memset(&codec->patch_ops, 0, sizeof(codec->patch_ops));
        snd_hda_jack_tbl_clear(codec);
        codec->proc_widget_hook = NULL;
        codec->spec = NULL;
@@ -2368,7 +2369,6 @@ int snd_hda_codec_reset(struct hda_codec *codec)
        codec->num_pcms = 0;
        codec->pcm_info = NULL;
        codec->preset = NULL;
-       memset(&codec->patch_ops, 0, sizeof(codec->patch_ops));
        codec->slave_dig_outs = NULL;
        codec->spdif_status_reset = 0;
        module_put(codec->owner);
index 60882c62f18006a3b2339d354b58550fdcd30718..c4763c52eaf64ced9e1d520f80fd8b245507dbd6 100644 (file)
@@ -2701,6 +2701,8 @@ static struct snd_pci_quirk position_fix_list[] __devinitdata = {
        SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS M2V", POS_FIX_LPIB),
+       SND_PCI_QUIRK(0x1043, 0x1ac3, "ASUS X53S", POS_FIX_POSBUF),
+       SND_PCI_QUIRK(0x1043, 0x1b43, "ASUS K53E", POS_FIX_POSBUF),
        SND_PCI_QUIRK(0x104d, 0x9069, "Sony VPCS11V9E", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x10de, 0xcb89, "Macbook Pro 7,1", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1297, 0x3166, "Shuttle", POS_FIX_LPIB),
index 6f806d3e56bbf878d27f08cdd8ce3f805c9e9f1e..3d4722f0a1cacba8fc9a923f22dbf767433dbd6c 100644 (file)
@@ -1075,7 +1075,7 @@ static struct snd_kcontrol_new stac_smux_mixer = {
 
 static const char * const slave_pfxs[] = {
        "Front", "Surround", "Center", "LFE", "Side",
-       "Headphone", "Speaker", "IEC958",
+       "Headphone", "Speaker", "IEC958", "PCM",
        NULL
 };
 
index 764cc93dbca402f6372b6f73a0854470f3046f73..075d5aa1fee003bef0dfaa4177ac21d010247277 100644 (file)
@@ -297,6 +297,7 @@ static int ak4396_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
 }
 
 static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
+static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
 
 static struct snd_kcontrol_new prodigy_hd2_controls[] __devinitdata = {
     {
@@ -307,7 +308,7 @@ static struct snd_kcontrol_new prodigy_hd2_controls[] __devinitdata = {
        .info = ak4396_dac_vol_info,
        .get = ak4396_dac_vol_get,
        .put = ak4396_dac_vol_put,
-       .tlv = { .p = db_scale_wm_dac },
+       .tlv = { .p = ak4396_db_scale },
     },
 };
 
index 5c9cacaf2d525cddabffd0b416c0695dfadaab99..1cf7a32d1b211e779cb7705746c32b07c11afa82 100644 (file)
@@ -426,7 +426,7 @@ static const int arizona_44k1_bclk_rates[] = {
        940800,
        1411200,
        1881600,
-       2882400,
+       2822400,
        3763200,
        5644800,
        7526400,
index 8f726c063f42badc9c598de4af670fb78f8937e1..115a403018105b0eaa2fb64abee56a47b5d32cc4 100644 (file)
@@ -659,7 +659,7 @@ static struct snd_soc_dai_driver mc13783_dai_async[] = {
                .id = MC13783_ID_STEREO_DAC,
                .playback = {
                        .stream_name = "Playback",
-                       .channels_min = 1,
+                       .channels_min = 2,
                        .channels_max = 2,
                        .rates = SNDRV_PCM_RATE_8000_96000,
                        .formats = MC13783_FORMATS,
@@ -670,7 +670,7 @@ static struct snd_soc_dai_driver mc13783_dai_async[] = {
                .id = MC13783_ID_STEREO_CODEC,
                .capture = {
                        .stream_name = "Capture",
-                       .channels_min = 1,
+                       .channels_min = 2,
                        .channels_max = 2,
                        .rates = MC13783_RATES_RECORD,
                        .formats = MC13783_FORMATS,
@@ -692,14 +692,14 @@ static struct snd_soc_dai_driver mc13783_dai_sync[] = {
                .id = MC13783_ID_SYNC,
                .playback = {
                        .stream_name = "Playback",
-                       .channels_min = 1,
+                       .channels_min = 2,
                        .channels_max = 2,
                        .rates = SNDRV_PCM_RATE_8000_96000,
                        .formats = MC13783_FORMATS,
                },
                .capture = {
                        .stream_name = "Capture",
-                       .channels_min = 1,
+                       .channels_min = 2,
                        .channels_max = 2,
                        .rates = MC13783_RATES_RECORD,
                        .formats = MC13783_FORMATS,
index 0013afe48e66a83171b93691b46593cb2e64d7ea..dc4262eea4b711990c71e602fe1a0fba7928250b 100644 (file)
@@ -100,7 +100,7 @@ static const struct reg_default wm8904_reg_defaults[] = {
        { 14,  0x0000 },     /* R14  - Power Management 2 */
        { 15,  0x0000 },     /* R15  - Power Management 3 */
        { 18,  0x0000 },     /* R18  - Power Management 6 */
-       { 19,  0x945E },     /* R20  - Clock Rates 0 */
+       { 20,  0x945E },     /* R20  - Clock Rates 0 */
        { 21,  0x0C05 },     /* R21  - Clock Rates 1 */
        { 22,  0x0006 },     /* R22  - Clock Rates 2 */
        { 24,  0x0050 },     /* R24  - Audio Interface 0 */
index fb21b17f17f54ae342b552e05b42df6a0afcaeca..199408ec42612dfe57f63e3933457e901cf7bcfa 100644 (file)
@@ -94,7 +94,7 @@ static int __devinit imx_sgtl5000_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "audmux internal port setup failed\n");
                return ret;
        }
-       imx_audmux_v2_configure_port(ext_port,
+       ret = imx_audmux_v2_configure_port(ext_port,
                        IMX_AUDMUX_V2_PTCR_SYN,
                        IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
        if (ret) {
index 009533ab8d1894054ed07c05ae140362d6738582..df65f98211ec2bdc3c664f7a87443f8477dc5476 100644 (file)
@@ -59,7 +59,7 @@ static int am3517evm_hw_params(struct snd_pcm_substream *substream,
                return ret;
        }
 
-       snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_FSR_SRC_FSX, 0,
+       ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_FSR_SRC_FSX, 0,
                                SND_SOC_CLOCK_IN);
        if (ret < 0) {
                printk(KERN_ERR "can't set CPU system clock OMAP_MCBSP_FSR_SRC_FSX\n");
index f3ebc38c10fe7633ea5ec260562b64bfdb7e402f..b70964ea448cef264bf540597850558e7c9fd23c 100644 (file)
@@ -34,9 +34,7 @@ static const struct snd_pcm_hardware dma_hardware = {
        .info                   = SNDRV_PCM_INFO_INTERLEAVED |
                                    SNDRV_PCM_INFO_BLOCK_TRANSFER |
                                    SNDRV_PCM_INFO_MMAP |
-                                   SNDRV_PCM_INFO_MMAP_VALID |
-                                   SNDRV_PCM_INFO_PAUSE |
-                                   SNDRV_PCM_INFO_RESUME,
+                                   SNDRV_PCM_INFO_MMAP_VALID,
        .formats                = SNDRV_PCM_FMTBIT_S16_LE |
                                    SNDRV_PCM_FMTBIT_U16_LE |
                                    SNDRV_PCM_FMTBIT_U8 |
@@ -248,15 +246,11 @@ static int dma_trigger(struct snd_pcm_substream *substream, int cmd)
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
-       case SNDRV_PCM_TRIGGER_RESUME:
-       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                prtd->state |= ST_RUNNING;
                prtd->params->ops->trigger(prtd->params->ch);
                break;
 
        case SNDRV_PCM_TRIGGER_STOP:
-       case SNDRV_PCM_TRIGGER_SUSPEND:
-       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
                prtd->state &= ~ST_RUNNING;
                prtd->params->ops->stop(prtd->params->ch);
                break;
index dd7c49fafd754f949014f88b9a2ad0b80d59cc20..f90139b5f50d74089dc98fd0d19de42cb3732a84 100644 (file)
@@ -291,8 +291,11 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,
                if (dapm->codec->driver->set_bias_level)
                        ret = dapm->codec->driver->set_bias_level(dapm->codec,
                                                                  level);
-       } else
+               else
+                       dapm->bias_level = level;
+       } else if (!card || dapm != &card->dapm) {
                dapm->bias_level = level;
+       }
 
        if (ret != 0)
                goto out;
index 97c2cac8e92c726712746d5db4658d7f0898e441..8c7f23729446b1582f6d7625d5933aad905e45d7 100644 (file)
@@ -138,7 +138,7 @@ static void spear_pcm_free(struct snd_pcm *pcm)
                        continue;
 
                buf = &substream->dma_buffer;
-               if (!buf && !buf->area)
+               if (!buf || !buf->area)
                        continue;
 
                dma_free_writecombine(pcm->card->dev, buf->bytes,
index e463529b38bbfbfd35cc745bf430f68399bfee91..76cb1b363b71c2ce2d1be75c27113c3cf2154127 100644 (file)
@@ -89,7 +89,6 @@ static struct snd_soc_jack_gpio tegra_alc5632_hp_jack_gpio = {
        .name = "Headset detection",
        .report = SND_JACK_HEADSET,
        .debounce_time = 150,
-       .invert = 1,
 };
 
 static const struct snd_soc_dapm_widget tegra_alc5632_dapm_widgets[] = {
index 5658bcec1931ce5a76a75a32b020730f41da8c13..8d6900c1ee47e8d1a263584bf470e5c2eec56af4 100644 (file)
@@ -334,11 +334,11 @@ static int tegra_pcm_hw_params(struct snd_pcm_substream *substream,
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
                slave_config.dst_addr = dmap->addr;
-               slave_config.src_maxburst = 0;
+               slave_config.dst_maxburst = 4;
        } else {
                slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
                slave_config.src_addr = dmap->addr;
-               slave_config.dst_maxburst = 0;
+               slave_config.src_maxburst = 4;
        }
        slave_config.slave_id = dmap->req_sel;
 
index 5c472f335a64d6e5c11a5ee82e755153312ee40b..eb85113d472a22503aceb1939d5191d9d2c856de 100644 (file)
@@ -663,7 +663,6 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev,
                        struct ux500_msp **msp_p,
                        struct msp_i2s_platform_data *platform_data)
 {
-       int ret = 0;
        struct resource *res = NULL;
        struct i2s_controller *i2s_cont;
        struct ux500_msp *msp;
@@ -685,15 +684,14 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev,
        if (res == NULL) {
                dev_err(&pdev->dev, "%s: ERROR: Unable to get resource!\n",
                        __func__);
-               ret = -ENOMEM;
-               goto err_res;
+               return -ENOMEM;
        }
 
-       msp->registers = ioremap(res->start, (res->end - res->start + 1));
+       msp->registers = devm_ioremap(&pdev->dev, res->start,
+                                     resource_size(res));
        if (msp->registers == NULL) {
                dev_err(&pdev->dev, "%s: ERROR: ioremap failed!\n", __func__);
-               ret = -ENOMEM;
-               goto err_res;
+               return -ENOMEM;
        }
 
        msp->msp_state = MSP_STATE_IDLE;
@@ -705,7 +703,7 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev,
                dev_err(&pdev->dev,
                        "%s: ERROR: Failed to allocate I2S-controller!\n",
                        __func__);
-               goto err_i2s_cont;
+               return -ENOMEM;
        }
        i2s_cont->dev.parent = &pdev->dev;
        i2s_cont->data = (void *)msp;
@@ -716,14 +714,6 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev,
        msp->i2s_cont = i2s_cont;
 
        return 0;
-
-err_i2s_cont:
-       iounmap(msp->registers);
-
-err_res:
-       devm_kfree(&pdev->dev, msp);
-
-       return ret;
 }
 
 void ux500_msp_i2s_cleanup_msp(struct platform_device *pdev,
@@ -732,11 +722,6 @@ void ux500_msp_i2s_cleanup_msp(struct platform_device *pdev,
        dev_dbg(msp->dev, "%s: Enter (id = %d).\n", __func__, msp->id);
 
        device_unregister(&msp->i2s_cont->dev);
-       devm_kfree(&pdev->dev, msp->i2s_cont);
-
-       iounmap(msp->registers);
-
-       devm_kfree(&pdev->dev, msp);
 }
 
 MODULE_LICENSE("GPL v2");
index fd5e982fc98c2af6b01f84d00450b1262b76f218..f782ce19bf5aa14be92a43df553d66b177cb9429 100644 (file)
@@ -1140,6 +1140,12 @@ static void retire_playback_urb(struct snd_usb_substream *subs,
        int processed = urb->transfer_buffer_length / stride;
        int est_delay;
 
+       /* ignore the delay accounting when procssed=0 is given, i.e.
+        * silent payloads are procssed before handling the actual data
+        */
+       if (!processed)
+               return;
+
        spin_lock_irqsave(&subs->lock, flags);
        est_delay = snd_usb_pcm_delay(subs, runtime->rate);
        /* update delay with exact number of samples played */