From aa22db5e515bfd9c93b22fe0d89dd2f85d6cc977 Mon Sep 17 00:00:00 2001 From: David Miller Date: Thu, 9 Feb 2012 16:43:01 -0500 Subject: [PATCH] --- yaml --- r: 287647 b: refs/heads/master c: 532691635475487b8c1d6fc618bd123084904897 h: refs/heads/master i: 287645: 02f1baa4eef67d07f8f1ebc6cc31369d65819d63 287643: a8f78c28b166cde66d1262861ed91ccbe6563c67 287639: 7030eb6417dcfa24194151682e9fdce57f8dcb30 287631: 665c924ef82339a539a9b2821abcfdaebe6540ff 287615: 98f07afcaf1e6c3144ca2c7c43450a2405afea39 v: v3 --- [refs] | 2 +- trunk/MAINTAINERS | 6 - trunk/arch/arm/kernel/traps.c | 5 +- trunk/arch/arm/kernel/vmlinux.lds.S | 1 - trunk/arch/arm/mach-omap2/board-4430sdp.c | 17 +- trunk/arch/arm/mach-omap2/board-omap3evm.c | 23 +- trunk/arch/arm/mach-omap2/board-omap4panda.c | 6 +- .../arm/mach-omap2/board-zoom-peripherals.c | 6 +- trunk/arch/arm/mach-omap2/hsmmc.c | 8 +- trunk/arch/arm/mach-omap2/mux.c | 22 +- trunk/arch/arm/mach-omap2/omap-headsmp.S | 1 - trunk/arch/arm/mach-omap2/omap_hwmod.c | 16 +- trunk/arch/arm/mach-omap2/prm44xx.c | 1 - trunk/arch/arm/mach-omap2/serial.c | 8 +- trunk/arch/arm/mach-omap2/vc.c | 10 +- trunk/arch/arm/mach-omap2/vp.c | 5 - trunk/arch/arm/mm/cache-v7.S | 6 - trunk/arch/powerpc/include/asm/ppc-pci.h | 5 - trunk/arch/powerpc/include/asm/ptrace.h | 20 +- trunk/arch/powerpc/kernel/irq.c | 6 +- trunk/arch/powerpc/kernel/rtas.c | 5 +- trunk/arch/powerpc/platforms/powernv/pci.c | 22 +- trunk/arch/powerpc/platforms/pseries/eeh.c | 4 +- .../arch/powerpc/platforms/pseries/suspend.c | 6 +- trunk/arch/powerpc/platforms/wsp/ics.c | 2 +- trunk/arch/powerpc/platforms/wsp/wsp_pci.c | 8 +- trunk/arch/x86/include/asm/i387.h | 56 +- .../arch/x86/kernel/cpu/perf_event_intel_ds.c | 1 + .../x86/kernel/cpu/perf_event_intel_lbr.c | 2 + trunk/arch/x86/kernel/traps.c | 7 +- trunk/arch/x86/pci/xen.c | 2 +- trunk/arch/x86/xen/smp.c | 7 - trunk/block/blk-cgroup.c | 2 +- trunk/block/blk-core.c | 33 +- trunk/block/blk-ioc.c | 111 +- trunk/block/blk-merge.c | 37 - trunk/block/blk.h | 2 - trunk/block/bsg.c | 3 +- trunk/block/cfq-iosched.c | 24 +- trunk/block/elevator.c | 55 +- trunk/crypto/sha512_generic.c | 70 +- trunk/drivers/base/cpu.c | 21 - trunk/drivers/base/memory.c | 31 +- trunk/drivers/base/node.c | 8 - trunk/drivers/bcma/main.c | 4 +- trunk/drivers/bcma/scan.c | 19 +- trunk/drivers/block/floppy.c | 17 +- trunk/drivers/block/loop.c | 24 +- trunk/drivers/block/mtip32xx/mtip32xx.c | 11 +- trunk/drivers/block/mtip32xx/mtip32xx.h | 5 + trunk/drivers/cdrom/cdrom.c | 20 +- trunk/drivers/gpu/drm/drm_ioc32.c | 3 +- trunk/drivers/gpu/drm/i915/intel_display.c | 8 +- trunk/drivers/gpu/drm/i915/intel_dp.c | 20 +- trunk/drivers/gpu/drm/i915/intel_lvds.c | 8 - trunk/drivers/gpu/drm/radeon/evergreen.c | 1 - trunk/drivers/hwmon/f75375s.c | 11 +- trunk/drivers/hwmon/w83627ehf.c | 5 + trunk/drivers/infiniband/ulp/ipoib/ipoib.h | 6 +- .../drivers/infiniband/ulp/ipoib/ipoib_main.c | 55 +- .../infiniband/ulp/ipoib/ipoib_multicast.c | 10 +- trunk/drivers/isdn/i4l/isdn_net.c | 2 +- trunk/drivers/mfd/Kconfig | 2 +- trunk/drivers/mfd/twl-core.c | 6 +- trunk/drivers/mfd/twl4030-power.c | 20 +- trunk/drivers/misc/Kconfig | 17 +- .../drivers/misc/c2port/c2port-duramar2150.c | 1 - trunk/drivers/misc/cb710/core.c | 1 - trunk/drivers/misc/cs5535-mfgpt.c | 2 +- trunk/drivers/misc/vmw_balloon.c | 14 +- trunk/drivers/mmc/card/block.c | 1 - trunk/drivers/mmc/core/core.c | 49 +- trunk/drivers/mmc/core/host.h | 21 + trunk/drivers/mmc/core/mmc.c | 26 +- trunk/drivers/mmc/core/sd.c | 22 +- trunk/drivers/mmc/core/sdio.c | 7 +- trunk/drivers/mmc/core/sdio_irq.c | 10 +- trunk/drivers/mmc/host/Kconfig | 1 + trunk/drivers/mmc/host/atmel-mci.c | 3 - trunk/drivers/mmc/host/dw_mmc.c | 144 +- trunk/drivers/mmc/host/of_mmc_spi.c | 4 +- trunk/drivers/mmc/host/sdhci-of-esdhc.c | 32 - trunk/drivers/mmc/host/sdhci-pci.c | 2 +- trunk/drivers/mmc/host/sdhci-pltfm.c | 10 +- trunk/drivers/mmc/host/sh_mmcif.c | 16 +- trunk/drivers/mmc/host/tmio_mmc.h | 7 +- trunk/drivers/mmc/host/tmio_mmc_dma.c | 12 - trunk/drivers/mmc/host/tmio_mmc_pio.c | 6 +- trunk/drivers/net/can/cc770/cc770.c | 4 +- trunk/drivers/net/can/cc770/cc770_isa.c | 16 +- trunk/drivers/net/can/flexcan.c | 7 +- trunk/drivers/net/can/pch_can.c | 3 +- trunk/drivers/net/can/sja1000/peak_pci.c | 23 +- trunk/drivers/net/can/ti_hecc.c | 3 +- trunk/drivers/net/can/usb/ems_usb.c | 9 + .../net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 3 +- .../net/ethernet/brocade/bna/bnad_ethtool.c | 10 +- .../net/ethernet/emulex/benet/be_ethtool.c | 6 +- trunk/drivers/net/ethernet/freescale/fec.c | 2 +- .../net/ethernet/intel/e1000/e1000_main.c | 10 +- .../drivers/net/ethernet/intel/igb/igb_main.c | 3 +- .../drivers/net/ethernet/intel/igbvf/Makefile | 2 +- .../net/ethernet/intel/igbvf/defines.h | 2 +- .../net/ethernet/intel/igbvf/ethtool.c | 2 +- .../drivers/net/ethernet/intel/igbvf/igbvf.h | 2 +- trunk/drivers/net/ethernet/intel/igbvf/mbx.c | 2 +- trunk/drivers/net/ethernet/intel/igbvf/mbx.h | 2 +- .../drivers/net/ethernet/intel/igbvf/netdev.c | 4 +- trunk/drivers/net/ethernet/intel/igbvf/regs.h | 2 +- trunk/drivers/net/ethernet/intel/igbvf/vf.c | 2 +- trunk/drivers/net/ethernet/intel/igbvf/vf.h | 2 +- .../drivers/net/ethernet/intel/ixgbe/Makefile | 2 +- .../drivers/net/ethernet/intel/ixgbe/ixgbe.h | 2 +- .../net/ethernet/intel/ixgbe/ixgbe_82598.c | 2 +- .../net/ethernet/intel/ixgbe/ixgbe_82599.c | 2 +- .../net/ethernet/intel/ixgbe/ixgbe_common.c | 2 +- .../net/ethernet/intel/ixgbe/ixgbe_common.h | 4 +- .../net/ethernet/intel/ixgbe/ixgbe_dcb.c | 2 +- .../net/ethernet/intel/ixgbe/ixgbe_dcb.h | 2 +- .../ethernet/intel/ixgbe/ixgbe_dcb_82598.c | 2 +- .../ethernet/intel/ixgbe/ixgbe_dcb_82598.h | 2 +- .../ethernet/intel/ixgbe/ixgbe_dcb_82599.c | 2 +- .../ethernet/intel/ixgbe/ixgbe_dcb_82599.h | 2 +- .../net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c | 13 +- .../net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 92 +- .../net/ethernet/intel/ixgbe/ixgbe_fcoe.c | 2 +- .../net/ethernet/intel/ixgbe/ixgbe_fcoe.h | 2 +- .../net/ethernet/intel/ixgbe/ixgbe_main.c | 22 +- .../net/ethernet/intel/ixgbe/ixgbe_mbx.c | 2 +- .../net/ethernet/intel/ixgbe/ixgbe_mbx.h | 2 +- .../net/ethernet/intel/ixgbe/ixgbe_phy.c | 2 +- .../net/ethernet/intel/ixgbe/ixgbe_phy.h | 2 +- .../net/ethernet/intel/ixgbe/ixgbe_sriov.c | 8 +- .../net/ethernet/intel/ixgbe/ixgbe_sriov.h | 2 +- .../net/ethernet/intel/ixgbe/ixgbe_type.h | 2 +- .../net/ethernet/intel/ixgbe/ixgbe_x540.c | 2 +- .../net/ethernet/intel/ixgbevf/Makefile | 2 +- .../net/ethernet/intel/ixgbevf/defines.h | 2 +- .../net/ethernet/intel/ixgbevf/ethtool.c | 2 +- .../net/ethernet/intel/ixgbevf/ixgbevf.h | 2 +- .../net/ethernet/intel/ixgbevf/ixgbevf_main.c | 12 +- .../drivers/net/ethernet/intel/ixgbevf/mbx.c | 2 +- .../drivers/net/ethernet/intel/ixgbevf/mbx.h | 2 +- .../drivers/net/ethernet/intel/ixgbevf/regs.h | 2 +- trunk/drivers/net/ethernet/intel/ixgbevf/vf.c | 21 +- trunk/drivers/net/ethernet/intel/ixgbevf/vf.h | 2 +- trunk/drivers/net/ethernet/marvell/skge.c | 71 +- .../drivers/net/ethernet/mellanox/mlx4/cmd.c | 10 +- .../net/ethernet/mellanox/mlx4/en_netdev.c | 3 +- .../net/ethernet/mellanox/mlx4/en_rx.c | 10 +- .../drivers/net/ethernet/mellanox/mlx4/mcg.c | 16 +- .../net/ethernet/mellanox/mlx4/mlx4_en.h | 3 +- trunk/drivers/net/ethernet/micrel/Kconfig | 1 + trunk/drivers/net/ethernet/renesas/sh_eth.c | 4 +- trunk/drivers/net/ethernet/renesas/sh_eth.h | 3 + trunk/drivers/net/ethernet/ti/davinci_emac.c | 3 +- trunk/drivers/net/ethernet/ti/davinci_mdio.c | 2 +- trunk/drivers/net/ethernet/toshiba/Kconfig | 2 +- trunk/drivers/net/ethernet/via/via-velocity.c | 3 + trunk/drivers/net/hyperv/netvsc_drv.c | 18 +- trunk/drivers/net/hyperv/rndis_filter.c | 32 - trunk/drivers/net/tokenring/Kconfig | 5 +- trunk/drivers/net/wireless/ath/ath9k/hw.c | 7 +- trunk/drivers/net/wireless/ath/ath9k/init.c | 9 +- trunk/drivers/net/wireless/ath/ath9k/rc.c | 2 +- trunk/drivers/net/wireless/ath/ath9k/recv.c | 8 - .../drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 14 +- .../net/wireless/iwlwifi/iwl-commands.h | 1 - .../net/wireless/iwlwifi/iwl-trans-pcie.c | 1 - trunk/drivers/net/wireless/mwifiex/init.c | 3 +- trunk/drivers/net/wireless/mwifiex/main.c | 12 +- .../drivers/net/wireless/mwifiex/sta_ioctl.c | 8 +- trunk/drivers/net/wireless/rt2x00/rt2800lib.c | 8 +- trunk/drivers/net/wireless/zd1211rw/zd_mac.c | 8 - trunk/drivers/pci/xen-pcifront.c | 10 +- trunk/drivers/power/bq27x00_battery.c | 15 +- trunk/drivers/power/charger-manager.c | 4 +- trunk/drivers/power/lp8727_charger.c | 1 - .../regulator/mc13xxx-regulator-core.c | 2 + trunk/drivers/ssb/driver_pcicore.c | 2 +- trunk/drivers/staging/Kconfig | 4 + trunk/drivers/staging/Makefile | 2 + trunk/drivers/staging/android/Kconfig | 5 +- trunk/drivers/staging/android/Makefile | 1 + trunk/drivers/staging/android/android_pmem.h | 93 + trunk/drivers/staging/android/binder.c | 13 +- .../drivers/staging/android/lowmemorykiller.c | 5 +- trunk/drivers/staging/android/pmem.c | 1345 +++++++++++ trunk/drivers/staging/asus_oled/asus_oled.c | 13 +- trunk/drivers/staging/gma500/Kconfig | 33 + trunk/drivers/staging/gma500/Makefile | 52 + trunk/drivers/staging/gma500/TODO | 15 + trunk/drivers/staging/gma500/accel_2d.c | 414 ++++ trunk/drivers/staging/gma500/backlight.c | 49 + trunk/drivers/staging/gma500/cdv_device.c | 350 +++ trunk/drivers/staging/gma500/cdv_device.h | 36 + trunk/drivers/staging/gma500/cdv_intel_crt.c | 326 +++ .../staging/gma500/cdv_intel_display.c | 1508 ++++++++++++ trunk/drivers/staging/gma500/cdv_intel_hdmi.c | 376 +++ trunk/drivers/staging/gma500/cdv_intel_lvds.c | 721 ++++++ trunk/drivers/staging/gma500/displays/hdmi.h | 33 + .../drivers/staging/gma500/displays/pyr_cmd.h | 34 + .../drivers/staging/gma500/displays/pyr_vid.h | 34 + .../drivers/staging/gma500/displays/tmd_cmd.h | 34 + .../drivers/staging/gma500/displays/tmd_vid.h | 34 + .../drivers/staging/gma500/displays/tpo_cmd.h | 35 + .../drivers/staging/gma500/displays/tpo_vid.h | 33 + trunk/drivers/staging/gma500/framebuffer.c | 856 +++++++ trunk/drivers/staging/gma500/framebuffer.h | 48 + trunk/drivers/staging/gma500/gem.c | 292 +++ trunk/drivers/staging/gma500/gem_glue.c | 89 + trunk/drivers/staging/gma500/gem_glue.h | 2 + trunk/drivers/staging/gma500/gtt.c | 553 +++++ trunk/drivers/staging/gma500/gtt.h | 64 + trunk/drivers/staging/gma500/intel_bios.c | 303 +++ trunk/drivers/staging/gma500/intel_bios.h | 430 ++++ trunk/drivers/staging/gma500/intel_i2c.c | 170 ++ trunk/drivers/staging/gma500/intel_opregion.c | 81 + trunk/drivers/staging/gma500/mdfld_device.c | 714 ++++++ trunk/drivers/staging/gma500/mdfld_dsi_dbi.c | 761 ++++++ trunk/drivers/staging/gma500/mdfld_dsi_dbi.h | 173 ++ .../staging/gma500/mdfld_dsi_dbi_dpu.c | 778 +++++++ .../staging/gma500/mdfld_dsi_dbi_dpu.h | 154 ++ trunk/drivers/staging/gma500/mdfld_dsi_dpi.c | 805 +++++++ trunk/drivers/staging/gma500/mdfld_dsi_dpi.h | 78 + .../drivers/staging/gma500/mdfld_dsi_output.c | 1014 ++++++++ .../drivers/staging/gma500/mdfld_dsi_output.h | 138 ++ .../staging/gma500/mdfld_dsi_pkg_sender.c | 1484 ++++++++++++ .../staging/gma500/mdfld_dsi_pkg_sender.h | 184 ++ .../staging/gma500/mdfld_intel_display.c | 1404 +++++++++++ trunk/drivers/staging/gma500/mdfld_msic.h | 31 + trunk/drivers/staging/gma500/mdfld_output.c | 171 ++ trunk/drivers/staging/gma500/mdfld_output.h | 41 + trunk/drivers/staging/gma500/mdfld_pyr_cmd.c | 558 +++++ trunk/drivers/staging/gma500/mdfld_tmd_vid.c | 206 ++ trunk/drivers/staging/gma500/mdfld_tpo_cmd.c | 509 ++++ trunk/drivers/staging/gma500/mdfld_tpo_vid.c | 140 ++ trunk/drivers/staging/gma500/medfield.h | 268 +++ trunk/drivers/staging/gma500/mid_bios.c | 270 +++ trunk/drivers/staging/gma500/mid_bios.h | 21 + trunk/drivers/staging/gma500/mmu.c | 858 +++++++ trunk/drivers/staging/gma500/mrst.h | 252 ++ trunk/drivers/staging/gma500/mrst_crtc.c | 604 +++++ trunk/drivers/staging/gma500/mrst_device.c | 634 +++++ trunk/drivers/staging/gma500/mrst_hdmi.c | 852 +++++++ trunk/drivers/staging/gma500/mrst_hdmi_i2c.c | 328 +++ trunk/drivers/staging/gma500/mrst_lvds.c | 407 ++++ trunk/drivers/staging/gma500/power.c | 318 +++ trunk/drivers/staging/gma500/power.h | 67 + trunk/drivers/staging/gma500/psb_device.c | 321 +++ trunk/drivers/staging/gma500/psb_drm.h | 219 ++ trunk/drivers/staging/gma500/psb_drv.c | 1230 ++++++++++ trunk/drivers/staging/gma500/psb_drv.h | 952 ++++++++ .../staging/gma500/psb_intel_display.c | 1429 ++++++++++++ .../staging/gma500/psb_intel_display.h | 28 + trunk/drivers/staging/gma500/psb_intel_drv.h | 230 ++ trunk/drivers/staging/gma500/psb_intel_lvds.c | 854 +++++++ .../drivers/staging/gma500/psb_intel_modes.c | 77 + trunk/drivers/staging/gma500/psb_intel_reg.h | 1235 ++++++++++ trunk/drivers/staging/gma500/psb_intel_sdvo.c | 1293 +++++++++++ .../staging/gma500/psb_intel_sdvo_regs.h | 338 +++ trunk/drivers/staging/gma500/psb_irq.c | 627 +++++ trunk/drivers/staging/gma500/psb_irq.h | 45 + trunk/drivers/staging/gma500/psb_lid.c | 88 + trunk/drivers/staging/gma500/psb_reg.h | 582 +++++ trunk/drivers/staging/omapdrm/Makefile | 1 - trunk/drivers/staging/omapdrm/omap_crtc.c | 191 +- trunk/drivers/staging/omapdrm/omap_drv.c | 81 +- trunk/drivers/staging/omapdrm/omap_drv.h | 52 +- trunk/drivers/staging/omapdrm/omap_fb.c | 224 +- trunk/drivers/staging/omapdrm/omap_fbdev.c | 59 +- trunk/drivers/staging/omapdrm/omap_gem.c | 23 +- trunk/drivers/staging/omapdrm/omap_plane.c | 344 --- trunk/drivers/staging/omapdrm/omap_priv.h | 12 +- trunk/drivers/staging/pohmelfs/Kconfig | 20 + trunk/drivers/staging/pohmelfs/Makefile | 3 + trunk/drivers/staging/pohmelfs/config.c | 611 +++++ trunk/drivers/staging/pohmelfs/crypto.c | 878 +++++++ trunk/drivers/staging/pohmelfs/dir.c | 1102 +++++++++ trunk/drivers/staging/pohmelfs/inode.c | 2055 +++++++++++++++++ trunk/drivers/staging/pohmelfs/lock.c | 182 ++ trunk/drivers/staging/pohmelfs/mcache.c | 171 ++ trunk/drivers/staging/pohmelfs/net.c | 1209 ++++++++++ trunk/drivers/staging/pohmelfs/netfs.h | 919 ++++++++ trunk/drivers/staging/pohmelfs/path_entry.c | 120 + trunk/drivers/staging/pohmelfs/trans.c | 706 ++++++ trunk/drivers/staging/rtl8712/drv_types.h | 7 - trunk/drivers/staging/rtl8712/hal_init.c | 62 +- trunk/drivers/staging/rtl8712/os_intfs.c | 14 +- trunk/drivers/staging/rtl8712/rtl8712_hal.h | 1 - .../drivers/staging/rtl8712/rtl871x_sta_mgt.c | 1 - trunk/drivers/staging/rtl8712/usb_intf.c | 10 +- .../staging/tidspbridge/core/tiomap3430.c | 2 + .../staging/tidspbridge/rmgr/drv_interface.c | 58 +- trunk/drivers/staging/usbip/stub_main.c | 4 +- trunk/drivers/staging/zcache/zcache-main.c | 25 +- trunk/drivers/tty/serial/8250/8250.c | 2 +- .../drivers/tty/serial/{ => 8250}/m32r_sio.c | 0 .../drivers/tty/serial/{ => 8250}/m32r_sio.h | 0 .../tty/serial/{ => 8250}/m32r_sio_reg.h | 0 trunk/drivers/tty/serial/omap-serial.c | 30 +- trunk/drivers/tty/serial/samsung.c | 3 +- trunk/drivers/usb/gadget/f_loopback.c | 2 +- trunk/drivers/usb/host/Kconfig | 8 +- trunk/drivers/usb/host/ehci-fsl.c | 11 +- trunk/drivers/usb/host/ehci-fsl.h | 1 - trunk/drivers/usb/host/pci-quirks.c | 6 - trunk/drivers/usb/musb/musb_io.h | 3 +- trunk/drivers/usb/otg/Kconfig | 2 +- trunk/drivers/usb/serial/ftdi_sio.c | 1 - trunk/drivers/usb/serial/ftdi_sio_ids.h | 7 - trunk/drivers/usb/serial/option.c | 129 +- trunk/drivers/usb/serial/qcserial.c | 16 +- trunk/drivers/video/omap2/dss/dpi.c | 5 - trunk/drivers/xen/cpu_hotplug.c | 3 +- trunk/drivers/xen/xen-pciback/pci_stub.c | 8 +- trunk/drivers/xen/xen-pciback/xenbus.c | 5 +- .../drivers/xen/xenbus/xenbus_dev_frontend.c | 4 - trunk/fs/bio.c | 10 +- trunk/fs/cifs/connect.c | 9 +- trunk/fs/cifs/dir.c | 2 +- trunk/fs/fs-writeback.c | 16 +- trunk/fs/ioprio.c | 2 +- trunk/fs/xfs/kmem.h | 6 + trunk/fs/xfs/xfs_dquot.c | 103 +- trunk/fs/xfs/xfs_log_recover.c | 2 +- trunk/fs/xfs/xfs_qm.c | 291 ++- trunk/fs/xfs/xfs_qm.h | 14 + trunk/fs/xfs/xfs_qm_stats.c | 4 +- trunk/fs/xfs/xfs_trace.h | 5 +- trunk/include/linux/blkdev.h | 3 + trunk/include/linux/cdrom.h | 3 +- trunk/include/linux/elevator.h | 9 +- trunk/include/linux/hyperv.h | 2 +- trunk/include/linux/iocontext.h | 5 +- trunk/include/linux/mmc/card.h | 4 - trunk/include/linux/mmc/dw_mmc.h | 6 +- trunk/include/linux/mmc/host.h | 20 - trunk/include/linux/proportions.h | 4 - trunk/include/linux/usb/ch9.h | 2 +- trunk/include/net/flow.h | 10 - trunk/include/net/netprio_cgroup.h | 48 +- trunk/include/net/route.h | 4 - trunk/include/net/sch_generic.h | 9 +- trunk/include/net/tcp.h | 14 +- trunk/include/trace/events/writeback.h | 7 +- trunk/kernel/events/core.c | 19 +- trunk/kernel/fork.c | 2 +- trunk/kernel/params.c | 3 +- trunk/kernel/relay.c | 10 +- trunk/lib/kstrtox.c | 18 +- trunk/mm/backing-dev.c | 23 +- trunk/net/caif/caif_socket.c | 10 +- trunk/net/caif/cfmuxl.c | 12 +- trunk/net/core/dev.c | 10 +- trunk/net/core/ethtool.c | 2 - trunk/net/core/netprio_cgroup.c | 15 +- trunk/net/core/sock.c | 7 +- trunk/net/ipv4/Kconfig | 2 +- trunk/net/ipv4/arp.c | 3 +- trunk/net/ipv4/ip_options.c | 2 +- trunk/net/ipv4/sysctl_net_ipv4.c | 6 + trunk/net/ipv4/tcp.c | 23 +- trunk/net/ipv4/tcp_ipv4.c | 5 - trunk/net/ipv4/tcp_timer.c | 5 +- trunk/net/mac80211/rx.c | 2 +- trunk/net/sched/sch_choke.c | 3 +- trunk/net/sched/sch_netem.c | 3 +- trunk/net/sched/sch_sfb.c | 3 +- trunk/net/sched/sch_sfq.c | 5 +- trunk/scripts/mod/file2alias.c | 2 +- trunk/sound/pci/hda/patch_realtek.c | 23 - trunk/sound/pci/hda/patch_sigmatel.c | 4 +- trunk/sound/pci/hda/patch_via.c | 3 - trunk/sound/pci/intel8x0.c | 6 - trunk/sound/soc/sh/fsi.c | 6 +- .../tools/perf/bench/mem-memcpy-x86-64-asm.S | 6 - trunk/tools/perf/util/event.c | 2 +- trunk/tools/perf/util/evsel.c | 1 - 379 files changed, 41881 insertions(+), 2636 deletions(-) create mode 100644 trunk/drivers/staging/android/android_pmem.h create mode 100644 trunk/drivers/staging/android/pmem.c create mode 100644 trunk/drivers/staging/gma500/Kconfig create mode 100644 trunk/drivers/staging/gma500/Makefile create mode 100644 trunk/drivers/staging/gma500/TODO create mode 100644 trunk/drivers/staging/gma500/accel_2d.c create mode 100644 trunk/drivers/staging/gma500/backlight.c create mode 100644 trunk/drivers/staging/gma500/cdv_device.c create mode 100644 trunk/drivers/staging/gma500/cdv_device.h create mode 100644 trunk/drivers/staging/gma500/cdv_intel_crt.c create mode 100644 trunk/drivers/staging/gma500/cdv_intel_display.c create mode 100644 trunk/drivers/staging/gma500/cdv_intel_hdmi.c create mode 100644 trunk/drivers/staging/gma500/cdv_intel_lvds.c create mode 100644 trunk/drivers/staging/gma500/displays/hdmi.h create mode 100644 trunk/drivers/staging/gma500/displays/pyr_cmd.h create mode 100644 trunk/drivers/staging/gma500/displays/pyr_vid.h create mode 100644 trunk/drivers/staging/gma500/displays/tmd_cmd.h create mode 100644 trunk/drivers/staging/gma500/displays/tmd_vid.h create mode 100644 trunk/drivers/staging/gma500/displays/tpo_cmd.h create mode 100644 trunk/drivers/staging/gma500/displays/tpo_vid.h create mode 100644 trunk/drivers/staging/gma500/framebuffer.c create mode 100644 trunk/drivers/staging/gma500/framebuffer.h create mode 100644 trunk/drivers/staging/gma500/gem.c create mode 100644 trunk/drivers/staging/gma500/gem_glue.c create mode 100644 trunk/drivers/staging/gma500/gem_glue.h create mode 100644 trunk/drivers/staging/gma500/gtt.c create mode 100644 trunk/drivers/staging/gma500/gtt.h create mode 100644 trunk/drivers/staging/gma500/intel_bios.c create mode 100644 trunk/drivers/staging/gma500/intel_bios.h create mode 100644 trunk/drivers/staging/gma500/intel_i2c.c create mode 100644 trunk/drivers/staging/gma500/intel_opregion.c create mode 100644 trunk/drivers/staging/gma500/mdfld_device.c create mode 100644 trunk/drivers/staging/gma500/mdfld_dsi_dbi.c create mode 100644 trunk/drivers/staging/gma500/mdfld_dsi_dbi.h create mode 100644 trunk/drivers/staging/gma500/mdfld_dsi_dbi_dpu.c create mode 100644 trunk/drivers/staging/gma500/mdfld_dsi_dbi_dpu.h create mode 100644 trunk/drivers/staging/gma500/mdfld_dsi_dpi.c create mode 100644 trunk/drivers/staging/gma500/mdfld_dsi_dpi.h create mode 100644 trunk/drivers/staging/gma500/mdfld_dsi_output.c create mode 100644 trunk/drivers/staging/gma500/mdfld_dsi_output.h create mode 100644 trunk/drivers/staging/gma500/mdfld_dsi_pkg_sender.c create mode 100644 trunk/drivers/staging/gma500/mdfld_dsi_pkg_sender.h create mode 100644 trunk/drivers/staging/gma500/mdfld_intel_display.c create mode 100644 trunk/drivers/staging/gma500/mdfld_msic.h create mode 100644 trunk/drivers/staging/gma500/mdfld_output.c create mode 100644 trunk/drivers/staging/gma500/mdfld_output.h create mode 100644 trunk/drivers/staging/gma500/mdfld_pyr_cmd.c create mode 100644 trunk/drivers/staging/gma500/mdfld_tmd_vid.c create mode 100644 trunk/drivers/staging/gma500/mdfld_tpo_cmd.c create mode 100644 trunk/drivers/staging/gma500/mdfld_tpo_vid.c create mode 100644 trunk/drivers/staging/gma500/medfield.h create mode 100644 trunk/drivers/staging/gma500/mid_bios.c create mode 100644 trunk/drivers/staging/gma500/mid_bios.h create mode 100644 trunk/drivers/staging/gma500/mmu.c create mode 100644 trunk/drivers/staging/gma500/mrst.h create mode 100644 trunk/drivers/staging/gma500/mrst_crtc.c create mode 100644 trunk/drivers/staging/gma500/mrst_device.c create mode 100644 trunk/drivers/staging/gma500/mrst_hdmi.c create mode 100644 trunk/drivers/staging/gma500/mrst_hdmi_i2c.c create mode 100644 trunk/drivers/staging/gma500/mrst_lvds.c create mode 100644 trunk/drivers/staging/gma500/power.c create mode 100644 trunk/drivers/staging/gma500/power.h create mode 100644 trunk/drivers/staging/gma500/psb_device.c create mode 100644 trunk/drivers/staging/gma500/psb_drm.h create mode 100644 trunk/drivers/staging/gma500/psb_drv.c create mode 100644 trunk/drivers/staging/gma500/psb_drv.h create mode 100644 trunk/drivers/staging/gma500/psb_intel_display.c create mode 100644 trunk/drivers/staging/gma500/psb_intel_display.h create mode 100644 trunk/drivers/staging/gma500/psb_intel_drv.h create mode 100644 trunk/drivers/staging/gma500/psb_intel_lvds.c create mode 100644 trunk/drivers/staging/gma500/psb_intel_modes.c create mode 100644 trunk/drivers/staging/gma500/psb_intel_reg.h create mode 100644 trunk/drivers/staging/gma500/psb_intel_sdvo.c create mode 100644 trunk/drivers/staging/gma500/psb_intel_sdvo_regs.h create mode 100644 trunk/drivers/staging/gma500/psb_irq.c create mode 100644 trunk/drivers/staging/gma500/psb_irq.h create mode 100644 trunk/drivers/staging/gma500/psb_lid.c create mode 100644 trunk/drivers/staging/gma500/psb_reg.h delete mode 100644 trunk/drivers/staging/omapdrm/omap_plane.c create mode 100644 trunk/drivers/staging/pohmelfs/Kconfig create mode 100644 trunk/drivers/staging/pohmelfs/Makefile create mode 100644 trunk/drivers/staging/pohmelfs/config.c create mode 100644 trunk/drivers/staging/pohmelfs/crypto.c create mode 100644 trunk/drivers/staging/pohmelfs/dir.c create mode 100644 trunk/drivers/staging/pohmelfs/inode.c create mode 100644 trunk/drivers/staging/pohmelfs/lock.c create mode 100644 trunk/drivers/staging/pohmelfs/mcache.c create mode 100644 trunk/drivers/staging/pohmelfs/net.c create mode 100644 trunk/drivers/staging/pohmelfs/netfs.h create mode 100644 trunk/drivers/staging/pohmelfs/path_entry.c create mode 100644 trunk/drivers/staging/pohmelfs/trans.c rename trunk/drivers/tty/serial/{ => 8250}/m32r_sio.c (100%) rename trunk/drivers/tty/serial/{ => 8250}/m32r_sio.h (100%) rename trunk/drivers/tty/serial/{ => 8250}/m32r_sio_reg.h (100%) diff --git a/[refs] b/[refs] index 66c97797152a..2133b3625e7f 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: ebf4bcbd5f249afc8f48a7aa32255f8ebbff5876 +refs/heads/master: 532691635475487b8c1d6fc618bd123084904897 diff --git a/trunk/MAINTAINERS b/trunk/MAINTAINERS index 9a648eb8e213..55ca0bea142c 100644 --- a/trunk/MAINTAINERS +++ b/trunk/MAINTAINERS @@ -3318,12 +3318,6 @@ S: Maintained F: net/ieee802154/ F: drivers/ieee802154/ -IIO SUBSYSTEM AND DRIVERS -M: Jonathan Cameron -L: linux-iio@vger.kernel.org -S: Maintained -F: drivers/staging/iio/ - IKANOS/ADI EAGLE ADSL USB DRIVER M: Matthieu Castet M: Stanislaw Gruszka diff --git a/trunk/arch/arm/kernel/traps.c b/trunk/arch/arm/kernel/traps.c index f84dfe67724f..99a572702509 100644 --- a/trunk/arch/arm/kernel/traps.c +++ b/trunk/arch/arm/kernel/traps.c @@ -266,7 +266,6 @@ void die(const char *str, struct pt_regs *regs, int err) { struct thread_info *thread = current_thread_info(); int ret; - enum bug_trap_type bug_type = BUG_TRAP_TYPE_NONE; oops_enter(); @@ -274,9 +273,7 @@ void die(const char *str, struct pt_regs *regs, int err) console_verbose(); bust_spinlocks(1); if (!user_mode(regs)) - bug_type = report_bug(regs->ARM_pc, regs); - if (bug_type != BUG_TRAP_TYPE_NONE) - str = "Oops - BUG"; + report_bug(regs->ARM_pc, regs); ret = __die(str, err, thread, regs); if (regs && kexec_should_crash(thread->task)) diff --git a/trunk/arch/arm/kernel/vmlinux.lds.S b/trunk/arch/arm/kernel/vmlinux.lds.S index 43a31fb06318..1e19691e0406 100644 --- a/trunk/arch/arm/kernel/vmlinux.lds.S +++ b/trunk/arch/arm/kernel/vmlinux.lds.S @@ -10,7 +10,6 @@ #include #define PROC_INFO \ - . = ALIGN(4); \ VMLINUX_SYMBOL(__proc_info_begin) = .; \ *(.proc.info.init) \ VMLINUX_SYMBOL(__proc_info_end) = .; diff --git a/trunk/arch/arm/mach-omap2/board-4430sdp.c b/trunk/arch/arm/mach-omap2/board-4430sdp.c index 4e9071589bfb..21fc87648660 100644 --- a/trunk/arch/arm/mach-omap2/board-4430sdp.c +++ b/trunk/arch/arm/mach-omap2/board-4430sdp.c @@ -814,7 +814,7 @@ static struct omap_dss_board_info sdp4430_dss_data = { .default_device = &sdp4430_lcd_device, }; -static void __init omap_4430sdp_display_init(void) +static void omap_4430sdp_display_init(void) { int r; @@ -851,7 +851,7 @@ static struct omap_board_mux board_mux[] __initdata = { #define board_mux NULL #endif -static void __init omap4_sdp4430_wifi_mux_init(void) +static void omap4_sdp4430_wifi_mux_init(void) { omap_mux_init_gpio(GPIO_WIFI_IRQ, OMAP_PIN_INPUT | OMAP_PIN_OFF_WAKEUPENABLE); @@ -878,17 +878,12 @@ static struct wl12xx_platform_data omap4_sdp4430_wlan_data __initdata = { .board_tcxo_clock = WL12XX_TCXOCLOCK_26, }; -static void __init omap4_sdp4430_wifi_init(void) +static void omap4_sdp4430_wifi_init(void) { - int ret; - omap4_sdp4430_wifi_mux_init(); - ret = wl12xx_set_platform_data(&omap4_sdp4430_wlan_data); - if (ret) - pr_err("Error setting wl12xx data: %d\n", ret); - ret = platform_device_register(&omap_vwlan_device); - if (ret) - pr_err("Error registering wl12xx device: %d\n", ret); + if (wl12xx_set_platform_data(&omap4_sdp4430_wlan_data)) + pr_err("Error setting wl12xx data\n"); + platform_device_register(&omap_vwlan_device); } static void __init omap_4430sdp_init(void) diff --git a/trunk/arch/arm/mach-omap2/board-omap3evm.c b/trunk/arch/arm/mach-omap2/board-omap3evm.c index c775bead1497..003fe34c9343 100644 --- a/trunk/arch/arm/mach-omap2/board-omap3evm.c +++ b/trunk/arch/arm/mach-omap2/board-omap3evm.c @@ -617,21 +617,6 @@ static struct gpio omap3_evm_ehci_gpios[] __initdata = { { OMAP3_EVM_EHCI_SELECT, GPIOF_OUT_INIT_LOW, "select EHCI port" }, }; -static void __init omap3_evm_wl12xx_init(void) -{ -#ifdef CONFIG_WL12XX_PLATFORM_DATA - int ret; - - /* WL12xx WLAN Init */ - ret = wl12xx_set_platform_data(&omap3evm_wlan_data); - if (ret) - pr_err("error setting wl12xx data: %d\n", ret); - ret = platform_device_register(&omap3evm_wlan_regulator); - if (ret) - pr_err("error registering wl12xx device: %d\n", ret); -#endif -} - static void __init omap3_evm_init(void) { omap3_evm_get_revision(); @@ -680,7 +665,13 @@ static void __init omap3_evm_init(void) omap_ads7846_init(1, OMAP3_EVM_TS_GPIO, 310, NULL); omap3evm_init_smsc911x(); omap3_evm_display_init(); - omap3_evm_wl12xx_init(); + +#ifdef CONFIG_WL12XX_PLATFORM_DATA + /* WL12xx WLAN Init */ + if (wl12xx_set_platform_data(&omap3evm_wlan_data)) + pr_err("error setting wl12xx data\n"); + platform_device_register(&omap3evm_wlan_regulator); +#endif } MACHINE_START(OMAP3EVM, "OMAP3 EVM") diff --git a/trunk/arch/arm/mach-omap2/board-omap4panda.c b/trunk/arch/arm/mach-omap2/board-omap4panda.c index 28fc271f7031..b7779c206a90 100644 --- a/trunk/arch/arm/mach-omap2/board-omap4panda.c +++ b/trunk/arch/arm/mach-omap2/board-omap4panda.c @@ -488,15 +488,13 @@ void omap4_panda_display_init(void) static void __init omap4_panda_init(void) { int package = OMAP_PACKAGE_CBS; - int ret; if (omap_rev() == OMAP4430_REV_ES1_0) package = OMAP_PACKAGE_CBL; omap4_mux_init(board_mux, NULL, package); - ret = wl12xx_set_platform_data(&omap_panda_wlan_data); - if (ret) - pr_err("error setting wl12xx data: %d\n", ret); + if (wl12xx_set_platform_data(&omap_panda_wlan_data)) + pr_err("error setting wl12xx data\n"); omap4_panda_i2c_init(); platform_add_devices(panda_devices, ARRAY_SIZE(panda_devices)); diff --git a/trunk/arch/arm/mach-omap2/board-zoom-peripherals.c b/trunk/arch/arm/mach-omap2/board-zoom-peripherals.c index c126461836ac..8d7ce11cfeaf 100644 --- a/trunk/arch/arm/mach-omap2/board-zoom-peripherals.c +++ b/trunk/arch/arm/mach-omap2/board-zoom-peripherals.c @@ -296,10 +296,8 @@ static void enable_board_wakeup_source(void) void __init zoom_peripherals_init(void) { - int ret = wl12xx_set_platform_data(&omap_zoom_wlan_data); - - if (ret) - pr_err("error setting wl12xx data: %d\n", ret); + if (wl12xx_set_platform_data(&omap_zoom_wlan_data)) + pr_err("error setting wl12xx data\n"); omap_i2c_init(); platform_device_register(&omap_vwlan_device); diff --git a/trunk/arch/arm/mach-omap2/hsmmc.c b/trunk/arch/arm/mach-omap2/hsmmc.c index b40c28895298..ad0adb5a1e0e 100644 --- a/trunk/arch/arm/mach-omap2/hsmmc.c +++ b/trunk/arch/arm/mach-omap2/hsmmc.c @@ -293,8 +293,8 @@ static inline void omap_hsmmc_mux(struct omap_mmc_platform_data *mmc_controller, } } -static int omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c, - struct omap_mmc_platform_data *mmc) +static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c, + struct omap_mmc_platform_data *mmc) { char *hc_name; @@ -430,7 +430,7 @@ static int omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c, #define MAX_OMAP_MMC_HWMOD_NAME_LEN 16 -void omap_init_hsmmc(struct omap2_hsmmc_info *hsmmcinfo, int ctrl_nr) +void __init omap_init_hsmmc(struct omap2_hsmmc_info *hsmmcinfo, int ctrl_nr) { struct omap_hwmod *oh; struct platform_device *pdev; @@ -487,7 +487,7 @@ void omap_init_hsmmc(struct omap2_hsmmc_info *hsmmcinfo, int ctrl_nr) kfree(mmc_data); } -void omap2_hsmmc_init(struct omap2_hsmmc_info *controllers) +void __init omap2_hsmmc_init(struct omap2_hsmmc_info *controllers) { u32 reg; diff --git a/trunk/arch/arm/mach-omap2/mux.c b/trunk/arch/arm/mach-omap2/mux.c index fb8bc9fa43b1..e1cc75d1a57a 100644 --- a/trunk/arch/arm/mach-omap2/mux.c +++ b/trunk/arch/arm/mach-omap2/mux.c @@ -100,8 +100,8 @@ void omap_mux_write_array(struct omap_mux_partition *partition, static char *omap_mux_options; -static int _omap_mux_init_gpio(struct omap_mux_partition *partition, - int gpio, int val) +static int __init _omap_mux_init_gpio(struct omap_mux_partition *partition, + int gpio, int val) { struct omap_mux_entry *e; struct omap_mux *gpio_mux = NULL; @@ -145,7 +145,7 @@ static int _omap_mux_init_gpio(struct omap_mux_partition *partition, return 0; } -int omap_mux_init_gpio(int gpio, int val) +int __init omap_mux_init_gpio(int gpio, int val) { struct omap_mux_partition *partition; int ret; @@ -159,9 +159,9 @@ int omap_mux_init_gpio(int gpio, int val) return -ENODEV; } -static int _omap_mux_get_by_name(struct omap_mux_partition *partition, - const char *muxname, - struct omap_mux **found_mux) +static int __init _omap_mux_get_by_name(struct omap_mux_partition *partition, + const char *muxname, + struct omap_mux **found_mux) { struct omap_mux *mux = NULL; struct omap_mux_entry *e; @@ -240,7 +240,7 @@ omap_mux_get_by_name(const char *muxname, return -ENODEV; } -int omap_mux_init_signal(const char *muxname, int val) +int __init omap_mux_init_signal(const char *muxname, int val) { struct omap_mux_partition *partition = NULL; struct omap_mux *mux = NULL; @@ -1094,8 +1094,8 @@ static void omap_mux_init_package(struct omap_mux *superset, omap_mux_package_init_balls(package_balls, superset); } -static void __init omap_mux_init_signals(struct omap_mux_partition *partition, - struct omap_board_mux *board_mux) +static void omap_mux_init_signals(struct omap_mux_partition *partition, + struct omap_board_mux *board_mux) { omap_mux_set_cmdline_signals(); omap_mux_write_array(partition, board_mux); @@ -1109,8 +1109,8 @@ static void omap_mux_init_package(struct omap_mux *superset, { } -static void __init omap_mux_init_signals(struct omap_mux_partition *partition, - struct omap_board_mux *board_mux) +static void omap_mux_init_signals(struct omap_mux_partition *partition, + struct omap_board_mux *board_mux) { } diff --git a/trunk/arch/arm/mach-omap2/omap-headsmp.S b/trunk/arch/arm/mach-omap2/omap-headsmp.S index 503ac777a2ba..b13ef7ef5ef4 100644 --- a/trunk/arch/arm/mach-omap2/omap-headsmp.S +++ b/trunk/arch/arm/mach-omap2/omap-headsmp.S @@ -18,7 +18,6 @@ #include #include - __CPUINIT /* * OMAP4 specific entry point for secondary CPU to jump from ROM * code. This routine also provides a holding flag into which diff --git a/trunk/arch/arm/mach-omap2/omap_hwmod.c b/trunk/arch/arm/mach-omap2/omap_hwmod.c index eba6cd3816f5..5192cabb40ed 100644 --- a/trunk/arch/arm/mach-omap2/omap_hwmod.c +++ b/trunk/arch/arm/mach-omap2/omap_hwmod.c @@ -1517,8 +1517,8 @@ static int _enable(struct omap_hwmod *oh) if (oh->_state != _HWMOD_STATE_INITIALIZED && oh->_state != _HWMOD_STATE_IDLE && oh->_state != _HWMOD_STATE_DISABLED) { - WARN(1, "omap_hwmod: %s: enabled state can only be entered from initialized, idle, or disabled state\n", - oh->name); + WARN(1, "omap_hwmod: %s: enabled state can only be entered " + "from initialized, idle, or disabled state\n", oh->name); return -EINVAL; } @@ -1600,8 +1600,8 @@ static int _idle(struct omap_hwmod *oh) pr_debug("omap_hwmod: %s: idling\n", oh->name); if (oh->_state != _HWMOD_STATE_ENABLED) { - WARN(1, "omap_hwmod: %s: idle state can only be entered from enabled state\n", - oh->name); + WARN(1, "omap_hwmod: %s: idle state can only be entered from " + "enabled state\n", oh->name); return -EINVAL; } @@ -1682,8 +1682,8 @@ static int _shutdown(struct omap_hwmod *oh) if (oh->_state != _HWMOD_STATE_IDLE && oh->_state != _HWMOD_STATE_ENABLED) { - WARN(1, "omap_hwmod: %s: disabled state can only be entered from idle, or enabled state\n", - oh->name); + WARN(1, "omap_hwmod: %s: disabled state can only be entered " + "from idle, or enabled state\n", oh->name); return -EINVAL; } @@ -2240,8 +2240,8 @@ void omap_hwmod_ocp_barrier(struct omap_hwmod *oh) BUG_ON(!oh); if (!oh->class->sysc || !oh->class->sysc->sysc_flags) { - WARN(1, "omap_device: %s: OCP barrier impossible due to device configuration\n", - oh->name); + WARN(1, "omap_device: %s: OCP barrier impossible due to " + "device configuration\n", oh->name); return; } diff --git a/trunk/arch/arm/mach-omap2/prm44xx.c b/trunk/arch/arm/mach-omap2/prm44xx.c index a1d6154dc120..33dd655e6aab 100644 --- a/trunk/arch/arm/mach-omap2/prm44xx.c +++ b/trunk/arch/arm/mach-omap2/prm44xx.c @@ -19,7 +19,6 @@ #include "common.h" #include -#include #include #include "vp.h" diff --git a/trunk/arch/arm/mach-omap2/serial.c b/trunk/arch/arm/mach-omap2/serial.c index f590afc1f673..247d89478f24 100644 --- a/trunk/arch/arm/mach-omap2/serial.c +++ b/trunk/arch/arm/mach-omap2/serial.c @@ -107,18 +107,18 @@ static void omap_uart_set_noidle(struct platform_device *pdev) omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_NO); } -static void omap_uart_set_smartidle(struct platform_device *pdev) +static void omap_uart_set_forceidle(struct platform_device *pdev) { struct omap_device *od = to_omap_device(pdev); - omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_SMART); + omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_FORCE); } #else static void omap_uart_enable_wakeup(struct platform_device *pdev, bool enable) {} static void omap_uart_set_noidle(struct platform_device *pdev) {} -static void omap_uart_set_smartidle(struct platform_device *pdev) {} +static void omap_uart_set_forceidle(struct platform_device *pdev) {} #endif /* CONFIG_PM */ #ifdef CONFIG_OMAP_MUX @@ -349,7 +349,7 @@ void __init omap_serial_init_port(struct omap_board_data *bdata, omap_up.uartclk = OMAP24XX_BASE_BAUD * 16; omap_up.flags = UPF_BOOT_AUTOCONF; omap_up.get_context_loss_count = omap_pm_get_dev_context_loss_count; - omap_up.set_forceidle = omap_uart_set_smartidle; + omap_up.set_forceidle = omap_uart_set_forceidle; omap_up.set_noidle = omap_uart_set_noidle; omap_up.enable_wakeup = omap_uart_enable_wakeup; omap_up.dma_rx_buf_size = info->dma_rx_buf_size; diff --git a/trunk/arch/arm/mach-omap2/vc.c b/trunk/arch/arm/mach-omap2/vc.c index 175b7d86d86a..031d116fbf10 100644 --- a/trunk/arch/arm/mach-omap2/vc.c +++ b/trunk/arch/arm/mach-omap2/vc.c @@ -247,7 +247,7 @@ static void __init omap4_vc_init_channel(struct voltagedomain *voltdm) * omap_vc_i2c_init - initialize I2C interface to PMIC * @voltdm: voltage domain containing VC data * - * Use PMIC supplied settings for I2C high-speed mode and + * Use PMIC supplied seetings for I2C high-speed mode and * master code (if set) and program the VC I2C configuration * register. * @@ -265,8 +265,8 @@ static void __init omap_vc_i2c_init(struct voltagedomain *voltdm) if (initialized) { if (voltdm->pmic->i2c_high_speed != i2c_high_speed) - pr_warn("%s: I2C config for vdd_%s does not match other channels (%u).", - __func__, voltdm->name, i2c_high_speed); + pr_warn("%s: I2C config for all channels must match.", + __func__); return; } @@ -292,7 +292,9 @@ void __init omap_vc_init_channel(struct voltagedomain *voltdm) u32 val; if (!voltdm->pmic || !voltdm->pmic->uv_to_vsel) { - pr_err("%s: No PMIC info for vdd_%s\n", __func__, voltdm->name); + pr_err("%s: PMIC info requried to configure vc for" + "vdd_%s not populated.Hence cannot initialize vc\n", + __func__, voltdm->name); return; } diff --git a/trunk/arch/arm/mach-omap2/vp.c b/trunk/arch/arm/mach-omap2/vp.c index 0df88820978d..807391d84a9d 100644 --- a/trunk/arch/arm/mach-omap2/vp.c +++ b/trunk/arch/arm/mach-omap2/vp.c @@ -41,11 +41,6 @@ void __init omap_vp_init(struct voltagedomain *voltdm) u32 val, sys_clk_rate, timeout, waittime; u32 vddmin, vddmax, vstepmin, vstepmax; - if (!voltdm->pmic || !voltdm->pmic->uv_to_vsel) { - pr_err("%s: No PMIC info for vdd_%s\n", __func__, voltdm->name); - return; - } - if (!voltdm->read || !voltdm->write) { pr_err("%s: No read/write API for accessing vdd_%s regs\n", __func__, voltdm->name); diff --git a/trunk/arch/arm/mm/cache-v7.S b/trunk/arch/arm/mm/cache-v7.S index 7a24d39661f0..07c4bc8ea0a4 100644 --- a/trunk/arch/arm/mm/cache-v7.S +++ b/trunk/arch/arm/mm/cache-v7.S @@ -54,15 +54,9 @@ loop1: and r1, r1, #7 @ mask of the bits for current cache only cmp r1, #2 @ see what cache we have at this level blt skip @ skip if no cache, or just i-cache -#ifdef CONFIG_PREEMPT - save_and_disable_irqs r9 @ make cssr&csidr read atomic -#endif mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr isb @ isb to sych the new cssr&csidr mrc p15, 1, r1, c0, c0, 0 @ read the new csidr -#ifdef CONFIG_PREEMPT - restore_irqs_notrace r9 -#endif and r2, r1, #7 @ extract the length of the cache lines add r2, r2, #4 @ add 4 (line length offset) ldr r4, =0x3ff diff --git a/trunk/arch/powerpc/include/asm/ppc-pci.h b/trunk/arch/powerpc/include/asm/ppc-pci.h index 6d422979ebaf..43268f15004e 100644 --- a/trunk/arch/powerpc/include/asm/ppc-pci.h +++ b/trunk/arch/powerpc/include/asm/ppc-pci.h @@ -142,11 +142,6 @@ static inline const char *eeh_pci_name(struct pci_dev *pdev) return pdev ? pci_name(pdev) : ""; } -static inline const char *eeh_driver_name(struct pci_dev *pdev) -{ - return (pdev && pdev->driver) ? pdev->driver->name : ""; -} - #endif /* CONFIG_EEH */ #else /* CONFIG_PCI */ diff --git a/trunk/arch/powerpc/include/asm/ptrace.h b/trunk/arch/powerpc/include/asm/ptrace.h index 84cc7840cd18..78a205162fd7 100644 --- a/trunk/arch/powerpc/include/asm/ptrace.h +++ b/trunk/arch/powerpc/include/asm/ptrace.h @@ -83,18 +83,8 @@ struct pt_regs { #ifndef __ASSEMBLY__ -#define GET_IP(regs) ((regs)->nip) -#define GET_USP(regs) ((regs)->gpr[1]) -#define GET_FP(regs) (0) -#define SET_FP(regs, val) - -#ifdef CONFIG_SMP -extern unsigned long profile_pc(struct pt_regs *regs); -#define profile_pc profile_pc -#endif - -#include - +#define instruction_pointer(regs) ((regs)->nip) +#define user_stack_pointer(regs) ((regs)->gpr[1]) #define kernel_stack_pointer(regs) ((regs)->gpr[1]) static inline int is_syscall_success(struct pt_regs *regs) { @@ -109,6 +99,12 @@ static inline long regs_return_value(struct pt_regs *regs) return -regs->gpr[3]; } +#ifdef CONFIG_SMP +extern unsigned long profile_pc(struct pt_regs *regs); +#else +#define profile_pc(regs) instruction_pointer(regs) +#endif + #ifdef __powerpc64__ #define user_mode(regs) ((((regs)->msr) >> MSR_PR_LG) & 0x1) #else diff --git a/trunk/arch/powerpc/kernel/irq.c b/trunk/arch/powerpc/kernel/irq.c index 01e2877e8e04..701d4aceb4f4 100644 --- a/trunk/arch/powerpc/kernel/irq.c +++ b/trunk/arch/powerpc/kernel/irq.c @@ -118,14 +118,10 @@ static inline notrace void set_soft_enabled(unsigned long enable) static inline notrace void decrementer_check_overflow(void) { u64 now = get_tb_or_rtc(); - u64 *next_tb; - - preempt_disable(); - next_tb = &__get_cpu_var(decrementers_next_tb); + u64 *next_tb = &__get_cpu_var(decrementers_next_tb); if (now >= *next_tb) set_dec(1); - preempt_enable(); } notrace void arch_local_irq_restore(unsigned long en) diff --git a/trunk/arch/powerpc/kernel/rtas.c b/trunk/arch/powerpc/kernel/rtas.c index 9f843cdfee9e..517b1d8f455b 100644 --- a/trunk/arch/powerpc/kernel/rtas.c +++ b/trunk/arch/powerpc/kernel/rtas.c @@ -716,6 +716,7 @@ static int __rtas_suspend_last_cpu(struct rtas_suspend_me_data *data, int wake_w int cpu; slb_set_size(SLB_MIN_SIZE); + stop_topology_update(); printk(KERN_DEBUG "calling ibm,suspend-me on cpu %i\n", smp_processor_id()); while (rc == H_MULTI_THREADS_ACTIVE && !atomic_read(&data->done) && @@ -731,6 +732,7 @@ static int __rtas_suspend_last_cpu(struct rtas_suspend_me_data *data, int wake_w rc = atomic_read(&data->error); atomic_set(&data->error, rc); + start_topology_update(); pSeries_coalesce_init(); if (wake_when_done) { @@ -844,7 +846,6 @@ int rtas_ibm_suspend_me(struct rtas_args *args) atomic_set(&data.error, 0); data.token = rtas_token("ibm,suspend-me"); data.complete = &done; - stop_topology_update(); /* Call function on all CPUs. One of us will make the * rtas call @@ -857,8 +858,6 @@ int rtas_ibm_suspend_me(struct rtas_args *args) if (atomic_read(&data.error) != 0) printk(KERN_ERR "Error doing global join\n"); - start_topology_update(); - return atomic_read(&data.error); } #else /* CONFIG_PPC_PSERIES */ diff --git a/trunk/arch/powerpc/platforms/powernv/pci.c b/trunk/arch/powerpc/platforms/powernv/pci.c index f92b9ef7340e..a70bc1e385eb 100644 --- a/trunk/arch/powerpc/platforms/powernv/pci.c +++ b/trunk/arch/powerpc/platforms/powernv/pci.c @@ -52,38 +52,32 @@ static int pnv_msi_check_device(struct pci_dev* pdev, int nvec, int type) static unsigned int pnv_get_one_msi(struct pnv_phb *phb) { - unsigned long flags; - unsigned int id, rc; - - spin_lock_irqsave(&phb->lock, flags); + unsigned int id; + spin_lock(&phb->lock); id = find_next_zero_bit(phb->msi_map, phb->msi_count, phb->msi_next); if (id >= phb->msi_count && phb->msi_next) id = find_next_zero_bit(phb->msi_map, phb->msi_count, 0); if (id >= phb->msi_count) { - rc = 0; - goto out; + spin_unlock(&phb->lock); + return 0; } __set_bit(id, phb->msi_map); - rc = id + phb->msi_base; -out: - spin_unlock_irqrestore(&phb->lock, flags); - return rc; + spin_unlock(&phb->lock); + return id + phb->msi_base; } static void pnv_put_msi(struct pnv_phb *phb, unsigned int hwirq) { - unsigned long flags; unsigned int id; if (WARN_ON(hwirq < phb->msi_base || hwirq >= (phb->msi_base + phb->msi_count))) return; id = hwirq - phb->msi_base; - - spin_lock_irqsave(&phb->lock, flags); + spin_lock(&phb->lock); __clear_bit(id, phb->msi_map); - spin_unlock_irqrestore(&phb->lock, flags); + spin_unlock(&phb->lock); } static int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) diff --git a/trunk/arch/powerpc/platforms/pseries/eeh.c b/trunk/arch/powerpc/platforms/pseries/eeh.c index c0b40af4ce4f..565869022e3d 100644 --- a/trunk/arch/powerpc/platforms/pseries/eeh.c +++ b/trunk/arch/powerpc/platforms/pseries/eeh.c @@ -551,9 +551,9 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) printk (KERN_ERR "EEH: %d reads ignored for recovering device at " "location=%s driver=%s pci addr=%s\n", pdn->eeh_check_count, location, - eeh_driver_name(dev), eeh_pci_name(dev)); + dev->driver->name, eeh_pci_name(dev)); printk (KERN_ERR "EEH: Might be infinite loop in %s driver\n", - eeh_driver_name(dev)); + dev->driver->name); dump_stack(); } goto dn_unlock; diff --git a/trunk/arch/powerpc/platforms/pseries/suspend.c b/trunk/arch/powerpc/platforms/pseries/suspend.c index 47226e04126d..b84a8b2238dd 100644 --- a/trunk/arch/powerpc/platforms/pseries/suspend.c +++ b/trunk/arch/powerpc/platforms/pseries/suspend.c @@ -24,7 +24,6 @@ #include #include #include -#include static u64 stream_id; static struct device suspend_dev; @@ -139,11 +138,8 @@ static ssize_t store_hibernate(struct device *dev, ssleep(1); } while (rc == -EAGAIN); - if (!rc) { - stop_topology_update(); + if (!rc) rc = pm_suspend(PM_SUSPEND_MEM); - start_topology_update(); - } stream_id = 0; diff --git a/trunk/arch/powerpc/platforms/wsp/ics.c b/trunk/arch/powerpc/platforms/wsp/ics.c index 97fe82ee8633..576874392543 100644 --- a/trunk/arch/powerpc/platforms/wsp/ics.c +++ b/trunk/arch/powerpc/platforms/wsp/ics.c @@ -346,7 +346,7 @@ static int wsp_chip_set_affinity(struct irq_data *d, * For the moment only implement delivery to all cpus or one cpu. * Get current irq_server for the given irq */ - ret = cache_hwirq_map(ics, hw_irq, cpumask); + ret = cache_hwirq_map(ics, d->irq, cpumask); if (ret == -1) { char cpulist[128]; cpumask_scnprintf(cpulist, sizeof(cpulist), cpumask); diff --git a/trunk/arch/powerpc/platforms/wsp/wsp_pci.c b/trunk/arch/powerpc/platforms/wsp/wsp_pci.c index d24b3acf858e..e0262cd0e2d3 100644 --- a/trunk/arch/powerpc/platforms/wsp/wsp_pci.c +++ b/trunk/arch/powerpc/platforms/wsp/wsp_pci.c @@ -468,15 +468,15 @@ static void __init wsp_pcie_configure_hw(struct pci_controller *hose) #define DUMP_REG(x) \ pr_debug("%-30s : 0x%016llx\n", #x, in_be64(hose->cfg_data + x)) - /* - * Some WSP variants has a bogus class code by default in the PCI-E - * root complex's built-in P2P bridge - */ +#ifdef CONFIG_WSP_DD1_WORKAROUND_BAD_PCIE_CLASS + /* WSP DD1 has a bogus class code by default in the PCI-E + * root complex's built-in P2P bridge */ val = in_be64(hose->cfg_data + PCIE_REG_SYS_CFG1); pr_debug("PCI-E SYS_CFG1 : 0x%llx\n", val); out_be64(hose->cfg_data + PCIE_REG_SYS_CFG1, (val & ~PCIE_REG_SYS_CFG1_CLASS_CODE) | (PCI_CLASS_BRIDGE_PCI << 8)); pr_debug("PCI-E SYS_CFG1 : 0x%llx\n", in_be64(hose->cfg_data + PCIE_REG_SYS_CFG1)); +#endif /* CONFIG_WSP_DD1_WORKAROUND_BAD_PCIE_CLASS */ #ifdef CONFIG_WSP_DD1_WORKAROUND_DD1_TCE_BUGS /* XXX Disable TCE caching, it doesn't work on DD1 */ diff --git a/trunk/arch/x86/include/asm/i387.h b/trunk/arch/x86/include/asm/i387.h index a29571821b99..6919e936345b 100644 --- a/trunk/arch/x86/include/asm/i387.h +++ b/trunk/arch/x86/include/asm/i387.h @@ -29,7 +29,7 @@ extern unsigned int sig_xstate_size; extern void fpu_init(void); extern void mxcsr_feature_mask_init(void); extern int init_fpu(struct task_struct *child); -extern void math_state_restore(void); +extern asmlinkage void math_state_restore(void); extern void __math_state_restore(void); extern int dump_fpu(struct pt_regs *, struct user_i387_struct *); @@ -307,54 +307,9 @@ static inline void __clear_fpu(struct task_struct *tsk) } } -/* - * Were we in an interrupt that interrupted kernel mode? - * - * We can do a kernel_fpu_begin/end() pair *ONLY* if that - * pair does nothing at all: TS_USEDFPU must be clear (so - * that we don't try to save the FPU state), and TS must - * be set (so that the clts/stts pair does nothing that is - * visible in the interrupted kernel thread). - */ -static inline bool interrupted_kernel_fpu_idle(void) -{ - return !(current_thread_info()->status & TS_USEDFPU) && - (read_cr0() & X86_CR0_TS); -} - -/* - * Were we in user mode (or vm86 mode) when we were - * interrupted? - * - * Doing kernel_fpu_begin/end() is ok if we are running - * in an interrupt context from user mode - we'll just - * save the FPU state as required. - */ -static inline bool interrupted_user_mode(void) -{ - struct pt_regs *regs = get_irq_regs(); - return regs && user_mode_vm(regs); -} - -/* - * Can we use the FPU in kernel mode with the - * whole "kernel_fpu_begin/end()" sequence? - * - * It's always ok in process context (ie "not interrupt") - * but it is sometimes ok even from an irq. - */ -static inline bool irq_fpu_usable(void) -{ - return !in_interrupt() || - interrupted_user_mode() || - interrupted_kernel_fpu_idle(); -} - static inline void kernel_fpu_begin(void) { struct thread_info *me = current_thread_info(); - - WARN_ON_ONCE(!irq_fpu_usable()); preempt_disable(); if (me->status & TS_USEDFPU) __save_init_fpu(me->task); @@ -368,6 +323,14 @@ static inline void kernel_fpu_end(void) preempt_enable(); } +static inline bool irq_fpu_usable(void) +{ + struct pt_regs *regs; + + return !in_interrupt() || !(regs = get_irq_regs()) || \ + user_mode(regs) || (read_cr0() & X86_CR0_TS); +} + /* * Some instructions like VIA's padlock instructions generate a spurious * DNA fault but don't modify SSE registers. And these instructions @@ -404,7 +367,6 @@ static inline void irq_ts_restore(int TS_state) */ static inline void save_init_fpu(struct task_struct *tsk) { - WARN_ON_ONCE(task_thread_info(tsk)->status & TS_USEDFPU); preempt_disable(); __save_init_fpu(tsk); stts(); diff --git a/trunk/arch/x86/kernel/cpu/perf_event_intel_ds.c b/trunk/arch/x86/kernel/cpu/perf_event_intel_ds.c index d6bd49faa40c..73da6b64f5b7 100644 --- a/trunk/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/trunk/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -439,6 +439,7 @@ void intel_pmu_pebs_enable(struct perf_event *event) hwc->config &= ~ARCH_PERFMON_EVENTSEL_INT; cpuc->pebs_enabled |= 1ULL << hwc->idx; + WARN_ON_ONCE(cpuc->enabled); if (x86_pmu.intel_cap.pebs_trap && event->attr.precise_ip > 1) intel_pmu_lbr_enable(event); diff --git a/trunk/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/trunk/arch/x86/kernel/cpu/perf_event_intel_lbr.c index 47a7e63bfe54..3fab3de3ce96 100644 --- a/trunk/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ b/trunk/arch/x86/kernel/cpu/perf_event_intel_lbr.c @@ -72,6 +72,8 @@ void intel_pmu_lbr_enable(struct perf_event *event) if (!x86_pmu.lbr_nr) return; + WARN_ON_ONCE(cpuc->enabled); + /* * Reset the LBR stack if we changed task context to * avoid data leaks. diff --git a/trunk/arch/x86/kernel/traps.c b/trunk/arch/x86/kernel/traps.c index 8ba27dbc107a..482ec3af2067 100644 --- a/trunk/arch/x86/kernel/traps.c +++ b/trunk/arch/x86/kernel/traps.c @@ -599,10 +599,10 @@ void __math_state_restore(void) * Careful.. There are problems with IBM-designed IRQ13 behaviour. * Don't touch unless you *really* know how it works. * - * Must be called with kernel preemption disabled (eg with local - * local interrupts as in the case of do_device_not_available). + * Must be called with kernel preemption disabled (in this case, + * local interrupts are disabled at the call-site in entry.S). */ -void math_state_restore(void) +asmlinkage void math_state_restore(void) { struct thread_info *thread = current_thread_info(); struct task_struct *tsk = thread->task; @@ -631,7 +631,6 @@ EXPORT_SYMBOL_GPL(math_state_restore); dotraplinkage void __kprobes do_device_not_available(struct pt_regs *regs, long error_code) { - WARN_ON_ONCE(!user_mode_vm(regs)); #ifdef CONFIG_MATH_EMULATION if (read_cr0() & X86_CR0_EM) { struct math_emu_info info = { }; diff --git a/trunk/arch/x86/pci/xen.c b/trunk/arch/x86/pci/xen.c index d99346ea8fdb..492ade8c978e 100644 --- a/trunk/arch/x86/pci/xen.c +++ b/trunk/arch/x86/pci/xen.c @@ -374,7 +374,7 @@ int __init pci_xen_init(void) int __init pci_xen_hvm_init(void) { - if (!xen_have_vector_callback || !xen_feature(XENFEAT_hvm_pirqs)) + if (!xen_feature(XENFEAT_hvm_pirqs)) return 0; #ifdef CONFIG_ACPI diff --git a/trunk/arch/x86/xen/smp.c b/trunk/arch/x86/xen/smp.c index 501d4e0244ba..041d4fe9dfe4 100644 --- a/trunk/arch/x86/xen/smp.c +++ b/trunk/arch/x86/xen/smp.c @@ -409,13 +409,6 @@ static void __cpuinit xen_play_dead(void) /* used only with HOTPLUG_CPU */ play_dead_common(); HYPERVISOR_vcpu_op(VCPUOP_down, smp_processor_id(), NULL); cpu_bringup(); - /* - * Balance out the preempt calls - as we are running in cpu_idle - * loop which has been called at bootup from cpu_bringup_and_idle. - * The cpucpu_bringup_and_idle called cpu_bringup which made a - * preempt_disable() So this preempt_enable will balance it out. - */ - preempt_enable(); } #else /* !CONFIG_HOTPLUG_CPU */ diff --git a/trunk/block/blk-cgroup.c b/trunk/block/blk-cgroup.c index 75642a352a8f..fa8f26309444 100644 --- a/trunk/block/blk-cgroup.c +++ b/trunk/block/blk-cgroup.c @@ -1659,7 +1659,7 @@ static void blkiocg_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, ioc = get_task_io_context(task, GFP_ATOMIC, NUMA_NO_NODE); if (ioc) { ioc_cgroup_changed(ioc); - put_io_context(ioc); + put_io_context(ioc, NULL); } } } diff --git a/trunk/block/blk-core.c b/trunk/block/blk-core.c index 3a78b00edd71..e6c05a97ee2b 100644 --- a/trunk/block/blk-core.c +++ b/trunk/block/blk-core.c @@ -642,7 +642,7 @@ static inline void blk_free_request(struct request_queue *q, struct request *rq) if (rq->cmd_flags & REQ_ELVPRIV) { elv_put_request(q, rq); if (rq->elv.icq) - put_io_context(rq->elv.icq->ioc); + put_io_context(rq->elv.icq->ioc, q); } mempool_free(rq, q->rq.rq_pool); @@ -872,15 +872,13 @@ static struct request *get_request(struct request_queue *q, int rw_flags, spin_unlock_irq(q->queue_lock); /* create icq if missing */ - if ((rw_flags & REQ_ELVPRIV) && unlikely(et->icq_cache && !icq)) { + if (unlikely(et->icq_cache && !icq)) icq = ioc_create_icq(q, gfp_mask); - if (!icq) - goto fail_icq; - } - rq = blk_alloc_request(q, icq, rw_flags, gfp_mask); + /* rqs are guaranteed to have icq on elv_set_request() if requested */ + if (likely(!et->icq_cache || icq)) + rq = blk_alloc_request(q, icq, rw_flags, gfp_mask); -fail_icq: if (unlikely(!rq)) { /* * Allocation failed presumably due to memory. Undo anything @@ -1212,6 +1210,7 @@ static bool bio_attempt_back_merge(struct request_queue *q, struct request *req, req->ioprio = ioprio_best(req->ioprio, bio_prio(bio)); drive_stat_acct(req, 0); + elv_bio_merged(q, req, bio); return true; } @@ -1242,6 +1241,7 @@ static bool bio_attempt_front_merge(struct request_queue *q, req->ioprio = ioprio_best(req->ioprio, bio_prio(bio)); drive_stat_acct(req, 0); + elv_bio_merged(q, req, bio); return true; } @@ -1255,12 +1255,13 @@ static bool bio_attempt_front_merge(struct request_queue *q, * on %current's plugged list. Returns %true if merge was successful, * otherwise %false. * - * Plugging coalesces IOs from the same issuer for the same purpose without - * going through @q->queue_lock. As such it's more of an issuing mechanism - * than scheduling, and the request, while may have elvpriv data, is not - * added on the elevator at this point. In addition, we don't have - * reliable access to the elevator outside queue lock. Only check basic - * merging parameters without querying the elevator. + * This function is called without @q->queue_lock; however, elevator is + * accessed iff there already are requests on the plugged list which in + * turn guarantees validity of the elevator. + * + * Note that, on successful merge, elevator operation + * elevator_bio_merged_fn() will be called without queue lock. Elevator + * must be ready for this. */ static bool attempt_plug_merge(struct request_queue *q, struct bio *bio, unsigned int *request_count) @@ -1279,10 +1280,10 @@ static bool attempt_plug_merge(struct request_queue *q, struct bio *bio, (*request_count)++; - if (rq->q != q || !blk_rq_merge_ok(rq, bio)) + if (rq->q != q) continue; - el_ret = blk_try_merge(rq, bio); + el_ret = elv_try_merge(rq, bio); if (el_ret == ELEVATOR_BACK_MERGE) { ret = bio_attempt_back_merge(q, rq, bio); if (ret) @@ -1344,14 +1345,12 @@ void blk_queue_bio(struct request_queue *q, struct bio *bio) el_ret = elv_merge(q, &req, bio); if (el_ret == ELEVATOR_BACK_MERGE) { if (bio_attempt_back_merge(q, req, bio)) { - elv_bio_merged(q, req, bio); if (!attempt_back_merge(q, req)) elv_merged_request(q, req, el_ret); goto out_unlock; } } else if (el_ret == ELEVATOR_FRONT_MERGE) { if (bio_attempt_front_merge(q, req, bio)) { - elv_bio_merged(q, req, bio); if (!attempt_front_merge(q, req)) elv_merged_request(q, req, el_ret); goto out_unlock; diff --git a/trunk/block/blk-ioc.c b/trunk/block/blk-ioc.c index 8b782a63c297..27a06e00eaec 100644 --- a/trunk/block/blk-ioc.c +++ b/trunk/block/blk-ioc.c @@ -29,6 +29,21 @@ void get_io_context(struct io_context *ioc) } EXPORT_SYMBOL(get_io_context); +/* + * Releasing ioc may nest into another put_io_context() leading to nested + * fast path release. As the ioc's can't be the same, this is okay but + * makes lockdep whine. Keep track of nesting and use it as subclass. + */ +#ifdef CONFIG_LOCKDEP +#define ioc_release_depth(q) ((q) ? (q)->ioc_release_depth : 0) +#define ioc_release_depth_inc(q) (q)->ioc_release_depth++ +#define ioc_release_depth_dec(q) (q)->ioc_release_depth-- +#else +#define ioc_release_depth(q) 0 +#define ioc_release_depth_inc(q) do { } while (0) +#define ioc_release_depth_dec(q) do { } while (0) +#endif + static void icq_free_icq_rcu(struct rcu_head *head) { struct io_cq *icq = container_of(head, struct io_cq, __rcu_head); @@ -60,8 +75,11 @@ static void ioc_exit_icq(struct io_cq *icq) if (rcu_dereference_raw(ioc->icq_hint) == icq) rcu_assign_pointer(ioc->icq_hint, NULL); - if (et->ops.elevator_exit_icq_fn) + if (et->ops.elevator_exit_icq_fn) { + ioc_release_depth_inc(q); et->ops.elevator_exit_icq_fn(icq); + ioc_release_depth_dec(q); + } /* * @icq->q might have gone away by the time RCU callback runs @@ -80,15 +98,8 @@ static void ioc_release_fn(struct work_struct *work) struct io_context *ioc = container_of(work, struct io_context, release_work); struct request_queue *last_q = NULL; - unsigned long flags; - /* - * Exiting icq may call into put_io_context() through elevator - * which will trigger lockdep warning. The ioc's are guaranteed to - * be different, use a different locking subclass here. Use - * irqsave variant as there's no spin_lock_irq_nested(). - */ - spin_lock_irqsave_nested(&ioc->lock, flags, 1); + spin_lock_irq(&ioc->lock); while (!hlist_empty(&ioc->icq_list)) { struct io_cq *icq = hlist_entry(ioc->icq_list.first, @@ -110,15 +121,15 @@ static void ioc_release_fn(struct work_struct *work) */ if (last_q) { spin_unlock(last_q->queue_lock); - spin_unlock_irqrestore(&ioc->lock, flags); + spin_unlock_irq(&ioc->lock); blk_put_queue(last_q); } else { - spin_unlock_irqrestore(&ioc->lock, flags); + spin_unlock_irq(&ioc->lock); } last_q = this_q; - spin_lock_irqsave(this_q->queue_lock, flags); - spin_lock_nested(&ioc->lock, 1); + spin_lock_irq(this_q->queue_lock); + spin_lock(&ioc->lock); continue; } ioc_exit_icq(icq); @@ -126,10 +137,10 @@ static void ioc_release_fn(struct work_struct *work) if (last_q) { spin_unlock(last_q->queue_lock); - spin_unlock_irqrestore(&ioc->lock, flags); + spin_unlock_irq(&ioc->lock); blk_put_queue(last_q); } else { - spin_unlock_irqrestore(&ioc->lock, flags); + spin_unlock_irq(&ioc->lock); } kmem_cache_free(iocontext_cachep, ioc); @@ -138,29 +149,79 @@ static void ioc_release_fn(struct work_struct *work) /** * put_io_context - put a reference of io_context * @ioc: io_context to put + * @locked_q: request_queue the caller is holding queue_lock of (hint) * * Decrement reference count of @ioc and release it if the count reaches - * zero. + * zero. If the caller is holding queue_lock of a queue, it can indicate + * that with @locked_q. This is an optimization hint and the caller is + * allowed to pass in %NULL even when it's holding a queue_lock. */ -void put_io_context(struct io_context *ioc) +void put_io_context(struct io_context *ioc, struct request_queue *locked_q) { + struct request_queue *last_q = locked_q; unsigned long flags; if (ioc == NULL) return; BUG_ON(atomic_long_read(&ioc->refcount) <= 0); + if (locked_q) + lockdep_assert_held(locked_q->queue_lock); + + if (!atomic_long_dec_and_test(&ioc->refcount)) + return; /* - * Releasing ioc requires reverse order double locking and we may - * already be holding a queue_lock. Do it asynchronously from wq. + * Destroy @ioc. This is a bit messy because icq's are chained + * from both ioc and queue, and ioc->lock nests inside queue_lock. + * The inner ioc->lock should be held to walk our icq_list and then + * for each icq the outer matching queue_lock should be grabbed. + * ie. We need to do reverse-order double lock dancing. + * + * Another twist is that we are often called with one of the + * matching queue_locks held as indicated by @locked_q, which + * prevents performing double-lock dance for other queues. + * + * So, we do it in two stages. The fast path uses the queue_lock + * the caller is holding and, if other queues need to be accessed, + * uses trylock to avoid introducing locking dependency. This can + * handle most cases, especially if @ioc was performing IO on only + * single device. + * + * If trylock doesn't cut it, we defer to @ioc->release_work which + * can do all the double-locking dancing. */ - if (atomic_long_dec_and_test(&ioc->refcount)) { - spin_lock_irqsave(&ioc->lock, flags); - if (!hlist_empty(&ioc->icq_list)) - schedule_work(&ioc->release_work); - spin_unlock_irqrestore(&ioc->lock, flags); + spin_lock_irqsave_nested(&ioc->lock, flags, + ioc_release_depth(locked_q)); + + while (!hlist_empty(&ioc->icq_list)) { + struct io_cq *icq = hlist_entry(ioc->icq_list.first, + struct io_cq, ioc_node); + struct request_queue *this_q = icq->q; + + if (this_q != last_q) { + if (last_q && last_q != locked_q) + spin_unlock(last_q->queue_lock); + last_q = NULL; + + if (!spin_trylock(this_q->queue_lock)) + break; + last_q = this_q; + continue; + } + ioc_exit_icq(icq); } + + if (last_q && last_q != locked_q) + spin_unlock(last_q->queue_lock); + + spin_unlock_irqrestore(&ioc->lock, flags); + + /* if no icq is left, we're done; otherwise, kick release_work */ + if (hlist_empty(&ioc->icq_list)) + kmem_cache_free(iocontext_cachep, ioc); + else + schedule_work(&ioc->release_work); } EXPORT_SYMBOL(put_io_context); @@ -175,7 +236,7 @@ void exit_io_context(struct task_struct *task) task_unlock(task); atomic_dec(&ioc->nr_tasks); - put_io_context(ioc); + put_io_context(ioc, NULL); } /** diff --git a/trunk/block/blk-merge.c b/trunk/block/blk-merge.c index 160035f54882..cfcc37cb222b 100644 --- a/trunk/block/blk-merge.c +++ b/trunk/block/blk-merge.c @@ -471,40 +471,3 @@ int blk_attempt_req_merge(struct request_queue *q, struct request *rq, { return attempt_merge(q, rq, next); } - -bool blk_rq_merge_ok(struct request *rq, struct bio *bio) -{ - if (!rq_mergeable(rq)) - return false; - - /* don't merge file system requests and discard requests */ - if ((bio->bi_rw & REQ_DISCARD) != (rq->bio->bi_rw & REQ_DISCARD)) - return false; - - /* don't merge discard requests and secure discard requests */ - if ((bio->bi_rw & REQ_SECURE) != (rq->bio->bi_rw & REQ_SECURE)) - return false; - - /* different data direction or already started, don't merge */ - if (bio_data_dir(bio) != rq_data_dir(rq)) - return false; - - /* must be same device and not a special request */ - if (rq->rq_disk != bio->bi_bdev->bd_disk || rq->special) - return false; - - /* only merge integrity protected bio into ditto rq */ - if (bio_integrity(bio) != blk_integrity_rq(rq)) - return false; - - return true; -} - -int blk_try_merge(struct request *rq, struct bio *bio) -{ - if (blk_rq_pos(rq) + blk_rq_sectors(rq) == bio->bi_sector) - return ELEVATOR_BACK_MERGE; - else if (blk_rq_pos(rq) - bio_sectors(bio) == bio->bi_sector) - return ELEVATOR_FRONT_MERGE; - return ELEVATOR_NO_MERGE; -} diff --git a/trunk/block/blk.h b/trunk/block/blk.h index 9c12f80882b0..7efd772336de 100644 --- a/trunk/block/blk.h +++ b/trunk/block/blk.h @@ -137,8 +137,6 @@ int blk_attempt_req_merge(struct request_queue *q, struct request *rq, struct request *next); void blk_recalc_rq_segments(struct request *rq); void blk_rq_set_mixed_merge(struct request *rq); -bool blk_rq_merge_ok(struct request *rq, struct bio *bio); -int blk_try_merge(struct request *rq, struct bio *bio); void blk_queue_congestion_threshold(struct request_queue *q); diff --git a/trunk/block/bsg.c b/trunk/block/bsg.c index ff64ae3bacee..4cf703fd98bb 100644 --- a/trunk/block/bsg.c +++ b/trunk/block/bsg.c @@ -983,8 +983,7 @@ void bsg_unregister_queue(struct request_queue *q) mutex_lock(&bsg_mutex); idr_remove(&bsg_minor_idr, bcd->minor); - if (q->kobj.sd) - sysfs_remove_link(&q->kobj, "bsg"); + sysfs_remove_link(&q->kobj, "bsg"); device_unregister(bcd->class_dev); bcd->class_dev = NULL; kref_put(&bcd->ref, bsg_kref_release_function); diff --git a/trunk/block/cfq-iosched.c b/trunk/block/cfq-iosched.c index d0ba50533668..ee55019066a1 100644 --- a/trunk/block/cfq-iosched.c +++ b/trunk/block/cfq-iosched.c @@ -1699,11 +1699,18 @@ static int cfq_allow_merge(struct request_queue *q, struct request *rq, /* * Lookup the cfqq that this bio will be queued with and allow - * merge only if rq is queued there. + * merge only if rq is queued there. This function can be called + * from plug merge without queue_lock. In such cases, ioc of @rq + * and %current are guaranteed to be equal. Avoid lookup which + * requires queue_lock by using @rq's cic. */ - cic = cfq_cic_lookup(cfqd, current->io_context); - if (!cic) - return false; + if (current->io_context == RQ_CIC(rq)->icq.ioc) { + cic = RQ_CIC(rq); + } else { + cic = cfq_cic_lookup(cfqd, current->io_context); + if (!cic) + return false; + } cfqq = cic_to_cfqq(cic, cfq_bio_sync(bio)); return cfqq == RQ_CFQQ(rq); @@ -1787,7 +1794,7 @@ __cfq_slice_expired(struct cfq_data *cfqd, struct cfq_queue *cfqq, cfqd->active_queue = NULL; if (cfqd->active_cic) { - put_io_context(cfqd->active_cic->icq.ioc); + put_io_context(cfqd->active_cic->icq.ioc, cfqd->queue); cfqd->active_cic = NULL; } } @@ -3110,18 +3117,17 @@ cfq_should_preempt(struct cfq_data *cfqd, struct cfq_queue *new_cfqq, */ static void cfq_preempt_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq) { - enum wl_type_t old_type = cfqq_type(cfqd->active_queue); - cfq_log_cfqq(cfqd, cfqq, "preempt"); - cfq_slice_expired(cfqd, 1); /* * workload type is changed, don't save slice, otherwise preempt * doesn't happen */ - if (old_type != cfqq_type(cfqq)) + if (cfqq_type(cfqd->active_queue) != cfqq_type(cfqq)) cfqq->cfqg->saved_workload_slice = 0; + cfq_slice_expired(cfqd, 1); + /* * Put the new queue at the front of the of the current list, * so we know that it will be selected next. diff --git a/trunk/block/elevator.c b/trunk/block/elevator.c index f016855a46b0..91e18f8af9be 100644 --- a/trunk/block/elevator.c +++ b/trunk/block/elevator.c @@ -70,9 +70,39 @@ static int elv_iosched_allow_merge(struct request *rq, struct bio *bio) /* * can we safely merge with this request? */ -bool elv_rq_merge_ok(struct request *rq, struct bio *bio) +int elv_rq_merge_ok(struct request *rq, struct bio *bio) { - if (!blk_rq_merge_ok(rq, bio)) + if (!rq_mergeable(rq)) + return 0; + + /* + * Don't merge file system requests and discard requests + */ + if ((bio->bi_rw & REQ_DISCARD) != (rq->bio->bi_rw & REQ_DISCARD)) + return 0; + + /* + * Don't merge discard requests and secure discard requests + */ + if ((bio->bi_rw & REQ_SECURE) != (rq->bio->bi_rw & REQ_SECURE)) + return 0; + + /* + * different data direction or already started, don't merge + */ + if (bio_data_dir(bio) != rq_data_dir(rq)) + return 0; + + /* + * must be same device and not a special request + */ + if (rq->rq_disk != bio->bi_bdev->bd_disk || rq->special) + return 0; + + /* + * only merge integrity protected bio into ditto rq + */ + if (bio_integrity(bio) != blk_integrity_rq(rq)) return 0; if (!elv_iosched_allow_merge(rq, bio)) @@ -82,6 +112,23 @@ bool elv_rq_merge_ok(struct request *rq, struct bio *bio) } EXPORT_SYMBOL(elv_rq_merge_ok); +int elv_try_merge(struct request *__rq, struct bio *bio) +{ + int ret = ELEVATOR_NO_MERGE; + + /* + * we can merge and sequence is ok, check if it's possible + */ + if (elv_rq_merge_ok(__rq, bio)) { + if (blk_rq_pos(__rq) + blk_rq_sectors(__rq) == bio->bi_sector) + ret = ELEVATOR_BACK_MERGE; + else if (blk_rq_pos(__rq) - bio_sectors(bio) == bio->bi_sector) + ret = ELEVATOR_FRONT_MERGE; + } + + return ret; +} + static struct elevator_type *elevator_find(const char *name) { struct elevator_type *e; @@ -431,8 +478,8 @@ int elv_merge(struct request_queue *q, struct request **req, struct bio *bio) /* * First try one-hit cache. */ - if (q->last_merge && elv_rq_merge_ok(q->last_merge, bio)) { - ret = blk_try_merge(q->last_merge, bio); + if (q->last_merge) { + ret = elv_try_merge(q->last_merge, bio); if (ret != ELEVATOR_NO_MERGE) { *req = q->last_merge; return ret; diff --git a/trunk/crypto/sha512_generic.c b/trunk/crypto/sha512_generic.c index f04af931a682..88f160b77b1f 100644 --- a/trunk/crypto/sha512_generic.c +++ b/trunk/crypto/sha512_generic.c @@ -78,7 +78,7 @@ static inline void LOAD_OP(int I, u64 *W, const u8 *input) static inline void BLEND_OP(int I, u64 *W) { - W[I & 15] += s1(W[(I-2) & 15]) + W[(I-7) & 15] + s0(W[(I-15) & 15]); + W[I % 16] += s1(W[(I-2) % 16]) + W[(I-7) % 16] + s0(W[(I-15) % 16]); } static void @@ -89,42 +89,46 @@ sha512_transform(u64 *state, const u8 *input) int i; u64 W[16]; + /* load the input */ + for (i = 0; i < 16; i++) + LOAD_OP(i, W, input); + /* load the state into our registers */ a=state[0]; b=state[1]; c=state[2]; d=state[3]; e=state[4]; f=state[5]; g=state[6]; h=state[7]; - /* now iterate */ - for (i=0; i<80; i+=8) { - if (!(i & 8)) { - int j; - - if (i < 16) { - /* load the input */ - for (j = 0; j < 16; j++) - LOAD_OP(i + j, W, input); - } else { - for (j = 0; j < 16; j++) { - BLEND_OP(i + j, W); - } - } - } - - t1 = h + e1(e) + Ch(e,f,g) + sha512_K[i ] + W[(i & 15)]; - t2 = e0(a) + Maj(a,b,c); d+=t1; h=t1+t2; - t1 = g + e1(d) + Ch(d,e,f) + sha512_K[i+1] + W[(i & 15) + 1]; - t2 = e0(h) + Maj(h,a,b); c+=t1; g=t1+t2; - t1 = f + e1(c) + Ch(c,d,e) + sha512_K[i+2] + W[(i & 15) + 2]; - t2 = e0(g) + Maj(g,h,a); b+=t1; f=t1+t2; - t1 = e + e1(b) + Ch(b,c,d) + sha512_K[i+3] + W[(i & 15) + 3]; - t2 = e0(f) + Maj(f,g,h); a+=t1; e=t1+t2; - t1 = d + e1(a) + Ch(a,b,c) + sha512_K[i+4] + W[(i & 15) + 4]; - t2 = e0(e) + Maj(e,f,g); h+=t1; d=t1+t2; - t1 = c + e1(h) + Ch(h,a,b) + sha512_K[i+5] + W[(i & 15) + 5]; - t2 = e0(d) + Maj(d,e,f); g+=t1; c=t1+t2; - t1 = b + e1(g) + Ch(g,h,a) + sha512_K[i+6] + W[(i & 15) + 6]; - t2 = e0(c) + Maj(c,d,e); f+=t1; b=t1+t2; - t1 = a + e1(f) + Ch(f,g,h) + sha512_K[i+7] + W[(i & 15) + 7]; - t2 = e0(b) + Maj(b,c,d); e+=t1; a=t1+t2; +#define SHA512_0_15(i, a, b, c, d, e, f, g, h) \ + t1 = h + e1(e) + Ch(e, f, g) + sha512_K[i] + W[i]; \ + t2 = e0(a) + Maj(a, b, c); \ + d += t1; \ + h = t1 + t2 + +#define SHA512_16_79(i, a, b, c, d, e, f, g, h) \ + BLEND_OP(i, W); \ + t1 = h + e1(e) + Ch(e, f, g) + sha512_K[i] + W[(i)%16]; \ + t2 = e0(a) + Maj(a, b, c); \ + d += t1; \ + h = t1 + t2 + + for (i = 0; i < 16; i += 8) { + SHA512_0_15(i, a, b, c, d, e, f, g, h); + SHA512_0_15(i + 1, h, a, b, c, d, e, f, g); + SHA512_0_15(i + 2, g, h, a, b, c, d, e, f); + SHA512_0_15(i + 3, f, g, h, a, b, c, d, e); + SHA512_0_15(i + 4, e, f, g, h, a, b, c, d); + SHA512_0_15(i + 5, d, e, f, g, h, a, b, c); + SHA512_0_15(i + 6, c, d, e, f, g, h, a, b); + SHA512_0_15(i + 7, b, c, d, e, f, g, h, a); + } + for (i = 16; i < 80; i += 8) { + SHA512_16_79(i, a, b, c, d, e, f, g, h); + SHA512_16_79(i + 1, h, a, b, c, d, e, f, g); + SHA512_16_79(i + 2, g, h, a, b, c, d, e, f); + SHA512_16_79(i + 3, f, g, h, a, b, c, d, e); + SHA512_16_79(i + 4, e, f, g, h, a, b, c, d); + SHA512_16_79(i + 5, d, e, f, g, h, a, b, c); + SHA512_16_79(i + 6, c, d, e, f, g, h, a, b); + SHA512_16_79(i + 7, b, c, d, e, f, g, h, a); } state[0] += a; state[1] += b; state[2] += c; state[3] += d; diff --git a/trunk/drivers/base/cpu.c b/trunk/drivers/base/cpu.c index 4dabf5077c48..db87e78d7459 100644 --- a/trunk/drivers/base/cpu.c +++ b/trunk/drivers/base/cpu.c @@ -208,25 +208,6 @@ static ssize_t print_cpus_offline(struct device *dev, } static DEVICE_ATTR(offline, 0444, print_cpus_offline, NULL); -static void cpu_device_release(struct device *dev) -{ - /* - * This is an empty function to prevent the driver core from spitting a - * warning at us. Yes, I know this is directly opposite of what the - * documentation for the driver core and kobjects say, and the author - * of this code has already been publically ridiculed for doing - * something as foolish as this. However, at this point in time, it is - * the only way to handle the issue of statically allocated cpu - * devices. The different architectures will have their cpu device - * code reworked to properly handle this in the near future, so this - * function will then be changed to correctly free up the memory held - * by the cpu device. - * - * Never copy this way of doing things, or you too will be made fun of - * on the linux-kerenl list, you have been warned. - */ -} - /* * register_cpu - Setup a sysfs device for a CPU. * @cpu - cpu->hotpluggable field set to 1 will generate a control file in @@ -240,10 +221,8 @@ int __cpuinit register_cpu(struct cpu *cpu, int num) int error; cpu->node_id = cpu_to_node(num); - memset(&cpu->dev, 0x00, sizeof(struct device)); cpu->dev.id = num; cpu->dev.bus = &cpu_subsys; - cpu->dev.release = cpu_device_release; error = device_register(&cpu->dev); if (!error && cpu->hotpluggable) register_cpu_control(cpu); diff --git a/trunk/drivers/base/memory.c b/trunk/drivers/base/memory.c index 9e60dbe9fd94..ed5de58c340f 100644 --- a/trunk/drivers/base/memory.c +++ b/trunk/drivers/base/memory.c @@ -572,36 +572,19 @@ static int init_memory_block(struct memory_block **memory, } static int add_memory_section(int nid, struct mem_section *section, - struct memory_block **mem_p, unsigned long state, enum mem_add_context context) { - struct memory_block *mem = NULL; - int scn_nr = __section_nr(section); + struct memory_block *mem; int ret = 0; mutex_lock(&mem_sysfs_mutex); - if (context == BOOT) { - /* same memory block ? */ - if (mem_p && *mem_p) - if (scn_nr >= (*mem_p)->start_section_nr && - scn_nr <= (*mem_p)->end_section_nr) { - mem = *mem_p; - kobject_get(&mem->dev.kobj); - } - } else - mem = find_memory_block(section); - + mem = find_memory_block(section); if (mem) { mem->section_count++; kobject_put(&mem->dev.kobj); - } else { + } else ret = init_memory_block(&mem, section, state); - /* store memory_block pointer for next loop */ - if (!ret && context == BOOT) - if (mem_p) - *mem_p = mem; - } if (!ret) { if (context == HOTPLUG && @@ -644,7 +627,7 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section, */ int register_new_memory(int nid, struct mem_section *section) { - return add_memory_section(nid, section, NULL, MEM_OFFLINE, HOTPLUG); + return add_memory_section(nid, section, MEM_OFFLINE, HOTPLUG); } int unregister_memory_section(struct mem_section *section) @@ -664,7 +647,6 @@ int __init memory_dev_init(void) int ret; int err; unsigned long block_sz; - struct memory_block *mem = NULL; ret = subsys_system_register(&memory_subsys, NULL); if (ret) @@ -680,10 +662,7 @@ int __init memory_dev_init(void) for (i = 0; i < NR_MEM_SECTIONS; i++) { if (!present_section_nr(i)) continue; - /* don't need to reuse memory_block if only one per block */ - err = add_memory_section(0, __nr_to_section(i), - (sections_per_block == 1) ? NULL : &mem, - MEM_ONLINE, + err = add_memory_section(0, __nr_to_section(i), MEM_ONLINE, BOOT); if (!ret) ret = err; diff --git a/trunk/drivers/base/node.c b/trunk/drivers/base/node.c index 90aa2a11a933..44f427a66117 100644 --- a/trunk/drivers/base/node.c +++ b/trunk/drivers/base/node.c @@ -456,15 +456,7 @@ static int link_mem_sections(int nid) if (!present_section_nr(section_nr)) continue; mem_sect = __nr_to_section(section_nr); - - /* same memblock ? */ - if (mem_blk) - if ((section_nr >= mem_blk->start_section_nr) && - (section_nr <= mem_blk->end_section_nr)) - continue; - mem_blk = find_memory_block_hinted(mem_sect, mem_blk); - ret = register_mem_sect_under_node(mem_blk, nid); if (!err) err = ret; diff --git a/trunk/drivers/bcma/main.c b/trunk/drivers/bcma/main.c index ec31f7dd5549..febbc0a1222a 100644 --- a/trunk/drivers/bcma/main.c +++ b/trunk/drivers/bcma/main.c @@ -169,8 +169,10 @@ int bcma_bus_register(struct bcma_bus *bus) err = bcma_sprom_get(bus); if (err == -ENOENT) { pr_err("No SPROM available\n"); - } else if (err) + } else if (err) { pr_err("Failed to get SPROM: %d\n", err); + return -ENOENT; + } /* Register found cores */ bcma_register_cores(bus); diff --git a/trunk/drivers/bcma/scan.c b/trunk/drivers/bcma/scan.c index 3a2f672db9ad..cad994857683 100644 --- a/trunk/drivers/bcma/scan.c +++ b/trunk/drivers/bcma/scan.c @@ -399,18 +399,15 @@ int bcma_bus_scan(struct bcma_bus *bus) core->bus = bus; err = bcma_get_next_core(bus, &eromptr, NULL, core_num, core); - if (err < 0) { - kfree(core); - if (err == -ENODEV) { - core_num++; - continue; - } else if (err == -ENXIO) { - continue; - } else if (err == -ESPIPE) { - break; - } + if (err == -ENODEV) { + core_num++; + continue; + } else if (err == -ENXIO) + continue; + else if (err == -ESPIPE) + break; + else if (err < 0) return err; - } core->core_index = core_num++; bus->nr_cores++; diff --git a/trunk/drivers/block/floppy.c b/trunk/drivers/block/floppy.c index 9baf11e86362..510fb10ec45a 100644 --- a/trunk/drivers/block/floppy.c +++ b/trunk/drivers/block/floppy.c @@ -4368,14 +4368,8 @@ static int __init floppy_init(void) out_put_disk: while (dr--) { del_timer_sync(&motor_off_timer[dr]); - if (disks[dr]->queue) { + if (disks[dr]->queue) blk_cleanup_queue(disks[dr]->queue); - /* - * put_disk() is not paired with add_disk() and - * will put queue reference one extra time. fix it. - */ - disks[dr]->queue = NULL; - } put_disk(disks[dr]); } return err; @@ -4585,15 +4579,6 @@ static void __exit floppy_module_exit(void) platform_device_unregister(&floppy_device[drive]); } blk_cleanup_queue(disks[drive]->queue); - - /* - * These disks have not called add_disk(). Don't put down - * queue reference in put_disk(). - */ - if (!(allowed_drive_mask & (1 << drive)) || - fdc_state[FDC(drive)].version == FDC_NONE) - disks[drive]->queue = NULL; - put_disk(disks[drive]); } diff --git a/trunk/drivers/block/loop.c b/trunk/drivers/block/loop.c index cd504353b278..f00257782fcc 100644 --- a/trunk/drivers/block/loop.c +++ b/trunk/drivers/block/loop.c @@ -356,14 +356,14 @@ lo_direct_splice_actor(struct pipe_inode_info *pipe, struct splice_desc *sd) return __splice_from_pipe(pipe, sd, lo_splice_actor); } -static ssize_t +static int do_lo_receive(struct loop_device *lo, struct bio_vec *bvec, int bsize, loff_t pos) { struct lo_read_data cookie; struct splice_desc sd; struct file *file; - ssize_t retval; + long retval; cookie.lo = lo; cookie.page = bvec->bv_page; @@ -379,28 +379,26 @@ do_lo_receive(struct loop_device *lo, file = lo->lo_backing_file; retval = splice_direct_to_actor(file, &sd, lo_direct_splice_actor); - return retval; + if (retval < 0) + return retval; + if (retval != bvec->bv_len) + return -EIO; + return 0; } static int lo_receive(struct loop_device *lo, struct bio *bio, int bsize, loff_t pos) { struct bio_vec *bvec; - ssize_t s; - int i; + int i, ret = 0; bio_for_each_segment(bvec, bio, i) { - s = do_lo_receive(lo, bvec, bsize, pos); - if (s < 0) - return s; - - if (s != bvec->bv_len) { - zero_fill_bio(bio); + ret = do_lo_receive(lo, bvec, bsize, pos); + if (ret < 0) break; - } pos += bvec->bv_len; } - return 0; + return ret; } static int do_bio_filebacked(struct loop_device *lo, struct bio *bio) diff --git a/trunk/drivers/block/mtip32xx/mtip32xx.c b/trunk/drivers/block/mtip32xx/mtip32xx.c index 8eb81c96608f..b74eab70c3d0 100644 --- a/trunk/drivers/block/mtip32xx/mtip32xx.c +++ b/trunk/drivers/block/mtip32xx/mtip32xx.c @@ -2068,6 +2068,8 @@ static int mtip_hw_ioctl(struct driver_data *dd, unsigned int cmd, * when the read completes. * @data Callback data passed to the callback function * when the read completes. + * @barrier If non-zero, this command must be completed before + * issuing any other commands. * @dir Direction (read or write) * * return value @@ -2075,7 +2077,7 @@ static int mtip_hw_ioctl(struct driver_data *dd, unsigned int cmd, */ static void mtip_hw_submit_io(struct driver_data *dd, sector_t start, int nsect, int nents, int tag, void *callback, - void *data, int dir) + void *data, int barrier, int dir) { struct host_to_dev_fis *fis; struct mtip_port *port = dd->port; @@ -2106,6 +2108,8 @@ static void mtip_hw_submit_io(struct driver_data *dd, sector_t start, *((unsigned int *) &fis->lba_low) = (start & 0xFFFFFF); *((unsigned int *) &fis->lba_low_ex) = ((start >> 24) & 0xFFFFFF); fis->device = 1 << 6; + if (barrier) + fis->device |= FUA_BIT; fis->features = nsect & 0xFF; fis->features_ex = (nsect >> 8) & 0xFF; fis->sect_count = ((tag << 3) | (tag >> 5)); @@ -3083,6 +3087,7 @@ static void mtip_make_request(struct request_queue *queue, struct bio *bio) tag, bio_endio, bio, + bio->bi_rw & REQ_FUA, bio_data_dir(bio)); } else bio_io_error(bio); @@ -3182,10 +3187,6 @@ static int mtip_block_initialize(struct driver_data *dd) blk_queue_max_segments(dd->queue, MTIP_MAX_SG); blk_queue_physical_block_size(dd->queue, 4096); blk_queue_io_min(dd->queue, 4096); - /* - * write back cache is not supported in the device. FUA depends on - * write back cache support, hence setting flush support to zero. - */ blk_queue_flush(dd->queue, 0); /* Set the capacity of the device in 512 byte sectors. */ diff --git a/trunk/drivers/block/mtip32xx/mtip32xx.h b/trunk/drivers/block/mtip32xx/mtip32xx.h index e0554a8f2233..723d7c4946dc 100644 --- a/trunk/drivers/block/mtip32xx/mtip32xx.h +++ b/trunk/drivers/block/mtip32xx/mtip32xx.h @@ -104,6 +104,9 @@ /* BAR number used to access the HBA registers. */ #define MTIP_ABAR 5 +/* Forced Unit Access Bit */ +#define FUA_BIT 0x80 + #ifdef DEBUG #define dbg_printk(format, arg...) \ printk(pr_fmt(format), ##arg); @@ -412,6 +415,8 @@ struct driver_data { atomic_t resumeflag; /* Atomic variable to track suspend/resume */ + atomic_t eh_active; /* Flag for error handling tracking */ + struct task_struct *mtip_svc_handler; /* task_struct of svc thd */ }; diff --git a/trunk/drivers/cdrom/cdrom.c b/trunk/drivers/cdrom/cdrom.c index d620b4495745..55eaf474d32c 100644 --- a/trunk/drivers/cdrom/cdrom.c +++ b/trunk/drivers/cdrom/cdrom.c @@ -286,6 +286,8 @@ /* used to tell the module to turn on full debugging messages */ static bool debug; +/* used to keep tray locked at all times */ +static int keeplocked; /* default compatibility mode */ static bool autoclose=1; static bool autoeject; @@ -1202,7 +1204,7 @@ void cdrom_release(struct cdrom_device_info *cdi, fmode_t mode) cdinfo(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", cdi->name); cdrom_dvd_rw_close_write(cdi); - if ((cdo->capability & CDC_LOCK) && !cdi->keeplocked) { + if ((cdo->capability & CDC_LOCK) && !keeplocked) { cdinfo(CD_CLOSE, "Unlocking door!\n"); cdo->lock_door(cdi, 0); } @@ -1369,7 +1371,7 @@ static int cdrom_select_disc(struct cdrom_device_info *cdi, int slot) curslot = info->hdr.curslot; kfree(info); - if (cdi->use_count > 1 || cdi->keeplocked) { + if (cdi->use_count > 1 || keeplocked) { if (slot == CDSL_CURRENT) { return curslot; } else { @@ -2117,6 +2119,11 @@ static int cdrom_read_cdda_old(struct cdrom_device_info *cdi, __u8 __user *ubuf, if (!nr) return -ENOMEM; + if (!access_ok(VERIFY_WRITE, ubuf, nframes * CD_FRAMESIZE_RAW)) { + ret = -EFAULT; + goto out; + } + cgc.data_direction = CGC_DATA_READ; while (nframes > 0) { if (nr > nframes) @@ -2125,7 +2132,7 @@ static int cdrom_read_cdda_old(struct cdrom_device_info *cdi, __u8 __user *ubuf, ret = cdrom_read_block(cdi, &cgc, lba, nr, 1, CD_FRAMESIZE_RAW); if (ret) break; - if (copy_to_user(ubuf, cgc.buffer, CD_FRAMESIZE_RAW * nr)) { + if (__copy_to_user(ubuf, cgc.buffer, CD_FRAMESIZE_RAW * nr)) { ret = -EFAULT; break; } @@ -2133,6 +2140,7 @@ static int cdrom_read_cdda_old(struct cdrom_device_info *cdi, __u8 __user *ubuf, nframes -= nr; lba += nr; } +out: kfree(cgc.buffer); return ret; } @@ -2287,7 +2295,7 @@ static int cdrom_ioctl_eject(struct cdrom_device_info *cdi) if (!CDROM_CAN(CDC_OPEN_TRAY)) return -ENOSYS; - if (cdi->use_count != 1 || cdi->keeplocked) + if (cdi->use_count != 1 || keeplocked) return -EBUSY; if (CDROM_CAN(CDC_LOCK)) { int ret = cdi->ops->lock_door(cdi, 0); @@ -2314,7 +2322,7 @@ static int cdrom_ioctl_eject_sw(struct cdrom_device_info *cdi, if (!CDROM_CAN(CDC_OPEN_TRAY)) return -ENOSYS; - if (cdi->keeplocked) + if (keeplocked) return -EBUSY; cdi->options &= ~(CDO_AUTO_CLOSE | CDO_AUTO_EJECT); @@ -2445,7 +2453,7 @@ static int cdrom_ioctl_lock_door(struct cdrom_device_info *cdi, if (!CDROM_CAN(CDC_LOCK)) return -EDRIVE_CANT_DO_THIS; - cdi->keeplocked = arg ? 1 : 0; + keeplocked = arg ? 1 : 0; /* * Don't unlock the door on multiple opens by default, but allow diff --git a/trunk/drivers/gpu/drm/drm_ioc32.c b/trunk/drivers/gpu/drm/drm_ioc32.c index 637fcc3766c7..ddd70db45f76 100644 --- a/trunk/drivers/gpu/drm/drm_ioc32.c +++ b/trunk/drivers/gpu/drm/drm_ioc32.c @@ -315,8 +315,7 @@ static int compat_drm_getclient(struct file *file, unsigned int cmd, if (err) return err; - if (__get_user(c32.idx, &client->idx) - || __get_user(c32.auth, &client->auth) + if (__get_user(c32.auth, &client->auth) || __get_user(c32.pid, &client->pid) || __get_user(c32.uid, &client->uid) || __get_user(c32.magic, &client->magic) diff --git a/trunk/drivers/gpu/drm/i915/intel_display.c b/trunk/drivers/gpu/drm/i915/intel_display.c index 00fbff5ddd81..b3b51c43dad0 100644 --- a/trunk/drivers/gpu/drm/i915/intel_display.c +++ b/trunk/drivers/gpu/drm/i915/intel_display.c @@ -1872,7 +1872,7 @@ static void intel_update_fbc(struct drm_device *dev) if (enable_fbc < 0) { DRM_DEBUG_KMS("fbc set to per-chip default\n"); enable_fbc = 1; - if (INTEL_INFO(dev)->gen <= 6) + if (INTEL_INFO(dev)->gen <= 5) enable_fbc = 0; } if (!enable_fbc) { @@ -5307,7 +5307,6 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, } } - pipeconf &= ~PIPECONF_INTERLACE_MASK; if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { pipeconf |= PIPECONF_INTERLACE_W_FIELD_INDICATION; /* the chip adds 2 halflines automatically */ @@ -5318,7 +5317,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, adjusted_mode->crtc_vsync_end -= 1; adjusted_mode->crtc_vsync_start -= 1; } else - pipeconf |= PIPECONF_PROGRESSIVE; + pipeconf &= ~PIPECONF_INTERLACE_MASK; /* progressive */ I915_WRITE(HTOTAL(pipe), (adjusted_mode->crtc_hdisplay - 1) | @@ -5903,7 +5902,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, } } - pipeconf &= ~PIPECONF_INTERLACE_MASK; if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { pipeconf |= PIPECONF_INTERLACE_W_FIELD_INDICATION; /* the chip adds 2 halflines automatically */ @@ -5914,7 +5912,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, adjusted_mode->crtc_vsync_end -= 1; adjusted_mode->crtc_vsync_start -= 1; } else - pipeconf |= PIPECONF_PROGRESSIVE; + pipeconf &= ~PIPECONF_INTERLACE_W_FIELD_INDICATION; /* progressive */ I915_WRITE(HTOTAL(pipe), (adjusted_mode->crtc_hdisplay - 1) | diff --git a/trunk/drivers/gpu/drm/i915/intel_dp.c b/trunk/drivers/gpu/drm/i915/intel_dp.c index 94f860cce3f7..db3b461ad412 100644 --- a/trunk/drivers/gpu/drm/i915/intel_dp.c +++ b/trunk/drivers/gpu/drm/i915/intel_dp.c @@ -208,8 +208,17 @@ intel_dp_link_clock(uint8_t link_bw) */ static int -intel_dp_link_required(int pixel_clock, int bpp) +intel_dp_link_required(struct intel_dp *intel_dp, int pixel_clock, int check_bpp) { + struct drm_crtc *crtc = intel_dp->base.base.crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int bpp = 24; + + if (check_bpp) + bpp = check_bpp; + else if (intel_crtc) + bpp = intel_crtc->bpp; + return (pixel_clock * bpp + 9) / 10; } @@ -236,11 +245,12 @@ intel_dp_mode_valid(struct drm_connector *connector, return MODE_PANEL; } - mode_rate = intel_dp_link_required(mode->clock, 24); + mode_rate = intel_dp_link_required(intel_dp, mode->clock, 0); max_rate = intel_dp_max_data_rate(max_link_clock, max_lanes); if (mode_rate > max_rate) { - mode_rate = intel_dp_link_required(mode->clock, 18); + mode_rate = intel_dp_link_required(intel_dp, + mode->clock, 18); if (mode_rate > max_rate) return MODE_CLOCK_HIGH; else @@ -673,7 +683,7 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, int lane_count, clock; int max_lane_count = intel_dp_max_lane_count(intel_dp); int max_clock = intel_dp_max_link_bw(intel_dp) == DP_LINK_BW_2_7 ? 1 : 0; - int bpp = mode->private_flags & INTEL_MODE_DP_FORCE_6BPC ? 18 : 24; + int bpp = mode->private_flags & INTEL_MODE_DP_FORCE_6BPC ? 18 : 0; static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 }; if (is_edp(intel_dp) && intel_dp->panel_fixed_mode) { @@ -691,7 +701,7 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, for (clock = 0; clock <= max_clock; clock++) { int link_avail = intel_dp_max_data_rate(intel_dp_link_clock(bws[clock]), lane_count); - if (intel_dp_link_required(mode->clock, bpp) + if (intel_dp_link_required(intel_dp, mode->clock, bpp) <= link_avail) { intel_dp->link_bw = bws[clock]; intel_dp->lane_count = lane_count; diff --git a/trunk/drivers/gpu/drm/i915/intel_lvds.c b/trunk/drivers/gpu/drm/i915/intel_lvds.c index aa84832b0e1a..798f6e1aa544 100644 --- a/trunk/drivers/gpu/drm/i915/intel_lvds.c +++ b/trunk/drivers/gpu/drm/i915/intel_lvds.c @@ -692,14 +692,6 @@ static const struct dmi_system_id intel_no_lvds[] = { DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"), }, }, - { - .callback = intel_no_lvds_dmi_callback, - .ident = "AOpen i45GMx-I", - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"), - DMI_MATCH(DMI_BOARD_NAME, "i45GMx-I"), - }, - }, { .callback = intel_no_lvds_dmi_callback, .ident = "Aopen i945GTt-VFA", diff --git a/trunk/drivers/gpu/drm/radeon/evergreen.c b/trunk/drivers/gpu/drm/radeon/evergreen.c index 9be353b894cc..ae09fe82afbc 100644 --- a/trunk/drivers/gpu/drm/radeon/evergreen.c +++ b/trunk/drivers/gpu/drm/radeon/evergreen.c @@ -3191,7 +3191,6 @@ static int evergreen_startup(struct radeon_device *rdev) if (r) { DRM_ERROR("radeon: failed testing IB (%d).\n", r); rdev->accel_working = false; - return r; } r = r600_audio_init(rdev); diff --git a/trunk/drivers/hwmon/f75375s.c b/trunk/drivers/hwmon/f75375s.c index f609b5727ba9..eedf574ab539 100644 --- a/trunk/drivers/hwmon/f75375s.c +++ b/trunk/drivers/hwmon/f75375s.c @@ -172,7 +172,7 @@ static inline void f75375_write8(struct i2c_client *client, u8 reg, static inline void f75375_write16(struct i2c_client *client, u8 reg, u16 value) { - int err = i2c_smbus_write_byte_data(client, reg, (value >> 8)); + int err = i2c_smbus_write_byte_data(client, reg, (value << 8)); if (err) return; i2c_smbus_write_byte_data(client, reg + 1, (value & 0xFF)); @@ -200,6 +200,9 @@ static struct f75375_data *f75375_update_device(struct device *dev) f75375_read16(client, F75375_REG_FAN_MIN(nr)); data->fan_target[nr] = f75375_read16(client, F75375_REG_FAN_EXP(nr)); + data->pwm[nr] = f75375_read8(client, + F75375_REG_FAN_PWM_DUTY(nr)); + } for (nr = 0; nr < 4; nr++) { data->in_max[nr] = @@ -215,8 +218,6 @@ static struct f75375_data *f75375_update_device(struct device *dev) if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) { for (nr = 0; nr < 2; nr++) { - data->pwm[nr] = f75375_read8(client, - F75375_REG_FAN_PWM_DUTY(nr)); /* assign MSB, therefore shift it by 8 bits */ data->temp11[nr] = f75375_read8(client, F75375_REG_TEMP(nr)) << 8; @@ -368,7 +369,7 @@ static int set_pwm_enable_direct(struct i2c_client *client, int nr, int val) fanmode |= (3 << FAN_CTRL_MODE(nr)); break; case 2: /* AUTOMATIC*/ - fanmode |= (1 << FAN_CTRL_MODE(nr)); + fanmode |= (2 << FAN_CTRL_MODE(nr)); break; case 3: /* fan speed */ break; @@ -722,7 +723,7 @@ static void f75375_init(struct i2c_client *client, struct f75375_data *data, if (data->kind == f75387) { bool manu, duty; - if (!(mode & (1 << F75387_FAN_CTRL_LINEAR(nr)))) + if (!(conf & (1 << F75387_FAN_CTRL_LINEAR(nr)))) data->pwm_mode[nr] = 1; manu = ((mode >> F75387_FAN_MANU_MODE(nr)) & 1); diff --git a/trunk/drivers/hwmon/w83627ehf.c b/trunk/drivers/hwmon/w83627ehf.c index 5276d1933dbc..4d383e7e051d 100644 --- a/trunk/drivers/hwmon/w83627ehf.c +++ b/trunk/drivers/hwmon/w83627ehf.c @@ -2349,6 +2349,11 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) /* Read fan clock dividers immediately */ w83627ehf_update_fan_div_common(dev, data); + /* Read pwm data to save original values */ + w83627ehf_update_pwm_common(dev, data); + for (i = 0; i < data->pwm_num; i++) + data->pwm_enable_orig[i] = data->pwm_enable[i]; + /* Read pwm data to save original values */ w83627ehf_update_pwm_common(dev, data); for (i = 0; i < data->pwm_num; i++) diff --git a/trunk/drivers/infiniband/ulp/ipoib/ipoib.h b/trunk/drivers/infiniband/ulp/ipoib/ipoib.h index 86df632ea612..b3cc1e062b17 100644 --- a/trunk/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/trunk/drivers/infiniband/ulp/ipoib/ipoib.h @@ -44,7 +44,6 @@ #include #include -#include #include @@ -118,9 +117,8 @@ struct ipoib_header { u16 reserved; }; -struct ipoib_cb { - struct qdisc_skb_cb qdisc_cb; - u8 hwaddr[INFINIBAND_ALEN]; +struct ipoib_pseudoheader { + u8 hwaddr[INFINIBAND_ALEN]; }; /* Used for all multicast joins (broadcast, IPv4 mcast and IPv6 mcast) */ diff --git a/trunk/drivers/infiniband/ulp/ipoib/ipoib_main.c b/trunk/drivers/infiniband/ulp/ipoib/ipoib_main.c index 3974c290b667..3514ca05deea 100644 --- a/trunk/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/trunk/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -653,7 +653,7 @@ static void ipoib_path_lookup(struct sk_buff *skb, struct neighbour *n, struct n } static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev, - struct ipoib_cb *cb) + struct ipoib_pseudoheader *phdr) { struct ipoib_dev_priv *priv = netdev_priv(dev); struct ipoib_path *path; @@ -661,15 +661,17 @@ static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev, spin_lock_irqsave(&priv->lock, flags); - path = __path_find(dev, cb->hwaddr + 4); + path = __path_find(dev, phdr->hwaddr + 4); if (!path || !path->valid) { int new_path = 0; if (!path) { - path = path_rec_create(dev, cb->hwaddr + 4); + path = path_rec_create(dev, phdr->hwaddr + 4); new_path = 1; } if (path) { + /* put pseudoheader back on for next time */ + skb_push(skb, sizeof *phdr); __skb_queue_tail(&path->queue, skb); if (!path->query && path_rec_start(dev, path)) { @@ -693,10 +695,12 @@ static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev, be16_to_cpu(path->pathrec.dlid)); spin_unlock_irqrestore(&priv->lock, flags); - ipoib_send(dev, skb, path->ah, IPOIB_QPN(cb->hwaddr)); + ipoib_send(dev, skb, path->ah, IPOIB_QPN(phdr->hwaddr)); return; } else if ((path->query || !path_rec_start(dev, path)) && skb_queue_len(&path->queue) < IPOIB_MAX_PATH_REC_QUEUE) { + /* put pseudoheader back on for next time */ + skb_push(skb, sizeof *phdr); __skb_queue_tail(&path->queue, skb); } else { ++dev->stats.tx_dropped; @@ -770,14 +774,16 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) dev_kfree_skb_any(skb); } } else { - struct ipoib_cb *cb = (struct ipoib_cb *) skb->cb; + struct ipoib_pseudoheader *phdr = + (struct ipoib_pseudoheader *) skb->data; + skb_pull(skb, sizeof *phdr); - if (cb->hwaddr[4] == 0xff) { + if (phdr->hwaddr[4] == 0xff) { /* Add in the P_Key for multicast*/ - cb->hwaddr[8] = (priv->pkey >> 8) & 0xff; - cb->hwaddr[9] = priv->pkey & 0xff; + phdr->hwaddr[8] = (priv->pkey >> 8) & 0xff; + phdr->hwaddr[9] = priv->pkey & 0xff; - ipoib_mcast_send(dev, cb->hwaddr + 4, skb); + ipoib_mcast_send(dev, phdr->hwaddr + 4, skb); } else { /* unicast GID -- should be ARP or RARP reply */ @@ -786,14 +792,14 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) ipoib_warn(priv, "Unicast, no %s: type %04x, QPN %06x %pI6\n", skb_dst(skb) ? "neigh" : "dst", be16_to_cpup((__be16 *) skb->data), - IPOIB_QPN(cb->hwaddr), - cb->hwaddr + 4); + IPOIB_QPN(phdr->hwaddr), + phdr->hwaddr + 4); dev_kfree_skb_any(skb); ++dev->stats.tx_dropped; goto unlock; } - unicast_arp_send(skb, dev, cb); + unicast_arp_send(skb, dev, phdr); } } unlock: @@ -819,6 +825,8 @@ static int ipoib_hard_header(struct sk_buff *skb, const void *daddr, const void *saddr, unsigned len) { struct ipoib_header *header; + struct dst_entry *dst; + struct neighbour *n; header = (struct ipoib_header *) skb_push(skb, sizeof *header); @@ -826,13 +834,18 @@ static int ipoib_hard_header(struct sk_buff *skb, header->reserved = 0; /* - * If we don't have a dst_entry structure, stuff the - * destination address into skb->cb so we can figure out where - * to send the packet later. + * If we don't have a neighbour structure, stuff the + * destination address onto the front of the skb so we can + * figure out where to send the packet later. */ - if (!skb_dst(skb)) { - struct ipoib_cb *cb = (struct ipoib_cb *) skb->cb; - memcpy(cb->hwaddr, daddr, INFINIBAND_ALEN); + dst = skb_dst(skb); + n = NULL; + if (dst) + n = dst_get_neighbour_noref_raw(dst); + if ((!dst || !n) && daddr) { + struct ipoib_pseudoheader *phdr = + (struct ipoib_pseudoheader *) skb_push(skb, sizeof *phdr); + memcpy(phdr->hwaddr, daddr, INFINIBAND_ALEN); } return 0; @@ -1008,7 +1021,11 @@ static void ipoib_setup(struct net_device *dev) dev->flags |= IFF_BROADCAST | IFF_MULTICAST; - dev->hard_header_len = IPOIB_ENCAP_LEN; + /* + * We add in INFINIBAND_ALEN to allow for the destination + * address "pseudoheader" for skbs without neighbour struct. + */ + dev->hard_header_len = IPOIB_ENCAP_LEN + INFINIBAND_ALEN; dev->addr_len = INFINIBAND_ALEN; dev->type = ARPHRD_INFINIBAND; dev->tx_queue_len = ipoib_sendq_size * 2; diff --git a/trunk/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/trunk/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index 20ebc6fd1bb9..f7ff9dd66cda 100644 --- a/trunk/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/trunk/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -262,13 +262,21 @@ static int ipoib_mcast_join_finish(struct ipoib_mcast *mcast, netif_tx_lock_bh(dev); while (!skb_queue_empty(&mcast->pkt_queue)) { struct sk_buff *skb = skb_dequeue(&mcast->pkt_queue); + struct dst_entry *dst = skb_dst(skb); + struct neighbour *n = NULL; netif_tx_unlock_bh(dev); skb->dev = dev; + if (dst) + n = dst_get_neighbour_noref_raw(dst); + if (!dst || !n) { + /* put pseudoheader back on for next time */ + skb_push(skb, sizeof (struct ipoib_pseudoheader)); + } + if (dev_queue_xmit(skb)) ipoib_warn(priv, "dev_queue_xmit failed to requeue packet\n"); - netif_tx_lock_bh(dev); } netif_tx_unlock_bh(dev); diff --git a/trunk/drivers/isdn/i4l/isdn_net.c b/trunk/drivers/isdn/i4l/isdn_net.c index 802ab87a78b6..2339d7396b9e 100644 --- a/trunk/drivers/isdn/i4l/isdn_net.c +++ b/trunk/drivers/isdn/i4l/isdn_net.c @@ -1901,7 +1901,7 @@ static int isdn_net_header(struct sk_buff *skb, struct net_device *dev, { isdn_net_local *lp = netdev_priv(dev); unsigned char *p; - int len = 0; + ushort len = 0; switch (lp->p_encap) { case ISDN_NET_ENCAP_ETHER: diff --git a/trunk/drivers/mfd/Kconfig b/trunk/drivers/mfd/Kconfig index f147395bac9a..cd13e9f2f5e6 100644 --- a/trunk/drivers/mfd/Kconfig +++ b/trunk/drivers/mfd/Kconfig @@ -200,7 +200,7 @@ config MENELAUS config TWL4030_CORE bool "Texas Instruments TWL4030/TWL5030/TWL6030/TPS659x0 Support" - depends on I2C=y && GENERIC_HARDIRQS + depends on I2C=y && GENERIC_HARDIRQS && IRQ_DOMAIN help Say yes here if you have TWL4030 / TWL6030 family chip on your board. This core driver provides register access and IRQ handling diff --git a/trunk/drivers/mfd/twl-core.c b/trunk/drivers/mfd/twl-core.c index 8ce3959c6919..e04e04ddc15e 100644 --- a/trunk/drivers/mfd/twl-core.c +++ b/trunk/drivers/mfd/twl-core.c @@ -263,9 +263,7 @@ struct twl_client { static struct twl_client twl_modules[TWL_NUM_SLAVES]; -#ifdef CONFIG_IRQ_DOMAIN static struct irq_domain domain; -#endif /* mapping the module id to slave id and base address */ struct twl_mapping { @@ -1228,13 +1226,13 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) pdata->irq_base = status; pdata->irq_end = pdata->irq_base + nr_irqs; -#ifdef CONFIG_IRQ_DOMAIN domain.irq_base = pdata->irq_base; domain.nr_irq = nr_irqs; +#ifdef CONFIG_OF_IRQ domain.of_node = of_node_get(node); domain.ops = &irq_domain_simple_ops; - irq_domain_add(&domain); #endif + irq_domain_add(&domain); if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) { dev_dbg(&client->dev, "can't talk I2C?\n"); diff --git a/trunk/drivers/mfd/twl4030-power.c b/trunk/drivers/mfd/twl4030-power.c index 79ca33dfacca..d905f5171153 100644 --- a/trunk/drivers/mfd/twl4030-power.c +++ b/trunk/drivers/mfd/twl4030-power.c @@ -124,7 +124,7 @@ static u8 res_config_addrs[] = { [RES_MAIN_REF] = 0x94, }; -static int __devinit twl4030_write_script_byte(u8 address, u8 byte) +static int __init twl4030_write_script_byte(u8 address, u8 byte) { int err; @@ -138,7 +138,7 @@ static int __devinit twl4030_write_script_byte(u8 address, u8 byte) return err; } -static int __devinit twl4030_write_script_ins(u8 address, u16 pmb_message, +static int __init twl4030_write_script_ins(u8 address, u16 pmb_message, u8 delay, u8 next) { int err; @@ -158,7 +158,7 @@ static int __devinit twl4030_write_script_ins(u8 address, u16 pmb_message, return err; } -static int __devinit twl4030_write_script(u8 address, struct twl4030_ins *script, +static int __init twl4030_write_script(u8 address, struct twl4030_ins *script, int len) { int err; @@ -183,7 +183,7 @@ static int __devinit twl4030_write_script(u8 address, struct twl4030_ins *script return err; } -static int __devinit twl4030_config_wakeup3_sequence(u8 address) +static int __init twl4030_config_wakeup3_sequence(u8 address) { int err; u8 data; @@ -208,7 +208,7 @@ static int __devinit twl4030_config_wakeup3_sequence(u8 address) return err; } -static int __devinit twl4030_config_wakeup12_sequence(u8 address) +static int __init twl4030_config_wakeup12_sequence(u8 address) { int err = 0; u8 data; @@ -262,7 +262,7 @@ static int __devinit twl4030_config_wakeup12_sequence(u8 address) return err; } -static int __devinit twl4030_config_sleep_sequence(u8 address) +static int __init twl4030_config_sleep_sequence(u8 address) { int err; @@ -276,7 +276,7 @@ static int __devinit twl4030_config_sleep_sequence(u8 address) return err; } -static int __devinit twl4030_config_warmreset_sequence(u8 address) +static int __init twl4030_config_warmreset_sequence(u8 address) { int err; u8 rd_data; @@ -324,7 +324,7 @@ static int __devinit twl4030_config_warmreset_sequence(u8 address) return err; } -static int __devinit twl4030_configure_resource(struct twl4030_resconfig *rconfig) +static int __init twl4030_configure_resource(struct twl4030_resconfig *rconfig) { int rconfig_addr; int err; @@ -416,7 +416,7 @@ static int __devinit twl4030_configure_resource(struct twl4030_resconfig *rconfi return 0; } -static int __devinit load_twl4030_script(struct twl4030_script *tscript, +static int __init load_twl4030_script(struct twl4030_script *tscript, u8 address) { int err; @@ -527,7 +527,7 @@ void twl4030_power_off(void) pr_err("TWL4030 Unable to power off\n"); } -void __devinit twl4030_power_init(struct twl4030_power_data *twl4030_scripts) +void __init twl4030_power_init(struct twl4030_power_data *twl4030_scripts) { int err = 0; int i; diff --git a/trunk/drivers/misc/Kconfig b/trunk/drivers/misc/Kconfig index c7795096d43b..6a1a092db146 100644 --- a/trunk/drivers/misc/Kconfig +++ b/trunk/drivers/misc/Kconfig @@ -2,14 +2,24 @@ # Misc strange devices # -menu "Misc devices" - +# This one has to live outside of the MISC_DEVICES conditional, +# because it may be selected by drivers/platform/x86/hp_accel. config SENSORS_LIS3LV02D tristate depends on INPUT select INPUT_POLLDEV default n +menuconfig MISC_DEVICES + bool "Misc devices" + ---help--- + Say Y here to get to see options for device drivers from various + different categories. This option alone does not add any kernel code. + + If you say N, all options in this submenu will be skipped and disabled. + +if MISC_DEVICES + config AD525X_DPOT tristate "Analog Devices Digital Potentiometers" depends on (I2C || SPI) && SYSFS @@ -506,4 +516,5 @@ source "drivers/misc/ti-st/Kconfig" source "drivers/misc/lis3lv02d/Kconfig" source "drivers/misc/carma/Kconfig" source "drivers/misc/altera-stapl/Kconfig" -endmenu + +endif # MISC_DEVICES diff --git a/trunk/drivers/misc/c2port/c2port-duramar2150.c b/trunk/drivers/misc/c2port/c2port-duramar2150.c index 5484301d57d9..778fc3fdfb9b 100644 --- a/trunk/drivers/misc/c2port/c2port-duramar2150.c +++ b/trunk/drivers/misc/c2port/c2port-duramar2150.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #define DATA_PORT 0x325 diff --git a/trunk/drivers/misc/cb710/core.c b/trunk/drivers/misc/cb710/core.c index 85cc7710193c..68cd05b6d829 100644 --- a/trunk/drivers/misc/cb710/core.c +++ b/trunk/drivers/misc/cb710/core.c @@ -245,7 +245,6 @@ static int __devinit cb710_probe(struct pci_dev *pdev, if (err) return err; - spin_lock_init(&chip->irq_lock); chip->pdev = pdev; chip->iobase = pcim_iomap_table(pdev)[0]; diff --git a/trunk/drivers/misc/cs5535-mfgpt.c b/trunk/drivers/misc/cs5535-mfgpt.c index 87a390de054c..bc685bfc4c33 100644 --- a/trunk/drivers/misc/cs5535-mfgpt.c +++ b/trunk/drivers/misc/cs5535-mfgpt.c @@ -262,7 +262,7 @@ static void __init reset_all_timers(void) * In other cases (such as with VSAless OpenFirmware), the system firmware * leaves timers available for us to use. */ -static int __devinit scan_timers(struct cs5535_mfgpt_chip *mfgpt) +static int __init scan_timers(struct cs5535_mfgpt_chip *mfgpt) { struct cs5535_mfgpt_timer timer = { .chip = mfgpt }; unsigned long flags; diff --git a/trunk/drivers/misc/vmw_balloon.c b/trunk/drivers/misc/vmw_balloon.c index cb56e270da11..cd41d403c9df 100644 --- a/trunk/drivers/misc/vmw_balloon.c +++ b/trunk/drivers/misc/vmw_balloon.c @@ -314,7 +314,7 @@ static bool vmballoon_send_get_target(struct vmballoon *b, u32 *new_target) * fear that guest will need it. Host may reject some pages, we need to * check the return value and maybe submit a different page. */ -static int vmballoon_send_lock_page(struct vmballoon *b, unsigned long pfn, +static bool vmballoon_send_lock_page(struct vmballoon *b, unsigned long pfn, unsigned int *hv_status) { unsigned long status, dummy; @@ -322,17 +322,17 @@ static int vmballoon_send_lock_page(struct vmballoon *b, unsigned long pfn, pfn32 = (u32)pfn; if (pfn32 != pfn) - return -1; + return false; STATS_INC(b->stats.lock); *hv_status = status = VMWARE_BALLOON_CMD(LOCK, pfn, dummy); if (vmballoon_check_status(b, status)) - return 0; + return true; pr_debug("%s - ppn %lx, hv returns %ld\n", __func__, pfn, status); STATS_INC(b->stats.lock_fail); - return 1; + return false; } /* @@ -411,7 +411,7 @@ static int vmballoon_reserve_page(struct vmballoon *b, bool can_sleep) struct page *page; gfp_t flags; unsigned int hv_status; - int locked; + bool locked = false; flags = can_sleep ? VMW_PAGE_ALLOC_CANSLEEP : VMW_PAGE_ALLOC_NOSLEEP; do { @@ -431,7 +431,7 @@ static int vmballoon_reserve_page(struct vmballoon *b, bool can_sleep) /* inform monitor */ locked = vmballoon_send_lock_page(b, page_to_pfn(page), &hv_status); - if (locked > 0) { + if (!locked) { STATS_INC(b->stats.refused_alloc); if (hv_status == VMW_BALLOON_ERROR_RESET || @@ -449,7 +449,7 @@ static int vmballoon_reserve_page(struct vmballoon *b, bool can_sleep) if (++b->n_refused_pages >= VMW_BALLOON_MAX_REFUSED) return -EIO; } - } while (locked != 0); + } while (!locked); /* track allocated page */ list_add(&page->lru, &b->pages); diff --git a/trunk/drivers/mmc/card/block.c b/trunk/drivers/mmc/card/block.c index c6a383d0244d..0cad48a284a8 100644 --- a/trunk/drivers/mmc/card/block.c +++ b/trunk/drivers/mmc/card/block.c @@ -1694,7 +1694,6 @@ static int mmc_add_disk(struct mmc_blk_data *md) md->power_ro_lock.show = power_ro_lock_show; md->power_ro_lock.store = power_ro_lock_store; - sysfs_attr_init(&md->power_ro_lock.attr); md->power_ro_lock.attr.mode = mode; md->power_ro_lock.attr.name = "ro_lock_until_next_power_on"; diff --git a/trunk/drivers/mmc/core/core.c b/trunk/drivers/mmc/core/core.c index 690255c7d4dc..f545a3e6eb80 100644 --- a/trunk/drivers/mmc/core/core.c +++ b/trunk/drivers/mmc/core/core.c @@ -290,11 +290,8 @@ static void mmc_wait_for_req_done(struct mmc_host *host, static void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq, bool is_first_req) { - if (host->ops->pre_req) { - mmc_host_clk_hold(host); + if (host->ops->pre_req) host->ops->pre_req(host, mrq, is_first_req); - mmc_host_clk_release(host); - } } /** @@ -309,11 +306,8 @@ static void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq, static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq, int err) { - if (host->ops->post_req) { - mmc_host_clk_hold(host); + if (host->ops->post_req) host->ops->post_req(host, mrq, err); - mmc_host_clk_release(host); - } } /** @@ -626,9 +620,7 @@ int mmc_host_enable(struct mmc_host *host) int err; host->en_dis_recurs = 1; - mmc_host_clk_hold(host); err = host->ops->enable(host); - mmc_host_clk_release(host); host->en_dis_recurs = 0; if (err) { @@ -648,9 +640,7 @@ static int mmc_host_do_disable(struct mmc_host *host, int lazy) int err; host->en_dis_recurs = 1; - mmc_host_clk_hold(host); err = host->ops->disable(host, lazy); - mmc_host_clk_release(host); host->en_dis_recurs = 0; if (err < 0) { @@ -1131,10 +1121,6 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc, * might not allow this operation */ voltage = regulator_get_voltage(supply); - - if (mmc->caps2 & MMC_CAP2_BROKEN_VOLTAGE) - min_uV = max_uV = voltage; - if (voltage < 0) result = voltage; else if (voltage < min_uV || voltage > max_uV) @@ -1217,11 +1203,8 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, bool cmd11 host->ios.signal_voltage = signal_voltage; - if (host->ops->start_signal_voltage_switch) { - mmc_host_clk_hold(host); + if (host->ops->start_signal_voltage_switch) err = host->ops->start_signal_voltage_switch(host, &host->ios); - mmc_host_clk_release(host); - } return err; } @@ -1256,7 +1239,6 @@ static void mmc_poweroff_notify(struct mmc_host *host) int err = 0; card = host->card; - mmc_claim_host(host); /* * Send power notify command only if card @@ -1287,7 +1269,6 @@ static void mmc_poweroff_notify(struct mmc_host *host) /* Set the card state to no notification after the poweroff */ card->poweroff_notify_state = MMC_NO_POWER_NOTIFICATION; } - mmc_release_host(host); } /* @@ -1346,28 +1327,12 @@ static void mmc_power_up(struct mmc_host *host) void mmc_power_off(struct mmc_host *host) { - int err = 0; mmc_host_clk_hold(host); host->ios.clock = 0; host->ios.vdd = 0; - /* - * For eMMC 4.5 device send AWAKE command before - * POWER_OFF_NOTIFY command, because in sleep state - * eMMC 4.5 devices respond to only RESET and AWAKE cmd - */ - if (host->card && mmc_card_is_sleep(host->card) && - host->bus_ops->resume) { - err = host->bus_ops->resume(host); - - if (!err) - mmc_poweroff_notify(host); - else - pr_warning("%s: error %d during resume " - "(continue with poweroff sequence)\n", - mmc_hostname(host), err); - } + mmc_poweroff_notify(host); /* * Reset ocr mask to be the highest possible voltage supported for @@ -2421,6 +2386,12 @@ int mmc_suspend_host(struct mmc_host *host) */ if (mmc_try_claim_host(host)) { if (host->bus_ops->suspend) { + /* + * For eMMC 4.5 device send notify command + * before sleep, because in sleep state eMMC 4.5 + * devices respond to only RESET and AWAKE cmd + */ + mmc_poweroff_notify(host); err = host->bus_ops->suspend(host); } mmc_do_release_host(host); diff --git a/trunk/drivers/mmc/core/host.h b/trunk/drivers/mmc/core/host.h index 08a7852ade44..fb8a5cd2e4a1 100644 --- a/trunk/drivers/mmc/core/host.h +++ b/trunk/drivers/mmc/core/host.h @@ -14,6 +14,27 @@ int mmc_register_host_class(void); void mmc_unregister_host_class(void); + +#ifdef CONFIG_MMC_CLKGATE +void mmc_host_clk_hold(struct mmc_host *host); +void mmc_host_clk_release(struct mmc_host *host); +unsigned int mmc_host_clk_rate(struct mmc_host *host); + +#else +static inline void mmc_host_clk_hold(struct mmc_host *host) +{ +} + +static inline void mmc_host_clk_release(struct mmc_host *host) +{ +} + +static inline unsigned int mmc_host_clk_rate(struct mmc_host *host) +{ + return host->ios.clock; +} +#endif + void mmc_host_deeper_disable(struct work_struct *work); #endif diff --git a/trunk/drivers/mmc/core/mmc.c b/trunk/drivers/mmc/core/mmc.c index a48066344fa8..59b9ba52e66a 100644 --- a/trunk/drivers/mmc/core/mmc.c +++ b/trunk/drivers/mmc/core/mmc.c @@ -376,7 +376,7 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) } card->ext_csd.raw_hc_erase_gap_size = - ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; + ext_csd[EXT_CSD_PARTITION_ATTRIBUTE]; card->ext_csd.raw_sec_trim_mult = ext_csd[EXT_CSD_SEC_TRIM_MULT]; card->ext_csd.raw_sec_erase_mult = @@ -551,7 +551,7 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width) goto out; /* only compare read only fields */ - err = !((card->ext_csd.raw_partition_support == + err = (!(card->ext_csd.raw_partition_support == bw_ext_csd[EXT_CSD_PARTITION_SUPPORT]) && (card->ext_csd.raw_erased_mem_count == bw_ext_csd[EXT_CSD_ERASED_MEM_CONT]) && @@ -1006,8 +1006,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, err = mmc_select_hs200(card); else if (host->caps & MMC_CAP_MMC_HIGHSPEED) err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_HS_TIMING, 1, - card->ext_csd.generic_cmd6_time); + EXT_CSD_HS_TIMING, 1, 0); if (err && err != -EBADMSG) goto free_card; @@ -1117,7 +1116,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, * Activate wide bus and DDR (if supported). */ if (!mmc_card_hs200(card) && - (card->csd.mmca_vsn >= CSD_SPEC_VER_4) && + (card->csd.mmca_vsn >= CSD_SPEC_VER_3) && (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) { static unsigned ext_csd_bits[][2] = { { EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 }, @@ -1316,13 +1315,11 @@ static int mmc_suspend(struct mmc_host *host) BUG_ON(!host->card); mmc_claim_host(host); - if (mmc_card_can_sleep(host)) { + if (mmc_card_can_sleep(host)) err = mmc_card_sleep(host); - if (!err) - mmc_card_set_sleep(host->card); - } else if (!mmc_host_is_spi(host)) + else if (!mmc_host_is_spi(host)) mmc_deselect_cards(host); - host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); + host->card->state &= ~MMC_STATE_HIGHSPEED; mmc_release_host(host); return err; @@ -1342,11 +1339,7 @@ static int mmc_resume(struct mmc_host *host) BUG_ON(!host->card); mmc_claim_host(host); - if (mmc_card_is_sleep(host->card)) { - err = mmc_card_awake(host); - mmc_card_clr_sleep(host->card); - } else - err = mmc_init_card(host, host->ocr, host->card); + err = mmc_init_card(host, host->ocr, host->card); mmc_release_host(host); return err; @@ -1356,8 +1349,7 @@ static int mmc_power_restore(struct mmc_host *host) { int ret; - host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); - mmc_card_clr_sleep(host->card); + host->card->state &= ~MMC_STATE_HIGHSPEED; mmc_claim_host(host); ret = mmc_init_card(host, host->ocr, host->card); mmc_release_host(host); diff --git a/trunk/drivers/mmc/core/sd.c b/trunk/drivers/mmc/core/sd.c index 5017f9354ce2..c63ad03c29c7 100644 --- a/trunk/drivers/mmc/core/sd.c +++ b/trunk/drivers/mmc/core/sd.c @@ -451,11 +451,9 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status) * information and let the hardware specific code * return what is possible given the options */ - mmc_host_clk_hold(card->host); drive_strength = card->host->ops->select_drive_strength( card->sw_caps.uhs_max_dtr, host_drv_type, card_drv_type); - mmc_host_clk_release(card->host); err = mmc_sd_switch(card, 1, 2, drive_strength, status); if (err) @@ -662,12 +660,9 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card) goto out; /* SPI mode doesn't define CMD19 */ - if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning) { - mmc_host_clk_hold(card->host); + if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning) err = card->host->ops->execute_tuning(card->host, MMC_SEND_TUNING_BLOCK); - mmc_host_clk_release(card->host); - } out: kfree(status); @@ -855,11 +850,8 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, if (!reinit) { int ro = -1; - if (host->ops->get_ro) { - mmc_host_clk_hold(card->host); + if (host->ops->get_ro) ro = host->ops->get_ro(host); - mmc_host_clk_release(card->host); - } if (ro < 0) { pr_warning("%s: host does not " @@ -975,11 +967,8 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, * Since initialization is now complete, enable preset * value registers for UHS-I cards. */ - if (host->ops->enable_preset_value) { - mmc_host_clk_hold(card->host); + if (host->ops->enable_preset_value) host->ops->enable_preset_value(host, true); - mmc_host_clk_release(card->host); - } } else { /* * Attempt to change to high-speed (if supported) @@ -1162,11 +1151,8 @@ int mmc_attach_sd(struct mmc_host *host) return err; /* Disable preset value enable if already set since last time */ - if (host->ops->enable_preset_value) { - mmc_host_clk_hold(host); + if (host->ops->enable_preset_value) host->ops->enable_preset_value(host, false); - mmc_host_clk_release(host); - } err = mmc_send_app_op_cond(host, 0, &ocr); if (err) diff --git a/trunk/drivers/mmc/core/sdio.c b/trunk/drivers/mmc/core/sdio.c index 12cde6ee17f5..bd7bacc950dc 100644 --- a/trunk/drivers/mmc/core/sdio.c +++ b/trunk/drivers/mmc/core/sdio.c @@ -98,11 +98,10 @@ static int sdio_init_func(struct mmc_card *card, unsigned int fn) return ret; } -static int sdio_read_cccr(struct mmc_card *card, u32 ocr) +static int sdio_read_cccr(struct mmc_card *card) { int ret; int cccr_vsn; - int uhs = ocr & R4_18V_PRESENT; unsigned char data; unsigned char speed; @@ -150,7 +149,7 @@ static int sdio_read_cccr(struct mmc_card *card, u32 ocr) card->scr.sda_spec3 = 0; card->sw_caps.sd3_bus_mode = 0; card->sw_caps.sd3_drv_type = 0; - if (cccr_vsn >= SDIO_CCCR_REV_3_00 && uhs) { + if (cccr_vsn >= SDIO_CCCR_REV_3_00) { card->scr.sda_spec3 = 1; ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_UHS, 0, &data); @@ -713,7 +712,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, /* * Read the common registers. */ - err = sdio_read_cccr(card, ocr); + err = sdio_read_cccr(card); if (err) goto remove; diff --git a/trunk/drivers/mmc/core/sdio_irq.c b/trunk/drivers/mmc/core/sdio_irq.c index f573e7f9f740..68f81b9ee0fb 100644 --- a/trunk/drivers/mmc/core/sdio_irq.c +++ b/trunk/drivers/mmc/core/sdio_irq.c @@ -146,21 +146,15 @@ static int sdio_irq_thread(void *_host) } set_current_state(TASK_INTERRUPTIBLE); - if (host->caps & MMC_CAP_SDIO_IRQ) { - mmc_host_clk_hold(host); + if (host->caps & MMC_CAP_SDIO_IRQ) host->ops->enable_sdio_irq(host, 1); - mmc_host_clk_release(host); - } if (!kthread_should_stop()) schedule_timeout(period); set_current_state(TASK_RUNNING); } while (!kthread_should_stop()); - if (host->caps & MMC_CAP_SDIO_IRQ) { - mmc_host_clk_hold(host); + if (host->caps & MMC_CAP_SDIO_IRQ) host->ops->enable_sdio_irq(host, 0); - mmc_host_clk_release(host); - } pr_debug("%s: IRQ thread exiting with code %d\n", mmc_hostname(host), ret); diff --git a/trunk/drivers/mmc/host/Kconfig b/trunk/drivers/mmc/host/Kconfig index 00fcbed1afd2..cf444b0ca2cc 100644 --- a/trunk/drivers/mmc/host/Kconfig +++ b/trunk/drivers/mmc/host/Kconfig @@ -477,6 +477,7 @@ config MMC_SDHI config MMC_CB710 tristate "ENE CB710 MMC/SD Interface support" depends on PCI + select MISC_DEVICES select CB710_CORE help This option enables support for MMC/SD part of ENE CB710/720 Flash diff --git a/trunk/drivers/mmc/host/atmel-mci.c b/trunk/drivers/mmc/host/atmel-mci.c index 6985cdb0bb26..fcfe1eb5acc8 100644 --- a/trunk/drivers/mmc/host/atmel-mci.c +++ b/trunk/drivers/mmc/host/atmel-mci.c @@ -969,14 +969,11 @@ static void atmci_start_request(struct atmel_mci *host, host->data_status = 0; if (host->need_reset) { - iflags = atmci_readl(host, ATMCI_IMR); - iflags &= (ATMCI_SDIOIRQA | ATMCI_SDIOIRQB); atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST); atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIEN); atmci_writel(host, ATMCI_MR, host->mode_reg); if (host->caps.has_cfg_reg) atmci_writel(host, ATMCI_CFG, host->cfg_reg); - atmci_writel(host, ATMCI_IER, iflags); host->need_reset = false; } atmci_writel(host, ATMCI_SDCR, slot->sdc_reg); diff --git a/trunk/drivers/mmc/host/dw_mmc.c b/trunk/drivers/mmc/host/dw_mmc.c index 8bec1c36b159..0e342793ff14 100644 --- a/trunk/drivers/mmc/host/dw_mmc.c +++ b/trunk/drivers/mmc/host/dw_mmc.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -501,14 +502,8 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) host->dir_status = DW_MCI_SEND_STATUS; if (dw_mci_submit_data_dma(host, data)) { - int flags = SG_MITER_ATOMIC; - if (host->data->flags & MMC_DATA_READ) - flags |= SG_MITER_TO_SG; - else - flags |= SG_MITER_FROM_SG; - - sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); host->sg = data->sg; + host->pio_offset = 0; host->part_buf_start = 0; host->part_buf_count = 0; @@ -977,7 +972,6 @@ static void dw_mci_tasklet_func(unsigned long priv) * generates a block interrupt, hence setting * the scatter-gather pointer to NULL. */ - sg_miter_stop(&host->sg_miter); host->sg = NULL; ctrl = mci_readl(host, CTRL); ctrl |= SDMMC_CTRL_FIFO_RESET; @@ -1317,44 +1311,54 @@ static void dw_mci_pull_data(struct dw_mci *host, void *buf, int cnt) static void dw_mci_read_data_pio(struct dw_mci *host) { - struct sg_mapping_iter *sg_miter = &host->sg_miter; - void *buf; - unsigned int offset; + struct scatterlist *sg = host->sg; + void *buf = sg_virt(sg); + unsigned int offset = host->pio_offset; struct mmc_data *data = host->data; int shift = host->data_shift; u32 status; unsigned int nbytes = 0, len; - unsigned int remain, fcnt; do { - if (!sg_miter_next(sg_miter)) - goto done; - - host->sg = sg_miter->__sg; - buf = sg_miter->addr; - remain = sg_miter->length; - offset = 0; - - do { - fcnt = (SDMMC_GET_FCNT(mci_readl(host, STATUS)) - << shift) + host->part_buf_count; - len = min(remain, fcnt); - if (!len) - break; + len = host->part_buf_count + + (SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift); + if (offset + len <= sg->length) { dw_mci_pull_data(host, (void *)(buf + offset), len); + offset += len; nbytes += len; - remain -= len; - } while (remain); - sg_miter->consumed = offset; + + if (offset == sg->length) { + flush_dcache_page(sg_page(sg)); + host->sg = sg = sg_next(sg); + if (!sg) + goto done; + + offset = 0; + buf = sg_virt(sg); + } + } else { + unsigned int remaining = sg->length - offset; + dw_mci_pull_data(host, (void *)(buf + offset), + remaining); + nbytes += remaining; + + flush_dcache_page(sg_page(sg)); + host->sg = sg = sg_next(sg); + if (!sg) + goto done; + + offset = len - remaining; + buf = sg_virt(sg); + dw_mci_pull_data(host, buf, offset); + nbytes += offset; + } status = mci_readl(host, MINTSTS); mci_writel(host, RINTSTS, SDMMC_INT_RXDR); if (status & DW_MCI_DATA_ERROR_FLAGS) { host->data_status = status; data->bytes_xfered += nbytes; - sg_miter_stop(sg_miter); - host->sg = NULL; smp_wmb(); set_bit(EVENT_DATA_ERROR, &host->pending_events); @@ -1363,66 +1367,65 @@ static void dw_mci_read_data_pio(struct dw_mci *host) return; } } while (status & SDMMC_INT_RXDR); /*if the RXDR is ready read again*/ + host->pio_offset = offset; data->bytes_xfered += nbytes; - - if (!remain) { - if (!sg_miter_next(sg_miter)) - goto done; - sg_miter->consumed = 0; - } - sg_miter_stop(sg_miter); return; done: data->bytes_xfered += nbytes; - sg_miter_stop(sg_miter); - host->sg = NULL; smp_wmb(); set_bit(EVENT_XFER_COMPLETE, &host->pending_events); } static void dw_mci_write_data_pio(struct dw_mci *host) { - struct sg_mapping_iter *sg_miter = &host->sg_miter; - void *buf; - unsigned int offset; + struct scatterlist *sg = host->sg; + void *buf = sg_virt(sg); + unsigned int offset = host->pio_offset; struct mmc_data *data = host->data; int shift = host->data_shift; u32 status; unsigned int nbytes = 0, len; - unsigned int fifo_depth = host->fifo_depth; - unsigned int remain, fcnt; do { - if (!sg_miter_next(sg_miter)) - goto done; - - host->sg = sg_miter->__sg; - buf = sg_miter->addr; - remain = sg_miter->length; - offset = 0; - - do { - fcnt = ((fifo_depth - - SDMMC_GET_FCNT(mci_readl(host, STATUS))) - << shift) - host->part_buf_count; - len = min(remain, fcnt); - if (!len) - break; + len = ((host->fifo_depth - + SDMMC_GET_FCNT(mci_readl(host, STATUS))) << shift) + - host->part_buf_count; + if (offset + len <= sg->length) { host->push_data(host, (void *)(buf + offset), len); + offset += len; nbytes += len; - remain -= len; - } while (remain); - sg_miter->consumed = offset; + if (offset == sg->length) { + host->sg = sg = sg_next(sg); + if (!sg) + goto done; + + offset = 0; + buf = sg_virt(sg); + } + } else { + unsigned int remaining = sg->length - offset; + + host->push_data(host, (void *)(buf + offset), + remaining); + nbytes += remaining; + + host->sg = sg = sg_next(sg); + if (!sg) + goto done; + + offset = len - remaining; + buf = sg_virt(sg); + host->push_data(host, (void *)buf, offset); + nbytes += offset; + } status = mci_readl(host, MINTSTS); mci_writel(host, RINTSTS, SDMMC_INT_TXDR); if (status & DW_MCI_DATA_ERROR_FLAGS) { host->data_status = status; data->bytes_xfered += nbytes; - sg_miter_stop(sg_miter); - host->sg = NULL; smp_wmb(); @@ -1432,20 +1435,12 @@ static void dw_mci_write_data_pio(struct dw_mci *host) return; } } while (status & SDMMC_INT_TXDR); /* if TXDR write again */ + host->pio_offset = offset; data->bytes_xfered += nbytes; - - if (!remain) { - if (!sg_miter_next(sg_miter)) - goto done; - sg_miter->consumed = 0; - } - sg_miter_stop(sg_miter); return; done: data->bytes_xfered += nbytes; - sg_miter_stop(sg_miter); - host->sg = NULL; smp_wmb(); set_bit(EVENT_XFER_COMPLETE, &host->pending_events); } @@ -1648,7 +1643,6 @@ static void dw_mci_work_routine_card(struct work_struct *work) * block interrupt, hence setting the * scatter-gather pointer to NULL. */ - sg_miter_stop(&host->sg_miter); host->sg = NULL; ctrl = mci_readl(host, CTRL); diff --git a/trunk/drivers/mmc/host/of_mmc_spi.c b/trunk/drivers/mmc/host/of_mmc_spi.c index 1534b582c419..ab66f2454dc4 100644 --- a/trunk/drivers/mmc/host/of_mmc_spi.c +++ b/trunk/drivers/mmc/host/of_mmc_spi.c @@ -113,8 +113,8 @@ struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi) const int j = i * 2; u32 mask; - mask = mmc_vddrange_to_ocrmask(be32_to_cpu(voltage_ranges[j]), - be32_to_cpu(voltage_ranges[j + 1])); + mask = mmc_vddrange_to_ocrmask(voltage_ranges[j], + voltage_ranges[j + 1]); if (!mask) { ret = -EINVAL; dev_err(dev, "OF: voltage-range #%d is invalid\n", i); diff --git a/trunk/drivers/mmc/host/sdhci-of-esdhc.c b/trunk/drivers/mmc/host/sdhci-of-esdhc.c index 5d876ff86f37..ff4adc018041 100644 --- a/trunk/drivers/mmc/host/sdhci-of-esdhc.c +++ b/trunk/drivers/mmc/host/sdhci-of-esdhc.c @@ -38,23 +38,6 @@ static u8 esdhc_readb(struct sdhci_host *host, int reg) int base = reg & ~0x3; int shift = (reg & 0x3) * 8; u8 ret = (in_be32(host->ioaddr + base) >> shift) & 0xff; - - /* - * "DMA select" locates at offset 0x28 in SD specification, but on - * P5020 or P3041, it locates at 0x29. - */ - if (reg == SDHCI_HOST_CONTROL) { - u32 dma_bits; - - dma_bits = in_be32(host->ioaddr + reg); - /* DMA select is 22,23 bits in Protocol Control Register */ - dma_bits = (dma_bits >> 5) & SDHCI_CTRL_DMA_MASK; - - /* fixup the result */ - ret &= ~SDHCI_CTRL_DMA_MASK; - ret |= dma_bits; - } - return ret; } @@ -73,21 +56,6 @@ static void esdhc_writew(struct sdhci_host *host, u16 val, int reg) static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) { - /* - * "DMA select" location is offset 0x28 in SD specification, but on - * P5020 or P3041, it's located at 0x29. - */ - if (reg == SDHCI_HOST_CONTROL) { - u32 dma_bits; - - /* DMA select is 22,23 bits in Protocol Control Register */ - dma_bits = (val & SDHCI_CTRL_DMA_MASK) << 5; - clrsetbits_be32(host->ioaddr + reg , SDHCI_CTRL_DMA_MASK << 5, - dma_bits); - val &= ~SDHCI_CTRL_DMA_MASK; - val |= in_be32(host->ioaddr + reg) & SDHCI_CTRL_DMA_MASK; - } - /* Prevent SDHCI core from writing reserved bits (e.g. HISPD). */ if (reg == SDHCI_HOST_CONTROL) val &= ~ESDHC_HOST_CONTROL_RES; diff --git a/trunk/drivers/mmc/host/sdhci-pci.c b/trunk/drivers/mmc/host/sdhci-pci.c index 6ebdc4010e7c..7165e6a09274 100644 --- a/trunk/drivers/mmc/host/sdhci-pci.c +++ b/trunk/drivers/mmc/host/sdhci-pci.c @@ -250,7 +250,7 @@ static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot) static int mfd_sdio_probe_slot(struct sdhci_pci_slot *slot) { - slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_NONREMOVABLE; + slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD; return 0; } diff --git a/trunk/drivers/mmc/host/sdhci-pltfm.c b/trunk/drivers/mmc/host/sdhci-pltfm.c index c5c2a48bdd94..03970bcb3495 100644 --- a/trunk/drivers/mmc/host/sdhci-pltfm.c +++ b/trunk/drivers/mmc/host/sdhci-pltfm.c @@ -2,7 +2,7 @@ * sdhci-pltfm.c Support for SDHCI platform devices * Copyright (c) 2009 Intel Corporation * - * Copyright (c) 2007, 2011 Freescale Semiconductor, Inc. + * Copyright (c) 2007 Freescale Semiconductor, Inc. * Copyright (c) 2009 MontaVista Software, Inc. * * Authors: Xiaobo Xie @@ -71,14 +71,6 @@ void sdhci_get_of_property(struct platform_device *pdev) if (sdhci_of_wp_inverted(np)) host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT; - if (of_device_is_compatible(np, "fsl,p2020-rev1-esdhc")) - host->quirks |= SDHCI_QUIRK_BROKEN_DMA; - - if (of_device_is_compatible(np, "fsl,p2020-esdhc") || - of_device_is_compatible(np, "fsl,p1010-esdhc") || - of_device_is_compatible(np, "fsl,mpc8536-esdhc")) - host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; - clk = of_get_property(np, "clock-frequency", &size); if (clk && size == sizeof(*clk) && *clk) pltfm_host->clock = be32_to_cpup(clk); diff --git a/trunk/drivers/mmc/host/sh_mmcif.c b/trunk/drivers/mmc/host/sh_mmcif.c index 352d4797865b..f5d8b53be333 100644 --- a/trunk/drivers/mmc/host/sh_mmcif.c +++ b/trunk/drivers/mmc/host/sh_mmcif.c @@ -1327,7 +1327,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) if (ret < 0) goto clean_up2; - INIT_DELAYED_WORK(&host->timeout_work, mmcif_timeout_work); + mmc_add_host(mmc); sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); @@ -1338,24 +1338,22 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) } ret = request_threaded_irq(irq[1], sh_mmcif_intr, sh_mmcif_irqt, 0, "sh_mmc:int", host); if (ret) { + free_irq(irq[0], host); dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n"); - goto clean_up4; + goto clean_up3; } - ret = mmc_add_host(mmc); - if (ret < 0) - goto clean_up5; + INIT_DELAYED_WORK(&host->timeout_work, mmcif_timeout_work); + + mmc_detect_change(host->mmc, 0); dev_info(&pdev->dev, "driver version %s\n", DRIVER_VERSION); dev_dbg(&pdev->dev, "chip ver H'%04x\n", sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff); return ret; -clean_up5: - free_irq(irq[1], host); -clean_up4: - free_irq(irq[0], host); clean_up3: + mmc_remove_host(mmc); pm_runtime_suspend(&pdev->dev); clean_up2: pm_runtime_disable(&pdev->dev); diff --git a/trunk/drivers/mmc/host/tmio_mmc.h b/trunk/drivers/mmc/host/tmio_mmc.h index f96c536d130a..a95e6d901726 100644 --- a/trunk/drivers/mmc/host/tmio_mmc.h +++ b/trunk/drivers/mmc/host/tmio_mmc.h @@ -20,8 +20,8 @@ #include #include #include -#include #include +#include /* Definitions for values the CTRL_SDIO_STATUS register can take. */ #define TMIO_SDIO_STAT_IOIRQ 0x0001 @@ -120,7 +120,6 @@ void tmio_mmc_start_dma(struct tmio_mmc_host *host, struct mmc_data *data); void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable); void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdata); void tmio_mmc_release_dma(struct tmio_mmc_host *host); -void tmio_mmc_abort_dma(struct tmio_mmc_host *host); #else static inline void tmio_mmc_start_dma(struct tmio_mmc_host *host, struct mmc_data *data) @@ -141,10 +140,6 @@ static inline void tmio_mmc_request_dma(struct tmio_mmc_host *host, static inline void tmio_mmc_release_dma(struct tmio_mmc_host *host) { } - -static inline void tmio_mmc_abort_dma(struct tmio_mmc_host *host) -{ -} #endif #ifdef CONFIG_PM diff --git a/trunk/drivers/mmc/host/tmio_mmc_dma.c b/trunk/drivers/mmc/host/tmio_mmc_dma.c index 8253ec12003e..7a6e6cc8f8b8 100644 --- a/trunk/drivers/mmc/host/tmio_mmc_dma.c +++ b/trunk/drivers/mmc/host/tmio_mmc_dma.c @@ -34,18 +34,6 @@ void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable) #endif } -void tmio_mmc_abort_dma(struct tmio_mmc_host *host) -{ - tmio_mmc_enable_dma(host, false); - - if (host->chan_rx) - dmaengine_terminate_all(host->chan_rx); - if (host->chan_tx) - dmaengine_terminate_all(host->chan_tx); - - tmio_mmc_enable_dma(host, true); -} - static void tmio_mmc_start_dma_rx(struct tmio_mmc_host *host) { struct scatterlist *sg = host->sg_ptr, *sg_tmp; diff --git a/trunk/drivers/mmc/host/tmio_mmc_pio.c b/trunk/drivers/mmc/host/tmio_mmc_pio.c index 5f9ad74fbf80..abad01b37cfb 100644 --- a/trunk/drivers/mmc/host/tmio_mmc_pio.c +++ b/trunk/drivers/mmc/host/tmio_mmc_pio.c @@ -41,8 +41,8 @@ #include #include #include -#include #include +#include #include "tmio_mmc.h" @@ -246,7 +246,6 @@ static void tmio_mmc_reset_work(struct work_struct *work) /* Ready for new calls */ host->mrq = NULL; - tmio_mmc_abort_dma(host); mmc_request_done(host->mmc, mrq); } @@ -273,9 +272,6 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host) host->mrq = NULL; spin_unlock_irqrestore(&host->lock, flags); - if (mrq->cmd->error || (mrq->data && mrq->data->error)) - tmio_mmc_abort_dma(host); - mmc_request_done(host->mmc, mrq); } diff --git a/trunk/drivers/net/can/cc770/cc770.c b/trunk/drivers/net/can/cc770/cc770.c index c30f0e6f1048..766896747643 100644 --- a/trunk/drivers/net/can/cc770/cc770.c +++ b/trunk/drivers/net/can/cc770/cc770.c @@ -440,14 +440,12 @@ static netdev_tx_t cc770_start_xmit(struct sk_buff *skb, struct net_device *dev) for (i = 0; i < dlc; i++) cc770_write_reg(priv, msgobj[mo].data[i], cf->data[i]); - /* Store echo skb before starting the transfer */ - can_put_echo_skb(skb, dev, 0); - cc770_write_reg(priv, msgobj[mo].ctrl1, RMTPND_RES | TXRQST_SET | CPUUPD_RES | NEWDAT_UNC); stats->tx_bytes += dlc; + can_put_echo_skb(skb, dev, 0); /* * HM: We had some cases of repeated IRQs so make sure the diff --git a/trunk/drivers/net/can/cc770/cc770_isa.c b/trunk/drivers/net/can/cc770/cc770_isa.c index 9f3a25ccd665..4be5fe2c40a5 100644 --- a/trunk/drivers/net/can/cc770/cc770_isa.c +++ b/trunk/drivers/net/can/cc770/cc770_isa.c @@ -110,11 +110,6 @@ MODULE_PARM_DESC(bcr, "Bus configuration register (default=0x40 [CBY])"); #define CC770_IOSIZE 0x20 #define CC770_IOSIZE_INDIRECT 0x02 -/* Spinlock for cc770_isa_port_write_reg_indirect - * and cc770_isa_port_read_reg_indirect - */ -static DEFINE_SPINLOCK(cc770_isa_port_lock); - static struct platform_device *cc770_isa_devs[MAXDEV]; static u8 cc770_isa_mem_read_reg(const struct cc770_priv *priv, int reg) @@ -143,27 +138,18 @@ static u8 cc770_isa_port_read_reg_indirect(const struct cc770_priv *priv, int reg) { unsigned long base = (unsigned long)priv->reg_base; - unsigned long flags; - u8 val; - spin_lock_irqsave(&cc770_isa_port_lock, flags); outb(reg, base); - val = inb(base + 1); - spin_unlock_irqrestore(&cc770_isa_port_lock, flags); - - return val; + return inb(base + 1); } static void cc770_isa_port_write_reg_indirect(const struct cc770_priv *priv, int reg, u8 val) { unsigned long base = (unsigned long)priv->reg_base; - unsigned long flags; - spin_lock_irqsave(&cc770_isa_port_lock, flags); outb(reg, base); outb(val, base + 1); - spin_unlock_irqrestore(&cc770_isa_port_lock, flags); } static int __devinit cc770_isa_probe(struct platform_device *pdev) diff --git a/trunk/drivers/net/can/flexcan.c b/trunk/drivers/net/can/flexcan.c index 96d235799ec1..7fd8089946fb 100644 --- a/trunk/drivers/net/can/flexcan.c +++ b/trunk/drivers/net/can/flexcan.c @@ -118,9 +118,6 @@ (FLEXCAN_ESR_TWRN_INT | FLEXCAN_ESR_RWRN_INT | FLEXCAN_ESR_BOFF_INT) #define FLEXCAN_ESR_ERR_ALL \ (FLEXCAN_ESR_ERR_BUS | FLEXCAN_ESR_ERR_STATE) -#define FLEXCAN_ESR_ALL_INT \ - (FLEXCAN_ESR_TWRN_INT | FLEXCAN_ESR_RWRN_INT | \ - FLEXCAN_ESR_BOFF_INT | FLEXCAN_ESR_ERR_INT) /* FLEXCAN interrupt flag register (IFLAG) bits */ #define FLEXCAN_TX_BUF_ID 8 @@ -580,9 +577,7 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id) reg_iflag1 = flexcan_read(®s->iflag1); reg_esr = flexcan_read(®s->esr); - /* ACK all bus error and state change IRQ sources */ - if (reg_esr & FLEXCAN_ESR_ALL_INT) - flexcan_write(reg_esr & FLEXCAN_ESR_ALL_INT, ®s->esr); + flexcan_write(FLEXCAN_ESR_ERR_INT, ®s->esr); /* ACK err IRQ */ /* * schedule NAPI in case of: diff --git a/trunk/drivers/net/can/pch_can.c b/trunk/drivers/net/can/pch_can.c index 6edc25e0dd15..d11fbb2b95ff 100644 --- a/trunk/drivers/net/can/pch_can.c +++ b/trunk/drivers/net/can/pch_can.c @@ -66,7 +66,6 @@ #define PCH_IF_CREQ_BUSY BIT(15) #define PCH_STATUS_INT 0x8000 -#define PCH_RP 0x00008000 #define PCH_REC 0x00007f00 #define PCH_TEC 0x000000ff @@ -528,7 +527,7 @@ static void pch_can_error(struct net_device *ndev, u32 status) priv->can.can_stats.error_passive++; state = CAN_STATE_ERROR_PASSIVE; cf->can_id |= CAN_ERR_CRTL; - if (errc & PCH_RP) + if (((errc & PCH_REC) >> 8) > 127) cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; if ((errc & PCH_TEC) > 127) cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE; diff --git a/trunk/drivers/net/can/sja1000/peak_pci.c b/trunk/drivers/net/can/sja1000/peak_pci.c index 214795945bc4..2c7f5036f570 100644 --- a/trunk/drivers/net/can/sja1000/peak_pci.c +++ b/trunk/drivers/net/can/sja1000/peak_pci.c @@ -39,9 +39,9 @@ MODULE_LICENSE("GPL v2"); #define DRV_NAME "peak_pci" struct peak_pci_chan { - void __iomem *cfg_base; /* Common for all channels */ - struct net_device *prev_dev; /* Chain of network devices */ - u16 icr_mask; /* Interrupt mask for fast ack */ + void __iomem *cfg_base; /* Common for all channels */ + struct net_device *next_dev; /* Chain of network devices */ + u16 icr_mask; /* Interrupt mask for fast ack */ }; #define PEAK_PCI_CAN_CLOCK (16000000 / 2) @@ -98,7 +98,7 @@ static int __devinit peak_pci_probe(struct pci_dev *pdev, { struct sja1000_priv *priv; struct peak_pci_chan *chan; - struct net_device *dev; + struct net_device *dev, *dev0 = NULL; void __iomem *cfg_base, *reg_base; u16 sub_sys_id, icr; int i, err, channels; @@ -196,14 +196,18 @@ static int __devinit peak_pci_probe(struct pci_dev *pdev, } /* Create chain of SJA1000 devices */ - chan->prev_dev = pci_get_drvdata(pdev); - pci_set_drvdata(pdev, dev); + if (i == 0) + dev0 = dev; + else + chan->next_dev = dev; dev_info(&pdev->dev, "%s at reg_base=0x%p cfg_base=0x%p irq=%d\n", dev->name, priv->reg_base, chan->cfg_base, dev->irq); } + pci_set_drvdata(pdev, dev0); + /* Enable interrupts */ writew(icr, cfg_base + PITA_ICR + 2); @@ -213,11 +217,12 @@ static int __devinit peak_pci_probe(struct pci_dev *pdev, /* Disable interrupts */ writew(0x0, cfg_base + PITA_ICR + 2); - for (dev = pci_get_drvdata(pdev); dev; dev = chan->prev_dev) { + for (dev = dev0; dev; dev = chan->next_dev) { unregister_sja1000dev(dev); free_sja1000dev(dev); priv = netdev_priv(dev); chan = priv->priv; + dev = chan->next_dev; } pci_iounmap(pdev, reg_base); @@ -236,7 +241,7 @@ static int __devinit peak_pci_probe(struct pci_dev *pdev, static void __devexit peak_pci_remove(struct pci_dev *pdev) { - struct net_device *dev = pci_get_drvdata(pdev); /* Last device */ + struct net_device *dev = pci_get_drvdata(pdev); /* First device */ struct sja1000_priv *priv = netdev_priv(dev); struct peak_pci_chan *chan = priv->priv; void __iomem *cfg_base = chan->cfg_base; @@ -250,7 +255,7 @@ static void __devexit peak_pci_remove(struct pci_dev *pdev) dev_info(&pdev->dev, "removing device %s\n", dev->name); unregister_sja1000dev(dev); free_sja1000dev(dev); - dev = chan->prev_dev; + dev = chan->next_dev; if (!dev) break; priv = netdev_priv(dev); diff --git a/trunk/drivers/net/can/ti_hecc.c b/trunk/drivers/net/can/ti_hecc.c index 5a2e1e3588a1..df809e3f130e 100644 --- a/trunk/drivers/net/can/ti_hecc.c +++ b/trunk/drivers/net/can/ti_hecc.c @@ -745,10 +745,9 @@ static int ti_hecc_error(struct net_device *ndev, int int_status, } } - netif_rx(skb); + netif_receive_skb(skb); stats->rx_packets++; stats->rx_bytes += cf->can_dlc; - return 0; } diff --git a/trunk/drivers/net/can/usb/ems_usb.c b/trunk/drivers/net/can/usb/ems_usb.c index 7dae64d44e83..9697c14b8dc6 100644 --- a/trunk/drivers/net/can/usb/ems_usb.c +++ b/trunk/drivers/net/can/usb/ems_usb.c @@ -627,6 +627,9 @@ static int ems_usb_start(struct ems_usb *dev) err = usb_submit_urb(urb, GFP_KERNEL); if (err) { + if (err == -ENODEV) + netif_device_detach(dev->netdev); + usb_unanchor_urb(urb); usb_free_coherent(dev->udev, RX_BUFFER_SIZE, buf, urb->transfer_dma); @@ -656,6 +659,9 @@ static int ems_usb_start(struct ems_usb *dev) err = usb_submit_urb(dev->intr_urb, GFP_KERNEL); if (err) { + if (err == -ENODEV) + netif_device_detach(dev->netdev); + dev_warn(netdev->dev.parent, "intr URB submit failed: %d\n", err); @@ -686,6 +692,9 @@ static int ems_usb_start(struct ems_usb *dev) return 0; failed: + if (err == -ENODEV) + netif_device_detach(dev->netdev); + dev_warn(netdev->dev.parent, "couldn't submit control: %d\n", err); return err; diff --git a/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 7aee46983be4..03f3935fd8c2 100644 --- a/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -523,6 +523,7 @@ static void bnx2x_tpa_stop(struct bnx2x *bp, struct bnx2x_fastpath *fp, skb = build_skb(data); if (likely(skb)) { + #ifdef BNX2X_STOP_ON_ERROR if (pad + len > fp->rx_buf_size) { BNX2X_ERR("skb_put is about to fail... " @@ -556,7 +557,7 @@ static void bnx2x_tpa_stop(struct bnx2x *bp, struct bnx2x_fastpath *fp, return; } - kfree(new_data); + drop: /* drop the packet and keep the buffer in the bin */ DP(NETIF_MSG_RX_STATUS, diff --git a/trunk/drivers/net/ethernet/brocade/bna/bnad_ethtool.c b/trunk/drivers/net/ethernet/brocade/bna/bnad_ethtool.c index 803ea32aa99d..9b44ec8096ba 100644 --- a/trunk/drivers/net/ethernet/brocade/bna/bnad_ethtool.c +++ b/trunk/drivers/net/ethernet/brocade/bna/bnad_ethtool.c @@ -946,7 +946,7 @@ bnad_get_flash_partition_by_offset(struct bnad *bnad, u32 offset, flash_attr = kzalloc(sizeof(struct bfa_flash_attr), GFP_KERNEL); if (!flash_attr) - return 0; + return -ENOMEM; fcomp.bnad = bnad; fcomp.comp_status = 0; @@ -958,7 +958,7 @@ bnad_get_flash_partition_by_offset(struct bnad *bnad, u32 offset, if (ret != BFA_STATUS_OK) { spin_unlock_irqrestore(&bnad->bna_lock, flags); kfree(flash_attr); - return 0; + goto out_err; } spin_unlock_irqrestore(&bnad->bna_lock, flags); wait_for_completion(&fcomp.comp); @@ -978,6 +978,8 @@ bnad_get_flash_partition_by_offset(struct bnad *bnad, u32 offset, } kfree(flash_attr); return flash_part; +out_err: + return -EINVAL; } static int @@ -1004,7 +1006,7 @@ bnad_get_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom, /* Query the flash partition based on the offset */ flash_part = bnad_get_flash_partition_by_offset(bnad, eeprom->offset, &base_offset); - if (flash_part == 0) + if (flash_part <= 0) return -EFAULT; fcomp.bnad = bnad; @@ -1046,7 +1048,7 @@ bnad_set_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom, /* Query the flash partition based on the offset */ flash_part = bnad_get_flash_partition_by_offset(bnad, eeprom->offset, &base_offset); - if (flash_part == 0) + if (flash_part <= 0) return -EFAULT; fcomp.bnad = bnad; diff --git a/trunk/drivers/net/ethernet/emulex/benet/be_ethtool.c b/trunk/drivers/net/ethernet/emulex/benet/be_ethtool.c index 802e5ddef8a8..6db6b6ae5e9b 100644 --- a/trunk/drivers/net/ethernet/emulex/benet/be_ethtool.c +++ b/trunk/drivers/net/ethernet/emulex/benet/be_ethtool.c @@ -716,8 +716,12 @@ static int be_do_flash(struct net_device *netdev, struct ethtool_flash *efl) { struct be_adapter *adapter = netdev_priv(netdev); + char file_name[ETHTOOL_FLASH_MAX_FILENAME]; - return be_load_fw(adapter, efl->data); + file_name[ETHTOOL_FLASH_MAX_FILENAME - 1] = 0; + strcpy(file_name, efl->data); + + return be_load_fw(adapter, file_name); } static int diff --git a/trunk/drivers/net/ethernet/freescale/fec.c b/trunk/drivers/net/ethernet/freescale/fec.c index 1c7aad8fa19c..7b25e9cf13f6 100644 --- a/trunk/drivers/net/ethernet/freescale/fec.c +++ b/trunk/drivers/net/ethernet/freescale/fec.c @@ -990,7 +990,7 @@ static int fec_enet_mii_probe(struct net_device *ndev) phy_id = 0; } - snprintf(phy_name, sizeof(phy_name), PHY_ID_FMT, mdio_bus_id, phy_id); + snprintf(phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT, mdio_bus_id, phy_id); phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, 0, fep->phy_interface); if (IS_ERR(phy_dev)) { diff --git a/trunk/drivers/net/ethernet/intel/e1000/e1000_main.c b/trunk/drivers/net/ethernet/intel/e1000/e1000_main.c index d94d64b5d695..669ca3800c01 100644 --- a/trunk/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/trunk/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -4740,14 +4740,12 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake) e1000_setup_rctl(adapter); e1000_set_rx_mode(netdev); - rctl = er32(RCTL); - /* turn on all-multi mode if wake on multicast is enabled */ - if (wufc & E1000_WUFC_MC) + if (wufc & E1000_WUFC_MC) { + rctl = er32(RCTL); rctl |= E1000_RCTL_MPE; - - /* enable receives in the hardware */ - ew32(RCTL, rctl | E1000_RCTL_EN); + ew32(RCTL, rctl); + } if (hw->mac_type >= e1000_82540) { ctrl = er32(CTRL); diff --git a/trunk/drivers/net/ethernet/intel/igb/igb_main.c b/trunk/drivers/net/ethernet/intel/igb/igb_main.c index 94be6c32fa7d..e91d73c8aa4e 100644 --- a/trunk/drivers/net/ethernet/intel/igb/igb_main.c +++ b/trunk/drivers/net/ethernet/intel/igb/igb_main.c @@ -5012,8 +5012,7 @@ static int igb_find_enabled_vfs(struct igb_adapter *adapter) vf_devfn = pdev->devfn + 0x80; pvfdev = pci_get_device(hw->vendor_id, device_id, NULL); while (pvfdev) { - if (pvfdev->devfn == vf_devfn && - (pvfdev->bus->number >= pdev->bus->number)) + if (pvfdev->devfn == vf_devfn) vfs_found++; vf_devfn += vf_stride; pvfdev = pci_get_device(hw->vendor_id, diff --git a/trunk/drivers/net/ethernet/intel/igbvf/Makefile b/trunk/drivers/net/ethernet/intel/igbvf/Makefile index 044b0ad5fcb9..0fa3db3dd8b6 100644 --- a/trunk/drivers/net/ethernet/intel/igbvf/Makefile +++ b/trunk/drivers/net/ethernet/intel/igbvf/Makefile @@ -1,7 +1,7 @@ ################################################################################ # # Intel(R) 82576 Virtual Function Linux driver -# Copyright(c) 2009 - 2012 Intel Corporation. +# Copyright(c) 2009 - 2010 Intel Corporation. # # This program is free software; you can redistribute it and/or modify it # under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/igbvf/defines.h b/trunk/drivers/net/ethernet/intel/igbvf/defines.h index 33f40d3474ae..79f2604673fe 100644 --- a/trunk/drivers/net/ethernet/intel/igbvf/defines.h +++ b/trunk/drivers/net/ethernet/intel/igbvf/defines.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) 82576 Virtual Function Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/igbvf/ethtool.c b/trunk/drivers/net/ethernet/intel/igbvf/ethtool.c index db7dce2351c2..2dba53446064 100644 --- a/trunk/drivers/net/ethernet/intel/igbvf/ethtool.c +++ b/trunk/drivers/net/ethernet/intel/igbvf/ethtool.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) 82576 Virtual Function Linux driver - Copyright(c) 2009 - 2012 Intel Corporation. + Copyright(c) 2009 - 2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/igbvf/igbvf.h b/trunk/drivers/net/ethernet/intel/igbvf/igbvf.h index 2c6d87e4d3d9..fd4a7b780fdd 100644 --- a/trunk/drivers/net/ethernet/intel/igbvf/igbvf.h +++ b/trunk/drivers/net/ethernet/intel/igbvf/igbvf.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) 82576 Virtual Function Linux driver - Copyright(c) 2009 - 2012 Intel Corporation. + Copyright(c) 2009 - 2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/igbvf/mbx.c b/trunk/drivers/net/ethernet/intel/igbvf/mbx.c index b4b65bc9fc5d..048aae248d06 100644 --- a/trunk/drivers/net/ethernet/intel/igbvf/mbx.c +++ b/trunk/drivers/net/ethernet/intel/igbvf/mbx.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) 82576 Virtual Function Linux driver - Copyright(c) 2009 - 2012 Intel Corporation. + Copyright(c) 2009 - 2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/igbvf/mbx.h b/trunk/drivers/net/ethernet/intel/igbvf/mbx.h index 24370bcb0e22..c2883c45d477 100644 --- a/trunk/drivers/net/ethernet/intel/igbvf/mbx.h +++ b/trunk/drivers/net/ethernet/intel/igbvf/mbx.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) 82576 Virtual Function Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/igbvf/netdev.c b/trunk/drivers/net/ethernet/intel/igbvf/netdev.c index 4e9141cfe81d..a4b20c865759 100644 --- a/trunk/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/trunk/drivers/net/ethernet/intel/igbvf/netdev.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) 82576 Virtual Function Linux driver - Copyright(c) 2009 - 2012 Intel Corporation. + Copyright(c) 2009 - 2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -53,7 +53,7 @@ const char igbvf_driver_version[] = DRV_VERSION; static const char igbvf_driver_string[] = "Intel(R) Gigabit Virtual Function Network Driver"; static const char igbvf_copyright[] = - "Copyright (c) 2009 - 2012 Intel Corporation."; + "Copyright (c) 2009 - 2011 Intel Corporation."; static int igbvf_poll(struct napi_struct *napi, int budget); static void igbvf_reset(struct igbvf_adapter *); diff --git a/trunk/drivers/net/ethernet/intel/igbvf/regs.h b/trunk/drivers/net/ethernet/intel/igbvf/regs.h index 7dc6341715dc..77e18d3d6b15 100644 --- a/trunk/drivers/net/ethernet/intel/igbvf/regs.h +++ b/trunk/drivers/net/ethernet/intel/igbvf/regs.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) 82576 Virtual Function Linux driver - Copyright(c) 2009 - 2012 Intel Corporation. + Copyright(c) 2009 - 2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/igbvf/vf.c b/trunk/drivers/net/ethernet/intel/igbvf/vf.c index 19551977b352..af3822f9ea9a 100644 --- a/trunk/drivers/net/ethernet/intel/igbvf/vf.c +++ b/trunk/drivers/net/ethernet/intel/igbvf/vf.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) 82576 Virtual Function Linux driver - Copyright(c) 2009 - 2012 Intel Corporation. + Copyright(c) 2009 - 2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/igbvf/vf.h b/trunk/drivers/net/ethernet/intel/igbvf/vf.h index 57db3c68dfcd..d7ed58fcd9bb 100644 --- a/trunk/drivers/net/ethernet/intel/igbvf/vf.h +++ b/trunk/drivers/net/ethernet/intel/igbvf/vf.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) 82576 Virtual Function Linux driver - Copyright(c) 2009 - 2012 Intel Corporation. + Copyright(c) 2009 - 2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/Makefile b/trunk/drivers/net/ethernet/intel/ixgbe/Makefile index 7a16177a12a5..7d7387fbdecd 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/Makefile +++ b/trunk/drivers/net/ethernet/intel/ixgbe/Makefile @@ -1,7 +1,7 @@ ################################################################################ # # Intel 10 Gigabit PCI Express Linux driver -# Copyright(c) 1999 - 2012 Intel Corporation. +# Copyright(c) 1999 - 2010 Intel Corporation. # # This program is free software; you can redistribute it and/or modify it # under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe.h index e6aeb64105a4..258164d6d45a 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c index b406c367b190..ef2afefb0cd4 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c index 4e59083a3de2..772072147bea 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c index 383b9413292e..a3aa6333073f 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h index 2c834c46bba1..863f9c1f145b 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -75,7 +75,7 @@ s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw, s32 ixgbe_enable_mc_generic(struct ixgbe_hw *hw); s32 ixgbe_disable_mc_generic(struct ixgbe_hw *hw); s32 ixgbe_enable_rx_dma_generic(struct ixgbe_hw *hw, u32 regval); -s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw, s32 packetbuf_num); +s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw, s32 packtetbuf_num); s32 ixgbe_fc_autoneg(struct ixgbe_hw *hw); s32 ixgbe_validate_mac_addr(u8 *mac_addr); diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c index 8bfaaee5ac5b..318caf4bf623 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h index 24333b718166..e162775064da 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c index d3695edfcb8b..fcd0e479721f 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.h b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.h index ba835708fcac..2f318935561a 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.h +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c index 888a419dc3d9..32cd97bc794d 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h index 4dec47faeb00..a59d5dc59d04 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c index 79a92fe987b9..da31735311f1 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -112,8 +112,6 @@ static u8 ixgbe_dcbnl_get_state(struct net_device *netdev) static u8 ixgbe_dcbnl_set_state(struct net_device *netdev, u8 state) { u8 err = 0; - u8 prio_tc[MAX_USER_PRIORITY] = {0}; - int i; struct ixgbe_adapter *adapter = netdev_priv(netdev); /* Fail command if not in CEE mode */ @@ -124,15 +122,10 @@ static u8 ixgbe_dcbnl_set_state(struct net_device *netdev, u8 state) if (!!state != !(adapter->flags & IXGBE_FLAG_DCB_ENABLED)) return err; - if (state > 0) { + if (state > 0) err = ixgbe_setup_tc(netdev, adapter->dcb_cfg.num_tcs.pg_tcs); - ixgbe_dcb_unpack_map(&adapter->dcb_cfg, DCB_TX_CONFIG, prio_tc); - } else { + else err = ixgbe_setup_tc(netdev, 0); - } - - for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) - netdev_set_prio_tc_map(netdev, i, prio_tc[i]); return err; } diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index a62975480e37..da7e580f517a 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -58,7 +58,7 @@ struct ixgbe_stats { sizeof(((struct rtnl_link_stats64 *)0)->m), \ offsetof(struct rtnl_link_stats64, m) -static const struct ixgbe_stats ixgbe_gstrings_stats[] = { +static struct ixgbe_stats ixgbe_gstrings_stats[] = { {"rx_packets", IXGBE_NETDEV_STAT(rx_packets)}, {"tx_packets", IXGBE_NETDEV_STAT(tx_packets)}, {"rx_bytes", IXGBE_NETDEV_STAT(rx_bytes)}, @@ -120,23 +120,19 @@ static const struct ixgbe_stats ixgbe_gstrings_stats[] = { #endif /* IXGBE_FCOE */ }; -/* ixgbe allocates num_tx_queues and num_rx_queues symmetrically so - * we set the num_rx_queues to evaluate to num_tx_queues. This is - * used because we do not have a good way to get the max number of - * rx queues with CONFIG_RPS disabled. - */ -#define IXGBE_NUM_RX_QUEUES netdev->num_tx_queues - -#define IXGBE_QUEUE_STATS_LEN ( \ - (netdev->num_tx_queues + IXGBE_NUM_RX_QUEUES) * \ +#define IXGBE_QUEUE_STATS_LEN \ + ((((struct ixgbe_adapter *)netdev_priv(netdev))->num_tx_queues + \ + ((struct ixgbe_adapter *)netdev_priv(netdev))->num_rx_queues) * \ (sizeof(struct ixgbe_queue_stats) / sizeof(u64))) #define IXGBE_GLOBAL_STATS_LEN ARRAY_SIZE(ixgbe_gstrings_stats) #define IXGBE_PB_STATS_LEN ( \ - (sizeof(((struct ixgbe_adapter *)0)->stats.pxonrxc) + \ - sizeof(((struct ixgbe_adapter *)0)->stats.pxontxc) + \ - sizeof(((struct ixgbe_adapter *)0)->stats.pxoffrxc) + \ - sizeof(((struct ixgbe_adapter *)0)->stats.pxofftxc)) \ - / sizeof(u64)) + (((struct ixgbe_adapter *)netdev_priv(netdev))->flags & \ + IXGBE_FLAG_DCB_ENABLED) ? \ + (sizeof(((struct ixgbe_adapter *)0)->stats.pxonrxc) + \ + sizeof(((struct ixgbe_adapter *)0)->stats.pxontxc) + \ + sizeof(((struct ixgbe_adapter *)0)->stats.pxoffrxc) + \ + sizeof(((struct ixgbe_adapter *)0)->stats.pxofftxc)) \ + / sizeof(u64) : 0) #define IXGBE_STATS_LEN (IXGBE_GLOBAL_STATS_LEN + \ IXGBE_PB_STATS_LEN + \ IXGBE_QUEUE_STATS_LEN) @@ -1082,15 +1078,8 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, data[i] = (ixgbe_gstrings_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } - for (j = 0; j < IXGBE_NUM_RX_QUEUES; j++) { + for (j = 0; j < adapter->num_tx_queues; j++) { ring = adapter->tx_ring[j]; - if (!ring) { - data[i] = 0; - data[i+1] = 0; - i += 2; - continue; - } - do { start = u64_stats_fetch_begin_bh(&ring->syncp); data[i] = ring->stats.packets; @@ -1098,15 +1087,8 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, } while (u64_stats_fetch_retry_bh(&ring->syncp, start)); i += 2; } - for (j = 0; j < IXGBE_NUM_RX_QUEUES; j++) { + for (j = 0; j < adapter->num_rx_queues; j++) { ring = adapter->rx_ring[j]; - if (!ring) { - data[i] = 0; - data[i+1] = 0; - i += 2; - continue; - } - do { start = u64_stats_fetch_begin_bh(&ring->syncp); data[i] = ring->stats.packets; @@ -1114,20 +1096,22 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, } while (u64_stats_fetch_retry_bh(&ring->syncp, start)); i += 2; } - - for (j = 0; j < IXGBE_MAX_PACKET_BUFFERS; j++) { - data[i++] = adapter->stats.pxontxc[j]; - data[i++] = adapter->stats.pxofftxc[j]; - } - for (j = 0; j < IXGBE_MAX_PACKET_BUFFERS; j++) { - data[i++] = adapter->stats.pxonrxc[j]; - data[i++] = adapter->stats.pxoffrxc[j]; + if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) { + for (j = 0; j < MAX_TX_PACKET_BUFFERS; j++) { + data[i++] = adapter->stats.pxontxc[j]; + data[i++] = adapter->stats.pxofftxc[j]; + } + for (j = 0; j < MAX_RX_PACKET_BUFFERS; j++) { + data[i++] = adapter->stats.pxonrxc[j]; + data[i++] = adapter->stats.pxoffrxc[j]; + } } } static void ixgbe_get_strings(struct net_device *netdev, u32 stringset, u8 *data) { + struct ixgbe_adapter *adapter = netdev_priv(netdev); char *p = (char *)data; int i; @@ -1142,29 +1126,31 @@ static void ixgbe_get_strings(struct net_device *netdev, u32 stringset, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } - for (i = 0; i < netdev->num_tx_queues; i++) { + for (i = 0; i < adapter->num_tx_queues; i++) { sprintf(p, "tx_queue_%u_packets", i); p += ETH_GSTRING_LEN; sprintf(p, "tx_queue_%u_bytes", i); p += ETH_GSTRING_LEN; } - for (i = 0; i < IXGBE_NUM_RX_QUEUES; i++) { + for (i = 0; i < adapter->num_rx_queues; i++) { sprintf(p, "rx_queue_%u_packets", i); p += ETH_GSTRING_LEN; sprintf(p, "rx_queue_%u_bytes", i); p += ETH_GSTRING_LEN; } - for (i = 0; i < IXGBE_MAX_PACKET_BUFFERS; i++) { - sprintf(p, "tx_pb_%u_pxon", i); - p += ETH_GSTRING_LEN; - sprintf(p, "tx_pb_%u_pxoff", i); - p += ETH_GSTRING_LEN; - } - for (i = 0; i < IXGBE_MAX_PACKET_BUFFERS; i++) { - sprintf(p, "rx_pb_%u_pxon", i); - p += ETH_GSTRING_LEN; - sprintf(p, "rx_pb_%u_pxoff", i); - p += ETH_GSTRING_LEN; + if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) { + for (i = 0; i < MAX_TX_PACKET_BUFFERS; i++) { + sprintf(p, "tx_pb_%u_pxon", i); + p += ETH_GSTRING_LEN; + sprintf(p, "tx_pb_%u_pxoff", i); + p += ETH_GSTRING_LEN; + } + for (i = 0; i < MAX_RX_PACKET_BUFFERS; i++) { + sprintf(p, "rx_pb_%u_pxon", i); + p += ETH_GSTRING_LEN; + sprintf(p, "rx_pb_%u_pxoff", i); + p += ETH_GSTRING_LEN; + } } /* BUG_ON(p - data != IXGBE_STATS_LEN * ETH_GSTRING_LEN); */ break; diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c index 4bc794249801..d18d6157dd2c 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h index 1dbed17c8107..261fd62dda18 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 3dc6cef58107..1ee5d0fbb905 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -64,7 +64,7 @@ char ixgbe_default_device_descr[] = __stringify(BUILD) "-k" const char ixgbe_driver_version[] = DRV_VERSION; static const char ixgbe_copyright[] = - "Copyright (c) 1999-2012 Intel Corporation."; + "Copyright (c) 1999-2011 Intel Corporation."; static const struct ixgbe_info *ixgbe_info_tbl[] = { [board_82598] = &ixgbe_82598_info, @@ -2633,22 +2633,22 @@ static void ixgbe_configure_rscctl(struct ixgbe_adapter *adapter, /* * we must limit the number of descriptors so that the * total size of max desc * buf_len is not greater - * than 65536 + * than 65535 */ if (ring_is_ps_enabled(ring)) { -#if (PAGE_SIZE < 8192) +#if (MAX_SKB_FRAGS > 16) rscctrl |= IXGBE_RSCCTL_MAXDESC_16; -#elif (PAGE_SIZE < 16384) +#elif (MAX_SKB_FRAGS > 8) rscctrl |= IXGBE_RSCCTL_MAXDESC_8; -#elif (PAGE_SIZE < 32768) +#elif (MAX_SKB_FRAGS > 4) rscctrl |= IXGBE_RSCCTL_MAXDESC_4; #else rscctrl |= IXGBE_RSCCTL_MAXDESC_1; #endif } else { - if (rx_buf_len <= IXGBE_RXBUFFER_4K) + if (rx_buf_len < IXGBE_RXBUFFER_4K) rscctrl |= IXGBE_RSCCTL_MAXDESC_16; - else if (rx_buf_len <= IXGBE_RXBUFFER_8K) + else if (rx_buf_len < IXGBE_RXBUFFER_8K) rscctrl |= IXGBE_RSCCTL_MAXDESC_8; else rscctrl |= IXGBE_RSCCTL_MAXDESC_4; @@ -2830,7 +2830,7 @@ static void ixgbe_configure_virtualization(struct ixgbe_adapter *adapter) IXGBE_WRITE_REG(hw, IXGBE_VT_CTL, vmdctl | vt_reg_bits); vf_shift = adapter->num_vfs % 32; - reg_offset = (adapter->num_vfs >= 32) ? 1 : 0; + reg_offset = (adapter->num_vfs > 32) ? 1 : 0; /* Enable only the PF's pool for Tx/Rx */ IXGBE_WRITE_REG(hw, IXGBE_VFRE(reg_offset), (1 << vf_shift)); @@ -4330,10 +4330,6 @@ static int ixgbe_set_num_queues(struct ixgbe_adapter *adapter) adapter->num_tx_queues = 1; done: - if ((adapter->netdev->reg_state == NETREG_UNREGISTERED) || - (adapter->netdev->reg_state == NETREG_UNREGISTERING)) - return 0; - /* Notify the stack of the (possibly) reduced queue counts. */ netif_set_real_num_tx_queues(adapter->netdev, adapter->num_tx_queues); return netif_set_real_num_rx_queues(adapter->netdev, diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c index 1f3e32b576a5..3f725d48336d 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h index 310bdd961075..b239bdac38da 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c index b91773551a38..7cf1e1f56c69 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h index cc18165b4c05..197bdd13106a 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index b01ecb4d2bb1..cf6812dd1436 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -67,8 +67,7 @@ static int ixgbe_find_enabled_vfs(struct ixgbe_adapter *adapter) vf_devfn = pdev->devfn + 0x80; pvfdev = pci_get_device(IXGBE_INTEL_VENDOR_ID, device_id, NULL); while (pvfdev) { - if (pvfdev->devfn == vf_devfn && - (pvfdev->bus->number >= pdev->bus->number)) + if (pvfdev->devfn == vf_devfn) vfs_found++; vf_devfn += 2; pvfdev = pci_get_device(IXGBE_INTEL_VENDOR_ID, @@ -647,9 +646,6 @@ static int ixgbe_rcv_msg_from_vf(struct ixgbe_adapter *adapter, u32 vf) ixgbe_ndo_set_vf_spoofchk(adapter->netdev, vf, false); retval = ixgbe_set_vf_macvlan(adapter, vf, index, (unsigned char *)(&msgbuf[1])); - if (retval == -ENOSPC) - e_warn(drv, "VF %d has requested a MACVLAN filter " - "but there is no space for it\n", vf); break; default: e_err(drv, "Unhandled Msg %8.8x\n", msgbuf[0]); diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h index 2ab38d5fda92..e8badab03359 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 9b95bef60970..775602ef90e5 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c index f838a2be8cfb..8cc5eccfd651 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c +++ b/trunk/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbevf/Makefile b/trunk/drivers/net/ethernet/intel/ixgbevf/Makefile index 4ce4c97ef5ad..1f35d229e71a 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbevf/Makefile +++ b/trunk/drivers/net/ethernet/intel/ixgbevf/Makefile @@ -1,7 +1,7 @@ ################################################################################ # # Intel 82599 Virtual Function driver -# Copyright(c) 1999 - 2012 Intel Corporation. +# Copyright(c) 1999 - 2010 Intel Corporation. # # This program is free software; you can redistribute it and/or modify it # under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbevf/defines.h b/trunk/drivers/net/ethernet/intel/ixgbevf/defines.h index 947b5c830735..2eb89cb94a0d 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbevf/defines.h +++ b/trunk/drivers/net/ethernet/intel/ixgbevf/defines.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 82599 Virtual Function driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/trunk/drivers/net/ethernet/intel/ixgbevf/ethtool.c index 2bfe0d1d7958..c85700318147 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbevf/ethtool.c +++ b/trunk/drivers/net/ethernet/intel/ixgbevf/ethtool.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 82599 Virtual Function driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2009 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h b/trunk/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h index dfed420a1bf6..9075c1d61039 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h +++ b/trunk/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 82599 Virtual Function driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/trunk/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index e51d552410ae..bed411bada21 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/trunk/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 82599 Virtual Function driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -60,7 +60,7 @@ static const char ixgbevf_driver_string[] = #define DRV_VERSION "2.2.0-k" const char ixgbevf_driver_version[] = DRV_VERSION; static char ixgbevf_copyright[] = - "Copyright (c) 2009 - 2012 Intel Corporation."; + "Copyright (c) 2009 - 2010 Intel Corporation."; static const struct ixgbevf_info *ixgbevf_info_tbl[] = { [board_82599_vf] = &ixgbevf_82599_vf_info, @@ -935,11 +935,7 @@ static irqreturn_t ixgbevf_msix_mbx(int irq, void *data) if (msg & IXGBE_VT_MSGTYPE_NACK) pr_warn("Last Request of type %2.2x to PF Nacked\n", msg & 0xFF); - /* - * Restore the PFSTS bit in case someone is polling for a - * return message from the PF - */ - hw->mbx.v2p_mailbox |= IXGBE_VFMAILBOX_PFSTS; + goto out; } /* @@ -949,7 +945,7 @@ static irqreturn_t ixgbevf_msix_mbx(int irq, void *data) */ if (got_ack) hw->mbx.v2p_mailbox |= IXGBE_VFMAILBOX_PFACK; - +out: return IRQ_HANDLED; } diff --git a/trunk/drivers/net/ethernet/intel/ixgbevf/mbx.c b/trunk/drivers/net/ethernet/intel/ixgbevf/mbx.c index 9c955900fe64..13532d9ba72d 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbevf/mbx.c +++ b/trunk/drivers/net/ethernet/intel/ixgbevf/mbx.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 82599 Virtual Function driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbevf/mbx.h b/trunk/drivers/net/ethernet/intel/ixgbevf/mbx.h index cf9131c5c115..9d38a94a348a 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbevf/mbx.h +++ b/trunk/drivers/net/ethernet/intel/ixgbevf/mbx.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 82599 Virtual Function driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbevf/regs.h b/trunk/drivers/net/ethernet/intel/ixgbevf/regs.h index debd8c0e1f28..5e4d5e5cdf38 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbevf/regs.h +++ b/trunk/drivers/net/ethernet/intel/ixgbevf/regs.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 82599 Virtual Function driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/intel/ixgbevf/vf.c b/trunk/drivers/net/ethernet/intel/ixgbevf/vf.c index 74be7411242a..d0138d7a31a1 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbevf/vf.c +++ b/trunk/drivers/net/ethernet/intel/ixgbevf/vf.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 82599 Virtual Function driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -283,17 +283,6 @@ static s32 ixgbevf_set_rar_vf(struct ixgbe_hw *hw, u32 index, u8 *addr, return ret_val; } -static void ixgbevf_write_msg_read_ack(struct ixgbe_hw *hw, - u32 *msg, u16 size) -{ - struct ixgbe_mbx_info *mbx = &hw->mbx; - u32 retmsg[IXGBE_VFMAILBOX_SIZE]; - s32 retval = mbx->ops.write_posted(hw, msg, size); - - if (!retval) - mbx->ops.read_posted(hw, retmsg, size); -} - /** * ixgbevf_update_mc_addr_list_vf - Update Multicast addresses * @hw: pointer to the HW structure @@ -305,6 +294,7 @@ static s32 ixgbevf_update_mc_addr_list_vf(struct ixgbe_hw *hw, struct net_device *netdev) { struct netdev_hw_addr *ha; + struct ixgbe_mbx_info *mbx = &hw->mbx; u32 msgbuf[IXGBE_VFMAILBOX_SIZE]; u16 *vector_list = (u16 *)&msgbuf[1]; u32 cnt, i; @@ -331,7 +321,7 @@ static s32 ixgbevf_update_mc_addr_list_vf(struct ixgbe_hw *hw, vector_list[i++] = ixgbevf_mta_vector(hw, ha->addr); } - ixgbevf_write_msg_read_ack(hw, msgbuf, IXGBE_VFMAILBOX_SIZE); + mbx->ops.write_posted(hw, msgbuf, IXGBE_VFMAILBOX_SIZE); return 0; } @@ -346,6 +336,7 @@ static s32 ixgbevf_update_mc_addr_list_vf(struct ixgbe_hw *hw, static s32 ixgbevf_set_vfta_vf(struct ixgbe_hw *hw, u32 vlan, u32 vind, bool vlan_on) { + struct ixgbe_mbx_info *mbx = &hw->mbx; u32 msgbuf[2]; msgbuf[0] = IXGBE_VF_SET_VLAN; @@ -353,9 +344,7 @@ static s32 ixgbevf_set_vfta_vf(struct ixgbe_hw *hw, u32 vlan, u32 vind, /* Setting the 8 bit field MSG INFO to TRUE indicates "add" */ msgbuf[0] |= vlan_on << IXGBE_VT_MSGINFO_SHIFT; - ixgbevf_write_msg_read_ack(hw, msgbuf, 2); - - return 0; + return mbx->ops.write_posted(hw, msgbuf, 2); } /** diff --git a/trunk/drivers/net/ethernet/intel/ixgbevf/vf.h b/trunk/drivers/net/ethernet/intel/ixgbevf/vf.h index 25c951daee5d..d556619a9212 100644 --- a/trunk/drivers/net/ethernet/intel/ixgbevf/vf.h +++ b/trunk/drivers/net/ethernet/intel/ixgbevf/vf.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 82599 Virtual Function driver - Copyright(c) 1999 - 2012 Intel Corporation. + Copyright(c) 1999 - 2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/trunk/drivers/net/ethernet/marvell/skge.c b/trunk/drivers/net/ethernet/marvell/skge.c index 33947ac595c0..edb9bda55d55 100644 --- a/trunk/drivers/net/ethernet/marvell/skge.c +++ b/trunk/drivers/net/ethernet/marvell/skge.c @@ -931,17 +931,20 @@ static int skge_ring_alloc(struct skge_ring *ring, void *vaddr, u32 base) } /* Allocate and setup a new buffer for receiving */ -static void skge_rx_setup(struct skge_port *skge, struct skge_element *e, - struct sk_buff *skb, unsigned int bufsize) +static int skge_rx_setup(struct pci_dev *pdev, + struct skge_element *e, + struct sk_buff *skb, unsigned int bufsize) { struct skge_rx_desc *rd = e->desc; - u64 map; + dma_addr_t map; - map = pci_map_single(skge->hw->pdev, skb->data, bufsize, + map = pci_map_single(pdev, skb->data, bufsize, PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(pdev, map)) + goto mapping_error; - rd->dma_lo = map; - rd->dma_hi = map >> 32; + rd->dma_lo = lower_32_bits(map); + rd->dma_hi = upper_32_bits(map); e->skb = skb; rd->csum1_start = ETH_HLEN; rd->csum2_start = ETH_HLEN; @@ -953,6 +956,13 @@ static void skge_rx_setup(struct skge_port *skge, struct skge_element *e, rd->control = BMU_OWN | BMU_STF | BMU_IRQ_EOF | BMU_TCP_CHECK | bufsize; dma_unmap_addr_set(e, mapaddr, map); dma_unmap_len_set(e, maplen, bufsize); + return 0; + +mapping_error: + if (net_ratelimit()) + dev_warn(&pdev->dev, "%s: rx mapping error\n", + skb->dev->name); + return -EIO; } /* Resume receiving using existing skb, @@ -1014,7 +1024,11 @@ static int skge_rx_fill(struct net_device *dev) return -ENOMEM; skb_reserve(skb, NET_IP_ALIGN); - skge_rx_setup(skge, e, skb, skge->rx_buf_size); + if (skge_rx_setup(skge->hw->pdev, e, skb, skge->rx_buf_size)) { + kfree_skb(skb); + return -ENOMEM; + } + } while ((e = e->next) != ring->start); ring->to_clean = ring->start; @@ -2729,7 +2743,7 @@ static netdev_tx_t skge_xmit_frame(struct sk_buff *skb, struct skge_tx_desc *td; int i; u32 control, len; - u64 map; + dma_addr_t map; if (skb_padto(skb, ETH_ZLEN)) return NETDEV_TX_OK; @@ -2743,11 +2757,14 @@ static netdev_tx_t skge_xmit_frame(struct sk_buff *skb, e->skb = skb; len = skb_headlen(skb); map = pci_map_single(hw->pdev, skb->data, len, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(hw->pdev, map)) + goto mapping_error; + dma_unmap_addr_set(e, mapaddr, map); dma_unmap_len_set(e, maplen, len); - td->dma_lo = map; - td->dma_hi = map >> 32; + td->dma_lo = lower_32_bits(map); + td->dma_hi = upper_32_bits(map); if (skb->ip_summed == CHECKSUM_PARTIAL) { const int offset = skb_checksum_start_offset(skb); @@ -2778,14 +2795,16 @@ static netdev_tx_t skge_xmit_frame(struct sk_buff *skb, map = skb_frag_dma_map(&hw->pdev->dev, frag, 0, skb_frag_size(frag), DMA_TO_DEVICE); + if (dma_mapping_error(&hw->pdev->dev, map)) + goto mapping_unwind; e = e->next; e->skb = skb; tf = e->desc; BUG_ON(tf->control & BMU_OWN); - tf->dma_lo = map; - tf->dma_hi = (u64) map >> 32; + tf->dma_lo = lower_32_bits(map); + tf->dma_hi = upper_32_bits(map); dma_unmap_addr_set(e, mapaddr, map); dma_unmap_len_set(e, maplen, skb_frag_size(frag)); @@ -2815,6 +2834,28 @@ static netdev_tx_t skge_xmit_frame(struct sk_buff *skb, } return NETDEV_TX_OK; + +mapping_unwind: + /* unroll any pages that were already mapped. */ + if (e != skge->tx_ring.to_use) { + struct skge_element *u; + + for (u = skge->tx_ring.to_use->next; u != e; u = u->next) + pci_unmap_page(hw->pdev, dma_unmap_addr(u, mapaddr), + dma_unmap_len(u, maplen), + PCI_DMA_TODEVICE); + e = skge->tx_ring.to_use; + } + /* undo the mapping for the skb header */ + pci_unmap_single(hw->pdev, dma_unmap_addr(e, mapaddr), + dma_unmap_len(e, maplen), + PCI_DMA_TODEVICE); +mapping_error: + /* mapping error causes error message and packet to be discarded. */ + if (net_ratelimit()) + dev_warn(&hw->pdev->dev, "%s: tx mapping error\n", dev->name); + dev_kfree_skb(skb); + return NETDEV_TX_OK; } @@ -3058,13 +3099,17 @@ static struct sk_buff *skge_rx_get(struct net_device *dev, if (!nskb) goto resubmit; + if (unlikely(skge_rx_setup(skge->hw->pdev, e, nskb, skge->rx_buf_size))) { + dev_kfree_skb(nskb); + goto resubmit; + } + pci_unmap_single(skge->hw->pdev, dma_unmap_addr(e, mapaddr), dma_unmap_len(e, maplen), PCI_DMA_FROMDEVICE); skb = e->skb; prefetch(skb->data); - skge_rx_setup(skge, e, nskb, skge->rx_buf_size); } skb_put(skb, len); diff --git a/trunk/drivers/net/ethernet/mellanox/mlx4/cmd.c b/trunk/drivers/net/ethernet/mellanox/mlx4/cmd.c index eaf09d4f02d0..405e6ac3faf6 100644 --- a/trunk/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/trunk/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -1616,12 +1616,12 @@ void mlx4_multi_func_cleanup(struct mlx4_dev *dev) kfree(priv->mfunc.master.slave_state[i].vlan_filter[port]); } kfree(priv->mfunc.master.slave_state); + iounmap(priv->mfunc.comm); + dma_free_coherent(&(dev->pdev->dev), PAGE_SIZE, + priv->mfunc.vhcr, + priv->mfunc.vhcr_dma); + priv->mfunc.vhcr = NULL; } - - iounmap(priv->mfunc.comm); - dma_free_coherent(&(dev->pdev->dev), PAGE_SIZE, - priv->mfunc.vhcr, priv->mfunc.vhcr_dma); - priv->mfunc.vhcr = NULL; } void mlx4_cmd_cleanup(struct mlx4_dev *dev) diff --git a/trunk/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/trunk/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 149e60da0a32..467ae5824875 100644 --- a/trunk/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/trunk/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -892,8 +892,7 @@ void mlx4_en_free_resources(struct mlx4_en_priv *priv) for (i = 0; i < priv->rx_ring_num; i++) { if (priv->rx_ring[i].rx_info) - mlx4_en_destroy_rx_ring(priv, &priv->rx_ring[i], - priv->prof->rx_ring_size, priv->stride); + mlx4_en_destroy_rx_ring(priv, &priv->rx_ring[i]); if (priv->rx_cq[i].buf) mlx4_en_destroy_cq(priv, &priv->rx_cq[i]); } diff --git a/trunk/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/trunk/drivers/net/ethernet/mellanox/mlx4/en_rx.c index d4ad8c226b51..971d4b6b8dfe 100644 --- a/trunk/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/trunk/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -168,12 +168,8 @@ static int mlx4_en_prepare_rx_desc(struct mlx4_en_priv *priv, return 0; err: - while (i--) { - dma_addr_t dma = be64_to_cpu(rx_desc->data[i].addr); - pci_unmap_single(priv->mdev->pdev, dma, skb_frags[i].size, - PCI_DMA_FROMDEVICE); + while (i--) put_page(skb_frags[i].page); - } return -ENOMEM; } @@ -384,12 +380,12 @@ int mlx4_en_activate_rx_rings(struct mlx4_en_priv *priv) } void mlx4_en_destroy_rx_ring(struct mlx4_en_priv *priv, - struct mlx4_en_rx_ring *ring, u32 size, u16 stride) + struct mlx4_en_rx_ring *ring) { struct mlx4_en_dev *mdev = priv->mdev; mlx4_en_unmap_buffer(&ring->wqres.buf); - mlx4_free_hwq_res(mdev->dev, &ring->wqres, size * stride + TXBB_SIZE); + mlx4_free_hwq_res(mdev->dev, &ring->wqres, ring->buf_size + TXBB_SIZE); vfree(ring->rx_info); ring->rx_info = NULL; } diff --git a/trunk/drivers/net/ethernet/mellanox/mlx4/mcg.c b/trunk/drivers/net/ethernet/mellanox/mlx4/mcg.c index ca574d850b39..0785d9b2a265 100644 --- a/trunk/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/trunk/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -136,7 +136,7 @@ static int new_steering_entry(struct mlx4_dev *dev, u8 port, u32 prot; int err; - s_steer = &mlx4_priv(dev)->steer[port - 1]; + s_steer = &mlx4_priv(dev)->steer[0]; new_entry = kzalloc(sizeof *new_entry, GFP_KERNEL); if (!new_entry) return -ENOMEM; @@ -220,7 +220,7 @@ static int existing_steering_entry(struct mlx4_dev *dev, u8 port, struct mlx4_promisc_qp *pqp; struct mlx4_promisc_qp *dqp; - s_steer = &mlx4_priv(dev)->steer[port - 1]; + s_steer = &mlx4_priv(dev)->steer[0]; pqp = get_promisc_qp(dev, 0, steer, qpn); if (!pqp) @@ -265,7 +265,7 @@ static bool check_duplicate_entry(struct mlx4_dev *dev, u8 port, struct mlx4_steer_index *tmp_entry, *entry = NULL; struct mlx4_promisc_qp *dqp, *tmp_dqp; - s_steer = &mlx4_priv(dev)->steer[port - 1]; + s_steer = &mlx4_priv(dev)->steer[0]; /* if qp is not promisc, it cannot be duplicated */ if (!get_promisc_qp(dev, 0, steer, qpn)) @@ -306,7 +306,7 @@ static bool can_remove_steering_entry(struct mlx4_dev *dev, u8 port, bool ret = false; int i; - s_steer = &mlx4_priv(dev)->steer[port - 1]; + s_steer = &mlx4_priv(dev)->steer[0]; mailbox = mlx4_alloc_cmd_mailbox(dev); if (IS_ERR(mailbox)) @@ -361,7 +361,7 @@ static int add_promisc_qp(struct mlx4_dev *dev, u8 port, int err; struct mlx4_priv *priv = mlx4_priv(dev); - s_steer = &mlx4_priv(dev)->steer[port - 1]; + s_steer = &mlx4_priv(dev)->steer[0]; mutex_lock(&priv->mcg_table.mutex); @@ -466,7 +466,7 @@ static int remove_promisc_qp(struct mlx4_dev *dev, u8 port, int loc, i; int err; - s_steer = &mlx4_priv(dev)->steer[port - 1]; + s_steer = &mlx4_priv(dev)->steer[0]; mutex_lock(&priv->mcg_table.mutex); pqp = get_promisc_qp(dev, 0, steer, qpn); @@ -1004,7 +1004,7 @@ EXPORT_SYMBOL_GPL(mlx4_multicast_promisc_remove); int mlx4_unicast_promisc_add(struct mlx4_dev *dev, u32 qpn, u8 port) { - if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER)) + if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER)) return 0; if (mlx4_is_mfunc(dev)) @@ -1016,7 +1016,7 @@ EXPORT_SYMBOL_GPL(mlx4_unicast_promisc_add); int mlx4_unicast_promisc_remove(struct mlx4_dev *dev, u32 qpn, u8 port) { - if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER)) + if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER)) return 0; if (mlx4_is_mfunc(dev)) diff --git a/trunk/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/trunk/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index d60335f3c473..35f08840813c 100644 --- a/trunk/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/trunk/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -528,8 +528,7 @@ int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv, struct mlx4_en_rx_ring *ring, u32 size, u16 stride); void mlx4_en_destroy_rx_ring(struct mlx4_en_priv *priv, - struct mlx4_en_rx_ring *ring, - u32 size, u16 stride); + struct mlx4_en_rx_ring *ring); int mlx4_en_activate_rx_rings(struct mlx4_en_priv *priv); void mlx4_en_deactivate_rx_ring(struct mlx4_en_priv *priv, struct mlx4_en_rx_ring *ring); diff --git a/trunk/drivers/net/ethernet/micrel/Kconfig b/trunk/drivers/net/ethernet/micrel/Kconfig index fe42fc00d8d3..1ea811cf515b 100644 --- a/trunk/drivers/net/ethernet/micrel/Kconfig +++ b/trunk/drivers/net/ethernet/micrel/Kconfig @@ -42,6 +42,7 @@ config KS8851 select NET_CORE select MII select CRC32 + select MISC_DEVICES select EEPROM_93CX6 ---help--- SPI driver for Micrel KS8851 SPI attached network chip. diff --git a/trunk/drivers/net/ethernet/renesas/sh_eth.c b/trunk/drivers/net/ethernet/renesas/sh_eth.c index 87b650131774..813d41c4a845 100644 --- a/trunk/drivers/net/ethernet/renesas/sh_eth.c +++ b/trunk/drivers/net/ethernet/renesas/sh_eth.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include "sh_eth.h" @@ -818,8 +817,7 @@ static int sh_eth_dev_init(struct net_device *ndev) sh_eth_write(ndev, 0, TRIMD); /* Recv frame limit set register */ - sh_eth_write(ndev, ndev->mtu + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN, - RFLR); + sh_eth_write(ndev, RFLR_VALUE, RFLR); sh_eth_write(ndev, sh_eth_read(ndev, EESR), EESR); sh_eth_write(ndev, mdp->cd->eesipr_value, EESIPR); diff --git a/trunk/drivers/net/ethernet/renesas/sh_eth.h b/trunk/drivers/net/ethernet/renesas/sh_eth.h index cdbd844662a7..47877b13ffad 100644 --- a/trunk/drivers/net/ethernet/renesas/sh_eth.h +++ b/trunk/drivers/net/ethernet/renesas/sh_eth.h @@ -575,6 +575,9 @@ enum RPADIR_BIT { RPADIR_PADR = 0x0003f, }; +/* RFLR */ +#define RFLR_VALUE 0x1000 + /* FDR */ #define DEFAULT_FDR_INIT 0x00000707 diff --git a/trunk/drivers/net/ethernet/ti/davinci_emac.c b/trunk/drivers/net/ethernet/ti/davinci_emac.c index 4fa0bcb25dfc..794ac30a577b 100644 --- a/trunk/drivers/net/ethernet/ti/davinci_emac.c +++ b/trunk/drivers/net/ethernet/ti/davinci_emac.c @@ -1600,9 +1600,8 @@ static int emac_dev_open(struct net_device *ndev) if (IS_ERR(priv->phydev)) { dev_err(emac_dev, "could not connect to phy %s\n", priv->phy_id); - ret = PTR_ERR(priv->phydev); priv->phydev = NULL; - return ret; + return PTR_ERR(priv->phydev); } priv->link = 0; diff --git a/trunk/drivers/net/ethernet/ti/davinci_mdio.c b/trunk/drivers/net/ethernet/ti/davinci_mdio.c index af8b8fc39eb2..ef7c9c17bfff 100644 --- a/trunk/drivers/net/ethernet/ti/davinci_mdio.c +++ b/trunk/drivers/net/ethernet/ti/davinci_mdio.c @@ -318,9 +318,9 @@ static int __devinit davinci_mdio_probe(struct platform_device *pdev) data->clk = clk_get(dev, NULL); if (IS_ERR(data->clk)) { + data->clk = NULL; dev_err(dev, "failed to get device clock\n"); ret = PTR_ERR(data->clk); - data->clk = NULL; goto bail_out; } diff --git a/trunk/drivers/net/ethernet/toshiba/Kconfig b/trunk/drivers/net/ethernet/toshiba/Kconfig index 74acb5cf6099..051764704559 100644 --- a/trunk/drivers/net/ethernet/toshiba/Kconfig +++ b/trunk/drivers/net/ethernet/toshiba/Kconfig @@ -5,7 +5,7 @@ config NET_VENDOR_TOSHIBA bool "Toshiba devices" default y - depends on PCI && (PPC_IBM_CELL_BLADE || PPC_CELLEB || MIPS) || PPC_PS3 + depends on PCI && (PPC_IBM_CELL_BLADE || PPC_CELLEB) || PPC_PS3 ---help--- If you have a network (Ethernet) card belonging to this class, say Y and read the Ethernet-HOWTO, available from diff --git a/trunk/drivers/net/ethernet/via/via-velocity.c b/trunk/drivers/net/ethernet/via/via-velocity.c index cb35b14b73bb..4128d6b8cc28 100644 --- a/trunk/drivers/net/ethernet/via/via-velocity.c +++ b/trunk/drivers/net/ethernet/via/via-velocity.c @@ -2491,6 +2491,9 @@ static int velocity_close(struct net_device *dev) if (dev->irq != 0) free_irq(dev->irq, dev); + /* Power down the chip */ + pci_set_power_state(vptr->pdev, PCI_D3hot); + velocity_free_rings(vptr); vptr->flags &= (~VELOCITY_FLAGS_OPENED); diff --git a/trunk/drivers/net/hyperv/netvsc_drv.c b/trunk/drivers/net/hyperv/netvsc_drv.c index 466c58a7353d..1a1ca6cfc74a 100644 --- a/trunk/drivers/net/hyperv/netvsc_drv.c +++ b/trunk/drivers/net/hyperv/netvsc_drv.c @@ -123,7 +123,7 @@ static int netvsc_close(struct net_device *net) struct hv_device *device_obj = net_device_ctx->device_ctx; int ret; - netif_tx_disable(net); + netif_stop_queue(net); ret = rndis_filter_close(device_obj); if (ret != 0) @@ -151,10 +151,10 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) int ret; unsigned int i, num_pages, npg_data; - /* Add multipages for skb->data and additional 2 for RNDIS */ + /* Add multipage for skb->data and additional one for RNDIS */ npg_data = (((unsigned long)skb->data + skb_headlen(skb) - 1) >> PAGE_SHIFT) - ((unsigned long)skb->data >> PAGE_SHIFT) + 1; - num_pages = skb_shinfo(skb)->nr_frags + npg_data + 2; + num_pages = skb_shinfo(skb)->nr_frags + npg_data + 1; /* Allocate a netvsc packet based on # of frags. */ packet = kzalloc(sizeof(struct hv_netvsc_packet) + @@ -173,8 +173,8 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) sizeof(struct hv_netvsc_packet) + (num_pages * sizeof(struct hv_page_buffer)); - /* If the rndis msg goes beyond 1 page, we will add 1 later */ - packet->page_buf_cnt = num_pages - 1; + /* Setup the rndis header */ + packet->page_buf_cnt = num_pages; /* Initialize it from the skb */ packet->total_data_buflen = skb->len; @@ -256,7 +256,7 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, schedule_delayed_work(&ndev_ctx->dwork, msecs_to_jiffies(20)); } else { netif_carrier_off(net); - netif_tx_disable(net); + netif_stop_queue(net); } } @@ -298,7 +298,7 @@ int netvsc_recv_callback(struct hv_device *device_obj, skb->ip_summed = CHECKSUM_NONE; net->stats.rx_packets++; - net->stats.rx_bytes += packet->total_data_buflen; + net->stats.rx_bytes += skb->len; /* * Pass the skb back up. Network stack will deallocate the skb when it @@ -337,7 +337,7 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) nvdev->start_remove = true; cancel_delayed_work_sync(&ndevctx->dwork); - netif_tx_disable(ndev); + netif_stop_queue(ndev); rndis_filter_device_remove(hdev); ndev->mtu = mtu; @@ -460,7 +460,7 @@ static int netvsc_remove(struct hv_device *dev) cancel_delayed_work_sync(&ndev_ctx->dwork); /* Stop outbound asap */ - netif_tx_disable(net); + netif_stop_queue(net); unregister_netdev(net); diff --git a/trunk/drivers/net/hyperv/rndis_filter.c b/trunk/drivers/net/hyperv/rndis_filter.c index 133b7fbf8595..da181f9a49d1 100644 --- a/trunk/drivers/net/hyperv/rndis_filter.c +++ b/trunk/drivers/net/hyperv/rndis_filter.c @@ -321,25 +321,6 @@ static void rndis_filter_receive_data(struct rndis_device *dev, data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset; pkt->total_data_buflen -= data_offset; - - /* - * Make sure we got a valid RNDIS message, now total_data_buflen - * should be the data packet size plus the trailer padding size - */ - if (pkt->total_data_buflen < rndis_pkt->data_len) { - netdev_err(dev->net_dev->ndev, "rndis message buffer " - "overflow detected (got %u, min %u)" - "...dropping this message!\n", - pkt->total_data_buflen, rndis_pkt->data_len); - return; - } - - /* - * Remove the rndis trailer padding from rndis packet message - * rndis_pkt->data_len tell us the real data length, we only copy - * the data packet to the stack, without the rndis trailer padding - */ - pkt->total_data_buflen = rndis_pkt->data_len; pkt->data = (void *)((unsigned long)pkt->data + data_offset); pkt->is_data_pkt = true; @@ -797,19 +778,6 @@ int rndis_filter_send(struct hv_device *dev, (unsigned long)rndisMessage & (PAGE_SIZE-1); pkt->page_buf[0].len = rndisMessageSize; - /* Add one page_buf if the rndis msg goes beyond page boundary */ - if (pkt->page_buf[0].offset + rndisMessageSize > PAGE_SIZE) { - int i; - for (i = pkt->page_buf_cnt; i > 1; i--) - pkt->page_buf[i] = pkt->page_buf[i-1]; - pkt->page_buf_cnt++; - pkt->page_buf[0].len = PAGE_SIZE - pkt->page_buf[0].offset; - pkt->page_buf[1].pfn = virt_to_phys((void *)((ulong) - rndisMessage + pkt->page_buf[0].len)) >> PAGE_SHIFT; - pkt->page_buf[1].offset = 0; - pkt->page_buf[1].len = rndisMessageSize - pkt->page_buf[0].len; - } - /* Save the packet send completion and context */ filterPacket->completion = pkt->completion.send.send_completion; filterPacket->completion_ctx = diff --git a/trunk/drivers/net/tokenring/Kconfig b/trunk/drivers/net/tokenring/Kconfig index 45550d42b368..c7e0149d1514 100644 --- a/trunk/drivers/net/tokenring/Kconfig +++ b/trunk/drivers/net/tokenring/Kconfig @@ -7,6 +7,7 @@ menuconfig TR bool "Token Ring driver support" depends on NETDEVICES && !UML depends on (PCI || ISA || MCA || CCW || PCMCIA) + select LLC help Token Ring is IBM's way of communication on a local network; the rest of the world uses Ethernet. To participate on a Token Ring @@ -19,10 +20,6 @@ menuconfig TR if TR -config WANT_LLC - def_bool y - select LLC - config PCMCIA_IBMTR tristate "IBM PCMCIA tokenring adapter support" depends on IBMTR!=y && PCMCIA diff --git a/trunk/drivers/net/wireless/ath/ath9k/hw.c b/trunk/drivers/net/wireless/ath/ath9k/hw.c index 87db1ee1c298..ee7759575050 100644 --- a/trunk/drivers/net/wireless/ath/ath9k/hw.c +++ b/trunk/drivers/net/wireless/ath/ath9k/hw.c @@ -1037,16 +1037,13 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah) /* * Workaround for early ACK timeouts, add an offset to match the - * initval's 64us ack timeout value. Use 48us for the CTS timeout. + * initval's 64us ack timeout value. * This was initially only meant to work around an issue with delayed * BA frames in some implementations, but it has been found to fix ACK * timeout issues in other cases as well. */ - if (conf->channel && conf->channel->band == IEEE80211_BAND_2GHZ) { + if (conf->channel && conf->channel->band == IEEE80211_BAND_2GHZ) acktimeout += 64 - sifstime - ah->slottime; - ctstimeout += 48 - sifstime - ah->slottime; - } - ath9k_hw_set_sifs_time(ah, sifstime); ath9k_hw_setslottime(ah, slottime); diff --git a/trunk/drivers/net/wireless/ath/ath9k/init.c b/trunk/drivers/net/wireless/ath/ath9k/init.c index 53a005d288aa..abf943557dee 100644 --- a/trunk/drivers/net/wireless/ath/ath9k/init.c +++ b/trunk/drivers/net/wireless/ath/ath9k/init.c @@ -822,11 +822,6 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, ARRAY_SIZE(ath9k_tpt_blink)); #endif - INIT_WORK(&sc->hw_reset_work, ath_reset_work); - INIT_WORK(&sc->hw_check_work, ath_hw_check); - INIT_WORK(&sc->paprd_work, ath_paprd_calibrate); - INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work); - /* Register with mac80211 */ error = ieee80211_register_hw(hw); if (error) @@ -845,6 +840,10 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, goto error_world; } + INIT_WORK(&sc->hw_reset_work, ath_reset_work); + INIT_WORK(&sc->hw_check_work, ath_hw_check); + INIT_WORK(&sc->paprd_work, ath_paprd_calibrate); + INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work); sc->last_rssi = ATH_RSSI_DUMMY_MARKER; ath_init_leds(sc); diff --git a/trunk/drivers/net/wireless/ath/ath9k/rc.c b/trunk/drivers/net/wireless/ath/ath9k/rc.c index 635b592ad961..b3c3798fe513 100644 --- a/trunk/drivers/net/wireless/ath/ath9k/rc.c +++ b/trunk/drivers/net/wireless/ath/ath9k/rc.c @@ -694,7 +694,7 @@ static u8 ath_rc_get_highest_rix(struct ath_softc *sc, return rate; /* This should not happen */ - WARN_ON_ONCE(1); + WARN_ON(1); rate = ath_rc_priv->valid_rate_index[0]; diff --git a/trunk/drivers/net/wireless/ath/ath9k/recv.c b/trunk/drivers/net/wireless/ath/ath9k/recv.c index 7e1a91af1497..0e666fbe0842 100644 --- a/trunk/drivers/net/wireless/ath/ath9k/recv.c +++ b/trunk/drivers/net/wireless/ath/ath9k/recv.c @@ -822,14 +822,6 @@ static bool ath9k_rx_accept(struct ath_common *common, (ATH9K_RXERR_DECRYPT | ATH9K_RXERR_CRC | ATH9K_RXERR_MIC | ATH9K_RXERR_KEYMISS)); - /* - * Key miss events are only relevant for pairwise keys where the - * descriptor does contain a valid key index. This has been observed - * mostly with CCMP encryption. - */ - if (rx_stats->rs_keyix == ATH9K_RXKEYIX_INVALID) - rx_stats->rs_status &= ~ATH9K_RXERR_KEYMISS; - if (!rx_stats->rs_datalen) return false; /* diff --git a/trunk/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/trunk/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index 63bbc60be28e..c664c2726553 100644 --- a/trunk/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/trunk/drivers/net/wireless/iwlwifi/iwl-agn-tx.c @@ -91,7 +91,6 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv, tx_cmd->tid_tspec = qc[0] & 0xf; tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; } else { - tx_cmd->tid_tspec = IWL_TID_NON_QOS; if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; else @@ -621,7 +620,7 @@ int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif, sta_priv->lq_sta.lq.agg_params.agg_frame_cnt_limit = sta_priv->max_agg_bufsize; - IWL_DEBUG_HT(priv, "Tx aggregation enabled on ra = %pM tid = %d\n", + IWL_INFO(priv, "Tx aggregation enabled on ra = %pM tid = %d\n", sta->addr, tid); return iwl_send_lq_cmd(priv, ctx, @@ -809,8 +808,6 @@ static void iwl_rx_reply_tx_agg(struct iwl_priv *priv, u32 status = le16_to_cpu(tx_resp->status.status); int i; - WARN_ON(tid == IWL_TID_NON_QOS); - if (agg->wait_for_ba) IWL_DEBUG_TX_REPLY(priv, "got tx response w/o block-ack\n"); @@ -1038,13 +1035,10 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb, } __skb_queue_head_init(&skbs); + priv->tid_data[sta_id][tid].next_reclaimed = next_reclaimed; - if (tid != IWL_TID_NON_QOS) { - priv->tid_data[sta_id][tid].next_reclaimed = - next_reclaimed; - IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d", - next_reclaimed); - } + IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d", + next_reclaimed); /*we can free until ssn % q.n_bd not inclusive */ WARN_ON(iwl_trans_reclaim(trans(priv), sta_id, tid, txq_id, diff --git a/trunk/drivers/net/wireless/iwlwifi/iwl-commands.h b/trunk/drivers/net/wireless/iwlwifi/iwl-commands.h index f822ac447c3b..265de39d394c 100644 --- a/trunk/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/trunk/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -815,7 +815,6 @@ struct iwl_qosparam_cmd { #define IWL_INVALID_STATION 255 #define IWL_MAX_TID_COUNT 8 -#define IWL_TID_NON_QOS IWL_MAX_TID_COUNT #define STA_FLG_TX_RATE_MSK cpu_to_le32(1 << 2) #define STA_FLG_PWR_SAVE_MSK cpu_to_le32(1 << 8) diff --git a/trunk/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c b/trunk/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c index 324d06dfb690..67d6e324e26f 100644 --- a/trunk/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c +++ b/trunk/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c @@ -1262,7 +1262,6 @@ static int iwl_trans_pcie_reclaim(struct iwl_trans *trans, int sta_id, int tid, txq->time_stamp = jiffies; if (unlikely(txq_id >= IWLAGN_FIRST_AMPDU_QUEUE && - tid != IWL_TID_NON_QOS && txq_id != trans_pcie->agg_txq[sta_id][tid])) { /* * FIXME: this is a uCode bug which need to be addressed, diff --git a/trunk/drivers/net/wireless/mwifiex/init.c b/trunk/drivers/net/wireless/mwifiex/init.c index 1d0ec57a0143..e05b417a3fae 100644 --- a/trunk/drivers/net/wireless/mwifiex/init.c +++ b/trunk/drivers/net/wireless/mwifiex/init.c @@ -382,8 +382,7 @@ mwifiex_free_adapter(struct mwifiex_adapter *adapter) adapter->if_ops.cleanup_if(adapter); - if (adapter->sleep_cfm) - dev_kfree_skb_any(adapter->sleep_cfm); + dev_kfree_skb_any(adapter->sleep_cfm); } /* diff --git a/trunk/drivers/net/wireless/mwifiex/main.c b/trunk/drivers/net/wireless/mwifiex/main.c index b728f54451e4..84be196188cc 100644 --- a/trunk/drivers/net/wireless/mwifiex/main.c +++ b/trunk/drivers/net/wireless/mwifiex/main.c @@ -822,9 +822,7 @@ int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem) continue; rtnl_lock(); - if (priv->wdev && priv->netdev) - mwifiex_del_virtual_intf(priv->wdev->wiphy, - priv->netdev); + mwifiex_del_virtual_intf(priv->wdev->wiphy, priv->netdev); rtnl_unlock(); } @@ -832,11 +830,9 @@ int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem) if (!priv) goto exit_remove; - if (priv->wdev) { - wiphy_unregister(priv->wdev->wiphy); - wiphy_free(priv->wdev->wiphy); - kfree(priv->wdev); - } + wiphy_unregister(priv->wdev->wiphy); + wiphy_free(priv->wdev->wiphy); + kfree(priv->wdev); mwifiex_terminate_workqueue(adapter); diff --git a/trunk/drivers/net/wireless/mwifiex/sta_ioctl.c b/trunk/drivers/net/wireless/mwifiex/sta_ioctl.c index b0fbf5d4fea0..470ca75ec250 100644 --- a/trunk/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/trunk/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -54,7 +54,7 @@ int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist, int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter) { bool cancel_flag = false; - int status; + int status = adapter->cmd_wait_q.status; struct cmd_ctrl_node *cmd_queued; if (!adapter->cmd_queued) @@ -79,8 +79,6 @@ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter) mwifiex_cancel_pending_ioctl(adapter); dev_dbg(adapter->dev, "cmd cancel\n"); } - - status = adapter->cmd_wait_q.status; adapter->cmd_wait_q.status = 0; return status; @@ -242,8 +240,6 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, if (!netif_queue_stopped(priv->netdev)) mwifiex_stop_net_dev_queue(priv->netdev, adapter); - if (netif_carrier_ok(priv->netdev)) - netif_carrier_off(priv->netdev); /* Clear any past association response stored for * application retrieval */ @@ -275,8 +271,6 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, if (!netif_queue_stopped(priv->netdev)) mwifiex_stop_net_dev_queue(priv->netdev, adapter); - if (netif_carrier_ok(priv->netdev)) - netif_carrier_off(priv->netdev); if (!ret) { dev_dbg(adapter->dev, "info: network found in scan" diff --git a/trunk/drivers/net/wireless/rt2x00/rt2800lib.c b/trunk/drivers/net/wireless/rt2x00/rt2800lib.c index 7bef66def10c..22a1a8fc6e02 100644 --- a/trunk/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/trunk/drivers/net/wireless/rt2x00/rt2800lib.c @@ -514,9 +514,9 @@ EXPORT_SYMBOL_GPL(rt2800_write_tx_data); static int rt2800_agc_to_rssi(struct rt2x00_dev *rt2x00dev, u32 rxwi_w2) { - s8 rssi0 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI0); - s8 rssi1 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI1); - s8 rssi2 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI2); + int rssi0 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI0); + int rssi1 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI1); + int rssi2 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI2); u16 eeprom; u8 offset0; u8 offset1; @@ -552,7 +552,7 @@ static int rt2800_agc_to_rssi(struct rt2x00_dev *rt2x00dev, u32 rxwi_w2) * which gives less energy... */ rssi0 = max(rssi0, rssi1); - return (int)max(rssi0, rssi2); + return max(rssi0, rssi2); } void rt2800_process_rxwi(struct queue_entry *entry, diff --git a/trunk/drivers/net/wireless/zd1211rw/zd_mac.c b/trunk/drivers/net/wireless/zd1211rw/zd_mac.c index 98a574a4a465..0a70149df3fc 100644 --- a/trunk/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/trunk/drivers/net/wireless/zd1211rw/zd_mac.c @@ -866,14 +866,6 @@ static int fill_ctrlset(struct zd_mac *mac, ZD_ASSERT(frag_len <= 0xffff); - /* - * Firmware computes the duration itself (for all frames except PSPoll) - * and needs the field set to 0 at input, otherwise firmware messes up - * duration_id and sets bits 14 and 15 on. - */ - if (!ieee80211_is_pspoll(hdr->frame_control)) - hdr->duration_id = 0; - txrate = ieee80211_get_tx_rate(mac->hw, info); cs->modulation = txrate->hw_value; diff --git a/trunk/drivers/pci/xen-pcifront.c b/trunk/drivers/pci/xen-pcifront.c index 1620088a0e7e..7cf3d2fcf56a 100644 --- a/trunk/drivers/pci/xen-pcifront.c +++ b/trunk/drivers/pci/xen-pcifront.c @@ -189,7 +189,7 @@ static int pcifront_bus_read(struct pci_bus *bus, unsigned int devfn, if (verbose_request) dev_info(&pdev->xdev->dev, - "read dev=%04x:%02x:%02x.%d - offset %x size %d\n", + "read dev=%04x:%02x:%02x.%01x - offset %x size %d\n", pci_domain_nr(bus), bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), where, size); @@ -228,7 +228,7 @@ static int pcifront_bus_write(struct pci_bus *bus, unsigned int devfn, if (verbose_request) dev_info(&pdev->xdev->dev, - "write dev=%04x:%02x:%02x.%d - " + "write dev=%04x:%02x:%02x.%01x - " "offset %x size %d val %x\n", pci_domain_nr(bus), bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), where, size, val); @@ -432,7 +432,7 @@ static int __devinit pcifront_scan_bus(struct pcifront_device *pdev, d = pci_scan_single_device(b, devfn); if (d) dev_info(&pdev->xdev->dev, "New device on " - "%04x:%02x:%02x.%d found.\n", domain, bus, + "%04x:%02x:%02x.%02x found.\n", domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); } @@ -1041,7 +1041,7 @@ static int pcifront_detach_devices(struct pcifront_device *pdev) pci_dev = pci_get_slot(pci_bus, PCI_DEVFN(slot, func)); if (!pci_dev) { dev_dbg(&pdev->xdev->dev, - "Cannot get PCI device %04x:%02x:%02x.%d\n", + "Cannot get PCI device %04x:%02x:%02x.%02x\n", domain, bus, slot, func); continue; } @@ -1049,7 +1049,7 @@ static int pcifront_detach_devices(struct pcifront_device *pdev) pci_dev_put(pci_dev); dev_dbg(&pdev->xdev->dev, - "PCI device %04x:%02x:%02x.%d removed.\n", + "PCI device %04x:%02x:%02x.%02x removed.\n", domain, bus, slot, func); } diff --git a/trunk/drivers/power/bq27x00_battery.c b/trunk/drivers/power/bq27x00_battery.c index 1ed6ea0bad6e..98bf5676318d 100644 --- a/trunk/drivers/power/bq27x00_battery.c +++ b/trunk/drivers/power/bq27x00_battery.c @@ -62,10 +62,11 @@ #define BQ27500_REG_SOC 0x2C #define BQ27500_REG_DCAP 0x3C /* Design capacity */ -#define BQ27500_FLAG_DSC BIT(0) +#define BQ27500_FLAG_DSG BIT(0) /* Discharging */ #define BQ27500_FLAG_SOCF BIT(1) /* State-of-Charge threshold final */ #define BQ27500_FLAG_SOC1 BIT(2) /* State-of-Charge threshold 1 */ -#define BQ27500_FLAG_FC BIT(9) +#define BQ27500_FLAG_CHG BIT(8) /* Charging */ +#define BQ27500_FLAG_FC BIT(9) /* Fully charged */ #define BQ27000_RS 20 /* Resistor sense */ @@ -311,7 +312,7 @@ static void bq27x00_update(struct bq27x00_device_info *di) struct bq27x00_reg_cache cache = {0, }; bool is_bq27500 = di->chip == BQ27500; - cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500); + cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, is_bq27500); if (cache.flags >= 0) { if (!is_bq27500 && (cache.flags & BQ27000_FLAG_CI)) { dev_info(di->dev, "battery is not calibrated! ignoring capacity values\n"); @@ -400,10 +401,14 @@ static int bq27x00_battery_status(struct bq27x00_device_info *di, if (di->chip == BQ27500) { if (di->cache.flags & BQ27500_FLAG_FC) status = POWER_SUPPLY_STATUS_FULL; - else if (di->cache.flags & BQ27500_FLAG_DSC) + else if (di->cache.flags & BQ27500_FLAG_DSG) status = POWER_SUPPLY_STATUS_DISCHARGING; - else + else if (di->cache.flags & BQ27500_FLAG_CHG) status = POWER_SUPPLY_STATUS_CHARGING; + else if (power_supply_am_i_supplied(&di->bat)) + status = POWER_SUPPLY_STATUS_NOT_CHARGING; + else + status = POWER_SUPPLY_STATUS_UNKNOWN; } else { if (di->cache.flags & BQ27000_FLAG_FC) status = POWER_SUPPLY_STATUS_FULL; diff --git a/trunk/drivers/power/charger-manager.c b/trunk/drivers/power/charger-manager.c index 88fd9710bda2..0378d019efae 100644 --- a/trunk/drivers/power/charger-manager.c +++ b/trunk/drivers/power/charger-manager.c @@ -974,11 +974,10 @@ static int __devexit charger_manager_remove(struct platform_device *pdev) return 0; } -static const struct platform_device_id charger_manager_id[] = { +const struct platform_device_id charger_manager_id[] = { { "charger-manager", 0 }, { }, }; -MODULE_DEVICE_TABLE(platform, charger_manager_id); static int cm_suspend_prepare(struct device *dev) { @@ -1070,3 +1069,4 @@ module_exit(charger_manager_cleanup); MODULE_AUTHOR("MyungJoo Ham "); MODULE_DESCRIPTION("Charger Manager"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("charger-manager"); diff --git a/trunk/drivers/power/lp8727_charger.c b/trunk/drivers/power/lp8727_charger.c index c53dd1292f81..b15b575c070c 100644 --- a/trunk/drivers/power/lp8727_charger.c +++ b/trunk/drivers/power/lp8727_charger.c @@ -464,7 +464,6 @@ static int __devexit lp8727_remove(struct i2c_client *cl) static const struct i2c_device_id lp8727_ids[] = { {"lp8727", 0}, - { } }; static struct i2c_driver lp8727_driver = { diff --git a/trunk/drivers/regulator/mc13xxx-regulator-core.c b/trunk/drivers/regulator/mc13xxx-regulator-core.c index 80ecafef1bc3..62dcd0a432bb 100644 --- a/trunk/drivers/regulator/mc13xxx-regulator-core.c +++ b/trunk/drivers/regulator/mc13xxx-regulator-core.c @@ -254,6 +254,7 @@ int __devinit mc13xxx_get_num_regulators_dt(struct platform_device *pdev) return num; } +EXPORT_SYMBOL_GPL(mc13xxx_get_num_regulators_dt); struct mc13xxx_regulator_init_data * __devinit mc13xxx_parse_regulators_dt( struct platform_device *pdev, struct mc13xxx_regulator *regulators, @@ -291,6 +292,7 @@ struct mc13xxx_regulator_init_data * __devinit mc13xxx_parse_regulators_dt( return data; } +EXPORT_SYMBOL_GPL(mc13xxx_parse_regulators_dt); #endif MODULE_LICENSE("GPL v2"); diff --git a/trunk/drivers/ssb/driver_pcicore.c b/trunk/drivers/ssb/driver_pcicore.c index 49d209173f55..520e8286db28 100644 --- a/trunk/drivers/ssb/driver_pcicore.c +++ b/trunk/drivers/ssb/driver_pcicore.c @@ -75,7 +75,7 @@ static u32 get_cfgspace_addr(struct ssb_pcicore *pc, u32 tmp; /* We do only have one cardbus device behind the bridge. */ - if (pc->cardbusmode && (dev > 1)) + if (pc->cardbusmode && (dev >= 1)) goto out; if (bus == 0) { diff --git a/trunk/drivers/staging/Kconfig b/trunk/drivers/staging/Kconfig index 9e6347249783..21e2f4b87f14 100644 --- a/trunk/drivers/staging/Kconfig +++ b/trunk/drivers/staging/Kconfig @@ -60,6 +60,8 @@ source "drivers/staging/rts5139/Kconfig" source "drivers/staging/frontier/Kconfig" +source "drivers/staging/pohmelfs/Kconfig" + source "drivers/staging/phison/Kconfig" source "drivers/staging/line6/Kconfig" @@ -118,6 +120,8 @@ source "drivers/staging/cptm1217/Kconfig" source "drivers/staging/ste_rmi4/Kconfig" +source "drivers/staging/gma500/Kconfig" + source "drivers/staging/mei/Kconfig" source "drivers/staging/nvec/Kconfig" diff --git a/trunk/drivers/staging/Makefile b/trunk/drivers/staging/Makefile index 943e14830753..7c5808d7212d 100644 --- a/trunk/drivers/staging/Makefile +++ b/trunk/drivers/staging/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_R8712U) += rtl8712/ obj-$(CONFIG_RTS_PSTOR) += rts_pstor/ obj-$(CONFIG_RTS5139) += rts5139/ obj-$(CONFIG_TRANZPORT) += frontier/ +obj-$(CONFIG_POHMELFS) += pohmelfs/ obj-$(CONFIG_IDE_PHISON) += phison/ obj-$(CONFIG_LINE6_USB) += line6/ obj-$(CONFIG_USB_SERIAL_QUATECH2) += serqt_usb2/ @@ -51,6 +52,7 @@ obj-$(CONFIG_FT1000) += ft1000/ obj-$(CONFIG_SPEAKUP) += speakup/ obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_TM1217) += cptm1217/ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/ +obj-$(CONFIG_DRM_PSB) += gma500/ obj-$(CONFIG_INTEL_MEI) += mei/ obj-$(CONFIG_MFD_NVEC) += nvec/ obj-$(CONFIG_DRM_OMAP) += omapdrm/ diff --git a/trunk/drivers/staging/android/Kconfig b/trunk/drivers/staging/android/Kconfig index fef3580ce8de..becf711117ef 100644 --- a/trunk/drivers/staging/android/Kconfig +++ b/trunk/drivers/staging/android/Kconfig @@ -27,7 +27,6 @@ config ANDROID_LOGGER config ANDROID_RAM_CONSOLE bool "Android RAM buffer console" - depends on !S390 && !UML default n config ANDROID_RAM_CONSOLE_ENABLE_VERBOSE @@ -100,6 +99,10 @@ config ANDROID_LOW_MEMORY_KILLER ---help--- Register processes to be killed when memory is low +config ANDROID_PMEM + bool "Android pmem allocator" + depends on ARM + source "drivers/staging/android/switch/Kconfig" endif # if ANDROID diff --git a/trunk/drivers/staging/android/Makefile b/trunk/drivers/staging/android/Makefile index 5fcc24ffdd58..eaed1ff64f0f 100644 --- a/trunk/drivers/staging/android/Makefile +++ b/trunk/drivers/staging/android/Makefile @@ -5,4 +5,5 @@ obj-$(CONFIG_ANDROID_RAM_CONSOLE) += ram_console.o obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o +obj-$(CONFIG_ANDROID_PMEM) += pmem.o obj-$(CONFIG_ANDROID_SWITCH) += switch/ diff --git a/trunk/drivers/staging/android/android_pmem.h b/trunk/drivers/staging/android/android_pmem.h new file mode 100644 index 000000000000..f633621f5be3 --- /dev/null +++ b/trunk/drivers/staging/android/android_pmem.h @@ -0,0 +1,93 @@ +/* include/linux/android_pmem.h + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _ANDROID_PMEM_H_ +#define _ANDROID_PMEM_H_ + +#define PMEM_IOCTL_MAGIC 'p' +#define PMEM_GET_PHYS _IOW(PMEM_IOCTL_MAGIC, 1, unsigned int) +#define PMEM_MAP _IOW(PMEM_IOCTL_MAGIC, 2, unsigned int) +#define PMEM_GET_SIZE _IOW(PMEM_IOCTL_MAGIC, 3, unsigned int) +#define PMEM_UNMAP _IOW(PMEM_IOCTL_MAGIC, 4, unsigned int) +/* This ioctl will allocate pmem space, backing the file, it will fail + * if the file already has an allocation, pass it the len as the argument + * to the ioctl */ +#define PMEM_ALLOCATE _IOW(PMEM_IOCTL_MAGIC, 5, unsigned int) +/* This will connect a one pmem file to another, pass the file that is already + * backed in memory as the argument to the ioctl + */ +#define PMEM_CONNECT _IOW(PMEM_IOCTL_MAGIC, 6, unsigned int) +/* Returns the total size of the pmem region it is sent to as a pmem_region + * struct (with offset set to 0). + */ +#define PMEM_GET_TOTAL_SIZE _IOW(PMEM_IOCTL_MAGIC, 7, unsigned int) +#define PMEM_CACHE_FLUSH _IOW(PMEM_IOCTL_MAGIC, 8, unsigned int) + +struct android_pmem_platform_data +{ + const char* name; + /* starting physical address of memory region */ + unsigned long start; + /* size of memory region */ + unsigned long size; + /* set to indicate the region should not be managed with an allocator */ + unsigned no_allocator; + /* set to indicate maps of this region should be cached, if a mix of + * cached and uncached is desired, set this and open the device with + * O_SYNC to get an uncached region */ + unsigned cached; + /* The MSM7k has bits to enable a write buffer in the bus controller*/ + unsigned buffered; +}; + +struct pmem_region { + unsigned long offset; + unsigned long len; +}; + +#ifdef CONFIG_ANDROID_PMEM +int is_pmem_file(struct file *file); +int get_pmem_file(int fd, unsigned long *start, unsigned long *vstart, + unsigned long *end, struct file **filp); +int get_pmem_user_addr(struct file *file, unsigned long *start, + unsigned long *end); +void put_pmem_file(struct file* file); +void flush_pmem_file(struct file *file, unsigned long start, unsigned long len); +int pmem_setup(struct android_pmem_platform_data *pdata, + long (*ioctl)(struct file *, unsigned int, unsigned long), + int (*release)(struct inode *, struct file *)); +int pmem_remap(struct pmem_region *region, struct file *file, + unsigned operation); + +#else +static inline int is_pmem_file(struct file *file) { return 0; } +static inline int get_pmem_file(int fd, unsigned long *start, + unsigned long *vstart, unsigned long *end, + struct file **filp) { return -ENOSYS; } +static inline int get_pmem_user_addr(struct file *file, unsigned long *start, + unsigned long *end) { return -ENOSYS; } +static inline void put_pmem_file(struct file* file) { return; } +static inline void flush_pmem_file(struct file *file, unsigned long start, + unsigned long len) { return; } +static inline int pmem_setup(struct android_pmem_platform_data *pdata, + long (*ioctl)(struct file *, unsigned int, unsigned long), + int (*release)(struct inode *, struct file *)) { return -ENOSYS; } + +static inline int pmem_remap(struct pmem_region *region, struct file *file, + unsigned operation) { return -ENOSYS; } +#endif + +#endif //_ANDROID_PPP_H_ + diff --git a/trunk/drivers/staging/android/binder.c b/trunk/drivers/staging/android/binder.c index f0b7e6605ab5..7491801a661c 100644 --- a/trunk/drivers/staging/android/binder.c +++ b/trunk/drivers/staging/android/binder.c @@ -38,7 +38,6 @@ static DEFINE_MUTEX(binder_lock); static DEFINE_MUTEX(binder_deferred_lock); -static DEFINE_MUTEX(binder_mmap_lock); static HLIST_HEAD(binder_procs); static HLIST_HEAD(binder_deferred_list); @@ -633,11 +632,6 @@ static int binder_update_page_range(struct binder_proc *proc, int allocate, if (mm) { down_write(&mm->mmap_sem); vma = proc->vma; - if (vma && mm != vma->vm_mm) { - pr_err("binder: %d: vma mm and task mm mismatch\n", - proc->pid); - vma = NULL; - } } if (allocate == 0) @@ -2765,6 +2759,7 @@ static void binder_vma_open(struct vm_area_struct *vma) proc->pid, vma->vm_start, vma->vm_end, (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags, (unsigned long)pgprot_val(vma->vm_page_prot)); + dump_stack(); } static void binder_vma_close(struct vm_area_struct *vma) @@ -2808,7 +2803,6 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) } vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE; - mutex_lock(&binder_mmap_lock); if (proc->buffer) { ret = -EBUSY; failure_string = "already mapped"; @@ -2823,7 +2817,6 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) } proc->buffer = area->addr; proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer; - mutex_unlock(&binder_mmap_lock); #ifdef CONFIG_CPU_CACHE_VIPT if (cache_is_vipt_aliasing()) { @@ -2856,7 +2849,7 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) binder_insert_free_buffer(proc, buffer); proc->free_async_space = proc->buffer_size / 2; barrier(); - proc->files = get_files_struct(proc->tsk); + proc->files = get_files_struct(current); proc->vma = vma; /*printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p\n", @@ -2867,12 +2860,10 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) kfree(proc->pages); proc->pages = NULL; err_alloc_pages_failed: - mutex_lock(&binder_mmap_lock); vfree(proc->buffer); proc->buffer = NULL; err_get_vm_area_failed: err_already_mapped: - mutex_unlock(&binder_mmap_lock); err_bad_arg: printk(KERN_ERR "binder_mmap: %d %lx-%lx %s failed %d\n", proc->pid, vma->vm_start, vma->vm_end, failure_string, ret); diff --git a/trunk/drivers/staging/android/lowmemorykiller.c b/trunk/drivers/staging/android/lowmemorykiller.c index efc7dc1f4831..2d8d2b796101 100644 --- a/trunk/drivers/staging/android/lowmemorykiller.c +++ b/trunk/drivers/staging/android/lowmemorykiller.c @@ -54,7 +54,6 @@ static size_t lowmem_minfree[6] = { static int lowmem_minfree_size = 4; static struct task_struct *lowmem_deathpending; -static unsigned long lowmem_deathpending_timeout; #define lowmem_print(level, x...) \ do { \ @@ -104,8 +103,7 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) * Note: Currently you need CONFIG_PROFILING * for this to work correctly. */ - if (lowmem_deathpending && - time_before_eq(jiffies, lowmem_deathpending_timeout)) + if (lowmem_deathpending) return 0; if (lowmem_adj_size < array_size) @@ -180,7 +178,6 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) */ #ifdef CONFIG_PROFILING lowmem_deathpending = selected; - lowmem_deathpending_timeout = jiffies + HZ; task_handoff_register(&task_nb); #endif force_sig(SIGKILL, selected); diff --git a/trunk/drivers/staging/android/pmem.c b/trunk/drivers/staging/android/pmem.c new file mode 100644 index 000000000000..7d97032c6508 --- /dev/null +++ b/trunk/drivers/staging/android/pmem.c @@ -0,0 +1,1345 @@ +/* pmem.c + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "android_pmem.h" + +#define PMEM_MAX_DEVICES 10 +#define PMEM_MAX_ORDER 128 +#define PMEM_MIN_ALLOC PAGE_SIZE + +#define PMEM_DEBUG 1 + +/* indicates that a refernce to this file has been taken via get_pmem_file, + * the file should not be released until put_pmem_file is called */ +#define PMEM_FLAGS_BUSY 0x1 +/* indicates that this is a suballocation of a larger master range */ +#define PMEM_FLAGS_CONNECTED 0x1 << 1 +/* indicates this is a master and not a sub allocation and that it is mmaped */ +#define PMEM_FLAGS_MASTERMAP 0x1 << 2 +/* submap and unsubmap flags indicate: + * 00: subregion has never been mmaped + * 10: subregion has been mmaped, reference to the mm was taken + * 11: subretion has ben released, refernece to the mm still held + * 01: subretion has been released, reference to the mm has been released + */ +#define PMEM_FLAGS_SUBMAP 0x1 << 3 +#define PMEM_FLAGS_UNSUBMAP 0x1 << 4 + + +struct pmem_data { + /* in alloc mode: an index into the bitmap + * in no_alloc mode: the size of the allocation */ + int index; + /* see flags above for descriptions */ + unsigned int flags; + /* protects this data field, if the mm_mmap sem will be held at the + * same time as this sem, the mm sem must be taken first (as this is + * the order for vma_open and vma_close ops */ + struct rw_semaphore sem; + /* info about the mmaping process */ + struct vm_area_struct *vma; + /* task struct of the mapping process */ + struct task_struct *task; + /* process id of teh mapping process */ + pid_t pid; + /* file descriptor of the master */ + int master_fd; + /* file struct of the master */ + struct file *master_file; + /* a list of currently available regions if this is a suballocation */ + struct list_head region_list; + /* a linked list of data so we can access them for debugging */ + struct list_head list; +#if PMEM_DEBUG + int ref; +#endif +}; + +struct pmem_bits { + unsigned allocated:1; /* 1 if allocated, 0 if free */ + unsigned order:7; /* size of the region in pmem space */ +}; + +struct pmem_region_node { + struct pmem_region region; + struct list_head list; +}; + +#define PMEM_DEBUG_MSGS 0 +#if PMEM_DEBUG_MSGS +#define DLOG(fmt,args...) \ + do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \ + ##args); } \ + while (0) +#else +#define DLOG(x...) do {} while (0) +#endif + +struct pmem_info { + struct miscdevice dev; + /* physical start address of the remaped pmem space */ + unsigned long base; + /* vitual start address of the remaped pmem space */ + unsigned char __iomem *vbase; + /* total size of the pmem space */ + unsigned long size; + /* number of entries in the pmem space */ + unsigned long num_entries; + /* pfn of the garbage page in memory */ + unsigned long garbage_pfn; + /* index of the garbage page in the pmem space */ + int garbage_index; + /* the bitmap for the region indicating which entries are allocated + * and which are free */ + struct pmem_bits *bitmap; + /* indicates the region should not be managed with an allocator */ + unsigned no_allocator; + /* indicates maps of this region should be cached, if a mix of + * cached and uncached is desired, set this and open the device with + * O_SYNC to get an uncached region */ + unsigned cached; + unsigned buffered; + /* in no_allocator mode the first mapper gets the whole space and sets + * this flag */ + unsigned allocated; + /* for debugging, creates a list of pmem file structs, the + * data_list_lock should be taken before pmem_data->sem if both are + * needed */ + struct mutex data_list_lock; + struct list_head data_list; + /* pmem_sem protects the bitmap array + * a write lock should be held when modifying entries in bitmap + * a read lock should be held when reading data from bits or + * dereferencing a pointer into bitmap + * + * pmem_data->sem protects the pmem data of a particular file + * Many of the function that require the pmem_data->sem have a non- + * locking version for when the caller is already holding that sem. + * + * IF YOU TAKE BOTH LOCKS TAKE THEM IN THIS ORDER: + * down(pmem_data->sem) => down(bitmap_sem) + */ + struct rw_semaphore bitmap_sem; + + long (*ioctl)(struct file *, unsigned int, unsigned long); + int (*release)(struct inode *, struct file *); +}; + +static struct pmem_info pmem[PMEM_MAX_DEVICES]; +static int id_count; + +#define PMEM_IS_FREE(id, index) !(pmem[id].bitmap[index].allocated) +#define PMEM_ORDER(id, index) pmem[id].bitmap[index].order +#define PMEM_BUDDY_INDEX(id, index) (index ^ (1 << PMEM_ORDER(id, index))) +#define PMEM_NEXT_INDEX(id, index) (index + (1 << PMEM_ORDER(id, index))) +#define PMEM_OFFSET(index) (index * PMEM_MIN_ALLOC) +#define PMEM_START_ADDR(id, index) (PMEM_OFFSET(index) + pmem[id].base) +#define PMEM_LEN(id, index) ((1 << PMEM_ORDER(id, index)) * PMEM_MIN_ALLOC) +#define PMEM_END_ADDR(id, index) (PMEM_START_ADDR(id, index) + \ + PMEM_LEN(id, index)) +#define PMEM_START_VADDR(id, index) (PMEM_OFFSET(id, index) + pmem[id].vbase) +#define PMEM_END_VADDR(id, index) (PMEM_START_VADDR(id, index) + \ + PMEM_LEN(id, index)) +#define PMEM_REVOKED(data) (data->flags & PMEM_FLAGS_REVOKED) +#define PMEM_IS_PAGE_ALIGNED(addr) (!((addr) & (~PAGE_MASK))) +#define PMEM_IS_SUBMAP(data) ((data->flags & PMEM_FLAGS_SUBMAP) && \ + (!(data->flags & PMEM_FLAGS_UNSUBMAP))) + +static int pmem_release(struct inode *, struct file *); +static int pmem_mmap(struct file *, struct vm_area_struct *); +static int pmem_open(struct inode *, struct file *); +static long pmem_ioctl(struct file *, unsigned int, unsigned long); + +struct file_operations pmem_fops = { + .release = pmem_release, + .mmap = pmem_mmap, + .open = pmem_open, + .unlocked_ioctl = pmem_ioctl, +}; + +static int get_id(struct file *file) +{ + return MINOR(file->f_dentry->d_inode->i_rdev); +} + +int is_pmem_file(struct file *file) +{ + int id; + + if (unlikely(!file || !file->f_dentry || !file->f_dentry->d_inode)) + return 0; + id = get_id(file); + if (unlikely(id >= PMEM_MAX_DEVICES)) + return 0; + if (unlikely(file->f_dentry->d_inode->i_rdev != + MKDEV(MISC_MAJOR, pmem[id].dev.minor))) + return 0; + return 1; +} + +static int has_allocation(struct file *file) +{ + struct pmem_data *data; + /* check is_pmem_file first if not accessed via pmem_file_ops */ + + if (unlikely(!file->private_data)) + return 0; + data = (struct pmem_data *)file->private_data; + if (unlikely(data->index < 0)) + return 0; + return 1; +} + +static int is_master_owner(struct file *file) +{ + struct file *master_file; + struct pmem_data *data; + int put_needed, ret = 0; + + if (!is_pmem_file(file) || !has_allocation(file)) + return 0; + data = (struct pmem_data *)file->private_data; + if (PMEM_FLAGS_MASTERMAP & data->flags) + return 1; + master_file = fget_light(data->master_fd, &put_needed); + if (master_file && data->master_file == master_file) + ret = 1; + fput_light(master_file, put_needed); + return ret; +} + +static int pmem_free(int id, int index) +{ + /* caller should hold the write lock on pmem_sem! */ + int buddy, curr = index; + DLOG("index %d\n", index); + + if (pmem[id].no_allocator) { + pmem[id].allocated = 0; + return 0; + } + /* clean up the bitmap, merging any buddies */ + pmem[id].bitmap[curr].allocated = 0; + /* find a slots buddy Buddy# = Slot# ^ (1 << order) + * if the buddy is also free merge them + * repeat until the buddy is not free or end of the bitmap is reached + */ + do { + buddy = PMEM_BUDDY_INDEX(id, curr); + if (PMEM_IS_FREE(id, buddy) && + PMEM_ORDER(id, buddy) == PMEM_ORDER(id, curr)) { + PMEM_ORDER(id, buddy)++; + PMEM_ORDER(id, curr)++; + curr = min(buddy, curr); + } else { + break; + } + } while (curr < pmem[id].num_entries); + + return 0; +} + +static void pmem_revoke(struct file *file, struct pmem_data *data); + +static int pmem_release(struct inode *inode, struct file *file) +{ + struct pmem_data *data = (struct pmem_data *)file->private_data; + struct pmem_region_node *region_node; + struct list_head *elt, *elt2; + int id = get_id(file), ret = 0; + + + mutex_lock(&pmem[id].data_list_lock); + /* if this file is a master, revoke all the memory in the connected + * files */ + if (PMEM_FLAGS_MASTERMAP & data->flags) { + struct pmem_data *sub_data; + list_for_each(elt, &pmem[id].data_list) { + sub_data = list_entry(elt, struct pmem_data, list); + down_read(&sub_data->sem); + if (PMEM_IS_SUBMAP(sub_data) && + file == sub_data->master_file) { + up_read(&sub_data->sem); + pmem_revoke(file, sub_data); + } else + up_read(&sub_data->sem); + } + } + list_del(&data->list); + mutex_unlock(&pmem[id].data_list_lock); + + + down_write(&data->sem); + + /* if its not a conencted file and it has an allocation, free it */ + if (!(PMEM_FLAGS_CONNECTED & data->flags) && has_allocation(file)) { + down_write(&pmem[id].bitmap_sem); + ret = pmem_free(id, data->index); + up_write(&pmem[id].bitmap_sem); + } + + /* if this file is a submap (mapped, connected file), downref the + * task struct */ + if (PMEM_FLAGS_SUBMAP & data->flags) + if (data->task) { + put_task_struct(data->task); + data->task = NULL; + } + + file->private_data = NULL; + + list_for_each_safe(elt, elt2, &data->region_list) { + region_node = list_entry(elt, struct pmem_region_node, list); + list_del(elt); + kfree(region_node); + } + BUG_ON(!list_empty(&data->region_list)); + + up_write(&data->sem); + kfree(data); + if (pmem[id].release) + ret = pmem[id].release(inode, file); + + return ret; +} + +static int pmem_open(struct inode *inode, struct file *file) +{ + struct pmem_data *data; + int id = get_id(file); + int ret = 0; + + DLOG("current %u file %p(%d)\n", current->pid, file, file_count(file)); + /* setup file->private_data to indicate its unmapped */ + /* you can only open a pmem device one time */ + if (file->private_data != NULL) + return -1; + data = kmalloc(sizeof(struct pmem_data), GFP_KERNEL); + if (!data) { + printk("pmem: unable to allocate memory for pmem metadata."); + return -1; + } + data->flags = 0; + data->index = -1; + data->task = NULL; + data->vma = NULL; + data->pid = 0; + data->master_file = NULL; +#if PMEM_DEBUG + data->ref = 0; +#endif + INIT_LIST_HEAD(&data->region_list); + init_rwsem(&data->sem); + + file->private_data = data; + INIT_LIST_HEAD(&data->list); + + mutex_lock(&pmem[id].data_list_lock); + list_add(&data->list, &pmem[id].data_list); + mutex_unlock(&pmem[id].data_list_lock); + return ret; +} + +static unsigned long pmem_order(unsigned long len) +{ + int i; + + len = (len + PMEM_MIN_ALLOC - 1)/PMEM_MIN_ALLOC; + len--; + for (i = 0; i < sizeof(len)*8; i++) + if (len >> i == 0) + break; + return i; +} + +static int pmem_allocate(int id, unsigned long len) +{ + /* caller should hold the write lock on pmem_sem! */ + /* return the corresponding pdata[] entry */ + int curr = 0; + int end = pmem[id].num_entries; + int best_fit = -1; + unsigned long order = pmem_order(len); + + if (pmem[id].no_allocator) { + DLOG("no allocator"); + if ((len > pmem[id].size) || pmem[id].allocated) + return -1; + pmem[id].allocated = 1; + return len; + } + + if (order > PMEM_MAX_ORDER) + return -1; + DLOG("order %lx\n", order); + + /* look through the bitmap: + * if you find a free slot of the correct order use it + * otherwise, use the best fit (smallest with size > order) slot + */ + while (curr < end) { + if (PMEM_IS_FREE(id, curr)) { + if (PMEM_ORDER(id, curr) == (unsigned char)order) { + /* set the not free bit and clear others */ + best_fit = curr; + break; + } + if (PMEM_ORDER(id, curr) > (unsigned char)order && + (best_fit < 0 || + PMEM_ORDER(id, curr) < PMEM_ORDER(id, best_fit))) + best_fit = curr; + } + curr = PMEM_NEXT_INDEX(id, curr); + } + + /* if best_fit < 0, there are no suitable slots, + * return an error + */ + if (best_fit < 0) { + printk("pmem: no space left to allocate!\n"); + return -1; + } + + /* now partition the best fit: + * split the slot into 2 buddies of order - 1 + * repeat until the slot is of the correct order + */ + while (PMEM_ORDER(id, best_fit) > (unsigned char)order) { + int buddy; + PMEM_ORDER(id, best_fit) -= 1; + buddy = PMEM_BUDDY_INDEX(id, best_fit); + PMEM_ORDER(id, buddy) = PMEM_ORDER(id, best_fit); + } + pmem[id].bitmap[best_fit].allocated = 1; + return best_fit; +} + +static pgprot_t pmem_access_prot(struct file *file, pgprot_t vma_prot) +{ + int id = get_id(file); +#ifdef pgprot_noncached + if (pmem[id].cached == 0 || file->f_flags & O_SYNC) + return pgprot_noncached(vma_prot); +#endif +#ifdef pgprot_ext_buffered + else if (pmem[id].buffered) + return pgprot_ext_buffered(vma_prot); +#endif + return vma_prot; +} + +static unsigned long pmem_start_addr(int id, struct pmem_data *data) +{ + if (pmem[id].no_allocator) + return PMEM_START_ADDR(id, 0); + else + return PMEM_START_ADDR(id, data->index); + +} + +static void *pmem_start_vaddr(int id, struct pmem_data *data) +{ + return pmem_start_addr(id, data) - pmem[id].base + pmem[id].vbase; +} + +static unsigned long pmem_len(int id, struct pmem_data *data) +{ + if (pmem[id].no_allocator) + return data->index; + else + return PMEM_LEN(id, data->index); +} + +static int pmem_map_garbage(int id, struct vm_area_struct *vma, + struct pmem_data *data, unsigned long offset, + unsigned long len) +{ + int i, garbage_pages = len >> PAGE_SHIFT; + + vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP | VM_SHARED | VM_WRITE; + for (i = 0; i < garbage_pages; i++) { + if (vm_insert_pfn(vma, vma->vm_start + offset + (i * PAGE_SIZE), + pmem[id].garbage_pfn)) + return -EAGAIN; + } + return 0; +} + +static int pmem_unmap_pfn_range(int id, struct vm_area_struct *vma, + struct pmem_data *data, unsigned long offset, + unsigned long len) +{ + int garbage_pages; + DLOG("unmap offset %lx len %lx\n", offset, len); + + BUG_ON(!PMEM_IS_PAGE_ALIGNED(len)); + + garbage_pages = len >> PAGE_SHIFT; + zap_page_range(vma, vma->vm_start + offset, len, NULL); + pmem_map_garbage(id, vma, data, offset, len); + return 0; +} + +static int pmem_map_pfn_range(int id, struct vm_area_struct *vma, + struct pmem_data *data, unsigned long offset, + unsigned long len) +{ + DLOG("map offset %lx len %lx\n", offset, len); + BUG_ON(!PMEM_IS_PAGE_ALIGNED(vma->vm_start)); + BUG_ON(!PMEM_IS_PAGE_ALIGNED(vma->vm_end)); + BUG_ON(!PMEM_IS_PAGE_ALIGNED(len)); + BUG_ON(!PMEM_IS_PAGE_ALIGNED(offset)); + + if (io_remap_pfn_range(vma, vma->vm_start + offset, + (pmem_start_addr(id, data) + offset) >> PAGE_SHIFT, + len, vma->vm_page_prot)) { + return -EAGAIN; + } + return 0; +} + +static int pmem_remap_pfn_range(int id, struct vm_area_struct *vma, + struct pmem_data *data, unsigned long offset, + unsigned long len) +{ + /* hold the mm semp for the vma you are modifying when you call this */ + BUG_ON(!vma); + zap_page_range(vma, vma->vm_start + offset, len, NULL); + return pmem_map_pfn_range(id, vma, data, offset, len); +} + +static void pmem_vma_open(struct vm_area_struct *vma) +{ + struct file *file = vma->vm_file; + struct pmem_data *data = file->private_data; + int id = get_id(file); + /* this should never be called as we don't support copying pmem + * ranges via fork */ + BUG_ON(!has_allocation(file)); + down_write(&data->sem); + /* remap the garbage pages, forkers don't get access to the data */ + pmem_unmap_pfn_range(id, vma, data, 0, vma->vm_start - vma->vm_end); + up_write(&data->sem); +} + +static void pmem_vma_close(struct vm_area_struct *vma) +{ + struct file *file = vma->vm_file; + struct pmem_data *data = file->private_data; + + DLOG("current %u ppid %u file %p count %d\n", current->pid, + current->parent->pid, file, file_count(file)); + if (unlikely(!is_pmem_file(file) || !has_allocation(file))) { + printk(KERN_WARNING "pmem: something is very wrong, you are " + "closing a vm backing an allocation that doesn't " + "exist!\n"); + return; + } + down_write(&data->sem); + if (data->vma == vma) { + data->vma = NULL; + if ((data->flags & PMEM_FLAGS_CONNECTED) && + (data->flags & PMEM_FLAGS_SUBMAP)) + data->flags |= PMEM_FLAGS_UNSUBMAP; + } + /* the kernel is going to free this vma now anyway */ + up_write(&data->sem); +} + +static struct vm_operations_struct vm_ops = { + .open = pmem_vma_open, + .close = pmem_vma_close, +}; + +static int pmem_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct pmem_data *data; + int index; + unsigned long vma_size = vma->vm_end - vma->vm_start; + int ret = 0, id = get_id(file); + + if (vma->vm_pgoff || !PMEM_IS_PAGE_ALIGNED(vma_size)) { +#if PMEM_DEBUG + printk(KERN_ERR "pmem: mmaps must be at offset zero, aligned" + " and a multiple of pages_size.\n"); +#endif + return -EINVAL; + } + + data = (struct pmem_data *)file->private_data; + down_write(&data->sem); + /* check this file isn't already mmaped, for submaps check this file + * has never been mmaped */ + if ((data->flags & PMEM_FLAGS_SUBMAP) || + (data->flags & PMEM_FLAGS_UNSUBMAP)) { +#if PMEM_DEBUG + printk(KERN_ERR "pmem: you can only mmap a pmem file once, " + "this file is already mmaped. %x\n", data->flags); +#endif + ret = -EINVAL; + goto error; + } + /* if file->private_data == unalloced, alloc*/ + if (data && data->index == -1) { + down_write(&pmem[id].bitmap_sem); + index = pmem_allocate(id, vma->vm_end - vma->vm_start); + up_write(&pmem[id].bitmap_sem); + data->index = index; + } + /* either no space was available or an error occured */ + if (!has_allocation(file)) { + ret = -EINVAL; + printk("pmem: could not find allocation for map.\n"); + goto error; + } + + if (pmem_len(id, data) < vma_size) { +#if PMEM_DEBUG + printk(KERN_WARNING "pmem: mmap size [%lu] does not match" + "size of backing region [%lu].\n", vma_size, + pmem_len(id, data)); +#endif + ret = -EINVAL; + goto error; + } + + vma->vm_pgoff = pmem_start_addr(id, data) >> PAGE_SHIFT; + vma->vm_page_prot = pmem_access_prot(file, vma->vm_page_prot); + + if (data->flags & PMEM_FLAGS_CONNECTED) { + struct pmem_region_node *region_node; + struct list_head *elt; + if (pmem_map_garbage(id, vma, data, 0, vma_size)) { + printk("pmem: mmap failed in kernel!\n"); + ret = -EAGAIN; + goto error; + } + list_for_each(elt, &data->region_list) { + region_node = list_entry(elt, struct pmem_region_node, + list); + DLOG("remapping file: %p %lx %lx\n", file, + region_node->region.offset, + region_node->region.len); + if (pmem_remap_pfn_range(id, vma, data, + region_node->region.offset, + region_node->region.len)) { + ret = -EAGAIN; + goto error; + } + } + data->flags |= PMEM_FLAGS_SUBMAP; + get_task_struct(current->group_leader); + data->task = current->group_leader; + data->vma = vma; +#if PMEM_DEBUG + data->pid = current->pid; +#endif + DLOG("submmapped file %p vma %p pid %u\n", file, vma, + current->pid); + } else { + if (pmem_map_pfn_range(id, vma, data, 0, vma_size)) { + printk(KERN_INFO "pmem: mmap failed in kernel!\n"); + ret = -EAGAIN; + goto error; + } + data->flags |= PMEM_FLAGS_MASTERMAP; + data->pid = current->pid; + } + vma->vm_ops = &vm_ops; +error: + up_write(&data->sem); + return ret; +} + +/* the following are the api for accessing pmem regions by other drivers + * from inside the kernel */ +int get_pmem_user_addr(struct file *file, unsigned long *start, + unsigned long *len) +{ + struct pmem_data *data; + if (!is_pmem_file(file) || !has_allocation(file)) { +#if PMEM_DEBUG + printk(KERN_INFO "pmem: requested pmem data from invalid" + "file.\n"); +#endif + return -1; + } + data = (struct pmem_data *)file->private_data; + down_read(&data->sem); + if (data->vma) { + *start = data->vma->vm_start; + *len = data->vma->vm_end - data->vma->vm_start; + } else { + *start = 0; + *len = 0; + } + up_read(&data->sem); + return 0; +} + +int get_pmem_addr(struct file *file, unsigned long *start, + unsigned long *vstart, unsigned long *len) +{ + struct pmem_data *data; + int id; + + if (!is_pmem_file(file) || !has_allocation(file)) { + return -1; + } + + data = (struct pmem_data *)file->private_data; + if (data->index == -1) { +#if PMEM_DEBUG + printk(KERN_INFO "pmem: requested pmem data from file with no " + "allocation.\n"); + return -1; +#endif + } + id = get_id(file); + + down_read(&data->sem); + *start = pmem_start_addr(id, data); + *len = pmem_len(id, data); + *vstart = (unsigned long)pmem_start_vaddr(id, data); + up_read(&data->sem); +#if PMEM_DEBUG + down_write(&data->sem); + data->ref++; + up_write(&data->sem); +#endif + return 0; +} + +int get_pmem_file(int fd, unsigned long *start, unsigned long *vstart, + unsigned long *len, struct file **filp) +{ + struct file *file; + + file = fget(fd); + if (unlikely(file == NULL)) { + printk(KERN_INFO "pmem: requested data from file descriptor " + "that doesn't exist."); + return -1; + } + + if (get_pmem_addr(file, start, vstart, len)) + goto end; + + if (filp) + *filp = file; + return 0; +end: + fput(file); + return -1; +} + +void put_pmem_file(struct file *file) +{ + struct pmem_data *data; + int id; + + if (!is_pmem_file(file)) + return; + id = get_id(file); + data = (struct pmem_data *)file->private_data; +#if PMEM_DEBUG + down_write(&data->sem); + if (data->ref == 0) { + printk("pmem: pmem_put > pmem_get %s (pid %d)\n", + pmem[id].dev.name, data->pid); + BUG(); + } + data->ref--; + up_write(&data->sem); +#endif + fput(file); +} + +void flush_pmem_file(struct file *file, unsigned long offset, unsigned long len) +{ + struct pmem_data *data; + int id; + void *vaddr; + struct pmem_region_node *region_node; + struct list_head *elt; + void *flush_start, *flush_end; + + if (!is_pmem_file(file) || !has_allocation(file)) { + return; + } + + id = get_id(file); + data = (struct pmem_data *)file->private_data; + if (!pmem[id].cached || file->f_flags & O_SYNC) + return; + + down_read(&data->sem); + vaddr = pmem_start_vaddr(id, data); + /* if this isn't a submmapped file, flush the whole thing */ + if (unlikely(!(data->flags & PMEM_FLAGS_CONNECTED))) { + dmac_flush_range(vaddr, vaddr + pmem_len(id, data)); + goto end; + } + /* otherwise, flush the region of the file we are drawing */ + list_for_each(elt, &data->region_list) { + region_node = list_entry(elt, struct pmem_region_node, list); + if ((offset >= region_node->region.offset) && + ((offset + len) <= (region_node->region.offset + + region_node->region.len))) { + flush_start = vaddr + region_node->region.offset; + flush_end = flush_start + region_node->region.len; + dmac_flush_range(flush_start, flush_end); + break; + } + } +end: + up_read(&data->sem); +} + +static int pmem_connect(unsigned long connect, struct file *file) +{ + struct pmem_data *data = (struct pmem_data *)file->private_data; + struct pmem_data *src_data; + struct file *src_file; + int ret = 0, put_needed; + + down_write(&data->sem); + /* retrieve the src file and check it is a pmem file with an alloc */ + src_file = fget_light(connect, &put_needed); + DLOG("connect %p to %p\n", file, src_file); + if (!src_file) { + printk("pmem: src file not found!\n"); + ret = -EINVAL; + goto err_no_file; + } + if (unlikely(!is_pmem_file(src_file) || !has_allocation(src_file))) { + printk(KERN_INFO "pmem: src file is not a pmem file or has no " + "alloc!\n"); + ret = -EINVAL; + goto err_bad_file; + } + src_data = (struct pmem_data *)src_file->private_data; + + if (has_allocation(file) && (data->index != src_data->index)) { + printk("pmem: file is already mapped but doesn't match this" + " src_file!\n"); + ret = -EINVAL; + goto err_bad_file; + } + data->index = src_data->index; + data->flags |= PMEM_FLAGS_CONNECTED; + data->master_fd = connect; + data->master_file = src_file; + +err_bad_file: + fput_light(src_file, put_needed); +err_no_file: + up_write(&data->sem); + return ret; +} + +static void pmem_unlock_data_and_mm(struct pmem_data *data, + struct mm_struct *mm) +{ + up_write(&data->sem); + if (mm != NULL) { + up_write(&mm->mmap_sem); + mmput(mm); + } +} + +static int pmem_lock_data_and_mm(struct file *file, struct pmem_data *data, + struct mm_struct **locked_mm) +{ + int ret = 0; + struct mm_struct *mm = NULL; + *locked_mm = NULL; +lock_mm: + down_read(&data->sem); + if (PMEM_IS_SUBMAP(data)) { + mm = get_task_mm(data->task); + if (!mm) { +#if PMEM_DEBUG + printk("pmem: can't remap task is gone!\n"); +#endif + up_read(&data->sem); + return -1; + } + } + up_read(&data->sem); + + if (mm) + down_write(&mm->mmap_sem); + + down_write(&data->sem); + /* check that the file didn't get mmaped before we could take the + * data sem, this should be safe b/c you can only submap each file + * once */ + if (PMEM_IS_SUBMAP(data) && !mm) { + pmem_unlock_data_and_mm(data, mm); + up_write(&data->sem); + goto lock_mm; + } + /* now check that vma.mm is still there, it could have been + * deleted by vma_close before we could get the data->sem */ + if ((data->flags & PMEM_FLAGS_UNSUBMAP) && (mm != NULL)) { + /* might as well release this */ + if (data->flags & PMEM_FLAGS_SUBMAP) { + put_task_struct(data->task); + data->task = NULL; + /* lower the submap flag to show the mm is gone */ + data->flags &= ~(PMEM_FLAGS_SUBMAP); + } + pmem_unlock_data_and_mm(data, mm); + return -1; + } + *locked_mm = mm; + return ret; +} + +int pmem_remap(struct pmem_region *region, struct file *file, + unsigned operation) +{ + int ret; + struct pmem_region_node *region_node; + struct mm_struct *mm = NULL; + struct list_head *elt, *elt2; + int id = get_id(file); + struct pmem_data *data = (struct pmem_data *)file->private_data; + + /* pmem region must be aligned on a page boundry */ + if (unlikely(!PMEM_IS_PAGE_ALIGNED(region->offset) || + !PMEM_IS_PAGE_ALIGNED(region->len))) { +#if PMEM_DEBUG + printk("pmem: request for unaligned pmem suballocation " + "%lx %lx\n", region->offset, region->len); +#endif + return -EINVAL; + } + + /* if userspace requests a region of len 0, there's nothing to do */ + if (region->len == 0) + return 0; + + /* lock the mm and data */ + ret = pmem_lock_data_and_mm(file, data, &mm); + if (ret) + return 0; + + /* only the owner of the master file can remap the client fds + * that back in it */ + if (!is_master_owner(file)) { +#if PMEM_DEBUG + printk("pmem: remap requested from non-master process\n"); +#endif + ret = -EINVAL; + goto err; + } + + /* check that the requested range is within the src allocation */ + if (unlikely((region->offset > pmem_len(id, data)) || + (region->len > pmem_len(id, data)) || + (region->offset + region->len > pmem_len(id, data)))) { +#if PMEM_DEBUG + printk(KERN_INFO "pmem: suballoc doesn't fit in src_file!\n"); +#endif + ret = -EINVAL; + goto err; + } + + if (operation == PMEM_MAP) { + region_node = kmalloc(sizeof(struct pmem_region_node), + GFP_KERNEL); + if (!region_node) { + ret = -ENOMEM; +#if PMEM_DEBUG + printk(KERN_INFO "No space to allocate metadata!"); +#endif + goto err; + } + region_node->region = *region; + list_add(®ion_node->list, &data->region_list); + } else if (operation == PMEM_UNMAP) { + int found = 0; + list_for_each_safe(elt, elt2, &data->region_list) { + region_node = list_entry(elt, struct pmem_region_node, + list); + if (region->len == 0 || + (region_node->region.offset == region->offset && + region_node->region.len == region->len)) { + list_del(elt); + kfree(region_node); + found = 1; + } + } + if (!found) { +#if PMEM_DEBUG + printk("pmem: Unmap region does not map any mapped " + "region!"); +#endif + ret = -EINVAL; + goto err; + } + } + + if (data->vma && PMEM_IS_SUBMAP(data)) { + if (operation == PMEM_MAP) + ret = pmem_remap_pfn_range(id, data->vma, data, + region->offset, region->len); + else if (operation == PMEM_UNMAP) + ret = pmem_unmap_pfn_range(id, data->vma, data, + region->offset, region->len); + } + +err: + pmem_unlock_data_and_mm(data, mm); + return ret; +} + +static void pmem_revoke(struct file *file, struct pmem_data *data) +{ + struct pmem_region_node *region_node; + struct list_head *elt, *elt2; + struct mm_struct *mm = NULL; + int id = get_id(file); + int ret = 0; + + data->master_file = NULL; + ret = pmem_lock_data_and_mm(file, data, &mm); + /* if lock_data_and_mm fails either the task that mapped the fd, or + * the vma that mapped it have already gone away, nothing more + * needs to be done */ + if (ret) + return; + /* unmap everything */ + /* delete the regions and region list nothing is mapped any more */ + if (data->vma) + list_for_each_safe(elt, elt2, &data->region_list) { + region_node = list_entry(elt, struct pmem_region_node, + list); + pmem_unmap_pfn_range(id, data->vma, data, + region_node->region.offset, + region_node->region.len); + list_del(elt); + kfree(region_node); + } + /* delete the master file */ + pmem_unlock_data_and_mm(data, mm); +} + +static void pmem_get_size(struct pmem_region *region, struct file *file) +{ + struct pmem_data *data = (struct pmem_data *)file->private_data; + int id = get_id(file); + + if (!has_allocation(file)) { + region->offset = 0; + region->len = 0; + return; + } else { + region->offset = pmem_start_addr(id, data); + region->len = pmem_len(id, data); + } + DLOG("offset %lx len %lx\n", region->offset, region->len); +} + + +static long pmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct pmem_data *data; + int id = get_id(file); + + switch (cmd) { + case PMEM_GET_PHYS: + { + struct pmem_region region; + DLOG("get_phys\n"); + if (!has_allocation(file)) { + region.offset = 0; + region.len = 0; + } else { + data = (struct pmem_data *)file->private_data; + region.offset = pmem_start_addr(id, data); + region.len = pmem_len(id, data); + } + printk(KERN_INFO "pmem: request for physical address of pmem region " + "from process %d.\n", current->pid); + if (copy_to_user((void __user *)arg, ®ion, + sizeof(struct pmem_region))) + return -EFAULT; + break; + } + case PMEM_MAP: + { + struct pmem_region region; + if (copy_from_user(®ion, (void __user *)arg, + sizeof(struct pmem_region))) + return -EFAULT; + data = (struct pmem_data *)file->private_data; + return pmem_remap(®ion, file, PMEM_MAP); + } + break; + case PMEM_UNMAP: + { + struct pmem_region region; + if (copy_from_user(®ion, (void __user *)arg, + sizeof(struct pmem_region))) + return -EFAULT; + data = (struct pmem_data *)file->private_data; + return pmem_remap(®ion, file, PMEM_UNMAP); + break; + } + case PMEM_GET_SIZE: + { + struct pmem_region region; + DLOG("get_size\n"); + pmem_get_size(®ion, file); + if (copy_to_user((void __user *)arg, ®ion, + sizeof(struct pmem_region))) + return -EFAULT; + break; + } + case PMEM_GET_TOTAL_SIZE: + { + struct pmem_region region; + DLOG("get total size\n"); + region.offset = 0; + get_id(file); + region.len = pmem[id].size; + if (copy_to_user((void __user *)arg, ®ion, + sizeof(struct pmem_region))) + return -EFAULT; + break; + } + case PMEM_ALLOCATE: + { + if (has_allocation(file)) + return -EINVAL; + data = (struct pmem_data *)file->private_data; + data->index = pmem_allocate(id, arg); + break; + } + case PMEM_CONNECT: + DLOG("connect\n"); + return pmem_connect(arg, file); + break; + case PMEM_CACHE_FLUSH: + { + struct pmem_region region; + DLOG("flush\n"); + if (copy_from_user(®ion, (void __user *)arg, + sizeof(struct pmem_region))) + return -EFAULT; + flush_pmem_file(file, region.offset, region.len); + break; + } + default: + if (pmem[id].ioctl) + return pmem[id].ioctl(file, cmd, arg); + return -EINVAL; + } + return 0; +} + +#if PMEM_DEBUG +static ssize_t debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t debug_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + struct list_head *elt, *elt2; + struct pmem_data *data; + struct pmem_region_node *region_node; + int id = (int)file->private_data; + const int debug_bufmax = 4096; + static char buffer[4096]; + int n = 0; + + DLOG("debug open\n"); + n = scnprintf(buffer, debug_bufmax, + "pid #: mapped regions (offset, len) (offset,len)...\n"); + + mutex_lock(&pmem[id].data_list_lock); + list_for_each(elt, &pmem[id].data_list) { + data = list_entry(elt, struct pmem_data, list); + down_read(&data->sem); + n += scnprintf(buffer + n, debug_bufmax - n, "pid %u:", + data->pid); + list_for_each(elt2, &data->region_list) { + region_node = list_entry(elt2, struct pmem_region_node, + list); + n += scnprintf(buffer + n, debug_bufmax - n, + "(%lx,%lx) ", + region_node->region.offset, + region_node->region.len); + } + n += scnprintf(buffer + n, debug_bufmax - n, "\n"); + up_read(&data->sem); + } + mutex_unlock(&pmem[id].data_list_lock); + + n++; + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static struct file_operations debug_fops = { + .read = debug_read, + .open = debug_open, +}; +#endif + +#if 0 +static struct miscdevice pmem_dev = { + .name = "pmem", + .fops = &pmem_fops, +}; +#endif + +int pmem_setup(struct android_pmem_platform_data *pdata, + long (*ioctl)(struct file *, unsigned int, unsigned long), + int (*release)(struct inode *, struct file *)) +{ + int err = 0; + int i, index = 0; + int id = id_count; + id_count++; + + pmem[id].no_allocator = pdata->no_allocator; + pmem[id].cached = pdata->cached; + pmem[id].buffered = pdata->buffered; + pmem[id].base = pdata->start; + pmem[id].size = pdata->size; + pmem[id].ioctl = ioctl; + pmem[id].release = release; + init_rwsem(&pmem[id].bitmap_sem); + mutex_init(&pmem[id].data_list_lock); + INIT_LIST_HEAD(&pmem[id].data_list); + pmem[id].dev.name = pdata->name; + pmem[id].dev.minor = id; + pmem[id].dev.fops = &pmem_fops; + printk(KERN_INFO "%s: %d init\n", pdata->name, pdata->cached); + + err = misc_register(&pmem[id].dev); + if (err) { + printk(KERN_ALERT "Unable to register pmem driver!\n"); + goto err_cant_register_device; + } + pmem[id].num_entries = pmem[id].size / PMEM_MIN_ALLOC; + + pmem[id].bitmap = kmalloc(pmem[id].num_entries * + sizeof(struct pmem_bits), GFP_KERNEL); + if (!pmem[id].bitmap) + goto err_no_mem_for_metadata; + + memset(pmem[id].bitmap, 0, sizeof(struct pmem_bits) * + pmem[id].num_entries); + + for (i = sizeof(pmem[id].num_entries) * 8 - 1; i >= 0; i--) { + if ((pmem[id].num_entries) & 1<name, S_IFREG | S_IRUGO, NULL, (void *)id, + &debug_fops); +#endif + return 0; +error_cant_remap: + kfree(pmem[id].bitmap); +err_no_mem_for_metadata: + misc_deregister(&pmem[id].dev); +err_cant_register_device: + return -1; +} + +static int pmem_probe(struct platform_device *pdev) +{ + struct android_pmem_platform_data *pdata; + + if (!pdev || !pdev->dev.platform_data) { + printk(KERN_ALERT "Unable to probe pmem!\n"); + return -1; + } + pdata = pdev->dev.platform_data; + return pmem_setup(pdata, NULL, NULL); +} + + +static int pmem_remove(struct platform_device *pdev) +{ + int id = pdev->id; + __free_page(pfn_to_page(pmem[id].garbage_pfn)); + misc_deregister(&pmem[id].dev); + return 0; +} + +static struct platform_driver pmem_driver = { + .probe = pmem_probe, + .remove = pmem_remove, + .driver = { .name = "android_pmem" } +}; + + +static int __init pmem_init(void) +{ + return platform_driver_register(&pmem_driver); +} + +static void __exit pmem_exit(void) +{ + platform_driver_unregister(&pmem_driver); +} + +module_init(pmem_init); +module_exit(pmem_exit); + diff --git a/trunk/drivers/staging/asus_oled/asus_oled.c b/trunk/drivers/staging/asus_oled/asus_oled.c index 1df9586f2730..e77e4e0396cf 100644 --- a/trunk/drivers/staging/asus_oled/asus_oled.c +++ b/trunk/drivers/staging/asus_oled/asus_oled.c @@ -355,14 +355,7 @@ static void send_data(struct asus_oled_dev *odev) static int append_values(struct asus_oled_dev *odev, uint8_t val, size_t count) { - odev->last_val = val; - - if (val == 0) { - odev->buf_offs += count; - return 0; - } - - while (count-- > 0) { + while (count-- > 0 && val) { size_t x = odev->buf_offs % odev->width; size_t y = odev->buf_offs / odev->width; size_t i; @@ -413,6 +406,7 @@ static int append_values(struct asus_oled_dev *odev, uint8_t val, size_t count) ; } + odev->last_val = val; odev->buf_offs++; } @@ -811,9 +805,10 @@ static int __init asus_oled_init(void) static void __exit asus_oled_exit(void) { - usb_deregister(&oled_driver); class_remove_file(oled_class, &class_attr_version.attr); class_destroy(oled_class); + + usb_deregister(&oled_driver); } module_init(asus_oled_init); diff --git a/trunk/drivers/staging/gma500/Kconfig b/trunk/drivers/staging/gma500/Kconfig new file mode 100644 index 000000000000..c7a2b3bc0a18 --- /dev/null +++ b/trunk/drivers/staging/gma500/Kconfig @@ -0,0 +1,33 @@ +config DRM_PSB + tristate "Intel GMA5/600 KMS Framebuffer" + depends on DRM && PCI && X86 && BROKEN + select FB_CFB_COPYAREA + select FB_CFB_FILLRECT + select FB_CFB_IMAGEBLIT + select DRM_KMS_HELPER + select DRM_TTM + help + Say yes for an experimental 2D KMS framebuffer driver for the + Intel GMA500 ('Poulsbo') and other Intel IMG based graphics + devices. + +config DRM_PSB_MRST + bool "Intel GMA600 support (Experimental)" + depends on DRM_PSB + help + Say yes to include support for GMA600 (Intel Moorestown/Oaktrail) + platforms with LVDS ports. HDMI and MIPI are not currently + supported. + +config DRM_PSB_MFLD + bool "Intel Medfield support (Experimental)" + depends on DRM_PSB + help + Say yes to include support for Intel Medfield platforms with MIPI + interfaces. + +config DRM_PSB_CDV + bool "Intel Cedarview support (Experimental)" + depends on DRM_PSB + help + Say yes to include support for Intel Cedarview platforms diff --git a/trunk/drivers/staging/gma500/Makefile b/trunk/drivers/staging/gma500/Makefile new file mode 100644 index 000000000000..c729868b1b10 --- /dev/null +++ b/trunk/drivers/staging/gma500/Makefile @@ -0,0 +1,52 @@ +# +# KMS driver for the GMA500 +# +ccflags-y += -Iinclude/drm + +psb_gfx-y += gem_glue.o \ + accel_2d.o \ + backlight.o \ + framebuffer.o \ + gem.o \ + gtt.o \ + intel_bios.o \ + intel_i2c.o \ + intel_opregion.o \ + mmu.o \ + power.o \ + psb_drv.o \ + psb_intel_display.o \ + psb_intel_lvds.o \ + psb_intel_modes.o \ + psb_intel_sdvo.o \ + psb_lid.o \ + psb_irq.o \ + psb_device.o \ + mid_bios.o + +psb_gfx-$(CONFIG_DRM_PSB_CDV) += cdv_device.o \ + cdv_intel_crt.o \ + cdv_intel_display.o \ + cdv_intel_hdmi.o \ + cdv_intel_lvds.o + +psb_gfx-$(CONFIG_DRM_PSB_MRST) += mrst_device.o \ + mrst_crtc.o \ + mrst_lvds.o \ + mrst_hdmi.o \ + mrst_hdmi_i2c.o + +psb_gfx-$(CONFIG_DRM_PSB_MFLD) += mdfld_device.o \ + mdfld_output.o \ + mdfld_pyr_cmd.o \ + mdfld_tmd_vid.o \ + mdfld_tpo_cmd.o \ + mdfld_tpo_vid.o \ + mdfld_dsi_pkg_sender.o \ + mdfld_dsi_dpi.o \ + mdfld_dsi_output.o \ + mdfld_dsi_dbi.o \ + mdfld_dsi_dbi_dpu.o \ + mdfld_intel_display.o + +obj-$(CONFIG_DRM_PSB) += psb_gfx.o diff --git a/trunk/drivers/staging/gma500/TODO b/trunk/drivers/staging/gma500/TODO new file mode 100644 index 000000000000..fc836158e74c --- /dev/null +++ b/trunk/drivers/staging/gma500/TODO @@ -0,0 +1,15 @@ +- Sort out the power management side. Not important for Poulsbo but + matters for Moorestown/Medfield +- Debug Oaktrail/Moorestown support (single pipe, no BIOS on mrst, + some other differences) +- Add 2D acceleration via console and DRM +- Add scrolling acceleration using the GTT to do remapping on the main + framebuffer. +- HDMI testing +- Oaktrail HDMI and other features +- Oaktrail MIPI +- Medfield needs a lot of further love + +As per kernel policy and the in the interest of the safety of various +kittens there is no support or plans to add hooks for the closed user space +stuff. diff --git a/trunk/drivers/staging/gma500/accel_2d.c b/trunk/drivers/staging/gma500/accel_2d.c new file mode 100644 index 000000000000..b8f78ebbb145 --- /dev/null +++ b/trunk/drivers/staging/gma500/accel_2d.c @@ -0,0 +1,414 @@ +/************************************************************************** + * Copyright (c) 2007-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. + * + * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to + * develop this driver. + * + **************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "psb_drv.h" +#include "psb_reg.h" +#include "framebuffer.h" + +/** + * psb_spank - reset the 2D engine + * @dev_priv: our PSB DRM device + * + * Soft reset the graphics engine and then reload the necessary registers. + * We use this at initialisation time but it will become relevant for + * accelerated X later + */ +void psb_spank(struct drm_psb_private *dev_priv) +{ + PSB_WSGX32(_PSB_CS_RESET_BIF_RESET | _PSB_CS_RESET_DPM_RESET | + _PSB_CS_RESET_TA_RESET | _PSB_CS_RESET_USE_RESET | + _PSB_CS_RESET_ISP_RESET | _PSB_CS_RESET_TSP_RESET | + _PSB_CS_RESET_TWOD_RESET, PSB_CR_SOFT_RESET); + PSB_RSGX32(PSB_CR_SOFT_RESET); + + msleep(1); + + PSB_WSGX32(0, PSB_CR_SOFT_RESET); + wmb(); + PSB_WSGX32(PSB_RSGX32(PSB_CR_BIF_CTRL) | _PSB_CB_CTRL_CLEAR_FAULT, + PSB_CR_BIF_CTRL); + wmb(); + (void) PSB_RSGX32(PSB_CR_BIF_CTRL); + + msleep(1); + PSB_WSGX32(PSB_RSGX32(PSB_CR_BIF_CTRL) & ~_PSB_CB_CTRL_CLEAR_FAULT, + PSB_CR_BIF_CTRL); + (void) PSB_RSGX32(PSB_CR_BIF_CTRL); + PSB_WSGX32(dev_priv->gtt.gatt_start, PSB_CR_BIF_TWOD_REQ_BASE); +} + +/** + * psb2_2d_wait_available - wait for FIFO room + * @dev_priv: our DRM device + * @size: size (in dwords) of the command we want to issue + * + * Wait until there is room to load the FIFO with our data. If the + * device is not responding then reset it + */ +static int psb_2d_wait_available(struct drm_psb_private *dev_priv, + unsigned size) +{ + uint32_t avail = PSB_RSGX32(PSB_CR_2D_SOCIF); + unsigned long t = jiffies + HZ; + + while (avail < size) { + avail = PSB_RSGX32(PSB_CR_2D_SOCIF); + if (time_after(jiffies, t)) { + psb_spank(dev_priv); + return -EIO; + } + } + return 0; +} + +/** + * psb_2d_submit - submit a 2D command + * @dev_priv: our DRM device + * @cmdbuf: command to issue + * @size: length (in dwords) + * + * Issue one or more 2D commands to the accelerator. This needs to be + * serialized later when we add the GEM interfaces for acceleration + */ +static int psbfb_2d_submit(struct drm_psb_private *dev_priv, uint32_t *cmdbuf, + unsigned size) +{ + int ret = 0; + int i; + unsigned submit_size; + unsigned long flags; + + spin_lock_irqsave(&dev_priv->lock_2d, flags); + while (size > 0) { + submit_size = (size < 0x60) ? size : 0x60; + size -= submit_size; + ret = psb_2d_wait_available(dev_priv, submit_size); + if (ret) + break; + + submit_size <<= 2; + + for (i = 0; i < submit_size; i += 4) + PSB_WSGX32(*cmdbuf++, PSB_SGX_2D_SLAVE_PORT + i); + + (void)PSB_RSGX32(PSB_SGX_2D_SLAVE_PORT + i - 4); + } + spin_unlock_irqrestore(&dev_priv->lock_2d, flags); + return ret; +} + + +/** + * psb_accel_2d_copy_direction - compute blit order + * @xdir: X direction of move + * @ydir: Y direction of move + * + * Compute the correct order setings to ensure that an overlapping blit + * correctly copies all the pixels. + */ +static u32 psb_accel_2d_copy_direction(int xdir, int ydir) +{ + if (xdir < 0) + return (ydir < 0) ? PSB_2D_COPYORDER_BR2TL : + PSB_2D_COPYORDER_TR2BL; + else + return (ydir < 0) ? PSB_2D_COPYORDER_BL2TR : + PSB_2D_COPYORDER_TL2BR; +} + +/** + * psb_accel_2d_copy - accelerated 2D copy + * @dev_priv: our DRM device + * @src_offset in bytes + * @src_stride in bytes + * @src_format psb 2D format defines + * @dst_offset in bytes + * @dst_stride in bytes + * @dst_format psb 2D format defines + * @src_x offset in pixels + * @src_y offset in pixels + * @dst_x offset in pixels + * @dst_y offset in pixels + * @size_x of the copied area + * @size_y of the copied area + * + * Format and issue a 2D accelerated copy command. + */ +static int psb_accel_2d_copy(struct drm_psb_private *dev_priv, + uint32_t src_offset, uint32_t src_stride, + uint32_t src_format, uint32_t dst_offset, + uint32_t dst_stride, uint32_t dst_format, + uint16_t src_x, uint16_t src_y, + uint16_t dst_x, uint16_t dst_y, + uint16_t size_x, uint16_t size_y) +{ + uint32_t blit_cmd; + uint32_t buffer[10]; + uint32_t *buf; + uint32_t direction; + + buf = buffer; + + direction = + psb_accel_2d_copy_direction(src_x - dst_x, src_y - dst_y); + + if (direction == PSB_2D_COPYORDER_BR2TL || + direction == PSB_2D_COPYORDER_TR2BL) { + src_x += size_x - 1; + dst_x += size_x - 1; + } + if (direction == PSB_2D_COPYORDER_BR2TL || + direction == PSB_2D_COPYORDER_BL2TR) { + src_y += size_y - 1; + dst_y += size_y - 1; + } + + blit_cmd = + PSB_2D_BLIT_BH | + PSB_2D_ROT_NONE | + PSB_2D_DSTCK_DISABLE | + PSB_2D_SRCCK_DISABLE | + PSB_2D_USE_PAT | PSB_2D_ROP3_SRCCOPY | direction; + + *buf++ = PSB_2D_FENCE_BH; + *buf++ = + PSB_2D_DST_SURF_BH | dst_format | (dst_stride << + PSB_2D_DST_STRIDE_SHIFT); + *buf++ = dst_offset; + *buf++ = + PSB_2D_SRC_SURF_BH | src_format | (src_stride << + PSB_2D_SRC_STRIDE_SHIFT); + *buf++ = src_offset; + *buf++ = + PSB_2D_SRC_OFF_BH | (src_x << PSB_2D_SRCOFF_XSTART_SHIFT) | + (src_y << PSB_2D_SRCOFF_YSTART_SHIFT); + *buf++ = blit_cmd; + *buf++ = + (dst_x << PSB_2D_DST_XSTART_SHIFT) | (dst_y << + PSB_2D_DST_YSTART_SHIFT); + *buf++ = + (size_x << PSB_2D_DST_XSIZE_SHIFT) | (size_y << + PSB_2D_DST_YSIZE_SHIFT); + *buf++ = PSB_2D_FLUSH_BH; + + return psbfb_2d_submit(dev_priv, buffer, buf - buffer); +} + +/** + * psbfb_copyarea_accel - copyarea acceleration for /dev/fb + * @info: our framebuffer + * @a: copyarea parameters from the framebuffer core + * + * Perform a 2D copy via the accelerator + */ +static void psbfb_copyarea_accel(struct fb_info *info, + const struct fb_copyarea *a) +{ + struct psb_fbdev *fbdev = info->par; + struct psb_framebuffer *psbfb = &fbdev->pfb; + struct drm_device *dev = psbfb->base.dev; + struct drm_framebuffer *fb = fbdev->psb_fb_helper.fb; + struct drm_psb_private *dev_priv = dev->dev_private; + uint32_t offset; + uint32_t stride; + uint32_t src_format; + uint32_t dst_format; + + if (!fb) + return; + + offset = psbfb->gtt->offset; + stride = fb->pitches[0]; + + switch (fb->depth) { + case 8: + src_format = PSB_2D_SRC_332RGB; + dst_format = PSB_2D_DST_332RGB; + break; + case 15: + src_format = PSB_2D_SRC_555RGB; + dst_format = PSB_2D_DST_555RGB; + break; + case 16: + src_format = PSB_2D_SRC_565RGB; + dst_format = PSB_2D_DST_565RGB; + break; + case 24: + case 32: + /* this is wrong but since we don't do blending its okay */ + src_format = PSB_2D_SRC_8888ARGB; + dst_format = PSB_2D_DST_8888ARGB; + break; + default: + /* software fallback */ + cfb_copyarea(info, a); + return; + } + + if (!gma_power_begin(dev, false)) { + cfb_copyarea(info, a); + return; + } + psb_accel_2d_copy(dev_priv, + offset, stride, src_format, + offset, stride, dst_format, + a->sx, a->sy, a->dx, a->dy, a->width, a->height); + gma_power_end(dev); +} + +/** + * psbfb_copyarea - 2D copy interface + * @info: our framebuffer + * @region: region to copy + * + * Copy an area of the framebuffer console either by the accelerator + * or directly using the cfb helpers according to the request + */ +void psbfb_copyarea(struct fb_info *info, + const struct fb_copyarea *region) +{ + if (unlikely(info->state != FBINFO_STATE_RUNNING)) + return; + + /* Avoid the 8 pixel erratum */ + if (region->width == 8 || region->height == 8 || + (info->flags & FBINFO_HWACCEL_DISABLED)) + return cfb_copyarea(info, region); + + psbfb_copyarea_accel(info, region); +} + +/** + * psbfb_sync - synchronize 2D + * @info: our framebuffer + * + * Wait for the 2D engine to quiesce so that we can do CPU + * access to the framebuffer again + */ +int psbfb_sync(struct fb_info *info) +{ + struct psb_fbdev *fbdev = info->par; + struct psb_framebuffer *psbfb = &fbdev->pfb; + struct drm_device *dev = psbfb->base.dev; + struct drm_psb_private *dev_priv = dev->dev_private; + unsigned long _end = jiffies + DRM_HZ; + int busy = 0; + unsigned long flags; + + spin_lock_irqsave(&dev_priv->lock_2d, flags); + /* + * First idle the 2D engine. + */ + + if ((PSB_RSGX32(PSB_CR_2D_SOCIF) == _PSB_C2_SOCIF_EMPTY) && + ((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) & _PSB_C2B_STATUS_BUSY) == 0)) + goto out; + + do { + busy = (PSB_RSGX32(PSB_CR_2D_SOCIF) != _PSB_C2_SOCIF_EMPTY); + cpu_relax(); + } while (busy && !time_after_eq(jiffies, _end)); + + if (busy) + busy = (PSB_RSGX32(PSB_CR_2D_SOCIF) != _PSB_C2_SOCIF_EMPTY); + if (busy) + goto out; + + do { + busy = ((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) & + _PSB_C2B_STATUS_BUSY) != 0); + cpu_relax(); + } while (busy && !time_after_eq(jiffies, _end)); + if (busy) + busy = ((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) & + _PSB_C2B_STATUS_BUSY) != 0); + +out: + spin_unlock_irqrestore(&dev_priv->lock_2d, flags); + return (busy) ? -EBUSY : 0; +} + +int psb_accel_ioctl(struct drm_device *dev, void *data, struct drm_file *file) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct drm_psb_2d_op *op = data; + u32 *op_ptr = &op->cmd[0]; + int i; + struct drm_gem_object *obj; + struct gtt_range *gtt; + int err = -EINVAL; + + if (!dev_priv->ops->accel_2d) + return -EOPNOTSUPP; + if (op->size > PSB_2D_OP_BUFLEN) + return -EINVAL; + + /* The GEM object being used. We need to support separate src/dst/etc + in the end but for now keep them all the same */ + obj = drm_gem_object_lookup(dev, file, op->src); + if (obj == NULL) + return -ENOENT; + gtt = container_of(obj, struct gtt_range, gem); + + if (psb_gtt_pin(gtt) < 0) + goto bad_2; + for (i = 0; i < op->size; i++, op_ptr++) { + u32 r = *op_ptr & 0xF0000000; + /* Fill in the GTT offsets for the command buffer */ + if (r == PSB_2D_SRC_SURF_BH || + r == PSB_2D_DST_SURF_BH || + r == PSB_2D_MASK_SURF_BH || + r == PSB_2D_PAT_SURF_BH) { + i++; + op_ptr++; + if (i == op->size) + goto bad; + if (*op_ptr) + goto bad; + *op_ptr = gtt->offset; + continue; + } + } + psbfb_2d_submit(dev_priv, op->cmd, op->size); + err = 0; +bad: + psb_gtt_unpin(gtt); +bad_2: + drm_gem_object_unreference(obj); + return err; +} diff --git a/trunk/drivers/staging/gma500/backlight.c b/trunk/drivers/staging/gma500/backlight.c new file mode 100644 index 000000000000..20793951fcac --- /dev/null +++ b/trunk/drivers/staging/gma500/backlight.c @@ -0,0 +1,49 @@ +/* + * GMA500 Backlight Interface + * + * Copyright (c) 2009-2011, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * 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. + * + * Authors: Eric Knopp + * + */ + +#include "psb_drv.h" +#include "psb_intel_reg.h" +#include "psb_intel_drv.h" +#include "intel_bios.h" +#include "power.h" + +int gma_backlight_init(struct drm_device *dev) +{ +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + struct drm_psb_private *dev_priv = dev->dev_private; + return dev_priv->ops->backlight_init(dev); +#else + return 0; +#endif +} + +void gma_backlight_exit(struct drm_device *dev) +{ +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + struct drm_psb_private *dev_priv = dev->dev_private; + if (dev_priv->backlight_device) { + dev_priv->backlight_device->props.brightness = 0; + backlight_update_status(dev_priv->backlight_device); + backlight_device_unregister(dev_priv->backlight_device); + } +#endif +} diff --git a/trunk/drivers/staging/gma500/cdv_device.c b/trunk/drivers/staging/gma500/cdv_device.c new file mode 100644 index 000000000000..8ec10caab13e --- /dev/null +++ b/trunk/drivers/staging/gma500/cdv_device.c @@ -0,0 +1,350 @@ +/************************************************************************** + * 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 +#include +#include +#include "psb_drm.h" +#include "psb_drv.h" +#include "psb_reg.h" +#include "psb_intel_reg.h" +#include "intel_bios.h" +#include "cdv_device.h" + +#define VGA_SR_INDEX 0x3c4 +#define VGA_SR_DATA 0x3c5 + +static void cdv_disable_vga(struct drm_device *dev) +{ + u8 sr1; + u32 vga_reg; + + vga_reg = VGACNTRL; + + outb(1, VGA_SR_INDEX); + sr1 = inb(VGA_SR_DATA); + outb(sr1 | 1<<5, VGA_SR_DATA); + udelay(300); + + REG_WRITE(vga_reg, VGA_DISP_DISABLE); + REG_READ(vga_reg); +} + +static int cdv_output_init(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + cdv_disable_vga(dev); + + cdv_intel_crt_init(dev, &dev_priv->mode_dev); + cdv_intel_lvds_init(dev, &dev_priv->mode_dev); + + /* These bits indicate HDMI not SDVO on CDV, but we don't yet support + the HDMI interface */ + if (REG_READ(SDVOB) & SDVO_DETECTED) + cdv_hdmi_init(dev, &dev_priv->mode_dev, SDVOB); + if (REG_READ(SDVOC) & SDVO_DETECTED) + cdv_hdmi_init(dev, &dev_priv->mode_dev, SDVOC); + return 0; +} + +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + +/* + * Poulsbo Backlight Interfaces + */ + +#define BLC_PWM_PRECISION_FACTOR 100 /* 10000000 */ +#define BLC_PWM_FREQ_CALC_CONSTANT 32 +#define MHz 1000000 + +#define PSB_BLC_PWM_PRECISION_FACTOR 10 +#define PSB_BLC_MAX_PWM_REG_FREQ 0xFFFE +#define PSB_BLC_MIN_PWM_REG_FREQ 0x2 + +#define PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE) +#define PSB_BACKLIGHT_PWM_CTL_SHIFT (16) + +static int cdv_brightness; +static struct backlight_device *cdv_backlight_device; + +static int cdv_get_brightness(struct backlight_device *bd) +{ + /* return locally cached var instead of HW read (due to DPST etc.) */ + /* FIXME: ideally return actual value in case firmware fiddled with + it */ + return cdv_brightness; +} + + +static int cdv_backlight_setup(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + unsigned long core_clock; + /* u32 bl_max_freq; */ + /* unsigned long value; */ + u16 bl_max_freq; + uint32_t value; + uint32_t blc_pwm_precision_factor; + + /* get bl_max_freq and pol from dev_priv*/ + if (!dev_priv->lvds_bl) { + dev_err(dev->dev, "Has no valid LVDS backlight info\n"); + return -ENOENT; + } + bl_max_freq = dev_priv->lvds_bl->freq; + blc_pwm_precision_factor = PSB_BLC_PWM_PRECISION_FACTOR; + + core_clock = dev_priv->core_freq; + + value = (core_clock * MHz) / BLC_PWM_FREQ_CALC_CONSTANT; + value *= blc_pwm_precision_factor; + value /= bl_max_freq; + value /= blc_pwm_precision_factor; + + if (value > (unsigned long long)PSB_BLC_MAX_PWM_REG_FREQ || + value < (unsigned long long)PSB_BLC_MIN_PWM_REG_FREQ) + return -ERANGE; + else { + /* FIXME */ + } + return 0; +} + +static int cdv_set_brightness(struct backlight_device *bd) +{ + int level = bd->props.brightness; + + /* Percentage 1-100% being valid */ + if (level < 1) + level = 1; + + /*cdv_intel_lvds_set_brightness(dev, level); FIXME */ + cdv_brightness = level; + return 0; +} + +static const struct backlight_ops cdv_ops = { + .get_brightness = cdv_get_brightness, + .update_status = cdv_set_brightness, +}; + +static int cdv_backlight_init(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + int ret; + struct backlight_properties props; + + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = 100; + props.type = BACKLIGHT_PLATFORM; + + cdv_backlight_device = backlight_device_register("psb-bl", + NULL, (void *)dev, &cdv_ops, &props); + if (IS_ERR(cdv_backlight_device)) + return PTR_ERR(cdv_backlight_device); + + ret = cdv_backlight_setup(dev); + if (ret < 0) { + backlight_device_unregister(cdv_backlight_device); + cdv_backlight_device = NULL; + return ret; + } + cdv_backlight_device->props.brightness = 100; + cdv_backlight_device->props.max_brightness = 100; + backlight_update_status(cdv_backlight_device); + dev_priv->backlight_device = cdv_backlight_device; + return 0; +} + +#endif + +/* + * Provide the Cedarview specific chip logic and low level methods + * for power management + * + * FIXME: we need to implement the apm/ospm base management bits + * for this and the MID devices. + */ + +static inline u32 CDV_MSG_READ32(uint port, uint offset) +{ + int mcr = (0x10<<24) | (port << 16) | (offset << 8); + uint32_t ret_val = 0; + struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0); + pci_write_config_dword(pci_root, 0xD0, mcr); + pci_read_config_dword(pci_root, 0xD4, &ret_val); + pci_dev_put(pci_root); + return ret_val; +} + +static inline void CDV_MSG_WRITE32(uint port, uint offset, u32 value) +{ + int mcr = (0x11<<24) | (port << 16) | (offset << 8) | 0xF0; + struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0); + pci_write_config_dword(pci_root, 0xD4, value); + pci_write_config_dword(pci_root, 0xD0, mcr); + pci_dev_put(pci_root); +} + +#define PSB_APM_CMD 0x0 +#define PSB_APM_STS 0x04 +#define PSB_PM_SSC 0x20 +#define PSB_PM_SSS 0x30 +#define PSB_PWRGT_GFX_MASK 0x3 +#define CDV_PWRGT_DISPLAY_CNTR 0x000fc00c +#define CDV_PWRGT_DISPLAY_STS 0x000fc00c + +static void cdv_init_pm(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + u32 pwr_cnt; + int i; + + dev_priv->apm_base = CDV_MSG_READ32(PSB_PUNIT_PORT, + PSB_APMBA) & 0xFFFF; + dev_priv->ospm_base = CDV_MSG_READ32(PSB_PUNIT_PORT, + PSB_OSPMBA) & 0xFFFF; + + /* Force power on for now */ + pwr_cnt = inl(dev_priv->apm_base + PSB_APM_CMD); + pwr_cnt &= ~PSB_PWRGT_GFX_MASK; + + outl(pwr_cnt, dev_priv->apm_base + PSB_APM_CMD); + for (i = 0; i < 5; i++) { + u32 pwr_sts = inl(dev_priv->apm_base + PSB_APM_STS); + if ((pwr_sts & PSB_PWRGT_GFX_MASK) == 0) + break; + udelay(10); + } + pwr_cnt = inl(dev_priv->ospm_base + PSB_PM_SSC); + pwr_cnt &= ~CDV_PWRGT_DISPLAY_CNTR; + outl(pwr_cnt, dev_priv->ospm_base + PSB_PM_SSC); + for (i = 0; i < 5; i++) { + u32 pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS); + if ((pwr_sts & CDV_PWRGT_DISPLAY_STS) == 0) + break; + udelay(10); + } +} + +/** + * cdv_save_display_registers - save registers lost on suspend + * @dev: our DRM device + * + * Save the state we need in order to be able to restore the interface + * upon resume from suspend + * + * FIXME: review + */ +static int cdv_save_display_registers(struct drm_device *dev) +{ + return 0; +} + +/** + * cdv_restore_display_registers - restore lost register state + * @dev: our DRM device + * + * Restore register state that was lost during suspend and resume. + * + * FIXME: review + */ +static int cdv_restore_display_registers(struct drm_device *dev) +{ + return 0; +} + +static int cdv_power_down(struct drm_device *dev) +{ + return 0; +} + +static int cdv_power_up(struct drm_device *dev) +{ + return 0; +} + +/* FIXME ? - shared with Poulsbo */ +static void cdv_get_core_freq(struct drm_device *dev) +{ + uint32_t clock; + struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0); + struct drm_psb_private *dev_priv = dev->dev_private; + + pci_write_config_dword(pci_root, 0xD0, 0xD0050300); + pci_read_config_dword(pci_root, 0xD4, &clock); + pci_dev_put(pci_root); + + switch (clock & 0x07) { + case 0: + dev_priv->core_freq = 100; + break; + case 1: + dev_priv->core_freq = 133; + break; + case 2: + dev_priv->core_freq = 150; + break; + case 3: + dev_priv->core_freq = 178; + break; + case 4: + dev_priv->core_freq = 200; + break; + case 5: + case 6: + case 7: + dev_priv->core_freq = 266; + default: + dev_priv->core_freq = 0; + } +} + +static int cdv_chip_setup(struct drm_device *dev) +{ + cdv_get_core_freq(dev); + gma_intel_opregion_init(dev); + psb_intel_init_bios(dev); + return 0; +} + +/* CDV is much like Poulsbo but has MID like SGX offsets and PM */ + +const struct psb_ops cdv_chip_ops = { + .name = "Cedartrail", + .accel_2d = 0, + .pipes = 2, + .sgx_offset = MRST_SGX_OFFSET, + .chip_setup = cdv_chip_setup, + + .crtc_helper = &cdv_intel_helper_funcs, + .crtc_funcs = &cdv_intel_crtc_funcs, + + .output_init = cdv_output_init, + +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + .backlight_init = cdv_backlight_init, +#endif + + .init_pm = cdv_init_pm, + .save_regs = cdv_save_display_registers, + .restore_regs = cdv_restore_display_registers, + .power_down = cdv_power_down, + .power_up = cdv_power_up, +}; diff --git a/trunk/drivers/staging/gma500/cdv_device.h b/trunk/drivers/staging/gma500/cdv_device.h new file mode 100644 index 000000000000..2a88b7beb551 --- /dev/null +++ b/trunk/drivers/staging/gma500/cdv_device.h @@ -0,0 +1,36 @@ +/* + * Copyright © 2011 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * 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. + */ + +extern const struct drm_crtc_helper_funcs cdv_intel_helper_funcs; +extern const struct drm_crtc_funcs cdv_intel_crtc_funcs; +extern void cdv_intel_crt_init(struct drm_device *dev, + struct psb_intel_mode_device *mode_dev); +extern void cdv_intel_lvds_init(struct drm_device *dev, + struct psb_intel_mode_device *mode_dev); +extern void cdv_hdmi_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, + int reg); +extern struct drm_display_mode *cdv_intel_crtc_mode_get(struct drm_device *dev, + struct drm_crtc *crtc); + +extern inline void cdv_intel_wait_for_vblank(struct drm_device *dev) +{ + /* Wait for 20ms, i.e. one cycle at 50hz. */ + /* FIXME: msleep ?? */ + mdelay(20); +} + + diff --git a/trunk/drivers/staging/gma500/cdv_intel_crt.c b/trunk/drivers/staging/gma500/cdv_intel_crt.c new file mode 100644 index 000000000000..efda63b97b45 --- /dev/null +++ b/trunk/drivers/staging/gma500/cdv_intel_crt.c @@ -0,0 +1,326 @@ +/* + * Copyright © 2006-2007 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: + * Eric Anholt + */ + +#include +#include + +#include "intel_bios.h" +#include "psb_drv.h" +#include "psb_intel_drv.h" +#include "psb_intel_reg.h" +#include "power.h" +#include + + +static void cdv_intel_crt_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + u32 temp, reg; + reg = ADPA; + + temp = REG_READ(reg); + temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); + temp &= ~ADPA_DAC_ENABLE; + + switch (mode) { + case DRM_MODE_DPMS_ON: + temp |= ADPA_DAC_ENABLE; + break; + case DRM_MODE_DPMS_STANDBY: + temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE; + break; + case DRM_MODE_DPMS_SUSPEND: + temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE; + break; + case DRM_MODE_DPMS_OFF: + temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE; + break; + } + + REG_WRITE(reg, temp); +} + +static int cdv_intel_crt_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + int max_clock = 0; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + + /* The lowest clock for CDV is 20000KHz */ + if (mode->clock < 20000) + return MODE_CLOCK_LOW; + + /* The max clock for CDV is 355 instead of 400 */ + max_clock = 355000; + if (mode->clock > max_clock) + return MODE_CLOCK_HIGH; + + if (mode->hdisplay > 1680 || mode->vdisplay > 1050) + return MODE_PANEL; + + return MODE_OK; +} + +static bool cdv_intel_crt_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void cdv_intel_crt_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + + struct drm_device *dev = encoder->dev; + struct drm_crtc *crtc = encoder->crtc; + struct psb_intel_crtc *psb_intel_crtc = + to_psb_intel_crtc(crtc); + int dpll_md_reg; + u32 adpa, dpll_md; + u32 adpa_reg; + + if (psb_intel_crtc->pipe == 0) + dpll_md_reg = DPLL_A_MD; + else + dpll_md_reg = DPLL_B_MD; + + adpa_reg = ADPA; + + /* + * Disable separate mode multiplier used when cloning SDVO to CRT + * XXX this needs to be adjusted when we really are cloning + */ + { + dpll_md = REG_READ(dpll_md_reg); + REG_WRITE(dpll_md_reg, + dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK); + } + + adpa = 0; + if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) + adpa |= ADPA_HSYNC_ACTIVE_HIGH; + if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) + adpa |= ADPA_VSYNC_ACTIVE_HIGH; + + if (psb_intel_crtc->pipe == 0) + adpa |= ADPA_PIPE_A_SELECT; + else + adpa |= ADPA_PIPE_B_SELECT; + + REG_WRITE(adpa_reg, adpa); +} + + +/** + * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence. + * + * \return true if CRT is connected. + * \return false if CRT is disconnected. + */ +static bool cdv_intel_crt_detect_hotplug(struct drm_connector *connector, + bool force) +{ + struct drm_device *dev = connector->dev; + u32 hotplug_en; + int i, tries = 0, ret = false; + u32 adpa_orig; + + /* disable the DAC when doing the hotplug detection */ + + adpa_orig = REG_READ(ADPA); + + REG_WRITE(ADPA, adpa_orig & ~(ADPA_DAC_ENABLE)); + + /* + * On a CDV thep, CRT detect sequence need to be done twice + * to get a reliable result. + */ + tries = 2; + + hotplug_en = REG_READ(PORT_HOTPLUG_EN); + hotplug_en &= ~(CRT_HOTPLUG_DETECT_MASK); + hotplug_en |= CRT_HOTPLUG_FORCE_DETECT; + + hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64; + hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50; + + for (i = 0; i < tries ; i++) { + unsigned long timeout; + /* turn on the FORCE_DETECT */ + REG_WRITE(PORT_HOTPLUG_EN, hotplug_en); + timeout = jiffies + msecs_to_jiffies(1000); + /* wait for FORCE_DETECT to go off */ + do { + if (!(REG_READ(PORT_HOTPLUG_EN) & + CRT_HOTPLUG_FORCE_DETECT)) + break; + msleep(1); + } while (time_after(timeout, jiffies)); + } + + if ((REG_READ(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) != + CRT_HOTPLUG_MONITOR_NONE) + ret = true; + + /* Restore the saved ADPA */ + REG_WRITE(ADPA, adpa_orig); + return ret; +} + +static enum drm_connector_status cdv_intel_crt_detect( + struct drm_connector *connector, bool force) +{ + if (cdv_intel_crt_detect_hotplug(connector, force)) + return connector_status_connected; + else + return connector_status_disconnected; +} + +static void cdv_intel_crt_destroy(struct drm_connector *connector) +{ + struct psb_intel_output *intel_output = to_psb_intel_output(connector); + + psb_intel_i2c_destroy(intel_output->ddc_bus); + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + kfree(connector); +} + +static int cdv_intel_crt_get_modes(struct drm_connector *connector) +{ + struct psb_intel_output *intel_output = + to_psb_intel_output(connector); + return psb_intel_ddc_get_modes(intel_output); +} + +static int cdv_intel_crt_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t value) +{ + return 0; +} + +/* + * Routines for controlling stuff on the analog port + */ + +static const struct drm_encoder_helper_funcs cdv_intel_crt_helper_funcs = { + .dpms = cdv_intel_crt_dpms, + .mode_fixup = cdv_intel_crt_mode_fixup, + .prepare = psb_intel_encoder_prepare, + .commit = psb_intel_encoder_commit, + .mode_set = cdv_intel_crt_mode_set, +}; + +static const struct drm_connector_funcs cdv_intel_crt_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = cdv_intel_crt_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = cdv_intel_crt_destroy, + .set_property = cdv_intel_crt_set_property, +}; + +static const struct drm_connector_helper_funcs + cdv_intel_crt_connector_helper_funcs = { + .mode_valid = cdv_intel_crt_mode_valid, + .get_modes = cdv_intel_crt_get_modes, + .best_encoder = psb_intel_best_encoder, +}; + +static void cdv_intel_crt_enc_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs cdv_intel_crt_enc_funcs = { + .destroy = cdv_intel_crt_enc_destroy, +}; + +void cdv_intel_crt_init(struct drm_device *dev, + struct psb_intel_mode_device *mode_dev) +{ + + struct psb_intel_output *psb_intel_output; + struct drm_connector *connector; + struct drm_encoder *encoder; + + u32 i2c_reg; + + psb_intel_output = kzalloc(sizeof(struct psb_intel_output), GFP_KERNEL); + if (!psb_intel_output) + return; + + psb_intel_output->mode_dev = mode_dev; + connector = &psb_intel_output->base; + drm_connector_init(dev, connector, + &cdv_intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA); + + encoder = &psb_intel_output->enc; + drm_encoder_init(dev, encoder, + &cdv_intel_crt_enc_funcs, DRM_MODE_ENCODER_DAC); + + drm_mode_connector_attach_encoder(&psb_intel_output->base, + &psb_intel_output->enc); + + /* Set up the DDC bus. */ + i2c_reg = GPIOA; + /* Remove the following code for CDV */ + /* + if (dev_priv->crt_ddc_bus != 0) + i2c_reg = dev_priv->crt_ddc_bus; + }*/ + psb_intel_output->ddc_bus = psb_intel_i2c_create(dev, + i2c_reg, "CRTDDC_A"); + if (!psb_intel_output->ddc_bus) { + dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " + "failed.\n"); + goto failed_ddc; + } + + psb_intel_output->type = INTEL_OUTPUT_ANALOG; + /* + psb_intel_output->clone_mask = (1 << INTEL_ANALOG_CLONE_BIT); + psb_intel_output->crtc_mask = (1 << 0) | (1 << 1); + */ + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + + drm_encoder_helper_add(encoder, &cdv_intel_crt_helper_funcs); + drm_connector_helper_add(connector, + &cdv_intel_crt_connector_helper_funcs); + + drm_sysfs_connector_add(connector); + + return; +failed_ddc: + drm_encoder_cleanup(&psb_intel_output->enc); + drm_connector_cleanup(&psb_intel_output->base); + kfree(psb_intel_output); + return; +} diff --git a/trunk/drivers/staging/gma500/cdv_intel_display.c b/trunk/drivers/staging/gma500/cdv_intel_display.c new file mode 100644 index 000000000000..c63a32776a9e --- /dev/null +++ b/trunk/drivers/staging/gma500/cdv_intel_display.c @@ -0,0 +1,1508 @@ +/* + * Copyright © 2006-2011 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * 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. + * + * Authors: + * Eric Anholt + */ + +#include +#include + +#include +#include "framebuffer.h" +#include "psb_drv.h" +#include "psb_intel_drv.h" +#include "psb_intel_reg.h" +#include "psb_intel_display.h" +#include "power.h" +#include "cdv_device.h" + + +struct cdv_intel_range_t { + int min, max; +}; + +struct cdv_intel_p2_t { + int dot_limit; + int p2_slow, p2_fast; +}; + +struct cdv_intel_clock_t { + /* given values */ + int n; + int m1, m2; + int p1, p2; + /* derived values */ + int dot; + int vco; + int m; + int p; +}; + +#define INTEL_P2_NUM 2 + +struct cdv_intel_limit_t { + struct cdv_intel_range_t dot, vco, n, m, m1, m2, p, p1; + struct cdv_intel_p2_t p2; +}; + +#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 + +static const struct cdv_intel_limit_t cdv_intel_limits[] = { + { /* CDV_SIGNLE_LVDS_96MHz */ + .dot = {.min = 20000, .max = 115500}, + .vco = {.min = 1800000, .max = 3600000}, + .n = {.min = 2, .max = 6}, + .m = {.min = 60, .max = 160}, + .m1 = {.min = 0, .max = 0}, + .m2 = {.min = 58, .max = 158}, + .p = {.min = 28, .max = 140}, + .p1 = {.min = 2, .max = 10}, + .p2 = {.dot_limit = 200000, + .p2_slow = 14, .p2_fast = 14}, + }, + { /* CDV_SINGLE_LVDS_100MHz */ + .dot = {.min = 20000, .max = 115500}, + .vco = {.min = 1800000, .max = 3600000}, + .n = {.min = 2, .max = 6}, + .m = {.min = 60, .max = 160}, + .m1 = {.min = 0, .max = 0}, + .m2 = {.min = 58, .max = 158}, + .p = {.min = 28, .max = 140}, + .p1 = {.min = 2, .max = 10}, + /* The single-channel range is 25-112Mhz, and dual-channel + * is 80-224Mhz. Prefer single channel as much as possible. + */ + .p2 = {.dot_limit = 200000, .p2_slow = 14, .p2_fast = 14}, + }, + { /* CDV_DAC_HDMI_27MHz */ + .dot = {.min = 20000, .max = 400000}, + .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 = 5}, + }, + { /* CDV_DAC_HDMI_96MHz */ + .dot = {.min = 20000, .max = 400000}, + .vco = {.min = 1800000, .max = 3600000}, + .n = {.min = 2, .max = 6}, + .m = {.min = 60, .max = 160}, + .m1 = {.min = 0, .max = 0}, + .m2 = {.min = 58, .max = 158}, + .p = {.min = 5, .max = 100}, + .p1 = {.min = 1, .max = 10}, + .p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 5}, + }, +}; + +#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) + + +static int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val) +{ + int ret; + + ret = wait_for((REG_READ(SB_PCKT) & SB_BUSY) == 0, 1000); + if (ret) { + DRM_ERROR("timeout waiting for SB to idle before read\n"); + return ret; + } + + REG_WRITE(SB_ADDR, reg); + REG_WRITE(SB_PCKT, + SET_FIELD(SB_OPCODE_READ, SB_OPCODE) | + SET_FIELD(SB_DEST_DPLL, SB_DEST) | + SET_FIELD(0xf, SB_BYTE_ENABLE)); + + ret = wait_for((REG_READ(SB_PCKT) & SB_BUSY) == 0, 1000); + if (ret) { + DRM_ERROR("timeout waiting for SB to idle after read\n"); + return ret; + } + + *val = REG_READ(SB_DATA); + + return 0; +} + +static int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val) +{ + int ret; + static bool dpio_debug = true; + u32 temp; + + if (dpio_debug) { + if (cdv_sb_read(dev, reg, &temp) == 0) + DRM_DEBUG_KMS("0x%08x: 0x%08x (before)\n", reg, temp); + DRM_DEBUG_KMS("0x%08x: 0x%08x\n", reg, val); + } + + ret = wait_for((REG_READ(SB_PCKT) & SB_BUSY) == 0, 1000); + if (ret) { + DRM_ERROR("timeout waiting for SB to idle before write\n"); + return ret; + } + + REG_WRITE(SB_ADDR, reg); + REG_WRITE(SB_DATA, val); + REG_WRITE(SB_PCKT, + SET_FIELD(SB_OPCODE_WRITE, SB_OPCODE) | + SET_FIELD(SB_DEST_DPLL, SB_DEST) | + SET_FIELD(0xf, SB_BYTE_ENABLE)); + + ret = wait_for((REG_READ(SB_PCKT) & SB_BUSY) == 0, 1000); + if (ret) { + DRM_ERROR("timeout waiting for SB to idle after write\n"); + return ret; + } + + if (dpio_debug) { + if (cdv_sb_read(dev, reg, &temp) == 0) + DRM_DEBUG_KMS("0x%08x: 0x%08x (after)\n", reg, temp); + } + + return 0; +} + +/* Reset the DPIO configuration register. The BIOS does this at every + * mode set. + */ +static void cdv_sb_reset(struct drm_device *dev) +{ + + REG_WRITE(DPIO_CFG, 0); + REG_READ(DPIO_CFG); + REG_WRITE(DPIO_CFG, DPIO_MODE_SELECT_0 | DPIO_CMN_RESET_N); +} + +/* Unlike most Intel display engines, on Cedarview the DPLL registers + * are behind this sideband bus. They must be programmed while the + * DPLL reference clock is on in the DPLL control register, but before + * the DPLL is enabled in the DPLL control register. + */ +static int +cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc, + struct cdv_intel_clock_t *clock) +{ + struct psb_intel_crtc *psb_crtc = + to_psb_intel_crtc(crtc); + int pipe = psb_crtc->pipe; + u32 m, n_vco, p; + int ret = 0; + int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; + u32 ref_value; + + cdv_sb_reset(dev); + + if ((REG_READ(dpll_reg) & DPLL_SYNCLOCK_ENABLE) == 0) { + DRM_ERROR("Attempting to set DPLL with refclk disabled\n"); + return -EBUSY; + } + + /* Follow the BIOS and write the REF/SFR Register. Hardcoded value */ + ref_value = 0x68A701; + + cdv_sb_write(dev, SB_REF_SFR(pipe), ref_value); + + /* We don't know what the other fields of these regs are, so + * leave them in place. + */ + ret = cdv_sb_read(dev, SB_M(pipe), &m); + if (ret) + return ret; + m &= ~SB_M_DIVIDER_MASK; + m |= ((clock->m2) << SB_M_DIVIDER_SHIFT); + ret = cdv_sb_write(dev, SB_M(pipe), m); + if (ret) + return ret; + + ret = cdv_sb_read(dev, SB_N_VCO(pipe), &n_vco); + if (ret) + return ret; + + /* Follow the BIOS to program the N_DIVIDER REG */ + n_vco &= 0xFFFF; + n_vco |= 0x107; + n_vco &= ~(SB_N_VCO_SEL_MASK | + SB_N_DIVIDER_MASK | + SB_N_CB_TUNE_MASK); + + n_vco |= ((clock->n) << SB_N_DIVIDER_SHIFT); + + if (clock->vco < 2250000) { + n_vco |= (2 << SB_N_CB_TUNE_SHIFT); + n_vco |= (0 << SB_N_VCO_SEL_SHIFT); + } else if (clock->vco < 2750000) { + n_vco |= (1 << SB_N_CB_TUNE_SHIFT); + n_vco |= (1 << SB_N_VCO_SEL_SHIFT); + } else if (clock->vco < 3300000) { + n_vco |= (0 << SB_N_CB_TUNE_SHIFT); + n_vco |= (2 << SB_N_VCO_SEL_SHIFT); + } else { + n_vco |= (0 << SB_N_CB_TUNE_SHIFT); + n_vco |= (3 << SB_N_VCO_SEL_SHIFT); + } + + ret = cdv_sb_write(dev, SB_N_VCO(pipe), n_vco); + if (ret) + return ret; + + ret = cdv_sb_read(dev, SB_P(pipe), &p); + if (ret) + return ret; + p &= ~(SB_P2_DIVIDER_MASK | SB_P1_DIVIDER_MASK); + p |= SET_FIELD(clock->p1, SB_P1_DIVIDER); + switch (clock->p2) { + case 5: + p |= SET_FIELD(SB_P2_5, SB_P2_DIVIDER); + break; + case 10: + p |= SET_FIELD(SB_P2_10, SB_P2_DIVIDER); + break; + case 14: + p |= SET_FIELD(SB_P2_14, SB_P2_DIVIDER); + break; + case 7: + p |= SET_FIELD(SB_P2_7, SB_P2_DIVIDER); + break; + default: + DRM_ERROR("Bad P2 clock: %d\n", clock->p2); + return -EINVAL; + } + ret = cdv_sb_write(dev, SB_P(pipe), p); + if (ret) + return ret; + + /* always Program the Lane Register for the Pipe A*/ + if (pipe == 0) { + /* Program the Lane0/1 for HDMI B */ + u32 lane_reg, lane_value; + + lane_reg = PSB_LANE0; + cdv_sb_read(dev, lane_reg, &lane_value); + lane_value &= ~(LANE_PLL_MASK); + lane_value |= LANE_PLL_ENABLE; + 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; + cdv_sb_write(dev, lane_reg, lane_value); + + /* Program the Lane2/3 for HDMI C */ + lane_reg = PSB_LANE2; + cdv_sb_read(dev, lane_reg, &lane_value); + lane_value &= ~(LANE_PLL_MASK); + lane_value |= LANE_PLL_ENABLE; + 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; + cdv_sb_write(dev, lane_reg, lane_value); + } + + return 0; +} + +/* + * Returns whether any output on the specified pipe is of the specified type + */ +bool cdv_intel_pipe_has_type(struct drm_crtc *crtc, int type) +{ + struct drm_device *dev = crtc->dev; + struct drm_mode_config *mode_config = &dev->mode_config; + struct drm_connector *l_entry; + + list_for_each_entry(l_entry, &mode_config->connector_list, head) { + if (l_entry->encoder && l_entry->encoder->crtc == crtc) { + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(l_entry); + if (psb_intel_output->type == type) + return true; + } + } + return false; +} + +static const struct cdv_intel_limit_t *cdv_intel_limit(struct drm_crtc *crtc, + int refclk) +{ + const struct cdv_intel_limit_t *limit; + if (cdv_intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { + /* + * Now only single-channel LVDS is supported on CDV. If it is + * incorrect, please add the dual-channel LVDS. + */ + if (refclk == 96000) + limit = &cdv_intel_limits[CDV_LIMIT_SINGLE_LVDS_96]; + else + limit = &cdv_intel_limits[CDV_LIMIT_SINGLE_LVDS_100]; + } else { + if (refclk == 27000) + limit = &cdv_intel_limits[CDV_LIMIT_DAC_HDMI_27]; + else + limit = &cdv_intel_limits[CDV_LIMIT_DAC_HDMI_96]; + } + return limit; +} + +/* m1 is reserved as 0 in CDV, n is a ring counter */ +static void cdv_intel_clock(struct drm_device *dev, + int refclk, struct cdv_intel_clock_t *clock) +{ + clock->m = clock->m2 + 2; + clock->p = clock->p1 * clock->p2; + clock->vco = (refclk * clock->m) / clock->n; + clock->dot = clock->vco / clock->p; +} + + +#define INTELPllInvalid(s) { /* ErrorF (s) */; return false; } +static bool cdv_intel_PLL_is_valid(struct drm_crtc *crtc, + const struct cdv_intel_limit_t *limit, + struct cdv_intel_clock_t *clock) +{ + if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1) + INTELPllInvalid("p1 out of range\n"); + if (clock->p < limit->p.min || limit->p.max < clock->p) + INTELPllInvalid("p out of range\n"); + /* unnecessary to check the range of m(m1/M2)/n again */ + if (clock->vco < limit->vco.min || limit->vco.max < clock->vco) + INTELPllInvalid("vco out of range\n"); + /* XXX: We may need to be checking "Dot clock" + * depending on the multiplier, connector, etc., + * rather than just a single range. + */ + if (clock->dot < limit->dot.min || limit->dot.max < clock->dot) + INTELPllInvalid("dot out of range\n"); + + return true; +} + +static bool cdv_intel_find_best_PLL(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; + + + if (cdv_intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && + (REG_READ(LVDS) & LVDS_PORT_EN) != 0) { + /* + * For LVDS, if the panel is on, just rely on its current + * settings for dual-channel. We haven't figured out how to + * reliably set up different single/dual channel state, if we + * even can. + */ + if ((REG_READ(LVDS) & LVDS_CLKB_POWER_MASK) == + LVDS_CLKB_POWER_UP) + clock.p2 = limit->p2.p2_fast; + else + clock.p2 = limit->p2.p2_slow; + } else { + if (target < limit->p2.dot_limit) + clock.p2 = limit->p2.p2_slow; + else + clock.p2 = limit->p2.p2_fast; + } + + memset(best_clock, 0, sizeof(*best_clock)); + clock.m1 = 0; + /* m1 is reserved as 0 in CDV, n is a ring counter. + So skip the m1 loop */ + for (clock.n = limit->n.min; clock.n <= limit->n.max; clock.n++) { + for (clock.m2 = limit->m2.min; clock.m2 <= limit->m2.max; + clock.m2++) { + for (clock.p1 = limit->p1.min; + clock.p1 <= limit->p1.max; + clock.p1++) { + int this_err; + + cdv_intel_clock(dev, refclk, &clock); + + if (!cdv_intel_PLL_is_valid(crtc, + limit, &clock)) + continue; + + this_err = abs(clock.dot - target); + if (this_err < err) { + *best_clock = clock; + err = this_err; + } + } + } + } + + return err != target; +} + +int cdv_intel_pipe_set_base(struct drm_crtc *crtc, + int x, int y, struct drm_framebuffer *old_fb) +{ + struct drm_device *dev = crtc->dev; + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb); + int pipe = psb_intel_crtc->pipe; + unsigned long start, offset; + int dspbase = (pipe == 0 ? DSPABASE : DSPBBASE); + int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF); + int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE; + int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; + u32 dspcntr; + int ret = 0; + + if (!gma_power_begin(dev, true)) + return 0; + + /* no fb bound */ + if (!crtc->fb) { + dev_err(dev->dev, "No FB bound\n"); + goto psb_intel_pipe_cleaner; + } + + + /* We are displaying this buffer, make sure it is actually loaded + into the GTT */ + ret = psb_gtt_pin(psbfb->gtt); + if (ret < 0) + goto psb_intel_pipe_set_base_exit; + start = psbfb->gtt->offset; + offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8); + + REG_WRITE(dspstride, crtc->fb->pitches[0]); + + dspcntr = REG_READ(dspcntr_reg); + dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; + + switch (crtc->fb->bits_per_pixel) { + case 8: + dspcntr |= DISPPLANE_8BPP; + break; + case 16: + if (crtc->fb->depth == 15) + dspcntr |= DISPPLANE_15_16BPP; + else + dspcntr |= DISPPLANE_16BPP; + break; + case 24: + case 32: + dspcntr |= DISPPLANE_32BPP_NO_ALPHA; + break; + default: + dev_err(dev->dev, "Unknown color depth\n"); + ret = -EINVAL; + goto psb_intel_pipe_set_base_exit; + } + REG_WRITE(dspcntr_reg, dspcntr); + + dev_dbg(dev->dev, + "Writing base %08lX %08lX %d %d\n", start, offset, x, y); + + REG_WRITE(dspbase, offset); + REG_READ(dspbase); + REG_WRITE(dspsurf, start); + REG_READ(dspsurf); + +psb_intel_pipe_cleaner: + /* If there was a previous display we can now unpin it */ + if (old_fb) + psb_gtt_unpin(to_psb_fb(old_fb)->gtt); + +psb_intel_pipe_set_base_exit: + gma_power_end(dev); + return ret; +} + +/** + * Sets the power management mode of the pipe and plane. + * + * This code should probably grow support for turning the cursor off and back + * on appropriately at the same time as we're turning the pipe off/on. + */ +static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct drm_device *dev = crtc->dev; + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + int pipe = psb_intel_crtc->pipe; + int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; + int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; + int dspbase_reg = (pipe == 0) ? DSPABASE : DSPBBASE; + int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; + u32 temp; + bool enabled; + + /* 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: + /* Enable the DPLL */ + temp = REG_READ(dpll_reg); + if ((temp & DPLL_VCO_ENABLE) == 0) { + REG_WRITE(dpll_reg, temp); + REG_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + udelay(150); + REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); + REG_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + udelay(150); + REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); + REG_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + udelay(150); + } + + /* Jim Bish - switch plan and pipe per scott */ + /* Enable the plane */ + temp = REG_READ(dspcntr_reg); + if ((temp & DISPLAY_PLANE_ENABLE) == 0) { + REG_WRITE(dspcntr_reg, + temp | DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + } + + udelay(150); + + /* Enable the pipe */ + temp = REG_READ(pipeconf_reg); + if ((temp & PIPEACONF_ENABLE) == 0) + REG_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE); + + psb_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 */ + break; + case DRM_MODE_DPMS_OFF: + /* Give the overlay scaler a chance to disable + * if it's on this pipe */ + /* psb_intel_crtc_dpms_video(crtc, FALSE); TODO */ + + /* Disable the VGA plane that we never use */ + REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); + + /* Jim Bish - changed pipe/plane here as well. */ + + /* Wait for vblank for the disable to take effect */ + cdv_intel_wait_for_vblank(dev); + + /* Next, disable display pipes */ + temp = REG_READ(pipeconf_reg); + if ((temp & PIPEACONF_ENABLE) != 0) { + REG_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE); + REG_READ(pipeconf_reg); + } + + /* Wait for vblank for the disable to take effect. */ + cdv_intel_wait_for_vblank(dev); + + udelay(150); + + /* Disable display plane */ + temp = REG_READ(dspcntr_reg); + if ((temp & DISPLAY_PLANE_ENABLE) != 0) { + REG_WRITE(dspcntr_reg, + temp & ~DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + REG_READ(dspbase_reg); + } + + temp = REG_READ(dpll_reg); + if ((temp & DPLL_VCO_ENABLE) != 0) { + REG_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE); + REG_READ(dpll_reg); + } + + /* Wait for the clocks to turn off. */ + udelay(150); + break; + } + enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF; + /*Set FIFO Watermarks*/ + REG_WRITE(DSPARB, 0x3F3E); +} + +static void cdv_intel_crtc_prepare(struct drm_crtc *crtc) +{ + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); +} + +static void cdv_intel_crtc_commit(struct drm_crtc *crtc) +{ + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); +} + +void cdv_intel_encoder_prepare(struct drm_encoder *encoder) +{ + struct drm_encoder_helper_funcs *encoder_funcs = + encoder->helper_private; + /* lvds has its own version of prepare see cdv_intel_lvds_prepare */ + encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF); +} + +void cdv_intel_encoder_commit(struct drm_encoder *encoder) +{ + struct drm_encoder_helper_funcs *encoder_funcs = + encoder->helper_private; + /* lvds has its own version of commit see cdv_intel_lvds_commit */ + encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); +} + +static bool cdv_intel_crtc_mode_fixup(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + + +/** + * Return the pipe currently connected to the panel fitter, + * or -1 if the panel fitter is not present or not in use + */ +static int cdv_intel_panel_fitter_pipe(struct drm_device *dev) +{ + u32 pfit_control; + + pfit_control = REG_READ(PFIT_CONTROL); + + /* See if the panel fitter is in use */ + if ((pfit_control & PFIT_ENABLE) == 0) + return -1; + return (pfit_control >> 29) & 0x3; +} + +static int cdv_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_device *dev = crtc->dev; + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + int pipe = psb_intel_crtc->pipe; + int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; + int dpll_md_reg = (psb_intel_crtc->pipe == 0) ? DPLL_A_MD : DPLL_B_MD; + int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; + int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; + int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; + int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; + int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; + int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B; + int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B; + int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B; + int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE; + int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS; + int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; + int refclk; + struct cdv_intel_clock_t clock; + u32 dpll = 0, dspcntr, pipeconf; + bool ok, is_sdvo = false, is_dvo = false; + bool is_crt = false, is_lvds = false, is_tv = false; + bool is_hdmi = false; + struct drm_mode_config *mode_config = &dev->mode_config; + struct drm_connector *connector; + + list_for_each_entry(connector, &mode_config->connector_list, head) { + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + + if (!connector->encoder + || connector->encoder->crtc != crtc) + continue; + + switch (psb_intel_output->type) { + case INTEL_OUTPUT_LVDS: + is_lvds = true; + break; + case INTEL_OUTPUT_SDVO: + is_sdvo = true; + break; + case INTEL_OUTPUT_DVO: + is_dvo = true; + break; + case INTEL_OUTPUT_TVOUT: + is_tv = true; + break; + case INTEL_OUTPUT_ANALOG: + is_crt = true; + break; + case INTEL_OUTPUT_HDMI: + is_hdmi = true; + break; + } + } + + refclk = 96000; + + /* Hack selection about ref clk for CRT */ + /* Select 27MHz as the reference clk for HDMI */ + if (is_crt || is_hdmi) + refclk = 27000; + + drm_mode_debug_printmodeline(adjusted_mode); + + ok = cdv_intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, + &clock); + if (!ok) { + dev_err(dev->dev, "Couldn't find PLL settings for mode!\n"); + return 0; + } + + dpll = DPLL_VGA_MODE_DIS; + if (is_tv) { + /* XXX: just matching BIOS for now */ +/* dpll |= PLL_REF_INPUT_TVCLKINBC; */ + dpll |= 3; + } + dpll |= PLL_REF_INPUT_DREFCLK; + + dpll |= DPLL_SYNCLOCK_ENABLE; + dpll |= DPLL_VGA_MODE_DIS; + if (is_lvds) + dpll |= DPLLB_MODE_LVDS; + else + dpll |= DPLLB_MODE_DAC_SERIAL; + /* dpll |= (2 << 11); */ + + /* setup pipeconf */ + pipeconf = REG_READ(pipeconf_reg); + + /* Set up the display plane register */ + dspcntr = DISPPLANE_GAMMA_ENABLE; + + if (pipe == 0) + dspcntr |= DISPPLANE_SEL_PIPE_A; + else + dspcntr |= DISPPLANE_SEL_PIPE_B; + + dspcntr |= DISPLAY_PLANE_ENABLE; + pipeconf |= PIPEACONF_ENABLE; + + REG_WRITE(dpll_reg, dpll | DPLL_VGA_MODE_DIS | DPLL_SYNCLOCK_ENABLE); + REG_READ(dpll_reg); + + cdv_dpll_set_clock_cdv(dev, crtc, &clock); + + udelay(150); + + + /* The LVDS pin pair needs to be on before the DPLLs are enabled. + * This is an exception to the general rule that mode_set doesn't turn + * things on. + */ + if (is_lvds) { + u32 lvds = REG_READ(LVDS); + + lvds |= + LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP | + LVDS_PIPEB_SELECT; + /* Set the B0-B3 data pairs corresponding to + * whether we're going to + * set the DPLLs for dual-channel mode or not. + */ + if (clock.p2 == 7) + lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; + else + lvds &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); + + /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) + * appropriately here, but we need to look more + * thoroughly into how panels behave in the two modes. + */ + + REG_WRITE(LVDS, lvds); + REG_READ(LVDS); + } + + dpll |= DPLL_VCO_ENABLE; + + /* Disable the panel fitter if it was on our pipe */ + if (cdv_intel_panel_fitter_pipe(dev) == pipe) + REG_WRITE(PFIT_CONTROL, 0); + + DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); + drm_mode_debug_printmodeline(mode); + + REG_WRITE(dpll_reg, + (REG_READ(dpll_reg) & ~DPLL_LOCK) | DPLL_VCO_ENABLE); + REG_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + udelay(150); /* 42 usec w/o calibration, 110 with. rounded up. */ + + if (!(REG_READ(dpll_reg) & DPLL_LOCK)) { + dev_err(dev->dev, "Failed to get DPLL lock\n"); + return -EBUSY; + } + + { + int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; + REG_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) | ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT)); + } + + REG_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) | + ((adjusted_mode->crtc_htotal - 1) << 16)); + REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | + ((adjusted_mode->crtc_hblank_end - 1) << 16)); + REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | + ((adjusted_mode->crtc_hsync_end - 1) << 16)); + REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | + ((adjusted_mode->crtc_vtotal - 1) << 16)); + REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | + ((adjusted_mode->crtc_vblank_end - 1) << 16)); + REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | + ((adjusted_mode->crtc_vsync_end - 1) << 16)); + /* pipesrc and dspsize control the size that is scaled from, + * which should always be the user's requested size. + */ + REG_WRITE(dspsize_reg, + ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); + REG_WRITE(dsppos_reg, 0); + REG_WRITE(pipesrc_reg, + ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); + REG_WRITE(pipeconf_reg, pipeconf); + REG_READ(pipeconf_reg); + + cdv_intel_wait_for_vblank(dev); + + REG_WRITE(dspcntr_reg, dspcntr); + + /* Flush the plane changes */ + { + struct drm_crtc_helper_funcs *crtc_funcs = + crtc->helper_private; + crtc_funcs->mode_set_base(crtc, x, y, old_fb); + } + + cdv_intel_wait_for_vblank(dev); + + return 0; +} + +/** Loads the palette/gamma unit for the CRTC with the prepared values */ +void cdv_intel_crtc_load_lut(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = + (struct drm_psb_private *)dev->dev_private; + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + int palreg = PALETTE_A; + int i; + + /* The clocks have to be on to load the palette. */ + if (!crtc->enabled) + return; + + switch (psb_intel_crtc->pipe) { + case 0: + break; + case 1: + palreg = PALETTE_B; + break; + case 2: + palreg = PALETTE_C; + break; + default: + dev_err(dev->dev, "Illegal Pipe Number.\n"); + return; + } + + if (gma_power_begin(dev, false)) { + for (i = 0; i < 256; i++) { + REG_WRITE(palreg + 4 * i, + ((psb_intel_crtc->lut_r[i] + + psb_intel_crtc->lut_adj[i]) << 16) | + ((psb_intel_crtc->lut_g[i] + + psb_intel_crtc->lut_adj[i]) << 8) | + (psb_intel_crtc->lut_b[i] + + psb_intel_crtc->lut_adj[i])); + } + gma_power_end(dev); + } else { + for (i = 0; i < 256; i++) { + dev_priv->save_palette_a[i] = + ((psb_intel_crtc->lut_r[i] + + psb_intel_crtc->lut_adj[i]) << 16) | + ((psb_intel_crtc->lut_g[i] + + psb_intel_crtc->lut_adj[i]) << 8) | + (psb_intel_crtc->lut_b[i] + + psb_intel_crtc->lut_adj[i]); + } + + } +} + +/** + * Save HW states of giving crtc + */ +static void cdv_intel_crtc_save(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + /* struct drm_psb_private *dev_priv = + (struct drm_psb_private *)dev->dev_private; */ + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + struct psb_intel_crtc_state *crtc_state = psb_intel_crtc->crtc_state; + int pipeA = (psb_intel_crtc->pipe == 0); + uint32_t paletteReg; + int i; + + if (!crtc_state) { + dev_dbg(dev->dev, "No CRTC state found\n"); + return; + } + + crtc_state->saveDSPCNTR = REG_READ(pipeA ? DSPACNTR : DSPBCNTR); + crtc_state->savePIPECONF = REG_READ(pipeA ? PIPEACONF : PIPEBCONF); + crtc_state->savePIPESRC = REG_READ(pipeA ? PIPEASRC : PIPEBSRC); + crtc_state->saveFP0 = REG_READ(pipeA ? FPA0 : FPB0); + crtc_state->saveFP1 = REG_READ(pipeA ? FPA1 : FPB1); + crtc_state->saveDPLL = REG_READ(pipeA ? DPLL_A : DPLL_B); + crtc_state->saveHTOTAL = REG_READ(pipeA ? HTOTAL_A : HTOTAL_B); + crtc_state->saveHBLANK = REG_READ(pipeA ? HBLANK_A : HBLANK_B); + crtc_state->saveHSYNC = REG_READ(pipeA ? HSYNC_A : HSYNC_B); + crtc_state->saveVTOTAL = REG_READ(pipeA ? VTOTAL_A : VTOTAL_B); + crtc_state->saveVBLANK = REG_READ(pipeA ? VBLANK_A : VBLANK_B); + crtc_state->saveVSYNC = REG_READ(pipeA ? VSYNC_A : VSYNC_B); + crtc_state->saveDSPSTRIDE = REG_READ(pipeA ? DSPASTRIDE : DSPBSTRIDE); + + /*NOTE: DSPSIZE DSPPOS only for psb*/ + crtc_state->saveDSPSIZE = REG_READ(pipeA ? DSPASIZE : DSPBSIZE); + crtc_state->saveDSPPOS = REG_READ(pipeA ? DSPAPOS : DSPBPOS); + + crtc_state->saveDSPBASE = REG_READ(pipeA ? DSPABASE : DSPBBASE); + + DRM_DEBUG("(%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x)\n", + crtc_state->saveDSPCNTR, + crtc_state->savePIPECONF, + crtc_state->savePIPESRC, + crtc_state->saveFP0, + crtc_state->saveFP1, + crtc_state->saveDPLL, + crtc_state->saveHTOTAL, + crtc_state->saveHBLANK, + crtc_state->saveHSYNC, + crtc_state->saveVTOTAL, + crtc_state->saveVBLANK, + crtc_state->saveVSYNC, + crtc_state->saveDSPSTRIDE, + crtc_state->saveDSPSIZE, + crtc_state->saveDSPPOS, + crtc_state->saveDSPBASE + ); + + paletteReg = pipeA ? PALETTE_A : PALETTE_B; + for (i = 0; i < 256; ++i) + crtc_state->savePalette[i] = REG_READ(paletteReg + (i << 2)); +} + +/** + * Restore HW states of giving crtc + */ +static void cdv_intel_crtc_restore(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + /* struct drm_psb_private * dev_priv = + (struct drm_psb_private *)dev->dev_private; */ + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + struct psb_intel_crtc_state *crtc_state = psb_intel_crtc->crtc_state; + /* struct drm_crtc_helper_funcs * crtc_funcs = crtc->helper_private; */ + int pipeA = (psb_intel_crtc->pipe == 0); + uint32_t paletteReg; + int i; + + if (!crtc_state) { + dev_dbg(dev->dev, "No crtc state\n"); + return; + } + + DRM_DEBUG( + "current:(%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x)\n", + REG_READ(pipeA ? DSPACNTR : DSPBCNTR), + REG_READ(pipeA ? PIPEACONF : PIPEBCONF), + REG_READ(pipeA ? PIPEASRC : PIPEBSRC), + REG_READ(pipeA ? FPA0 : FPB0), + REG_READ(pipeA ? FPA1 : FPB1), + REG_READ(pipeA ? DPLL_A : DPLL_B), + REG_READ(pipeA ? HTOTAL_A : HTOTAL_B), + REG_READ(pipeA ? HBLANK_A : HBLANK_B), + REG_READ(pipeA ? HSYNC_A : HSYNC_B), + REG_READ(pipeA ? VTOTAL_A : VTOTAL_B), + REG_READ(pipeA ? VBLANK_A : VBLANK_B), + REG_READ(pipeA ? VSYNC_A : VSYNC_B), + REG_READ(pipeA ? DSPASTRIDE : DSPBSTRIDE), + REG_READ(pipeA ? DSPASIZE : DSPBSIZE), + REG_READ(pipeA ? DSPAPOS : DSPBPOS), + REG_READ(pipeA ? DSPABASE : DSPBBASE) + ); + + DRM_DEBUG( + "saved: (%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x)\n", + crtc_state->saveDSPCNTR, + crtc_state->savePIPECONF, + crtc_state->savePIPESRC, + crtc_state->saveFP0, + crtc_state->saveFP1, + crtc_state->saveDPLL, + crtc_state->saveHTOTAL, + crtc_state->saveHBLANK, + crtc_state->saveHSYNC, + crtc_state->saveVTOTAL, + crtc_state->saveVBLANK, + crtc_state->saveVSYNC, + crtc_state->saveDSPSTRIDE, + crtc_state->saveDSPSIZE, + crtc_state->saveDSPPOS, + crtc_state->saveDSPBASE + ); + + + if (crtc_state->saveDPLL & DPLL_VCO_ENABLE) { + REG_WRITE(pipeA ? DPLL_A : DPLL_B, + crtc_state->saveDPLL & ~DPLL_VCO_ENABLE); + REG_READ(pipeA ? DPLL_A : DPLL_B); + DRM_DEBUG("write dpll: %x\n", + REG_READ(pipeA ? DPLL_A : DPLL_B)); + udelay(150); + } + + REG_WRITE(pipeA ? FPA0 : FPB0, crtc_state->saveFP0); + REG_READ(pipeA ? FPA0 : FPB0); + + REG_WRITE(pipeA ? FPA1 : FPB1, crtc_state->saveFP1); + REG_READ(pipeA ? FPA1 : FPB1); + + REG_WRITE(pipeA ? DPLL_A : DPLL_B, crtc_state->saveDPLL); + REG_READ(pipeA ? DPLL_A : DPLL_B); + udelay(150); + + REG_WRITE(pipeA ? HTOTAL_A : HTOTAL_B, crtc_state->saveHTOTAL); + REG_WRITE(pipeA ? HBLANK_A : HBLANK_B, crtc_state->saveHBLANK); + REG_WRITE(pipeA ? HSYNC_A : HSYNC_B, crtc_state->saveHSYNC); + REG_WRITE(pipeA ? VTOTAL_A : VTOTAL_B, crtc_state->saveVTOTAL); + REG_WRITE(pipeA ? VBLANK_A : VBLANK_B, crtc_state->saveVBLANK); + REG_WRITE(pipeA ? VSYNC_A : VSYNC_B, crtc_state->saveVSYNC); + REG_WRITE(pipeA ? DSPASTRIDE : DSPBSTRIDE, crtc_state->saveDSPSTRIDE); + + REG_WRITE(pipeA ? DSPASIZE : DSPBSIZE, crtc_state->saveDSPSIZE); + REG_WRITE(pipeA ? DSPAPOS : DSPBPOS, crtc_state->saveDSPPOS); + + REG_WRITE(pipeA ? PIPEASRC : PIPEBSRC, crtc_state->savePIPESRC); + REG_WRITE(pipeA ? DSPABASE : DSPBBASE, crtc_state->saveDSPBASE); + REG_WRITE(pipeA ? PIPEACONF : PIPEBCONF, crtc_state->savePIPECONF); + + cdv_intel_wait_for_vblank(dev); + + REG_WRITE(pipeA ? DSPACNTR : DSPBCNTR, crtc_state->saveDSPCNTR); + REG_WRITE(pipeA ? DSPABASE : DSPBBASE, crtc_state->saveDSPBASE); + + cdv_intel_wait_for_vblank(dev); + + paletteReg = pipeA ? PALETTE_A : PALETTE_B; + for (i = 0; i < 256; ++i) + REG_WRITE(paletteReg + (i << 2), crtc_state->savePalette[i]); +} + +static int cdv_intel_crtc_cursor_set(struct drm_crtc *crtc, + struct drm_file *file_priv, + uint32_t handle, + uint32_t width, uint32_t height) +{ + struct drm_device *dev = crtc->dev; + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + int pipe = psb_intel_crtc->pipe; + uint32_t control = (pipe == 0) ? CURACNTR : CURBCNTR; + uint32_t base = (pipe == 0) ? CURABASE : CURBBASE; + uint32_t temp; + size_t addr = 0; + struct gtt_range *gt; + struct drm_gem_object *obj; + int ret; + + /* if we want to turn of the cursor ignore width and height */ + if (!handle) { + /* turn off the cursor */ + temp = CURSOR_MODE_DISABLE; + + if (gma_power_begin(dev, false)) { + REG_WRITE(control, temp); + REG_WRITE(base, 0); + gma_power_end(dev); + } + + /* unpin the old GEM object */ + if (psb_intel_crtc->cursor_obj) { + gt = container_of(psb_intel_crtc->cursor_obj, + struct gtt_range, gem); + psb_gtt_unpin(gt); + drm_gem_object_unreference(psb_intel_crtc->cursor_obj); + psb_intel_crtc->cursor_obj = NULL; + } + + return 0; + } + + /* Currently we only support 64x64 cursors */ + if (width != 64 || height != 64) { + dev_dbg(dev->dev, "we currently only support 64x64 cursors\n"); + return -EINVAL; + } + + obj = drm_gem_object_lookup(dev, file_priv, handle); + if (!obj) + return -ENOENT; + + if (obj->size < width * height * 4) { + dev_dbg(dev->dev, "buffer is to small\n"); + return -ENOMEM; + } + + gt = container_of(obj, struct gtt_range, gem); + + /* Pin the memory into the GTT */ + ret = psb_gtt_pin(gt); + if (ret) { + dev_err(dev->dev, "Can not pin down handle 0x%x\n", handle); + return ret; + } + + addr = gt->offset; /* Or resource.start ??? */ + + psb_intel_crtc->cursor_addr = addr; + + temp = 0; + /* set the pipe for the cursor */ + temp |= (pipe << 28); + temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE; + + if (gma_power_begin(dev, false)) { + REG_WRITE(control, temp); + REG_WRITE(base, addr); + gma_power_end(dev); + } + + /* unpin the old GEM object */ + if (psb_intel_crtc->cursor_obj) { + gt = container_of(psb_intel_crtc->cursor_obj, + struct gtt_range, gem); + psb_gtt_unpin(gt); + drm_gem_object_unreference(psb_intel_crtc->cursor_obj); + psb_intel_crtc->cursor_obj = obj; + } + return 0; +} + +static int cdv_intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) +{ + struct drm_device *dev = crtc->dev; + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + int pipe = psb_intel_crtc->pipe; + uint32_t temp = 0; + uint32_t adder; + + + if (x < 0) { + temp |= (CURSOR_POS_SIGN << CURSOR_X_SHIFT); + x = -x; + } + if (y < 0) { + temp |= (CURSOR_POS_SIGN << CURSOR_Y_SHIFT); + y = -y; + } + + temp |= ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT); + temp |= ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT); + + adder = psb_intel_crtc->cursor_addr; + + if (gma_power_begin(dev, false)) { + REG_WRITE((pipe == 0) ? CURAPOS : CURBPOS, temp); + REG_WRITE((pipe == 0) ? CURABASE : CURBBASE, adder); + gma_power_end(dev); + } + return 0; +} + +static void cdv_intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, + u16 *green, u16 *blue, uint32_t start, uint32_t size) +{ + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + int i; + int end = (start + size > 256) ? 256 : start + size; + + for (i = start; i < end; i++) { + psb_intel_crtc->lut_r[i] = red[i] >> 8; + psb_intel_crtc->lut_g[i] = green[i] >> 8; + psb_intel_crtc->lut_b[i] = blue[i] >> 8; + } + + cdv_intel_crtc_load_lut(crtc); +} + +static int cdv_crtc_set_config(struct drm_mode_set *set) +{ + int ret = 0; + struct drm_device *dev = set->crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + + if (!dev_priv->rpm_enabled) + return drm_crtc_helper_set_config(set); + + pm_runtime_forbid(&dev->pdev->dev); + + ret = drm_crtc_helper_set_config(set); + + pm_runtime_allow(&dev->pdev->dev); + + return ret; +} + +/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */ + +/* FIXME: why are we using this, should it be cdv_ in this tree ? */ + +static void i8xx_clock(int refclk, struct cdv_intel_clock_t *clock) +{ + clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); + clock->p = clock->p1 * clock->p2; + clock->vco = refclk * clock->m / (clock->n + 2); + clock->dot = clock->vco / clock->p; +} + +/* Returns the clock of the currently programmed mode of the given pipe. */ +static int cdv_intel_crtc_clock_get(struct drm_device *dev, + struct drm_crtc *crtc) +{ + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + int pipe = psb_intel_crtc->pipe; + u32 dpll; + u32 fp; + struct cdv_intel_clock_t clock; + bool is_lvds; + struct drm_psb_private *dev_priv = dev->dev_private; + + if (gma_power_begin(dev, false)) { + dpll = REG_READ((pipe == 0) ? DPLL_A : DPLL_B); + if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) + fp = REG_READ((pipe == 0) ? FPA0 : FPB0); + else + fp = REG_READ((pipe == 0) ? FPA1 : FPB1); + is_lvds = (pipe == 1) && (REG_READ(LVDS) & LVDS_PORT_EN); + gma_power_end(dev); + } else { + dpll = (pipe == 0) ? + dev_priv->saveDPLL_A : dev_priv->saveDPLL_B; + + if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) + fp = (pipe == 0) ? + dev_priv->saveFPA0 : + dev_priv->saveFPB0; + else + fp = (pipe == 0) ? + dev_priv->saveFPA1 : + dev_priv->saveFPB1; + + is_lvds = (pipe == 1) && (dev_priv->saveLVDS & LVDS_PORT_EN); + } + + clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT; + clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT; + clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT; + + if (is_lvds) { + clock.p1 = + ffs((dpll & + DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >> + DPLL_FPA01_P1_POST_DIV_SHIFT); + if (clock.p1 == 0) { + clock.p1 = 4; + dev_err(dev->dev, "PLL %d\n", dpll); + } + clock.p2 = 14; + + if ((dpll & PLL_REF_INPUT_MASK) == + PLLB_REF_INPUT_SPREADSPECTRUMIN) { + /* XXX: might not be 66MHz */ + i8xx_clock(66000, &clock); + } else + i8xx_clock(48000, &clock); + } else { + if (dpll & PLL_P1_DIVIDE_BY_TWO) + clock.p1 = 2; + else { + clock.p1 = + ((dpll & + DPLL_FPA01_P1_POST_DIV_MASK_I830) >> + DPLL_FPA01_P1_POST_DIV_SHIFT) + 2; + } + if (dpll & PLL_P2_DIVIDE_BY_4) + clock.p2 = 4; + else + clock.p2 = 2; + + i8xx_clock(48000, &clock); + } + + /* XXX: It would be nice to validate the clocks, but we can't reuse + * i830PllIsValid() because it relies on the xf86_config connector + * configuration being accurate, which it isn't necessarily. + */ + + return clock.dot; +} + +/** Returns the currently programmed mode of the given pipe. */ +struct drm_display_mode *cdv_intel_crtc_mode_get(struct drm_device *dev, + struct drm_crtc *crtc) +{ + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + int pipe = psb_intel_crtc->pipe; + struct drm_display_mode *mode; + int htot; + int hsync; + int vtot; + int vsync; + struct drm_psb_private *dev_priv = dev->dev_private; + + if (gma_power_begin(dev, false)) { + htot = REG_READ((pipe == 0) ? HTOTAL_A : HTOTAL_B); + hsync = REG_READ((pipe == 0) ? HSYNC_A : HSYNC_B); + vtot = REG_READ((pipe == 0) ? VTOTAL_A : VTOTAL_B); + vsync = REG_READ((pipe == 0) ? VSYNC_A : VSYNC_B); + gma_power_end(dev); + } else { + htot = (pipe == 0) ? + dev_priv->saveHTOTAL_A : dev_priv->saveHTOTAL_B; + hsync = (pipe == 0) ? + dev_priv->saveHSYNC_A : dev_priv->saveHSYNC_B; + vtot = (pipe == 0) ? + dev_priv->saveVTOTAL_A : dev_priv->saveVTOTAL_B; + vsync = (pipe == 0) ? + dev_priv->saveVSYNC_A : dev_priv->saveVSYNC_B; + } + + mode = kzalloc(sizeof(*mode), GFP_KERNEL); + if (!mode) + return NULL; + + mode->clock = cdv_intel_crtc_clock_get(dev, crtc); + mode->hdisplay = (htot & 0xffff) + 1; + mode->htotal = ((htot & 0xffff0000) >> 16) + 1; + mode->hsync_start = (hsync & 0xffff) + 1; + mode->hsync_end = ((hsync & 0xffff0000) >> 16) + 1; + mode->vdisplay = (vtot & 0xffff) + 1; + mode->vtotal = ((vtot & 0xffff0000) >> 16) + 1; + mode->vsync_start = (vsync & 0xffff) + 1; + mode->vsync_end = ((vsync & 0xffff0000) >> 16) + 1; + + drm_mode_set_name(mode); + drm_mode_set_crtcinfo(mode, 0); + + return mode; +} + +static void cdv_intel_crtc_destroy(struct drm_crtc *crtc) +{ + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + + kfree(psb_intel_crtc->crtc_state); + drm_crtc_cleanup(crtc); + kfree(psb_intel_crtc); +} + +const struct drm_crtc_helper_funcs cdv_intel_helper_funcs = { + .dpms = cdv_intel_crtc_dpms, + .mode_fixup = cdv_intel_crtc_mode_fixup, + .mode_set = cdv_intel_crtc_mode_set, + .mode_set_base = cdv_intel_pipe_set_base, + .prepare = cdv_intel_crtc_prepare, + .commit = cdv_intel_crtc_commit, +}; + +const struct drm_crtc_funcs cdv_intel_crtc_funcs = { + .save = cdv_intel_crtc_save, + .restore = cdv_intel_crtc_restore, + .cursor_set = cdv_intel_crtc_cursor_set, + .cursor_move = cdv_intel_crtc_cursor_move, + .gamma_set = cdv_intel_crtc_gamma_set, + .set_config = cdv_crtc_set_config, + .destroy = cdv_intel_crtc_destroy, +}; + +/* + * Set the default value of cursor control and base register + * to zero. This is a workaround for h/w defect on oaktrail + */ +void cdv_intel_cursor_init(struct drm_device *dev, int pipe) +{ + uint32_t control; + uint32_t base; + + switch (pipe) { + case 0: + control = CURACNTR; + base = CURABASE; + break; + case 1: + control = CURBCNTR; + base = CURBBASE; + break; + case 2: + control = CURCCNTR; + base = CURCBASE; + break; + default: + return; + } + + REG_WRITE(control, 0); + REG_WRITE(base, 0); +} + diff --git a/trunk/drivers/staging/gma500/cdv_intel_hdmi.c b/trunk/drivers/staging/gma500/cdv_intel_hdmi.c new file mode 100644 index 000000000000..cbca2b0c7d58 --- /dev/null +++ b/trunk/drivers/staging/gma500/cdv_intel_hdmi.c @@ -0,0 +1,376 @@ +/* + * Copyright © 2006-2011 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: + * jim liu + * + * FIXME: + * We should probably make this generic and share it with Medfield + */ + +#include +#include +#include +#include +#include "psb_intel_drv.h" +#include "psb_drv.h" +#include "psb_intel_reg.h" +#include + +/* hdmi control bits */ +#define HDMI_NULL_PACKETS_DURING_VSYNC (1 << 9) +#define HDMI_BORDER_ENABLE (1 << 7) +#define HDMI_AUDIO_ENABLE (1 << 6) +#define HDMI_VSYNC_ACTIVE_HIGH (1 << 4) +#define HDMI_HSYNC_ACTIVE_HIGH (1 << 3) +/* hdmi-b control bits */ +#define HDMIB_PIPE_B_SELECT (1 << 30) + + +struct mid_intel_hdmi_priv { + u32 hdmi_reg; + u32 save_HDMIB; + bool has_hdmi_sink; + bool has_hdmi_audio; + /* Should set this when detect hotplug */ + bool hdmi_device_connected; + struct mdfld_hdmi_i2c *i2c_bus; + struct i2c_adapter *hdmi_i2c_adapter; /* for control functions */ + struct drm_device *dev; +}; + +static void cdv_hdmi_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + struct mid_intel_hdmi_priv *hdmi_priv = output->dev_priv; + u32 hdmib; + struct drm_crtc *crtc = encoder->crtc; + struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc); + + hdmib = (2 << 10); + + if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) + hdmib |= HDMI_VSYNC_ACTIVE_HIGH; + if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) + hdmib |= HDMI_HSYNC_ACTIVE_HIGH; + + if (intel_crtc->pipe == 1) + hdmib |= HDMIB_PIPE_B_SELECT; + + if (hdmi_priv->has_hdmi_audio) { + hdmib |= HDMI_AUDIO_ENABLE; + hdmib |= HDMI_NULL_PACKETS_DURING_VSYNC; + } + + REG_WRITE(hdmi_priv->hdmi_reg, hdmib); + REG_READ(hdmi_priv->hdmi_reg); +} + +static bool cdv_hdmi_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void cdv_hdmi_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + struct mid_intel_hdmi_priv *hdmi_priv = output->dev_priv; + u32 hdmib; + + hdmib = REG_READ(hdmi_priv->hdmi_reg); + + if (mode != DRM_MODE_DPMS_ON) + REG_WRITE(hdmi_priv->hdmi_reg, hdmib & ~HDMIB_PORT_EN); + else + REG_WRITE(hdmi_priv->hdmi_reg, hdmib | HDMIB_PORT_EN); + REG_READ(hdmi_priv->hdmi_reg); +} + +static void cdv_hdmi_save(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct psb_intel_output *output = to_psb_intel_output(connector); + struct mid_intel_hdmi_priv *hdmi_priv = output->dev_priv; + + hdmi_priv->save_HDMIB = REG_READ(hdmi_priv->hdmi_reg); +} + +static void cdv_hdmi_restore(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct psb_intel_output *output = to_psb_intel_output(connector); + struct mid_intel_hdmi_priv *hdmi_priv = output->dev_priv; + + REG_WRITE(hdmi_priv->hdmi_reg, hdmi_priv->save_HDMIB); + REG_READ(hdmi_priv->hdmi_reg); +} + +static enum drm_connector_status cdv_hdmi_detect( + struct drm_connector *connector, bool force) +{ + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + struct mid_intel_hdmi_priv *hdmi_priv = psb_intel_output->dev_priv; + struct edid *edid = NULL; + enum drm_connector_status status = connector_status_disconnected; + + edid = drm_get_edid(&psb_intel_output->base, + psb_intel_output->hdmi_i2c_adapter); + + hdmi_priv->has_hdmi_sink = false; + hdmi_priv->has_hdmi_audio = false; + if (edid) { + if (edid->input & DRM_EDID_INPUT_DIGITAL) { + status = connector_status_connected; + hdmi_priv->has_hdmi_sink = + drm_detect_hdmi_monitor(edid); + hdmi_priv->has_hdmi_audio = + drm_detect_monitor_audio(edid); + } + + psb_intel_output->base.display_info.raw_edid = NULL; + kfree(edid); + } + return status; +} + +static int cdv_hdmi_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t value) +{ + struct drm_encoder *encoder = connector->encoder; + + if (!strcmp(property->name, "scaling mode") && encoder) { + struct psb_intel_crtc *crtc = to_psb_intel_crtc(encoder->crtc); + bool centre; + uint64_t curValue; + + if (!crtc) + return -1; + + switch (value) { + case DRM_MODE_SCALE_FULLSCREEN: + break; + case DRM_MODE_SCALE_NO_SCALE: + break; + case DRM_MODE_SCALE_ASPECT: + break; + default: + return -1; + } + + if (drm_connector_property_get_value(connector, + property, &curValue)) + return -1; + + if (curValue == value) + return 0; + + if (drm_connector_property_set_value(connector, + property, value)) + return -1; + + centre = (curValue == DRM_MODE_SCALE_NO_SCALE) || + (value == DRM_MODE_SCALE_NO_SCALE); + + if (crtc->saved_mode.hdisplay != 0 && + crtc->saved_mode.vdisplay != 0) { + if (centre) { + if (!drm_crtc_helper_set_mode(encoder->crtc, &crtc->saved_mode, + encoder->crtc->x, encoder->crtc->y, encoder->crtc->fb)) + return -1; + } else { + struct drm_encoder_helper_funcs *helpers + = encoder->helper_private; + helpers->mode_set(encoder, &crtc->saved_mode, + &crtc->saved_adjusted_mode); + } + } + } + return 0; +} + +/* + * Return the list of HDMI DDC modes if available. + */ +static int cdv_hdmi_get_modes(struct drm_connector *connector) +{ + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + struct edid *edid = NULL; + int ret = 0; + + edid = drm_get_edid(&psb_intel_output->base, + psb_intel_output->hdmi_i2c_adapter); + if (edid) { + drm_mode_connector_update_edid_property(&psb_intel_output-> + base, edid); + ret = drm_add_edid_modes(&psb_intel_output->base, edid); + kfree(edid); + } + return ret; +} + +static int cdv_hdmi_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + + if (mode->clock > 165000) + return MODE_CLOCK_HIGH; + if (mode->clock < 20000) + return MODE_CLOCK_HIGH; + + /* just in case */ + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + + /* just in case */ + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + return MODE_NO_INTERLACE; + + /* + * FIXME: for now we limit the size to 1680x1050 on CDV, otherwise it + * will go beyond the stolen memory size allocated to the framebuffer + */ + if (mode->hdisplay > 1680) + return MODE_PANEL; + if (mode->vdisplay > 1050) + return MODE_PANEL; + return MODE_OK; +} + +static void cdv_hdmi_destroy(struct drm_connector *connector) +{ + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + + if (psb_intel_output->ddc_bus) + psb_intel_i2c_destroy(psb_intel_output->ddc_bus); + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + kfree(connector); +} + +static const struct drm_encoder_helper_funcs cdv_hdmi_helper_funcs = { + .dpms = cdv_hdmi_dpms, + .mode_fixup = cdv_hdmi_mode_fixup, + .prepare = psb_intel_encoder_prepare, + .mode_set = cdv_hdmi_mode_set, + .commit = psb_intel_encoder_commit, +}; + +static const struct drm_connector_helper_funcs + cdv_hdmi_connector_helper_funcs = { + .get_modes = cdv_hdmi_get_modes, + .mode_valid = cdv_hdmi_mode_valid, + .best_encoder = psb_intel_best_encoder, +}; + +static const struct drm_connector_funcs cdv_hdmi_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .save = cdv_hdmi_save, + .restore = cdv_hdmi_restore, + .detect = cdv_hdmi_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = cdv_hdmi_set_property, + .destroy = cdv_hdmi_destroy, +}; + +void cdv_hdmi_init(struct drm_device *dev, + struct psb_intel_mode_device *mode_dev, int reg) +{ + struct psb_intel_output *psb_intel_output; + struct drm_connector *connector; + struct drm_encoder *encoder; + struct mid_intel_hdmi_priv *hdmi_priv; + int ddc_bus; + + psb_intel_output = kzalloc(sizeof(struct psb_intel_output) + + sizeof(struct mid_intel_hdmi_priv), GFP_KERNEL); + if (!psb_intel_output) + return; + + hdmi_priv = (struct mid_intel_hdmi_priv *)(psb_intel_output + 1); + psb_intel_output->mode_dev = mode_dev; + connector = &psb_intel_output->base; + encoder = &psb_intel_output->enc; + drm_connector_init(dev, &psb_intel_output->base, + &cdv_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_DVID); + + drm_encoder_init(dev, &psb_intel_output->enc, &psb_intel_lvds_enc_funcs, + DRM_MODE_ENCODER_TMDS); + + drm_mode_connector_attach_encoder(&psb_intel_output->base, + &psb_intel_output->enc); + psb_intel_output->type = INTEL_OUTPUT_HDMI; + hdmi_priv->hdmi_reg = reg; + hdmi_priv->has_hdmi_sink = false; + psb_intel_output->dev_priv = hdmi_priv; + + drm_encoder_helper_add(encoder, &cdv_hdmi_helper_funcs); + drm_connector_helper_add(connector, + &cdv_hdmi_connector_helper_funcs); + connector->display_info.subpixel_order = SubPixelHorizontalRGB; + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + + drm_connector_attach_property(connector, + dev->mode_config.scaling_mode_property, DRM_MODE_SCALE_FULLSCREEN); + + switch (reg) { + case SDVOB: + ddc_bus = GPIOE; + break; + case SDVOC: + ddc_bus = GPIOD; + break; + default: + DRM_ERROR("unknown reg 0x%x for HDMI\n", reg); + goto failed_ddc; + break; + } + + psb_intel_output->ddc_bus = psb_intel_i2c_create(dev, + ddc_bus, (reg == SDVOB) ? "HDMIB" : "HDMIC"); + + if (!psb_intel_output->ddc_bus) { + dev_err(dev->dev, "No ddc adapter available!\n"); + goto failed_ddc; + } + psb_intel_output->hdmi_i2c_adapter = + &(psb_intel_output->ddc_bus->adapter); + hdmi_priv->dev = dev; + drm_sysfs_connector_add(connector); + return; + +failed_ddc: + drm_encoder_cleanup(&psb_intel_output->enc); + drm_connector_cleanup(&psb_intel_output->base); + kfree(psb_intel_output); +} diff --git a/trunk/drivers/staging/gma500/cdv_intel_lvds.c b/trunk/drivers/staging/gma500/cdv_intel_lvds.c new file mode 100644 index 000000000000..988b2d0acf43 --- /dev/null +++ b/trunk/drivers/staging/gma500/cdv_intel_lvds.c @@ -0,0 +1,721 @@ +/* + * Copyright © 2006-2011 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * 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. + * + * Authors: + * Eric Anholt + * Dave Airlie + * Jesse Barnes + */ + +#include +#include +#include + +#include "intel_bios.h" +#include "psb_drv.h" +#include "psb_intel_drv.h" +#include "psb_intel_reg.h" +#include "power.h" +#include +#include "cdv_device.h" + +/** + * LVDS I2C backlight control macros + */ +#define BRIGHTNESS_MAX_LEVEL 100 +#define BRIGHTNESS_MASK 0xFF +#define BLC_I2C_TYPE 0x01 +#define BLC_PWM_TYPT 0x02 + +#define BLC_POLARITY_NORMAL 0 +#define BLC_POLARITY_INVERSE 1 + +#define PSB_BLC_MAX_PWM_REG_FREQ (0xFFFE) +#define PSB_BLC_MIN_PWM_REG_FREQ (0x2) +#define PSB_BLC_PWM_PRECISION_FACTOR (10) +#define PSB_BACKLIGHT_PWM_CTL_SHIFT (16) +#define PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE) + +struct cdv_intel_lvds_priv { + /** + * Saved LVDO output states + */ + uint32_t savePP_ON; + uint32_t savePP_OFF; + uint32_t saveLVDS; + uint32_t savePP_CONTROL; + uint32_t savePP_CYCLE; + uint32_t savePFIT_CONTROL; + uint32_t savePFIT_PGM_RATIOS; + uint32_t saveBLC_PWM_CTL; +}; + +/* + * Returns the maximum level of the backlight duty cycle field. + */ +static u32 cdv_intel_lvds_get_max_backlight(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + u32 retval; + + if (gma_power_begin(dev, false)) { + retval = ((REG_READ(BLC_PWM_CTL) & + BACKLIGHT_MODULATION_FREQ_MASK) >> + BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; + + gma_power_end(dev); + } else + retval = ((dev_priv->saveBLC_PWM_CTL & + BACKLIGHT_MODULATION_FREQ_MASK) >> + BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; + + return retval; +} + +/* + * Set LVDS backlight level by I2C command + */ +static int cdv_lvds_i2c_set_brightness(struct drm_device *dev, + unsigned int level) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_intel_i2c_chan *lvds_i2c_bus = dev_priv->lvds_i2c_bus; + u8 out_buf[2]; + unsigned int blc_i2c_brightness; + + struct i2c_msg msgs[] = { + { + .addr = lvds_i2c_bus->slave_addr, + .flags = 0, + .len = 2, + .buf = out_buf, + } + }; + + blc_i2c_brightness = BRIGHTNESS_MASK & ((unsigned int)level * + BRIGHTNESS_MASK / + BRIGHTNESS_MAX_LEVEL); + + if (dev_priv->lvds_bl->pol == BLC_POLARITY_INVERSE) + blc_i2c_brightness = BRIGHTNESS_MASK - blc_i2c_brightness; + + out_buf[0] = dev_priv->lvds_bl->brightnesscmd; + out_buf[1] = (u8)blc_i2c_brightness; + + if (i2c_transfer(&lvds_i2c_bus->adapter, msgs, 1) == 1) + return 0; + + DRM_ERROR("I2C transfer error\n"); + return -1; +} + + +static int cdv_lvds_pwm_set_brightness(struct drm_device *dev, int level) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + + u32 max_pwm_blc; + u32 blc_pwm_duty_cycle; + + max_pwm_blc = cdv_intel_lvds_get_max_backlight(dev); + + /*BLC_PWM_CTL Should be initiated while backlight device init*/ + BUG_ON((max_pwm_blc & PSB_BLC_MAX_PWM_REG_FREQ) == 0); + + blc_pwm_duty_cycle = level * max_pwm_blc / BRIGHTNESS_MAX_LEVEL; + + if (dev_priv->lvds_bl->pol == BLC_POLARITY_INVERSE) + blc_pwm_duty_cycle = max_pwm_blc - blc_pwm_duty_cycle; + + blc_pwm_duty_cycle &= PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR; + REG_WRITE(BLC_PWM_CTL, + (max_pwm_blc << PSB_BACKLIGHT_PWM_CTL_SHIFT) | + (blc_pwm_duty_cycle)); + + return 0; +} + +/* + * Set LVDS backlight level either by I2C or PWM + */ +void cdv_intel_lvds_set_brightness(struct drm_device *dev, int level) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + + if (!dev_priv->lvds_bl) { + DRM_ERROR("NO LVDS Backlight Info\n"); + return; + } + + if (dev_priv->lvds_bl->type == BLC_I2C_TYPE) + cdv_lvds_i2c_set_brightness(dev, level); + else + cdv_lvds_pwm_set_brightness(dev, level); +} + +/** + * Sets the backlight level. + * + * level backlight level, from 0 to cdv_intel_lvds_get_max_backlight(). + */ +static void cdv_intel_lvds_set_backlight(struct drm_device *dev, int level) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + u32 blc_pwm_ctl; + + if (gma_power_begin(dev, false)) { + blc_pwm_ctl = + REG_READ(BLC_PWM_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK; + REG_WRITE(BLC_PWM_CTL, + (blc_pwm_ctl | + (level << BACKLIGHT_DUTY_CYCLE_SHIFT))); + gma_power_end(dev); + } else { + blc_pwm_ctl = dev_priv->saveBLC_PWM_CTL & + ~BACKLIGHT_DUTY_CYCLE_MASK; + dev_priv->saveBLC_PWM_CTL = (blc_pwm_ctl | + (level << BACKLIGHT_DUTY_CYCLE_SHIFT)); + } +} + +/** + * Sets the power state for the panel. + */ +static void cdv_intel_lvds_set_power(struct drm_device *dev, + struct psb_intel_output *output, bool on) +{ + u32 pp_status; + + if (!gma_power_begin(dev, true)) + return; + + if (on) { + REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) | + POWER_TARGET_ON); + do { + pp_status = REG_READ(PP_STATUS); + } while ((pp_status & PP_ON) == 0); + + cdv_intel_lvds_set_backlight(dev, + output-> + mode_dev->backlight_duty_cycle); + } else { + cdv_intel_lvds_set_backlight(dev, 0); + + REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) & + ~POWER_TARGET_ON); + do { + pp_status = REG_READ(PP_STATUS); + } while (pp_status & PP_ON); + } + gma_power_end(dev); +} + +static void cdv_intel_lvds_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + if (mode == DRM_MODE_DPMS_ON) + cdv_intel_lvds_set_power(dev, output, true); + else + cdv_intel_lvds_set_power(dev, output, false); + /* XXX: We never power down the LVDS pairs. */ +} + +static void cdv_intel_lvds_save(struct drm_connector *connector) +{ +} + +static void cdv_intel_lvds_restore(struct drm_connector *connector) +{ +} + +int cdv_intel_lvds_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + struct drm_display_mode *fixed_mode = + psb_intel_output->mode_dev->panel_fixed_mode; + + /* just in case */ + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + + /* just in case */ + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + return MODE_NO_INTERLACE; + + if (fixed_mode) { + if (mode->hdisplay > fixed_mode->hdisplay) + return MODE_PANEL; + if (mode->vdisplay > fixed_mode->vdisplay) + return MODE_PANEL; + } + return MODE_OK; +} + +bool cdv_intel_lvds_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct psb_intel_mode_device *mode_dev = + enc_to_psb_intel_output(encoder)->mode_dev; + struct drm_device *dev = encoder->dev; + struct drm_encoder *tmp_encoder; + struct drm_display_mode *panel_fixed_mode = mode_dev->panel_fixed_mode; + + /* Should never happen!! */ + list_for_each_entry(tmp_encoder, &dev->mode_config.encoder_list, + head) { + if (tmp_encoder != encoder + && tmp_encoder->crtc == encoder->crtc) { + printk(KERN_ERR "Can't enable LVDS and another " + "encoder on the same pipe\n"); + return false; + } + } + + /* + * If we have timings from the BIOS for the panel, put them in + * to the adjusted mode. The CRTC will be set up for this mode, + * with the panel scaling set up to source from the H/VDisplay + * of the original mode. + */ + if (panel_fixed_mode != NULL) { + adjusted_mode->hdisplay = panel_fixed_mode->hdisplay; + adjusted_mode->hsync_start = panel_fixed_mode->hsync_start; + adjusted_mode->hsync_end = panel_fixed_mode->hsync_end; + adjusted_mode->htotal = panel_fixed_mode->htotal; + adjusted_mode->vdisplay = panel_fixed_mode->vdisplay; + adjusted_mode->vsync_start = panel_fixed_mode->vsync_start; + adjusted_mode->vsync_end = panel_fixed_mode->vsync_end; + adjusted_mode->vtotal = panel_fixed_mode->vtotal; + adjusted_mode->clock = panel_fixed_mode->clock; + drm_mode_set_crtcinfo(adjusted_mode, + CRTC_INTERLACE_HALVE_V); + } + + /* + * XXX: It would be nice to support lower refresh rates on the + * panels to reduce power consumption, and perhaps match the + * user's requested refresh rate. + */ + + return true; +} + +static void cdv_intel_lvds_prepare(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + struct psb_intel_mode_device *mode_dev = output->mode_dev; + + if (!gma_power_begin(dev, true)) + return; + + mode_dev->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL); + mode_dev->backlight_duty_cycle = (mode_dev->saveBLC_PWM_CTL & + BACKLIGHT_DUTY_CYCLE_MASK); + + cdv_intel_lvds_set_power(dev, output, false); + + gma_power_end(dev); +} + +static void cdv_intel_lvds_commit(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + struct psb_intel_mode_device *mode_dev = output->mode_dev; + + if (mode_dev->backlight_duty_cycle == 0) + mode_dev->backlight_duty_cycle = + cdv_intel_lvds_get_max_backlight(dev); + + cdv_intel_lvds_set_power(dev, output, true); +} + +static void cdv_intel_lvds_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + u32 pfit_control; + + /* + * The LVDS pin pair will already have been turned on in the + * cdv_intel_crtc_mode_set since it has a large impact on the DPLL + * settings. + */ + + /* + * Enable automatic panel scaling so that non-native modes fill the + * screen. Should be enabled before the pipe is enabled, according to + * register description and PRM. + */ + if (mode->hdisplay != adjusted_mode->hdisplay || + mode->vdisplay != adjusted_mode->vdisplay) + pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE | + HORIZ_AUTO_SCALE | VERT_INTERP_BILINEAR | + HORIZ_INTERP_BILINEAR); + else + pfit_control = 0; + + if (dev_priv->lvds_dither) + pfit_control |= PANEL_8TO6_DITHER_ENABLE; + + REG_WRITE(PFIT_CONTROL, pfit_control); +} + +/** + * Detect the LVDS connection. + * + * This always returns CONNECTOR_STATUS_CONNECTED. + * This connector should only have + * been set up if the LVDS was actually connected anyway. + */ +static enum drm_connector_status cdv_intel_lvds_detect( + struct drm_connector *connector, bool force) +{ + return connector_status_connected; +} + +/** + * Return the list of DDC modes if available, or the BIOS fixed mode otherwise. + */ +static int cdv_intel_lvds_get_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + struct psb_intel_mode_device *mode_dev = + psb_intel_output->mode_dev; + int ret; + + ret = psb_intel_ddc_get_modes(psb_intel_output); + + if (ret) + return ret; + + /* Didn't get an EDID, so + * Set wide sync ranges so we get all modes + * handed to valid_mode for checking + */ + connector->display_info.min_vfreq = 0; + connector->display_info.max_vfreq = 200; + connector->display_info.min_hfreq = 0; + connector->display_info.max_hfreq = 200; + if (mode_dev->panel_fixed_mode != NULL) { + struct drm_display_mode *mode = + drm_mode_duplicate(dev, mode_dev->panel_fixed_mode); + drm_mode_probed_add(connector, mode); + return 1; + } + + return 0; +} + +/** + * cdv_intel_lvds_destroy - unregister and free LVDS structures + * @connector: connector to free + * + * Unregister the DDC bus for this connector then free the driver private + * structure. + */ +void cdv_intel_lvds_destroy(struct drm_connector *connector) +{ + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + + if (psb_intel_output->ddc_bus) + psb_intel_i2c_destroy(psb_intel_output->ddc_bus); + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + kfree(connector); +} + +int cdv_intel_lvds_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t value) +{ + struct drm_encoder *encoder = connector->encoder; + + if (!strcmp(property->name, "scaling mode") && encoder) { + struct psb_intel_crtc *crtc = + to_psb_intel_crtc(encoder->crtc); + uint64_t curValue; + + if (!crtc) + return -1; + + switch (value) { + case DRM_MODE_SCALE_FULLSCREEN: + break; + case DRM_MODE_SCALE_NO_SCALE: + break; + case DRM_MODE_SCALE_ASPECT: + break; + default: + return -1; + } + + if (drm_connector_property_get_value(connector, + property, + &curValue)) + return -1; + + if (curValue == value) + return 0; + + if (drm_connector_property_set_value(connector, + property, + value)) + return -1; + + if (crtc->saved_mode.hdisplay != 0 && + crtc->saved_mode.vdisplay != 0) { + if (!drm_crtc_helper_set_mode(encoder->crtc, + &crtc->saved_mode, + encoder->crtc->x, + encoder->crtc->y, + encoder->crtc->fb)) + return -1; + } + } else if (!strcmp(property->name, "backlight") && encoder) { + if (drm_connector_property_set_value(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 if (!strcmp(property->name, "DPMS") && encoder) { + struct drm_encoder_helper_funcs *helpers = + encoder->helper_private; + helpers->dpms(encoder, value); + } + return 0; +} + +static const struct drm_encoder_helper_funcs + cdv_intel_lvds_helper_funcs = { + .dpms = cdv_intel_lvds_encoder_dpms, + .mode_fixup = cdv_intel_lvds_mode_fixup, + .prepare = cdv_intel_lvds_prepare, + .mode_set = cdv_intel_lvds_mode_set, + .commit = cdv_intel_lvds_commit, +}; + +static const struct drm_connector_helper_funcs + cdv_intel_lvds_connector_helper_funcs = { + .get_modes = cdv_intel_lvds_get_modes, + .mode_valid = cdv_intel_lvds_mode_valid, + .best_encoder = psb_intel_best_encoder, +}; + +static const struct drm_connector_funcs cdv_intel_lvds_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .save = cdv_intel_lvds_save, + .restore = cdv_intel_lvds_restore, + .detect = cdv_intel_lvds_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = cdv_intel_lvds_set_property, + .destroy = cdv_intel_lvds_destroy, +}; + + +static void cdv_intel_lvds_enc_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +const struct drm_encoder_funcs cdv_intel_lvds_enc_funcs = { + .destroy = cdv_intel_lvds_enc_destroy, +}; + +/** + * cdv_intel_lvds_init - setup LVDS connectors on this device + * @dev: drm device + * + * Create the connector, register the LVDS DDC bus, and try to figure out what + * modes we can display on the LVDS panel (if present). + */ +void cdv_intel_lvds_init(struct drm_device *dev, + struct psb_intel_mode_device *mode_dev) +{ + struct psb_intel_output *psb_intel_output; + struct cdv_intel_lvds_priv *lvds_priv; + struct drm_connector *connector; + struct drm_encoder *encoder; + struct drm_display_mode *scan; + struct drm_crtc *crtc; + struct drm_psb_private *dev_priv = dev->dev_private; + u32 lvds; + int pipe; + + psb_intel_output = kzalloc(sizeof(struct psb_intel_output) + + sizeof(struct cdv_intel_lvds_priv), GFP_KERNEL); + if (!psb_intel_output) + return; + + lvds_priv = (struct cdv_intel_lvds_priv *)(psb_intel_output + 1); + + psb_intel_output->dev_priv = lvds_priv; + + psb_intel_output->mode_dev = mode_dev; + connector = &psb_intel_output->base; + encoder = &psb_intel_output->enc; + + + drm_connector_init(dev, &psb_intel_output->base, + &cdv_intel_lvds_connector_funcs, + DRM_MODE_CONNECTOR_LVDS); + + drm_encoder_init(dev, &psb_intel_output->enc, + &cdv_intel_lvds_enc_funcs, + DRM_MODE_ENCODER_LVDS); + + + drm_mode_connector_attach_encoder(&psb_intel_output->base, + &psb_intel_output->enc); + psb_intel_output->type = INTEL_OUTPUT_LVDS; + + drm_encoder_helper_add(encoder, &cdv_intel_lvds_helper_funcs); + drm_connector_helper_add(connector, + &cdv_intel_lvds_connector_helper_funcs); + connector->display_info.subpixel_order = SubPixelHorizontalRGB; + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + + /*Attach connector properties*/ + drm_connector_attach_property(connector, + dev->mode_config.scaling_mode_property, + DRM_MODE_SCALE_FULLSCREEN); + drm_connector_attach_property(connector, + dev_priv->backlight_property, + BRIGHTNESS_MAX_LEVEL); + + /** + * Set up I2C bus + * FIXME: distroy i2c_bus when exit + */ + psb_intel_output->i2c_bus = psb_intel_i2c_create(dev, + GPIOB, + "LVDSBLC_B"); + if (!psb_intel_output->i2c_bus) { + dev_printk(KERN_ERR, + &dev->pdev->dev, "I2C bus registration failed.\n"); + goto failed_blc_i2c; + } + psb_intel_output->i2c_bus->slave_addr = 0x2C; + dev_priv->lvds_i2c_bus = psb_intel_output->i2c_bus; + + /* + * LVDS discovery: + * 1) check for EDID on DDC + * 2) check for VBT data + * 3) check to see if LVDS is already on + * if none of the above, no panel + * 4) make sure lid is open + * if closed, act like it's not there for now + */ + + /* Set up the DDC bus. */ + psb_intel_output->ddc_bus = psb_intel_i2c_create(dev, + GPIOC, + "LVDSDDC_C"); + if (!psb_intel_output->ddc_bus) { + dev_printk(KERN_ERR, &dev->pdev->dev, + "DDC bus registration " "failed.\n"); + goto failed_ddc; + } + + /* + * Attempt to get the fixed panel mode from DDC. Assume that the + * preferred mode is the right one. + */ + psb_intel_ddc_get_modes(psb_intel_output); + list_for_each_entry(scan, &connector->probed_modes, head) { + if (scan->type & DRM_MODE_TYPE_PREFERRED) { + mode_dev->panel_fixed_mode = + drm_mode_duplicate(dev, scan); + goto out; /* FIXME: check for quirks */ + } + } + + /* Failed to get EDID, what about VBT? do we need this?*/ + if (dev_priv->lfp_lvds_vbt_mode) { + mode_dev->panel_fixed_mode = + drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); + if (mode_dev->panel_fixed_mode) { + mode_dev->panel_fixed_mode->type |= + DRM_MODE_TYPE_PREFERRED; + goto out; /* FIXME: check for quirks */ + } + } + /* + * If we didn't get EDID, try checking if the panel is already turned + * on. If so, assume that whatever is currently programmed is the + * correct mode. + */ + lvds = REG_READ(LVDS); + pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0; + crtc = psb_intel_get_crtc_from_pipe(dev, pipe); + + if (crtc && (lvds & LVDS_PORT_EN)) { + mode_dev->panel_fixed_mode = + cdv_intel_crtc_mode_get(dev, crtc); + if (mode_dev->panel_fixed_mode) { + mode_dev->panel_fixed_mode->type |= + DRM_MODE_TYPE_PREFERRED; + goto out; /* FIXME: check for quirks */ + } + } + + /* If we still don't have a mode after all that, give up. */ + if (!mode_dev->panel_fixed_mode) { + DRM_DEBUG + ("Found no modes on the lvds, ignoring the LVDS\n"); + goto failed_find; + } + +out: + drm_sysfs_connector_add(connector); + return; + +failed_find: + printk(KERN_ERR "Failed find\n"); + if (psb_intel_output->ddc_bus) + psb_intel_i2c_destroy(psb_intel_output->ddc_bus); +failed_ddc: + printk(KERN_ERR "Failed DDC\n"); + if (psb_intel_output->i2c_bus) + psb_intel_i2c_destroy(psb_intel_output->i2c_bus); +failed_blc_i2c: + printk(KERN_ERR "Failed BLC\n"); + drm_encoder_cleanup(encoder); + drm_connector_cleanup(connector); + kfree(connector); +} diff --git a/trunk/drivers/staging/gma500/displays/hdmi.h b/trunk/drivers/staging/gma500/displays/hdmi.h new file mode 100644 index 000000000000..d58ba9bd010f --- /dev/null +++ b/trunk/drivers/staging/gma500/displays/hdmi.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010 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: + * Thomas Eaton + * Scott Rowe + */ + +#ifndef HDMI_H +#define HDMI_H + +extern void hdmi_init(struct drm_device *dev); + +#endif diff --git a/trunk/drivers/staging/gma500/displays/pyr_cmd.h b/trunk/drivers/staging/gma500/displays/pyr_cmd.h new file mode 100644 index 000000000000..84bae5c8c552 --- /dev/null +++ b/trunk/drivers/staging/gma500/displays/pyr_cmd.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010 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, sublicensen + * 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: + * Thomas Eaton + * Scott Rowe + */ + +#ifndef PYR_CMD_H +#define PYR_CMD_H + +extern void pyr_cmd_init(struct drm_device *dev, struct panel_funcs *p_funcs); + +#endif + diff --git a/trunk/drivers/staging/gma500/displays/pyr_vid.h b/trunk/drivers/staging/gma500/displays/pyr_vid.h new file mode 100644 index 000000000000..ce98860fa68a --- /dev/null +++ b/trunk/drivers/staging/gma500/displays/pyr_vid.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010 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, sublicensen + * 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: + * Thomas Eaton + * Scott Rowe +*/ + +#ifndef PYR_VID_H +#define PYR_VID_H + +extern void pyr_vid_init(struct drm_device *dev, struct panel_funcs *p_funcs); +extern struct drm_display_mode *pyr_vid_get_config_mode(struct drm_device* dev); + +#endif diff --git a/trunk/drivers/staging/gma500/displays/tmd_cmd.h b/trunk/drivers/staging/gma500/displays/tmd_cmd.h new file mode 100644 index 000000000000..641e85eedece --- /dev/null +++ b/trunk/drivers/staging/gma500/displays/tmd_cmd.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010 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, sublicensen + * 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: + * Thomas Eaton + * Scott Rowe + */ + +#ifndef TMD_CMD_H +#define TMD_CMD_H + +extern void tmd_cmd_init(struct drm_device *dev, struct panel_funcs *p_funcs); +extern struct drm_display_mode *tmd_cmd_get_config_mode(struct drm_device *dev); + +#endif diff --git a/trunk/drivers/staging/gma500/displays/tmd_vid.h b/trunk/drivers/staging/gma500/displays/tmd_vid.h new file mode 100644 index 000000000000..7a5fa3b935e3 --- /dev/null +++ b/trunk/drivers/staging/gma500/displays/tmd_vid.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010 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, sublicensen + * 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: + * Thomas Eaton + * Scott Rowe +*/ + +#ifndef TMD_VID_H +#define TMD_VID_H + +extern void tmd_vid_init(struct drm_device *dev, struct panel_funcs *p_funcs); +extern struct drm_display_mode *tmd_vid_get_config_mode(struct drm_device *dev); + +#endif diff --git a/trunk/drivers/staging/gma500/displays/tpo_cmd.h b/trunk/drivers/staging/gma500/displays/tpo_cmd.h new file mode 100644 index 000000000000..610552730d71 --- /dev/null +++ b/trunk/drivers/staging/gma500/displays/tpo_cmd.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2010 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, sublicensen + * 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: + * Thomas Eaton + * Scott Rowe +*/ + +#ifndef TPO_CMD_H +#define TPO_CMD_H + +extern void tpo_cmd_init(struct drm_device *dev, struct panel_funcs *p_funcs); +/* extern struct drm_display_mode * */ +/* tpo_cmd_get_config_mode(struct drm_device *dev); */ + +#endif diff --git a/trunk/drivers/staging/gma500/displays/tpo_vid.h b/trunk/drivers/staging/gma500/displays/tpo_vid.h new file mode 100644 index 000000000000..c24f05722de1 --- /dev/null +++ b/trunk/drivers/staging/gma500/displays/tpo_vid.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010 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, sublicensen + * 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: + * Thomas Eaton + * Scott Rowe + */ + +#ifndef TPO_VID_H +#define TPO_VID_H + +extern void tpo_vid_init(struct drm_device *dev, struct panel_funcs *p_funcs); + +#endif diff --git a/trunk/drivers/staging/gma500/framebuffer.c b/trunk/drivers/staging/gma500/framebuffer.c new file mode 100644 index 000000000000..b00761cba144 --- /dev/null +++ b/trunk/drivers/staging/gma500/framebuffer.c @@ -0,0 +1,856 @@ +/************************************************************************** + * Copyright (c) 2007-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "psb_drv.h" +#include "psb_intel_reg.h" +#include "psb_intel_drv.h" +#include "framebuffer.h" +#include "gtt.h" + +#include "mdfld_output.h" + +static void psb_user_framebuffer_destroy(struct drm_framebuffer *fb); +static int psb_user_framebuffer_create_handle(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int *handle); + +static const struct drm_framebuffer_funcs psb_fb_funcs = { + .destroy = psb_user_framebuffer_destroy, + .create_handle = psb_user_framebuffer_create_handle, +}; + +#define CMAP_TOHW(_val, _width) ((((_val) << (_width)) + 0x7FFF - (_val)) >> 16) + +static int psbfb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + struct psb_fbdev *fbdev = info->par; + struct drm_framebuffer *fb = fbdev->psb_fb_helper.fb; + uint32_t v; + + if (!fb) + return -ENOMEM; + + if (regno > 255) + return 1; + + red = CMAP_TOHW(red, info->var.red.length); + blue = CMAP_TOHW(blue, info->var.blue.length); + green = CMAP_TOHW(green, info->var.green.length); + transp = CMAP_TOHW(transp, info->var.transp.length); + + v = (red << info->var.red.offset) | + (green << info->var.green.offset) | + (blue << info->var.blue.offset) | + (transp << info->var.transp.offset); + + if (regno < 16) { + switch (fb->bits_per_pixel) { + case 16: + ((uint32_t *) info->pseudo_palette)[regno] = v; + break; + case 24: + case 32: + ((uint32_t *) info->pseudo_palette)[regno] = v; + break; + } + } + + return 0; +} + +static int psbfb_pan(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct psb_fbdev *fbdev = info->par; + struct psb_framebuffer *psbfb = &fbdev->pfb; + struct drm_device *dev = psbfb->base.dev; + + /* + * We have to poke our nose in here. The core fb code assumes + * panning is part of the hardware that can be invoked before + * the actual fb is mapped. In our case that isn't quite true. + */ + if (psbfb->gtt->npage) + psb_gtt_roll(dev, psbfb->gtt, var->yoffset); + return 0; +} + +void psbfb_suspend(struct drm_device *dev) +{ + struct drm_framebuffer *fb = 0; + struct psb_framebuffer *psbfb = to_psb_fb(fb); + + console_lock(); + mutex_lock(&dev->mode_config.mutex); + list_for_each_entry(fb, &dev->mode_config.fb_list, head) { + struct fb_info *info = psbfb->fbdev; + fb_set_suspend(info, 1); + drm_fb_helper_blank(FB_BLANK_POWERDOWN, info); + } + mutex_unlock(&dev->mode_config.mutex); + console_unlock(); +} + +void psbfb_resume(struct drm_device *dev) +{ + struct drm_framebuffer *fb = 0; + struct psb_framebuffer *psbfb = to_psb_fb(fb); + + console_lock(); + mutex_lock(&dev->mode_config.mutex); + list_for_each_entry(fb, &dev->mode_config.fb_list, head) { + struct fb_info *info = psbfb->fbdev; + fb_set_suspend(info, 0); + drm_fb_helper_blank(FB_BLANK_UNBLANK, info); + } + mutex_unlock(&dev->mode_config.mutex); + console_unlock(); + drm_helper_disable_unused_functions(dev); +} + +static int psbfb_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct psb_framebuffer *psbfb = vma->vm_private_data; + struct drm_device *dev = psbfb->base.dev; + struct drm_psb_private *dev_priv = dev->dev_private; + int page_num; + int i; + unsigned long address; + int ret; + unsigned long pfn; + /* FIXME: assumes fb at stolen base which may not be true */ + unsigned long phys_addr = (unsigned long)dev_priv->stolen_base; + + page_num = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; + address = (unsigned long)vmf->virtual_address; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + for (i = 0; i < page_num; i++) { + pfn = (phys_addr >> PAGE_SHIFT); + + ret = vm_insert_mixed(vma, address, pfn); + if (unlikely((ret == -EBUSY) || (ret != 0 && i > 0))) + break; + else if (unlikely(ret != 0)) { + ret = (ret == -ENOMEM) ? VM_FAULT_OOM : VM_FAULT_SIGBUS; + return ret; + } + address += PAGE_SIZE; + phys_addr += PAGE_SIZE; + } + return VM_FAULT_NOPAGE; +} + +static void psbfb_vm_open(struct vm_area_struct *vma) +{ +} + +static void psbfb_vm_close(struct vm_area_struct *vma) +{ +} + +static struct vm_operations_struct psbfb_vm_ops = { + .fault = psbfb_vm_fault, + .open = psbfb_vm_open, + .close = psbfb_vm_close +}; + +static int psbfb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct psb_fbdev *fbdev = info->par; + struct psb_framebuffer *psbfb = &fbdev->pfb; + + if (vma->vm_pgoff != 0) + return -EINVAL; + if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) + return -EINVAL; + + if (!psbfb->addr_space) + psbfb->addr_space = vma->vm_file->f_mapping; + /* + * If this is a GEM object then info->screen_base is the virtual + * kernel remapping of the object. FIXME: Review if this is + * suitable for our mmap work + */ + vma->vm_ops = &psbfb_vm_ops; + vma->vm_private_data = (void *)psbfb; + vma->vm_flags |= VM_RESERVED | VM_IO | + VM_MIXEDMAP | VM_DONTEXPAND; + return 0; +} + +static int psbfb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + return -ENOTTY; +} + +static struct fb_ops psbfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, + .fb_blank = drm_fb_helper_blank, + .fb_setcolreg = psbfb_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = psbfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_mmap = psbfb_mmap, + .fb_sync = psbfb_sync, + .fb_ioctl = psbfb_ioctl, +}; + +static struct fb_ops psbfb_roll_ops = { + .owner = THIS_MODULE, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, + .fb_blank = drm_fb_helper_blank, + .fb_setcolreg = psbfb_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_pan_display = psbfb_pan, + .fb_mmap = psbfb_mmap, + .fb_sync = psbfb_sync, + .fb_ioctl = psbfb_ioctl, +}; + +static struct fb_ops psbfb_unaccel_ops = { + .owner = THIS_MODULE, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, + .fb_blank = drm_fb_helper_blank, + .fb_setcolreg = psbfb_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_mmap = psbfb_mmap, + .fb_ioctl = psbfb_ioctl, +}; + +/** + * psb_framebuffer_init - initialize a framebuffer + * @dev: our DRM device + * @fb: framebuffer to set up + * @mode_cmd: mode description + * @gt: backing object + * + * Configure and fill in the boilerplate for our frame buffer. Return + * 0 on success or an error code if we fail. + */ +static int psb_framebuffer_init(struct drm_device *dev, + struct psb_framebuffer *fb, + struct drm_mode_fb_cmd2 *mode_cmd, + struct gtt_range *gt) +{ + u32 bpp, depth; + int ret; + + drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp); + + if (mode_cmd->pitches[0] & 63) + return -EINVAL; + switch (bpp) { + case 8: + case 16: + case 24: + case 32: + break; + default: + return -EINVAL; + } + ret = drm_framebuffer_init(dev, &fb->base, &psb_fb_funcs); + if (ret) { + dev_err(dev->dev, "framebuffer init failed: %d\n", ret); + return ret; + } + drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd); + fb->gtt = gt; + return 0; +} + +/** + * psb_framebuffer_create - create a framebuffer backed by gt + * @dev: our DRM device + * @mode_cmd: the description of the requested mode + * @gt: the backing object + * + * Create a framebuffer object backed by the gt, and fill in the + * boilerplate required + * + * TODO: review object references + */ + +static struct drm_framebuffer *psb_framebuffer_create + (struct drm_device *dev, + struct drm_mode_fb_cmd2 *mode_cmd, + struct gtt_range *gt) +{ + struct psb_framebuffer *fb; + int ret; + + fb = kzalloc(sizeof(*fb), GFP_KERNEL); + if (!fb) + return ERR_PTR(-ENOMEM); + + ret = psb_framebuffer_init(dev, fb, mode_cmd, gt); + if (ret) { + kfree(fb); + return ERR_PTR(ret); + } + return &fb->base; +} + +/** + * psbfb_alloc - allocate frame buffer memory + * @dev: the DRM device + * @aligned_size: space needed + * @force: fall back to GEM buffers if need be + * + * Allocate the frame buffer. In the usual case we get a GTT range that + * is stolen memory backed and life is simple. If there isn't sufficient + * stolen memory or the system has no stolen memory we allocate a range + * and back it with a GEM object. + * + * In this case the GEM object has no handle. + */ +static struct gtt_range *psbfb_alloc(struct drm_device *dev, + int aligned_size, int force) +{ + struct gtt_range *backing; + /* Begin by trying to use stolen memory backing */ + backing = psb_gtt_alloc_range(dev, aligned_size, "fb", 1); + if (backing) { + if (drm_gem_private_object_init(dev, + &backing->gem, aligned_size) == 0) + return backing; + psb_gtt_free_range(dev, backing); + } + if (!force) + return NULL; + + /* Next try using GEM host memory */ + backing = psb_gtt_alloc_range(dev, aligned_size, "fb(gem)", 0); + if (backing == NULL) + return NULL; + + /* Now back it with an object */ + if (drm_gem_object_init(dev, &backing->gem, aligned_size) != 0) { + psb_gtt_free_range(dev, backing); + return NULL; + } + return backing; +} + +/** + * psbfb_create - create a framebuffer + * @fbdev: the framebuffer device + * @sizes: specification of the layout + * + * Create a framebuffer to the specifications provided + */ +static int psbfb_create(struct psb_fbdev *fbdev, + struct drm_fb_helper_surface_size *sizes) +{ + struct drm_device *dev = fbdev->psb_fb_helper.dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct fb_info *info; + struct drm_framebuffer *fb; + struct psb_framebuffer *psbfb = &fbdev->pfb; + struct drm_mode_fb_cmd2 mode_cmd; + struct device *device = &dev->pdev->dev; + int size; + int ret; + struct gtt_range *backing; + int gtt_roll = 1; + u32 bpp, depth; + + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + bpp = sizes->surface_bpp; + + /* No 24bit packed */ + if (bpp == 24) + bpp = 32; + + /* Acceleration via the GTT requires pitch to be 4096 byte aligned + (ie 1024 or 2048 pixels in normal use) */ + mode_cmd.pitches[0] = ALIGN(mode_cmd.width * ((bpp + 7) / 8), 4096); + depth = sizes->surface_depth; + + size = mode_cmd.pitches[0] * mode_cmd.height; + size = ALIGN(size, PAGE_SIZE); + + /* Allocate the framebuffer in the GTT with stolen page backing */ + backing = psbfb_alloc(dev, size, 0); + if (backing == NULL) { + /* + * We couldn't get the space we wanted, fall back to the + * display engine requirement instead. The HW requires + * the pitch to be 64 byte aligned + */ + + gtt_roll = 0; /* Don't use GTT accelerated scrolling */ + + mode_cmd.pitches[0] = ALIGN(mode_cmd.width * ((bpp + 7) / 8), 64); + depth = sizes->surface_depth; + + size = mode_cmd.pitches[0] * mode_cmd.height; + size = ALIGN(size, PAGE_SIZE); + + /* Allocate the framebuffer in the GTT with stolen page + backing when there is room */ + backing = psbfb_alloc(dev, size, 1); + if (backing == NULL) + return -ENOMEM; + } + + mutex_lock(&dev->struct_mutex); + + info = framebuffer_alloc(0, device); + if (!info) { + ret = -ENOMEM; + goto out_err1; + } + info->par = fbdev; + + mode_cmd.pixel_format = drm_mode_legacy_fb_format(bpp, depth); + + ret = psb_framebuffer_init(dev, psbfb, &mode_cmd, backing); + if (ret) + goto out_unref; + + fb = &psbfb->base; + psbfb->fbdev = info; + + fbdev->psb_fb_helper.fb = fb; + fbdev->psb_fb_helper.fbdev = info; + + strcpy(info->fix.id, "psbfb"); + + info->flags = FBINFO_DEFAULT; + if (gtt_roll) { /* GTT rolling seems best */ + info->fbops = &psbfb_roll_ops; + info->flags |= FBINFO_HWACCEL_YPAN; + } + else if (dev_priv->ops->accel_2d) /* 2D engine */ + info->fbops = &psbfb_ops; + else /* Software */ + info->fbops = &psbfb_unaccel_ops; + + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret) { + ret = -ENOMEM; + goto out_unref; + } + + info->fix.smem_start = dev->mode_config.fb_base; + info->fix.smem_len = size; + info->fix.ywrapstep = gtt_roll; + info->fix.ypanstep = gtt_roll; + + if (backing->stolen) { + /* Accessed stolen memory directly */ + info->screen_base = (char *)dev_priv->vram_addr + + backing->offset; + } else { + /* Pin the pages into the GTT and create a mapping to them */ + psb_gtt_pin(backing); + info->screen_base = vm_map_ram(backing->pages, backing->npage, + -1, PAGE_KERNEL); + if (info->screen_base == NULL) { + psb_gtt_unpin(backing); + ret = -ENOMEM; + goto out_unref; + } + psbfb->vm_map = 1; + } + info->screen_size = size; + + if (dev_priv->gtt.stolen_size) { + info->apertures = alloc_apertures(1); + if (!info->apertures) { + ret = -ENOMEM; + goto out_unref; + } + info->apertures->ranges[0].base = dev->mode_config.fb_base; + info->apertures->ranges[0].size = dev_priv->gtt.stolen_size; + } + + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + drm_fb_helper_fill_var(info, &fbdev->psb_fb_helper, + sizes->fb_width, sizes->fb_height); + + info->fix.mmio_start = pci_resource_start(dev->pdev, 0); + info->fix.mmio_len = pci_resource_len(dev->pdev, 0); + + info->pixmap.size = 64 * 1024; + info->pixmap.buf_align = 8; + info->pixmap.access_align = 32; + info->pixmap.flags = FB_PIXMAP_SYSTEM; + info->pixmap.scan_align = 1; + + dev_info(dev->dev, "allocated %dx%d fb\n", + psbfb->base.width, psbfb->base.height); + + mutex_unlock(&dev->struct_mutex); + return 0; +out_unref: + if (backing->stolen) + psb_gtt_free_range(dev, backing); + else { + if (psbfb->vm_map) + vm_unmap_ram(info->screen_base, backing->npage); + drm_gem_object_unreference(&backing->gem); + } +out_err1: + mutex_unlock(&dev->struct_mutex); + psb_gtt_free_range(dev, backing); + return ret; +} + +/** + * psb_user_framebuffer_create - create framebuffer + * @dev: our DRM device + * @filp: client file + * @cmd: mode request + * + * Create a new framebuffer backed by a userspace GEM object + */ +static struct drm_framebuffer *psb_user_framebuffer_create + (struct drm_device *dev, struct drm_file *filp, + struct drm_mode_fb_cmd2 *cmd) +{ + struct gtt_range *r; + struct drm_gem_object *obj; + + /* + * Find the GEM object and thus the gtt range object that is + * to back this space + */ + obj = drm_gem_object_lookup(dev, filp, cmd->handles[0]); + if (obj == NULL) + return ERR_PTR(-ENOENT); + + /* Let the core code do all the work */ + r = container_of(obj, struct gtt_range, gem); + return psb_framebuffer_create(dev, cmd, r); +} + +static void psbfb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, + u16 blue, int regno) +{ +} + +static void psbfb_gamma_get(struct drm_crtc *crtc, u16 *red, + u16 *green, u16 *blue, int regno) +{ +} + +static int psbfb_probe(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct psb_fbdev *psb_fbdev = (struct psb_fbdev *)helper; + int new_fb = 0; + int ret; + + if (!helper->fb) { + ret = psbfb_create(psb_fbdev, sizes); + if (ret) + return ret; + new_fb = 1; + } + return new_fb; +} + +struct drm_fb_helper_funcs psb_fb_helper_funcs = { + .gamma_set = psbfb_gamma_set, + .gamma_get = psbfb_gamma_get, + .fb_probe = psbfb_probe, +}; + +int psb_fbdev_destroy(struct drm_device *dev, struct psb_fbdev *fbdev) +{ + struct fb_info *info; + struct psb_framebuffer *psbfb = &fbdev->pfb; + + if (fbdev->psb_fb_helper.fbdev) { + info = fbdev->psb_fb_helper.fbdev; + + /* If this is our base framebuffer then kill any virtual map + for the framebuffer layer and unpin it */ + if (psbfb->vm_map) { + vm_unmap_ram(info->screen_base, psbfb->gtt->npage); + psb_gtt_unpin(psbfb->gtt); + } + unregister_framebuffer(info); + if (info->cmap.len) + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); + } + drm_fb_helper_fini(&fbdev->psb_fb_helper); + drm_framebuffer_cleanup(&psbfb->base); + + if (psbfb->gtt) + drm_gem_object_unreference(&psbfb->gtt->gem); + return 0; +} + +int psb_fbdev_init(struct drm_device *dev) +{ + struct psb_fbdev *fbdev; + struct drm_psb_private *dev_priv = dev->dev_private; + + fbdev = kzalloc(sizeof(struct psb_fbdev), GFP_KERNEL); + if (!fbdev) { + dev_err(dev->dev, "no memory\n"); + return -ENOMEM; + } + + dev_priv->fbdev = fbdev; + fbdev->psb_fb_helper.funcs = &psb_fb_helper_funcs; + + drm_fb_helper_init(dev, &fbdev->psb_fb_helper, dev_priv->ops->crtcs, + INTELFB_CONN_LIMIT); + + drm_fb_helper_single_add_all_connectors(&fbdev->psb_fb_helper); + drm_fb_helper_initial_config(&fbdev->psb_fb_helper, 32); + return 0; +} + +void psb_fbdev_fini(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + + if (!dev_priv->fbdev) + return; + + psb_fbdev_destroy(dev, dev_priv->fbdev); + kfree(dev_priv->fbdev); + dev_priv->fbdev = NULL; +} + +static void psbfb_output_poll_changed(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_fbdev *fbdev = (struct psb_fbdev *)dev_priv->fbdev; + drm_fb_helper_hotplug_event(&fbdev->psb_fb_helper); +} + +/** + * psb_user_framebuffer_create_handle - add hamdle to a framebuffer + * @fb: framebuffer + * @file_priv: our DRM file + * @handle: returned handle + * + * Our framebuffer object is a GTT range which also contains a GEM + * object. We need to turn it into a handle for userspace. GEM will do + * the work for us + */ +static int psb_user_framebuffer_create_handle(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int *handle) +{ + struct psb_framebuffer *psbfb = to_psb_fb(fb); + struct gtt_range *r = psbfb->gtt; + return drm_gem_handle_create(file_priv, &r->gem, handle); +} + +/** + * psb_user_framebuffer_destroy - destruct user created fb + * @fb: framebuffer + * + * User framebuffers are backed by GEM objects so all we have to do is + * clean up a bit and drop the reference, GEM will handle the fallout + */ +static void psb_user_framebuffer_destroy(struct drm_framebuffer *fb) +{ + struct psb_framebuffer *psbfb = to_psb_fb(fb); + struct gtt_range *r = psbfb->gtt; + struct drm_device *dev = fb->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_fbdev *fbdev = dev_priv->fbdev; + struct drm_crtc *crtc; + int reset = 0; + + /* Should never get stolen memory for a user fb */ + WARN_ON(r->stolen); + + /* Check if we are erroneously live */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + if (crtc->fb == fb) + reset = 1; + + if (reset) + /* + * Now force a sane response before we permit the DRM CRTC + * layer to do stupid things like blank the display. Instead + * we reset this framebuffer as if the user had forced a reset. + * We must do this before the cleanup so that the DRM layer + * doesn't get a chance to stick its oar in where it isn't + * wanted. + */ + drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper); + + /* Let DRM do its clean up */ + drm_framebuffer_cleanup(fb); + /* We are no longer using the resource in GEM */ + drm_gem_object_unreference_unlocked(&r->gem); + kfree(fb); +} + +static const struct drm_mode_config_funcs psb_mode_funcs = { + .fb_create = psb_user_framebuffer_create, + .output_poll_changed = psbfb_output_poll_changed, +}; + +static int psb_create_backlight_property(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct drm_property *backlight; + + if (dev_priv->backlight_property) + return 0; + + backlight = drm_property_create(dev, DRM_MODE_PROP_RANGE, + "backlight", 2); + backlight->values[0] = 0; + backlight->values[1] = 100; + + dev_priv->backlight_property = backlight; + + return 0; +} + +static void psb_setup_outputs(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct drm_connector *connector; + + drm_mode_create_scaling_mode_property(dev); + psb_create_backlight_property(dev); + + dev_priv->ops->output_init(dev); + + list_for_each_entry(connector, &dev->mode_config.connector_list, + head) { + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + struct drm_encoder *encoder = &psb_intel_output->enc; + int crtc_mask = 0, clone_mask = 0; + + /* valid crtcs */ + switch (psb_intel_output->type) { + case INTEL_OUTPUT_ANALOG: + crtc_mask = (1 << 0); + clone_mask = (1 << INTEL_OUTPUT_ANALOG); + break; + case INTEL_OUTPUT_SDVO: + crtc_mask = ((1 << 0) | (1 << 1)); + clone_mask = (1 << INTEL_OUTPUT_SDVO); + break; + case INTEL_OUTPUT_LVDS: + if (IS_MRST(dev)) + crtc_mask = (1 << 0); + else + crtc_mask = (1 << 1); + clone_mask = (1 << INTEL_OUTPUT_LVDS); + break; + case INTEL_OUTPUT_MIPI: + crtc_mask = (1 << 0); + clone_mask = (1 << INTEL_OUTPUT_MIPI); + break; + case INTEL_OUTPUT_MIPI2: + crtc_mask = (1 << 2); + clone_mask = (1 << INTEL_OUTPUT_MIPI2); + break; + case INTEL_OUTPUT_HDMI: + /* HDMI on crtc 1 for SoC devices and crtc 0 for + Cedarview. HDMI on Poulsbo is only via external + logic */ + if (IS_MFLD(dev) || IS_MRST(dev)) + crtc_mask = (1 << 1); + else + crtc_mask = (1 << 0); /* Cedarview */ + clone_mask = (1 << INTEL_OUTPUT_HDMI); + break; + } + encoder->possible_crtcs = crtc_mask; + encoder->possible_clones = + psb_intel_connector_clones(dev, clone_mask); + } +} + +void psb_modeset_init(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev; + int i; + + drm_mode_config_init(dev); + + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + + dev->mode_config.funcs = (void *) &psb_mode_funcs; + + /* set memory base */ + /* MRST and PSB should use BAR 2*/ + pci_read_config_dword(dev->pdev, PSB_BSM, (u32 *) + &(dev->mode_config.fb_base)); + + /* num pipes is 2 for PSB but 1 for Mrst */ + for (i = 0; i < dev_priv->num_pipe; i++) + psb_intel_crtc_init(dev, i, mode_dev); + + dev->mode_config.max_width = 2048; + dev->mode_config.max_height = 2048; + + psb_setup_outputs(dev); +} + +void psb_modeset_cleanup(struct drm_device *dev) +{ + mutex_lock(&dev->struct_mutex); + + drm_kms_helper_poll_fini(dev); + psb_fbdev_fini(dev); + drm_mode_config_cleanup(dev); + + mutex_unlock(&dev->struct_mutex); +} diff --git a/trunk/drivers/staging/gma500/framebuffer.h b/trunk/drivers/staging/gma500/framebuffer.h new file mode 100644 index 000000000000..d1b2289447f0 --- /dev/null +++ b/trunk/drivers/staging/gma500/framebuffer.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2008-2011, Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * 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. + * + * Authors: + * Eric Anholt + * + */ + +#ifndef _FRAMEBUFFER_H_ +#define _FRAMEBUFFER_H_ + +#include +#include + +#include "psb_drv.h" + +struct psb_framebuffer { + struct drm_framebuffer base; + struct address_space *addr_space; + struct fb_info *fbdev; + struct gtt_range *gtt; + bool vm_map; /* True if we must undo a vm_map_ram */ +}; + +struct psb_fbdev { + struct drm_fb_helper psb_fb_helper; + struct psb_framebuffer pfb; +}; + +#define to_psb_fb(x) container_of(x, struct psb_framebuffer, base) + +extern int psb_intel_connector_clones(struct drm_device *dev, int type_mask); + +#endif + diff --git a/trunk/drivers/staging/gma500/gem.c b/trunk/drivers/staging/gma500/gem.c new file mode 100644 index 000000000000..f6433c037d24 --- /dev/null +++ b/trunk/drivers/staging/gma500/gem.c @@ -0,0 +1,292 @@ +/* + * psb GEM interface + * + * Copyright (c) 2011, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * 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. + * + * Authors: Alan Cox + * + * TODO: + * - we need to work out if the MMU is relevant (eg for + * accelerated operations on a GEM object) + */ + +#include +#include +#include "psb_drm.h" +#include "psb_drv.h" + +int psb_gem_init_object(struct drm_gem_object *obj) +{ + return -EINVAL; +} + +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); + /* This must occur last as it frees up the memory of the GEM object */ + psb_gtt_free_range(obj->dev, gtt); +} + +int psb_gem_get_aperture(struct drm_device *dev, void *data, + struct drm_file *file) +{ + return -EINVAL; +} + +/** + * psb_gem_dumb_map_gtt - buffer mapping for dumb interface + * @file: our drm client file + * @dev: drm device + * @handle: GEM handle to the object (from dumb_create) + * + * Do the necessary setup to allow the mapping of the frame buffer + * into user memory. We don't have to do much here at the moment. + */ +int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev, + uint32_t handle, uint64_t *offset) +{ + int ret = 0; + struct drm_gem_object *obj; + + if (!(dev->driver->driver_features & DRIVER_GEM)) + return -ENODEV; + + mutex_lock(&dev->struct_mutex); + + /* GEM does all our handle to object mapping */ + obj = drm_gem_object_lookup(dev, file, handle); + if (obj == NULL) { + ret = -ENOENT; + goto unlock; + } + /* What validation is needed here ? */ + + /* Make it mmapable */ + if (!obj->map_list.map) { + ret = gem_create_mmap_offset(obj); + if (ret) + goto out; + } + /* GEM should really work out the hash offsets for us */ + *offset = (u64)obj->map_list.hash.key << PAGE_SHIFT; +out: + drm_gem_object_unreference(obj); +unlock: + mutex_unlock(&dev->struct_mutex); + return ret; +} + +/** + * psb_gem_create - create a mappable object + * @file: the DRM file of the client + * @dev: our device + * @size: the size requested + * @handlep: returned handle (opaque number) + * + * Create a GEM object, fill in the boilerplate and attach a handle to + * it so that userspace can speak about it. This does the core work + * for the various methods that do/will create GEM objects for things + */ +static int psb_gem_create(struct drm_file *file, + struct drm_device *dev, uint64_t size, uint32_t *handlep) +{ + struct gtt_range *r; + int ret; + u32 handle; + + size = roundup(size, PAGE_SIZE); + + /* Allocate our object - for now a direct gtt range which is not + stolen memory backed */ + r = psb_gtt_alloc_range(dev, size, "gem", 0); + if (r == NULL) { + dev_err(dev->dev, "no memory for %lld byte GEM object\n", size); + return -ENOSPC; + } + /* Initialize the extra goodies GEM needs to do all the hard work */ + if (drm_gem_object_init(dev, &r->gem, size) != 0) { + psb_gtt_free_range(dev, r); + /* GEM doesn't give an error code so use -ENOMEM */ + dev_err(dev->dev, "GEM init failed for %lld\n", size); + return -ENOMEM; + } + /* Give the object a handle so we can carry it more easily */ + ret = drm_gem_handle_create(file, &r->gem, &handle); + if (ret) { + dev_err(dev->dev, "GEM handle failed for %p, %lld\n", + &r->gem, size); + drm_gem_object_release(&r->gem); + psb_gtt_free_range(dev, r); + return ret; + } + /* We have the initial and handle reference but need only one now */ + drm_gem_object_unreference(&r->gem); + *handlep = handle; + return 0; +} + +/** + * psb_gem_dumb_create - create a dumb buffer + * @drm_file: our client file + * @dev: our device + * @args: the requested arguments copied from userspace + * + * Allocate a buffer suitable for use for a frame buffer of the + * form described by user space. Give userspace a handle by which + * to reference it. + */ +int psb_gem_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + args->pitch = ALIGN(args->width * ((args->bpp + 7) / 8), 64); + args->size = args->pitch * args->height; + return psb_gem_create(file, dev, args->size, &args->handle); +} + +/** + * psb_gem_dumb_destroy - destroy a dumb buffer + * @file: client file + * @dev: our DRM device + * @handle: the object handle + * + * Destroy a handle that was created via psb_gem_dumb_create, at least + * we hope it was created that way. i915 seems to assume the caller + * does the checking but that might be worth review ! FIXME + */ +int psb_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev, + uint32_t handle) +{ + /* No special work needed, drop the reference and see what falls out */ + return drm_gem_handle_delete(file, handle); +} + +/** + * psb_gem_fault - pagefault handler for GEM objects + * @vma: the VMA of the GEM object + * @vmf: fault detail + * + * Invoked when a fault occurs on an mmap of a GEM managed area. GEM + * does most of the work for us including the actual map/unmap calls + * but we need to do the actual page work. + * + * This code eventually needs to handle faulting objects in and out + * of the GTT and repacking it when we run out of space. We can put + * that off for now and for our simple uses + * + * The VMA was set up by GEM. In doing so it also ensured that the + * vma->vm_private_data points to the GEM object that is backing this + * mapping. + */ +int psb_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct drm_gem_object *obj; + struct gtt_range *r; + int ret; + unsigned long pfn; + pgoff_t page_offset; + struct drm_device *dev; + struct drm_psb_private *dev_priv; + + obj = vma->vm_private_data; /* GEM object */ + dev = obj->dev; + dev_priv = dev->dev_private; + + r = container_of(obj, struct gtt_range, gem); /* Get the gtt range */ + + /* Make sure we don't parallel update on a fault, nor move or remove + something from beneath our feet */ + mutex_lock(&dev->struct_mutex); + + /* For now the mmap pins the object and it stays pinned. As things + stand that will do us no harm */ + if (r->mmapping == 0) { + ret = psb_gtt_pin(r); + if (ret < 0) { + dev_err(dev->dev, "gma500: pin failed: %d\n", ret); + goto fail; + } + r->mmapping = 1; + } + + /* Page relative to the VMA start - we must calculate this ourselves + because vmf->pgoff is the fake GEM offset */ + page_offset = ((unsigned long) vmf->virtual_address - vma->vm_start) + >> PAGE_SHIFT; + + /* CPU view of the page, don't go via the GART for CPU writes */ + if (r->stolen) + pfn = (dev_priv->stolen_base + r->offset) >> PAGE_SHIFT; + else + pfn = page_to_pfn(r->pages[page_offset]); + ret = vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn); + +fail: + mutex_unlock(&dev->struct_mutex); + switch (ret) { + case 0: + case -ERESTARTSYS: + case -EINTR: + return VM_FAULT_NOPAGE; + case -ENOMEM: + return VM_FAULT_OOM; + default: + return VM_FAULT_SIGBUS; + } +} + +static int psb_gem_create_stolen(struct drm_file *file, struct drm_device *dev, + int size, u32 *handle) +{ + struct gtt_range *gtt = psb_gtt_alloc_range(dev, size, "gem", 1); + if (gtt == NULL) + return -ENOMEM; + if (drm_gem_private_object_init(dev, >t->gem, size) != 0) + goto free_gtt; + if (drm_gem_handle_create(file, >t->gem, handle) == 0) + return 0; +free_gtt: + psb_gtt_free_range(dev, gtt); + return -ENOMEM; +} + +/* + * GEM interfaces for our specific client + */ +int psb_gem_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_psb_gem_create *args = data; + int ret; + if (args->flags & PSB_GEM_CREATE_STOLEN) { + ret = psb_gem_create_stolen(file, dev, args->size, + &args->handle); + if (ret == 0) + return 0; + /* Fall throguh */ + args->flags &= ~PSB_GEM_CREATE_STOLEN; + } + return psb_gem_create(file, dev, args->size, &args->handle); +} + +int psb_gem_mmap_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_psb_gem_mmap *args = data; + return dev->driver->dumb_map_offset(file, dev, + args->handle, &args->offset); +} + diff --git a/trunk/drivers/staging/gma500/gem_glue.c b/trunk/drivers/staging/gma500/gem_glue.c new file mode 100644 index 000000000000..daac12120653 --- /dev/null +++ b/trunk/drivers/staging/gma500/gem_glue.c @@ -0,0 +1,89 @@ +/************************************************************************** + * 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 +#include + +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/trunk/drivers/staging/gma500/gem_glue.h b/trunk/drivers/staging/gma500/gem_glue.h new file mode 100644 index 000000000000..ce5ce30f74db --- /dev/null +++ b/trunk/drivers/staging/gma500/gem_glue.h @@ -0,0 +1,2 @@ +extern void drm_gem_object_release_wrap(struct drm_gem_object *obj); +extern int gem_create_mmap_offset(struct drm_gem_object *obj); diff --git a/trunk/drivers/staging/gma500/gtt.c b/trunk/drivers/staging/gma500/gtt.c new file mode 100644 index 000000000000..e770bd190a5c --- /dev/null +++ b/trunk/drivers/staging/gma500/gtt.c @@ -0,0 +1,553 @@ +/* + * Copyright (c) 2007, 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. + * + * Authors: Thomas Hellstrom + * Alan Cox + */ + +#include +#include "psb_drv.h" + + +/* + * GTT resource allocator - manage page mappings in GTT space + */ + +/** + * psb_gtt_mask_pte - generate GTT pte entry + * @pfn: page number to encode + * @type: type of memory in the GTT + * + * Set the GTT entry for the appropriate memory type. + */ +static inline uint32_t psb_gtt_mask_pte(uint32_t pfn, int type) +{ + uint32_t mask = PSB_PTE_VALID; + + if (type & PSB_MMU_CACHED_MEMORY) + mask |= PSB_PTE_CACHED; + if (type & PSB_MMU_RO_MEMORY) + mask |= PSB_PTE_RO; + if (type & PSB_MMU_WO_MEMORY) + mask |= PSB_PTE_WO; + + return (pfn << PAGE_SHIFT) | mask; +} + +/** + * psb_gtt_entry - find the GTT entries for a gtt_range + * @dev: our DRM device + * @r: our GTT range + * + * Given a gtt_range object return the GTT offset of the page table + * entries for this gtt_range + */ +u32 *psb_gtt_entry(struct drm_device *dev, struct gtt_range *r) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + unsigned long offset; + + offset = r->resource.start - dev_priv->gtt_mem->start; + + return dev_priv->gtt_map + (offset >> PAGE_SHIFT); +} + +/** + * psb_gtt_insert - put an object into the GTT + * @dev: our DRM device + * @r: our GTT range + * + * Take our preallocated GTT range and insert the GEM object into + * the GTT. This is protected via the gtt mutex which the caller + * must hold. + */ +static int psb_gtt_insert(struct drm_device *dev, struct gtt_range *r) +{ + u32 *gtt_slot, pte; + struct page **pages; + int i; + + if (r->pages == NULL) { + WARN_ON(1); + return -EINVAL; + } + + WARN_ON(r->stolen); /* refcount these maybe ? */ + + gtt_slot = psb_gtt_entry(dev, r); + pages = r->pages; + + /* Make sure changes are visible to the GPU */ + set_pages_array_uc(pages, r->npage); + + /* Write our page entries into the GTT itself */ + for (i = r->roll; i < r->npage; i++) { + pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0); + iowrite32(pte, gtt_slot++); + } + for (i = 0; i < r->roll; i++) { + pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0); + iowrite32(pte, gtt_slot++); + } + /* Make sure all the entries are set before we return */ + ioread32(gtt_slot - 1); + + return 0; +} + +/** + * psb_gtt_remove - remove an object from the GTT + * @dev: our DRM device + * @r: our GTT range + * + * Remove a preallocated GTT range from the GTT. Overwrite all the + * page table entries with the dummy page. This is protected via the gtt + * mutex which the caller must hold. + */ +static void psb_gtt_remove(struct drm_device *dev, struct gtt_range *r) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + u32 *gtt_slot, pte; + int i; + + WARN_ON(r->stolen); + + gtt_slot = psb_gtt_entry(dev, r); + pte = psb_gtt_mask_pte(page_to_pfn(dev_priv->scratch_page), 0); + + for (i = 0; i < r->npage; i++) + iowrite32(pte, gtt_slot++); + ioread32(gtt_slot - 1); + set_pages_array_wb(r->pages, r->npage); +} + +/** + * psb_gtt_roll - set scrolling position + * @dev: our DRM device + * @r: the gtt mapping we are using + * @roll: roll offset + * + * Roll an existing pinned mapping by moving the pages through the GTT. + * This allows us to implement hardware scrolling on the consoles without + * a 2D engine + */ +void psb_gtt_roll(struct drm_device *dev, struct gtt_range *r, int roll) +{ + u32 *gtt_slot, pte; + int i; + + if (roll >= r->npage) { + WARN_ON(1); + return; + } + + r->roll = roll; + + /* Not currently in the GTT - no worry we will write the mapping at + the right position when it gets pinned */ + if (!r->stolen && !r->in_gart) + return; + + gtt_slot = psb_gtt_entry(dev, r); + + for (i = r->roll; i < r->npage; i++) { + pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0); + iowrite32(pte, gtt_slot++); + } + for (i = 0; i < r->roll; i++) { + pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0); + iowrite32(pte, gtt_slot++); + } + ioread32(gtt_slot - 1); +} + +/** + * psb_gtt_attach_pages - attach and pin GEM pages + * @gt: the gtt range + * + * Pin and build an in kernel list of the pages that back our GEM object. + * While we hold this the pages cannot be swapped out. This is protected + * via the gtt mutex which the caller must hold. + */ +static int psb_gtt_attach_pages(struct gtt_range *gt) +{ + struct inode *inode; + struct address_space *mapping; + int i; + struct page *p; + int pages = gt->gem.size / PAGE_SIZE; + + WARN_ON(gt->pages); + + /* This is the shared memory object that backs the GEM resource */ + inode = gt->gem.filp->f_path.dentry->d_inode; + mapping = inode->i_mapping; + + gt->pages = kmalloc(pages * sizeof(struct page *), GFP_KERNEL); + if (gt->pages == NULL) + return -ENOMEM; + gt->npage = pages; + + for (i = 0; i < pages; i++) { + /* FIXME: needs updating as per mail from Hugh Dickins */ + p = read_cache_page_gfp(mapping, i, + __GFP_COLD | GFP_KERNEL); + if (IS_ERR(p)) + goto err; + gt->pages[i] = p; + } + return 0; + +err: + while (i--) + page_cache_release(gt->pages[i]); + kfree(gt->pages); + gt->pages = NULL; + return PTR_ERR(p); +} + +/** + * psb_gtt_detach_pages - attach and pin GEM pages + * @gt: the gtt range + * + * Undo the effect of psb_gtt_attach_pages. At this point the pages + * must have been removed from the GTT as they could now be paged out + * and move bus address. This is protected via the gtt mutex which the + * caller must hold. + */ +static void psb_gtt_detach_pages(struct gtt_range *gt) +{ + int i; + for (i = 0; i < gt->npage; i++) { + /* FIXME: do we need to force dirty */ + set_page_dirty(gt->pages[i]); + page_cache_release(gt->pages[i]); + } + kfree(gt->pages); + gt->pages = NULL; +} + +/** + * psb_gtt_pin - pin pages into the GTT + * @gt: range to pin + * + * Pin a set of pages into the GTT. The pins are refcounted so that + * multiple pins need multiple unpins to undo. + * + * Non GEM backed objects treat this as a no-op as they are always GTT + * backed objects. + */ +int psb_gtt_pin(struct gtt_range *gt) +{ + int ret = 0; + struct drm_device *dev = gt->gem.dev; + struct drm_psb_private *dev_priv = dev->dev_private; + + mutex_lock(&dev_priv->gtt_mutex); + + if (gt->in_gart == 0 && gt->stolen == 0) { + ret = psb_gtt_attach_pages(gt); + if (ret < 0) + goto out; + ret = psb_gtt_insert(dev, gt); + if (ret < 0) { + psb_gtt_detach_pages(gt); + goto out; + } + } + gt->in_gart++; +out: + mutex_unlock(&dev_priv->gtt_mutex); + return ret; +} + +/** + * psb_gtt_unpin - Drop a GTT pin requirement + * @gt: range to pin + * + * Undoes the effect of psb_gtt_pin. On the last drop the GEM object + * will be removed from the GTT which will also drop the page references + * and allow the VM to clean up or page stuff. + * + * Non GEM backed objects treat this as a no-op as they are always GTT + * backed objects. + */ +void psb_gtt_unpin(struct gtt_range *gt) +{ + struct drm_device *dev = gt->gem.dev; + struct drm_psb_private *dev_priv = dev->dev_private; + + mutex_lock(&dev_priv->gtt_mutex); + + WARN_ON(!gt->in_gart); + + gt->in_gart--; + if (gt->in_gart == 0 && gt->stolen == 0) { + psb_gtt_remove(dev, gt); + psb_gtt_detach_pages(gt); + } + mutex_unlock(&dev_priv->gtt_mutex); +} + +/* + * GTT resource allocator - allocate and manage GTT address space + */ + +/** + * psb_gtt_alloc_range - allocate GTT address space + * @dev: Our DRM device + * @len: length (bytes) of address space required + * @name: resource name + * @backed: resource should be backed by stolen pages + * + * Ask the kernel core to find us a suitable range of addresses + * to use for a GTT mapping. + * + * Returns a gtt_range structure describing the object, or NULL on + * error. On successful return the resource is both allocated and marked + * as in use. + */ +struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len, + const char *name, int backed) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct gtt_range *gt; + struct resource *r = dev_priv->gtt_mem; + int ret; + unsigned long start, end; + + if (backed) { + /* The start of the GTT is the stolen pages */ + start = r->start; + end = r->start + dev_priv->gtt.stolen_size - 1; + } else { + /* The rest we will use for GEM backed objects */ + start = r->start + dev_priv->gtt.stolen_size; + end = r->end; + } + + gt = kzalloc(sizeof(struct gtt_range), GFP_KERNEL); + if (gt == NULL) + return NULL; + gt->resource.name = name; + gt->stolen = backed; + gt->in_gart = backed; + gt->roll = 0; + /* Ensure this is set for non GEM objects */ + gt->gem.dev = dev; + ret = allocate_resource(dev_priv->gtt_mem, >->resource, + len, start, end, PAGE_SIZE, NULL, NULL); + if (ret == 0) { + gt->offset = gt->resource.start - r->start; + return gt; + } + kfree(gt); + return NULL; +} + +/** + * psb_gtt_free_range - release GTT address space + * @dev: our DRM device + * @gt: a mapping created with psb_gtt_alloc_range + * + * Release a resource that was allocated with psb_gtt_alloc_range. If the + * object has been pinned by mmap users we clean this up here currently. + */ +void psb_gtt_free_range(struct drm_device *dev, struct gtt_range *gt) +{ + /* Undo the mmap pin if we are destroying the object */ + if (gt->mmapping) { + psb_gtt_unpin(gt); + gt->mmapping = 0; + } + WARN_ON(gt->in_gart && !gt->stolen); + release_resource(>->resource); + kfree(gt); +} + +void psb_gtt_alloc(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + init_rwsem(&dev_priv->gtt.sem); +} + +void psb_gtt_takedown(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + + if (dev_priv->gtt_map) { + iounmap(dev_priv->gtt_map); + dev_priv->gtt_map = NULL; + } + if (dev_priv->gtt_initialized) { + pci_write_config_word(dev->pdev, PSB_GMCH_CTRL, + dev_priv->gmch_ctrl); + PSB_WVDC32(dev_priv->pge_ctl, PSB_PGETBL_CTL); + (void) PSB_RVDC32(PSB_PGETBL_CTL); + } + if (dev_priv->vram_addr) + iounmap(dev_priv->gtt_map); +} + +int psb_gtt_init(struct drm_device *dev, int resume) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + unsigned gtt_pages; + unsigned long stolen_size, vram_stolen_size; + unsigned i, num_pages; + unsigned pfn_base; + uint32_t vram_pages; + uint32_t dvmt_mode = 0; + struct psb_gtt *pg; + + int ret = 0; + uint32_t pte; + + mutex_init(&dev_priv->gtt_mutex); + + psb_gtt_alloc(dev); + pg = &dev_priv->gtt; + + /* Enable the GTT */ + pci_read_config_word(dev->pdev, PSB_GMCH_CTRL, &dev_priv->gmch_ctrl); + pci_write_config_word(dev->pdev, PSB_GMCH_CTRL, + dev_priv->gmch_ctrl | _PSB_GMCH_ENABLED); + + dev_priv->pge_ctl = PSB_RVDC32(PSB_PGETBL_CTL); + PSB_WVDC32(dev_priv->pge_ctl | _PSB_PGETBL_ENABLED, PSB_PGETBL_CTL); + (void) PSB_RVDC32(PSB_PGETBL_CTL); + + /* The root resource we allocate address space from */ + dev_priv->gtt_initialized = 1; + + pg->gtt_phys_start = dev_priv->pge_ctl & PAGE_MASK; + + /* + * The video mmu has a hw bug when accessing 0x0D0000000. + * Make gatt start at 0x0e000,0000. This doesn't actually + * matter for us but may do if the video acceleration ever + * gets opened up. + */ + pg->mmu_gatt_start = 0xE0000000; + + pg->gtt_start = pci_resource_start(dev->pdev, PSB_GTT_RESOURCE); + gtt_pages = pci_resource_len(dev->pdev, PSB_GTT_RESOURCE) + >> PAGE_SHIFT; + /* Some CDV firmware doesn't report this currently. In which case the + system has 64 gtt pages */ + if (pg->gtt_start == 0 || gtt_pages == 0) { + dev_err(dev->dev, "GTT PCI BAR not initialized.\n"); + gtt_pages = 64; + pg->gtt_start = dev_priv->pge_ctl; + } + + pg->gatt_start = pci_resource_start(dev->pdev, PSB_GATT_RESOURCE); + pg->gatt_pages = pci_resource_len(dev->pdev, PSB_GATT_RESOURCE) + >> PAGE_SHIFT; + dev_priv->gtt_mem = &dev->pdev->resource[PSB_GATT_RESOURCE]; + + if (pg->gatt_pages == 0 || pg->gatt_start == 0) { + static struct resource fudge; /* Preferably peppermint */ + /* This can occur on CDV SDV systems. Fudge it in this case. + We really don't care what imaginary space is being allocated + at this point */ + dev_err(dev->dev, "GATT PCI BAR not initialized.\n"); + pg->gatt_start = 0x40000000; + pg->gatt_pages = (128 * 1024 * 1024) >> PAGE_SHIFT; + /* This is a little confusing but in fact the GTT is providing + a view from the GPU into memory and not vice versa. As such + this is really allocating space that is not the same as the + CPU address space on CDV */ + fudge.start = 0x40000000; + fudge.end = 0x40000000 + 128 * 1024 * 1024 - 1; + fudge.name = "fudge"; + fudge.flags = IORESOURCE_MEM; + dev_priv->gtt_mem = &fudge; + } + + pci_read_config_dword(dev->pdev, PSB_BSM, &dev_priv->stolen_base); + vram_stolen_size = pg->gtt_phys_start - dev_priv->stolen_base + - PAGE_SIZE; + + stolen_size = vram_stolen_size; + + printk(KERN_INFO "Stolen memory information\n"); + printk(KERN_INFO " base in RAM: 0x%x\n", dev_priv->stolen_base); + printk(KERN_INFO " size: %luK, calculated by (GTT RAM base) - (Stolen base), seems wrong\n", + vram_stolen_size/1024); + dvmt_mode = (dev_priv->gmch_ctrl >> 4) & 0x7; + printk(KERN_INFO " the correct size should be: %dM(dvmt mode=%d)\n", + (dvmt_mode == 1) ? 1 : (2 << (dvmt_mode - 1)), dvmt_mode); + + if (resume && (gtt_pages != pg->gtt_pages) && + (stolen_size != pg->stolen_size)) { + dev_err(dev->dev, "GTT resume error.\n"); + ret = -EINVAL; + goto out_err; + } + + pg->gtt_pages = gtt_pages; + pg->stolen_size = stolen_size; + dev_priv->vram_stolen_size = vram_stolen_size; + + /* + * Map the GTT and the stolen memory area + */ + dev_priv->gtt_map = ioremap_nocache(pg->gtt_phys_start, + gtt_pages << PAGE_SHIFT); + if (!dev_priv->gtt_map) { + dev_err(dev->dev, "Failure to map gtt.\n"); + ret = -ENOMEM; + goto out_err; + } + + dev_priv->vram_addr = ioremap_wc(dev_priv->stolen_base, stolen_size); + if (!dev_priv->vram_addr) { + dev_err(dev->dev, "Failure to map stolen base.\n"); + ret = -ENOMEM; + goto out_err; + } + + /* + * Insert vram stolen pages into the GTT + */ + + pfn_base = dev_priv->stolen_base >> PAGE_SHIFT; + vram_pages = num_pages = vram_stolen_size >> PAGE_SHIFT; + printk(KERN_INFO"Set up %d stolen pages starting at 0x%08x, GTT offset %dK\n", + num_pages, pfn_base << PAGE_SHIFT, 0); + for (i = 0; i < num_pages; ++i) { + pte = psb_gtt_mask_pte(pfn_base + i, 0); + iowrite32(pte, dev_priv->gtt_map + i); + } + + /* + * Init rest of GTT to the scratch page to avoid accidents or scribbles + */ + + pfn_base = page_to_pfn(dev_priv->scratch_page); + pte = psb_gtt_mask_pte(pfn_base, 0); + for (; i < gtt_pages; ++i) + iowrite32(pte, dev_priv->gtt_map + i); + + (void) ioread32(dev_priv->gtt_map + i - 1); + return 0; + +out_err: + psb_gtt_takedown(dev); + return ret; +} diff --git a/trunk/drivers/staging/gma500/gtt.h b/trunk/drivers/staging/gma500/gtt.h new file mode 100644 index 000000000000..aa1742387f5a --- /dev/null +++ b/trunk/drivers/staging/gma500/gtt.h @@ -0,0 +1,64 @@ +/************************************************************************** + * Copyright (c) 2007-2008, 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. + * + **************************************************************************/ + +#ifndef _PSB_GTT_H_ +#define _PSB_GTT_H_ + +#include + +/* This wants cleaning up with respect to the psb_dev and un-needed stuff */ +struct psb_gtt { + uint32_t gatt_start; + uint32_t mmu_gatt_start; + uint32_t gtt_start; + uint32_t gtt_phys_start; + unsigned gtt_pages; + unsigned gatt_pages; + unsigned long stolen_size; + unsigned long vram_stolen_size; + struct rw_semaphore sem; +}; + +/* Exported functions */ +extern int psb_gtt_init(struct drm_device *dev, int resume); +extern void psb_gtt_takedown(struct drm_device *dev); + +/* Each gtt_range describes an allocation in the GTT area */ +struct gtt_range { + struct resource resource; /* Resource for our allocation */ + u32 offset; /* GTT offset of our object */ + struct drm_gem_object gem; /* GEM high level stuff */ + int in_gart; /* Currently in the GART (ref ct) */ + bool stolen; /* Backed from stolen RAM */ + bool mmapping; /* Is mmappable */ + struct page **pages; /* Backing pages if present */ + int npage; /* Number of backing pages */ + int roll; /* Roll applied to the GTT entries */ +}; + +extern struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len, + const char *name, int backed); +extern void psb_gtt_kref_put(struct gtt_range *gt); +extern void psb_gtt_free_range(struct drm_device *dev, struct gtt_range *gt); +extern int psb_gtt_pin(struct gtt_range *gt); +extern void psb_gtt_unpin(struct gtt_range *gt); +extern void psb_gtt_roll(struct drm_device *dev, + struct gtt_range *gt, int roll); + +#endif diff --git a/trunk/drivers/staging/gma500/intel_bios.c b/trunk/drivers/staging/gma500/intel_bios.c new file mode 100644 index 000000000000..096757f9bc89 --- /dev/null +++ b/trunk/drivers/staging/gma500/intel_bios.c @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2006 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * 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. + * + * Authors: + * Eric Anholt + * + */ +#include +#include +#include "psb_drm.h" +#include "psb_drv.h" +#include "psb_intel_drv.h" +#include "psb_intel_reg.h" +#include "intel_bios.h" + + +static void *find_section(struct bdb_header *bdb, int section_id) +{ + u8 *base = (u8 *)bdb; + int index = 0; + u16 total, current_size; + u8 current_id; + + /* skip to first section */ + index += bdb->header_size; + total = bdb->bdb_size; + + /* walk the sections looking for section_id */ + while (index < total) { + current_id = *(base + index); + index++; + current_size = *((u16 *)(base + index)); + index += 2; + if (current_id == section_id) + return base + index; + index += current_size; + } + + return NULL; +} + +static void fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode, + struct lvds_dvo_timing *dvo_timing) +{ + panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) | + dvo_timing->hactive_lo; + panel_fixed_mode->hsync_start = panel_fixed_mode->hdisplay + + ((dvo_timing->hsync_off_hi << 8) | dvo_timing->hsync_off_lo); + panel_fixed_mode->hsync_end = panel_fixed_mode->hsync_start + + dvo_timing->hsync_pulse_width; + panel_fixed_mode->htotal = panel_fixed_mode->hdisplay + + ((dvo_timing->hblank_hi << 8) | dvo_timing->hblank_lo); + + panel_fixed_mode->vdisplay = (dvo_timing->vactive_hi << 8) | + dvo_timing->vactive_lo; + panel_fixed_mode->vsync_start = panel_fixed_mode->vdisplay + + dvo_timing->vsync_off; + panel_fixed_mode->vsync_end = panel_fixed_mode->vsync_start + + dvo_timing->vsync_pulse_width; + panel_fixed_mode->vtotal = panel_fixed_mode->vdisplay + + ((dvo_timing->vblank_hi << 8) | dvo_timing->vblank_lo); + panel_fixed_mode->clock = dvo_timing->clock * 10; + panel_fixed_mode->type = DRM_MODE_TYPE_PREFERRED; + + /* Some VBTs have bogus h/vtotal values */ + if (panel_fixed_mode->hsync_end > panel_fixed_mode->htotal) + panel_fixed_mode->htotal = panel_fixed_mode->hsync_end + 1; + if (panel_fixed_mode->vsync_end > panel_fixed_mode->vtotal) + panel_fixed_mode->vtotal = panel_fixed_mode->vsync_end + 1; + + drm_mode_set_name(panel_fixed_mode); +} + +static void parse_backlight_data(struct drm_psb_private *dev_priv, + struct bdb_header *bdb) +{ + struct bdb_lvds_backlight *vbt_lvds_bl = NULL; + struct bdb_lvds_backlight *lvds_bl; + u8 p_type = 0; + void *bl_start = NULL; + struct bdb_lvds_options *lvds_opts + = find_section(bdb, BDB_LVDS_OPTIONS); + + dev_priv->lvds_bl = NULL; + + if (lvds_opts) + p_type = lvds_opts->panel_type; + else + return; + + bl_start = find_section(bdb, BDB_LVDS_BACKLIGHT); + vbt_lvds_bl = (struct bdb_lvds_backlight *)(bl_start + 1) + p_type; + + lvds_bl = kzalloc(sizeof(*vbt_lvds_bl), GFP_KERNEL); + if (!lvds_bl) { + dev_err(dev_priv->dev->dev, "out of memory for backlight data\n"); + return; + } + memcpy(lvds_bl, vbt_lvds_bl, sizeof(*vbt_lvds_bl)); + dev_priv->lvds_bl = lvds_bl; +} + +/* Try to find integrated panel data */ +static void parse_lfp_panel_data(struct drm_psb_private *dev_priv, + struct bdb_header *bdb) +{ + struct bdb_lvds_options *lvds_options; + struct bdb_lvds_lfp_data *lvds_lfp_data; + struct bdb_lvds_lfp_data_entry *entry; + struct lvds_dvo_timing *dvo_timing; + struct drm_display_mode *panel_fixed_mode; + + /* Defaults if we can't find VBT info */ + dev_priv->lvds_dither = 0; + dev_priv->lvds_vbt = 0; + + lvds_options = find_section(bdb, BDB_LVDS_OPTIONS); + if (!lvds_options) + return; + + dev_priv->lvds_dither = lvds_options->pixel_dither; + if (lvds_options->panel_type == 0xff) + return; + + lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA); + if (!lvds_lfp_data) + return; + + + entry = &lvds_lfp_data->data[lvds_options->panel_type]; + dvo_timing = &entry->dvo_timing; + + panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), + GFP_KERNEL); + if (panel_fixed_mode == NULL) { + dev_err(dev_priv->dev->dev, "out of memory for fixed panel mode\n"); + return; + } + + dev_priv->lvds_vbt = 1; + fill_detail_timing_data(panel_fixed_mode, dvo_timing); + + if (panel_fixed_mode->htotal > 0 && panel_fixed_mode->vtotal > 0) { + dev_priv->lfp_lvds_vbt_mode = panel_fixed_mode; + drm_mode_debug_printmodeline(panel_fixed_mode); + } else { + dev_dbg(dev_priv->dev->dev, "ignoring invalid LVDS VBT\n"); + dev_priv->lvds_vbt = 0; + kfree(panel_fixed_mode); + } + return; +} + +/* Try to find sdvo panel data */ +static void parse_sdvo_panel_data(struct drm_psb_private *dev_priv, + struct bdb_header *bdb) +{ + struct bdb_sdvo_lvds_options *sdvo_lvds_options; + struct lvds_dvo_timing *dvo_timing; + struct drm_display_mode *panel_fixed_mode; + + dev_priv->sdvo_lvds_vbt_mode = NULL; + + sdvo_lvds_options = find_section(bdb, BDB_SDVO_LVDS_OPTIONS); + if (!sdvo_lvds_options) + return; + + dvo_timing = find_section(bdb, BDB_SDVO_PANEL_DTDS); + if (!dvo_timing) + return; + + panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL); + + if (!panel_fixed_mode) + return; + + fill_detail_timing_data(panel_fixed_mode, + dvo_timing + sdvo_lvds_options->panel_type); + + dev_priv->sdvo_lvds_vbt_mode = panel_fixed_mode; + + return; +} + +static void parse_general_features(struct drm_psb_private *dev_priv, + struct bdb_header *bdb) +{ + struct bdb_general_features *general; + + /* Set sensible defaults in case we can't find the general block */ + dev_priv->int_tv_support = 1; + dev_priv->int_crt_support = 1; + + general = find_section(bdb, BDB_GENERAL_FEATURES); + if (general) { + dev_priv->int_tv_support = general->int_tv_support; + dev_priv->int_crt_support = general->int_crt_support; + dev_priv->lvds_use_ssc = general->enable_ssc; + + if (dev_priv->lvds_use_ssc) { + dev_priv->lvds_ssc_freq + = general->ssc_freq ? 100 : 96; + } + } +} + +/** + * psb_intel_init_bios - initialize VBIOS settings & find VBT + * @dev: DRM device + * + * Loads the Video BIOS and checks that the VBT exists. Sets scratch registers + * to appropriate values. + * + * VBT existence is a sanity check that is relied on by other i830_bios.c code. + * Note that it would be better to use a BIOS call to get the VBT, as BIOSes may + * feed an updated VBT back through that, compared to what we'll fetch using + * this method of groping around in the BIOS data. + * + * Returns 0 on success, nonzero on failure. + */ +bool psb_intel_init_bios(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct pci_dev *pdev = dev->pdev; + struct vbt_header *vbt = NULL; + struct bdb_header *bdb; + u8 __iomem *bios; + size_t size; + int i; + + bios = pci_map_rom(pdev, &size); + if (!bios) + return -1; + + /* Scour memory looking for the VBT signature */ + for (i = 0; i + 4 < size; i++) { + if (!memcmp(bios + i, "$VBT", 4)) { + vbt = (struct vbt_header *)(bios + i); + break; + } + } + + if (!vbt) { + dev_err(dev->dev, "VBT signature missing\n"); + pci_unmap_rom(pdev, bios); + return -1; + } + + bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset); + + /* Grab useful general definitions */ + parse_general_features(dev_priv, bdb); + parse_lfp_panel_data(dev_priv, bdb); + parse_sdvo_panel_data(dev_priv, bdb); + parse_backlight_data(dev_priv, bdb); + + pci_unmap_rom(pdev, bios); + + return 0; +} + +/** + * Destroy and free VBT data + */ +void psb_intel_destroy_bios(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct drm_display_mode *sdvo_lvds_vbt_mode = + dev_priv->sdvo_lvds_vbt_mode; + struct drm_display_mode *lfp_lvds_vbt_mode = + dev_priv->lfp_lvds_vbt_mode; + struct bdb_lvds_backlight *lvds_bl = + dev_priv->lvds_bl; + + /*free sdvo panel mode*/ + if (sdvo_lvds_vbt_mode) { + dev_priv->sdvo_lvds_vbt_mode = NULL; + kfree(sdvo_lvds_vbt_mode); + } + + if (lfp_lvds_vbt_mode) { + dev_priv->lfp_lvds_vbt_mode = NULL; + kfree(lfp_lvds_vbt_mode); + } + + if (lvds_bl) { + dev_priv->lvds_bl = NULL; + kfree(lvds_bl); + } +} diff --git a/trunk/drivers/staging/gma500/intel_bios.h b/trunk/drivers/staging/gma500/intel_bios.h new file mode 100644 index 000000000000..70f1bf018183 --- /dev/null +++ b/trunk/drivers/staging/gma500/intel_bios.h @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2006 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * 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. + * + * Authors: + * Eric Anholt + * + */ + +#ifndef _I830_BIOS_H_ +#define _I830_BIOS_H_ + +#include + +struct vbt_header { + u8 signature[20]; /**< Always starts with 'VBT$' */ + u16 version; /**< decimal */ + u16 header_size; /**< in bytes */ + u16 vbt_size; /**< in bytes */ + u8 vbt_checksum; + u8 reserved0; + u32 bdb_offset; /**< from beginning of VBT */ + u32 aim_offset[4]; /**< from beginning of VBT */ +} __attribute__((packed)); + + +struct bdb_header { + u8 signature[16]; /**< Always 'BIOS_DATA_BLOCK' */ + u16 version; /**< decimal */ + u16 header_size; /**< in bytes */ + u16 bdb_size; /**< in bytes */ +}; + +/* strictly speaking, this is a "skip" block, but it has interesting info */ +struct vbios_data { + u8 type; /* 0 == desktop, 1 == mobile */ + u8 relstage; + u8 chipset; + u8 lvds_present:1; + u8 tv_present:1; + u8 rsvd2:6; /* finish byte */ + u8 rsvd3[4]; + u8 signon[155]; + u8 copyright[61]; + u16 code_segment; + u8 dos_boot_mode; + u8 bandwidth_percent; + u8 rsvd4; /* popup memory size */ + u8 resize_pci_bios; + u8 rsvd5; /* is crt already on ddc2 */ +} __attribute__((packed)); + +/* + * There are several types of BIOS data blocks (BDBs), each block has + * an ID and size in the first 3 bytes (ID in first, size in next 2). + * Known types are listed below. + */ +#define BDB_GENERAL_FEATURES 1 +#define BDB_GENERAL_DEFINITIONS 2 +#define BDB_OLD_TOGGLE_LIST 3 +#define BDB_MODE_SUPPORT_LIST 4 +#define BDB_GENERIC_MODE_TABLE 5 +#define BDB_EXT_MMIO_REGS 6 +#define BDB_SWF_IO 7 +#define BDB_SWF_MMIO 8 +#define BDB_DOT_CLOCK_TABLE 9 +#define BDB_MODE_REMOVAL_TABLE 10 +#define BDB_CHILD_DEVICE_TABLE 11 +#define BDB_DRIVER_FEATURES 12 +#define BDB_DRIVER_PERSISTENCE 13 +#define BDB_EXT_TABLE_PTRS 14 +#define BDB_DOT_CLOCK_OVERRIDE 15 +#define BDB_DISPLAY_SELECT 16 +/* 17 rsvd */ +#define BDB_DRIVER_ROTATION 18 +#define BDB_DISPLAY_REMOVE 19 +#define BDB_OEM_CUSTOM 20 +#define BDB_EFP_LIST 21 /* workarounds for VGA hsync/vsync */ +#define BDB_SDVO_LVDS_OPTIONS 22 +#define BDB_SDVO_PANEL_DTDS 23 +#define BDB_SDVO_LVDS_PNP_IDS 24 +#define BDB_SDVO_LVDS_POWER_SEQ 25 +#define BDB_TV_OPTIONS 26 +#define BDB_LVDS_OPTIONS 40 +#define BDB_LVDS_LFP_DATA_PTRS 41 +#define BDB_LVDS_LFP_DATA 42 +#define BDB_LVDS_BACKLIGHT 43 +#define BDB_LVDS_POWER 44 +#define BDB_SKIP 254 /* VBIOS private block, ignore */ + +struct bdb_general_features { + /* bits 1 */ + u8 panel_fitting:2; + u8 flexaim:1; + u8 msg_enable:1; + u8 clear_screen:3; + u8 color_flip:1; + + /* bits 2 */ + u8 download_ext_vbt:1; + u8 enable_ssc:1; + u8 ssc_freq:1; + u8 enable_lfp_on_override:1; + u8 disable_ssc_ddt:1; + u8 rsvd8:3; /* finish byte */ + + /* bits 3 */ + u8 disable_smooth_vision:1; + u8 single_dvi:1; + u8 rsvd9:6; /* finish byte */ + + /* bits 4 */ + u8 legacy_monitor_detect; + + /* bits 5 */ + u8 int_crt_support:1; + u8 int_tv_support:1; + u8 rsvd11:6; /* finish byte */ +} __attribute__((packed)); + +struct bdb_general_definitions { + /* DDC GPIO */ + u8 crt_ddc_gmbus_pin; + + /* DPMS bits */ + u8 dpms_acpi:1; + u8 skip_boot_crt_detect:1; + u8 dpms_aim:1; + u8 rsvd1:5; /* finish byte */ + + /* boot device bits */ + u8 boot_display[2]; + u8 child_dev_size; + + /* device info */ + u8 tv_or_lvds_info[33]; + u8 dev1[33]; + u8 dev2[33]; + u8 dev3[33]; + u8 dev4[33]; + /* may be another device block here on some platforms */ +}; + +struct bdb_lvds_options { + u8 panel_type; + u8 rsvd1; + /* LVDS capabilities, stored in a dword */ + u8 pfit_mode:2; + u8 pfit_text_mode_enhanced:1; + u8 pfit_gfx_mode_enhanced:1; + u8 pfit_ratio_auto:1; + u8 pixel_dither:1; + u8 lvds_edid:1; + u8 rsvd2:1; + u8 rsvd4; +} __attribute__((packed)); + +struct bdb_lvds_backlight { + u8 type:2; + u8 pol:1; + u8 gpio:3; + u8 gmbus:2; + u16 freq; + u8 minbrightness; + u8 i2caddr; + u8 brightnesscmd; + /*FIXME: more...*/ +} __attribute__((packed)); + +/* LFP pointer table contains entries to the struct below */ +struct bdb_lvds_lfp_data_ptr { + u16 fp_timing_offset; /* offsets are from start of bdb */ + u8 fp_table_size; + u16 dvo_timing_offset; + u8 dvo_table_size; + u16 panel_pnp_id_offset; + u8 pnp_table_size; +} __attribute__((packed)); + +struct bdb_lvds_lfp_data_ptrs { + u8 lvds_entries; /* followed by one or more lvds_data_ptr structs */ + struct bdb_lvds_lfp_data_ptr ptr[16]; +} __attribute__((packed)); + +/* LFP data has 3 blocks per entry */ +struct lvds_fp_timing { + u16 x_res; + u16 y_res; + u32 lvds_reg; + u32 lvds_reg_val; + u32 pp_on_reg; + u32 pp_on_reg_val; + u32 pp_off_reg; + u32 pp_off_reg_val; + u32 pp_cycle_reg; + u32 pp_cycle_reg_val; + u32 pfit_reg; + u32 pfit_reg_val; + u16 terminator; +} __attribute__((packed)); + +struct lvds_dvo_timing { + u16 clock; /**< In 10khz */ + u8 hactive_lo; + u8 hblank_lo; + u8 hblank_hi:4; + u8 hactive_hi:4; + u8 vactive_lo; + u8 vblank_lo; + u8 vblank_hi:4; + u8 vactive_hi:4; + u8 hsync_off_lo; + u8 hsync_pulse_width; + u8 vsync_pulse_width:4; + u8 vsync_off:4; + u8 rsvd0:6; + u8 hsync_off_hi:2; + u8 h_image; + u8 v_image; + u8 max_hv; + u8 h_border; + u8 v_border; + u8 rsvd1:3; + u8 digital:2; + u8 vsync_positive:1; + u8 hsync_positive:1; + u8 rsvd2:1; +} __attribute__((packed)); + +struct lvds_pnp_id { + u16 mfg_name; + u16 product_code; + u32 serial; + u8 mfg_week; + u8 mfg_year; +} __attribute__((packed)); + +struct bdb_lvds_lfp_data_entry { + struct lvds_fp_timing fp_timing; + struct lvds_dvo_timing dvo_timing; + struct lvds_pnp_id pnp_id; +} __attribute__((packed)); + +struct bdb_lvds_lfp_data { + struct bdb_lvds_lfp_data_entry data[16]; +} __attribute__((packed)); + +struct aimdb_header { + char signature[16]; + char oem_device[20]; + u16 aimdb_version; + u16 aimdb_header_size; + u16 aimdb_size; +} __attribute__((packed)); + +struct aimdb_block { + u8 aimdb_id; + u16 aimdb_size; +} __attribute__((packed)); + +struct vch_panel_data { + u16 fp_timing_offset; + u8 fp_timing_size; + u16 dvo_timing_offset; + u8 dvo_timing_size; + u16 text_fitting_offset; + u8 text_fitting_size; + u16 graphics_fitting_offset; + u8 graphics_fitting_size; +} __attribute__((packed)); + +struct vch_bdb_22 { + struct aimdb_block aimdb_block; + struct vch_panel_data panels[16]; +} __attribute__((packed)); + +struct bdb_sdvo_lvds_options { + u8 panel_backlight; + u8 h40_set_panel_type; + u8 panel_type; + u8 ssc_clk_freq; + u16 als_low_trip; + u16 als_high_trip; + u8 sclalarcoeff_tab_row_num; + u8 sclalarcoeff_tab_row_size; + u8 coefficient[8]; + u8 panel_misc_bits_1; + u8 panel_misc_bits_2; + u8 panel_misc_bits_3; + u8 panel_misc_bits_4; +} __attribute__((packed)); + + +extern bool psb_intel_init_bios(struct drm_device *dev); +extern void psb_intel_destroy_bios(struct drm_device *dev); + +/* + * Driver<->VBIOS interaction occurs through scratch bits in + * GR18 & SWF*. + */ + +/* GR18 bits are set on display switch and hotkey events */ +#define GR18_DRIVER_SWITCH_EN (1<<7) /* 0: VBIOS control, 1: driver control */ +#define GR18_HOTKEY_MASK 0x78 /* See also SWF4 15:0 */ +#define GR18_HK_NONE (0x0<<3) +#define GR18_HK_LFP_STRETCH (0x1<<3) +#define GR18_HK_TOGGLE_DISP (0x2<<3) +#define GR18_HK_DISP_SWITCH (0x4<<3) /* see SWF14 15:0 for what to enable */ +#define GR18_HK_POPUP_DISABLED (0x6<<3) +#define GR18_HK_POPUP_ENABLED (0x7<<3) +#define GR18_HK_PFIT (0x8<<3) +#define GR18_HK_APM_CHANGE (0xa<<3) +#define GR18_HK_MULTIPLE (0xc<<3) +#define GR18_USER_INT_EN (1<<2) +#define GR18_A0000_FLUSH_EN (1<<1) +#define GR18_SMM_EN (1<<0) + +/* Set by driver, cleared by VBIOS */ +#define SWF00_YRES_SHIFT 16 +#define SWF00_XRES_SHIFT 0 +#define SWF00_RES_MASK 0xffff + +/* Set by VBIOS at boot time and driver at runtime */ +#define SWF01_TV2_FORMAT_SHIFT 8 +#define SWF01_TV1_FORMAT_SHIFT 0 +#define SWF01_TV_FORMAT_MASK 0xffff + +#define SWF10_VBIOS_BLC_I2C_EN (1<<29) +#define SWF10_GTT_OVERRIDE_EN (1<<28) +#define SWF10_LFP_DPMS_OVR (1<<27) /* override DPMS on display switch */ +#define SWF10_ACTIVE_TOGGLE_LIST_MASK (7<<24) +#define SWF10_OLD_TOGGLE 0x0 +#define SWF10_TOGGLE_LIST_1 0x1 +#define SWF10_TOGGLE_LIST_2 0x2 +#define SWF10_TOGGLE_LIST_3 0x3 +#define SWF10_TOGGLE_LIST_4 0x4 +#define SWF10_PANNING_EN (1<<23) +#define SWF10_DRIVER_LOADED (1<<22) +#define SWF10_EXTENDED_DESKTOP (1<<21) +#define SWF10_EXCLUSIVE_MODE (1<<20) +#define SWF10_OVERLAY_EN (1<<19) +#define SWF10_PLANEB_HOLDOFF (1<<18) +#define SWF10_PLANEA_HOLDOFF (1<<17) +#define SWF10_VGA_HOLDOFF (1<<16) +#define SWF10_ACTIVE_DISP_MASK 0xffff +#define SWF10_PIPEB_LFP2 (1<<15) +#define SWF10_PIPEB_EFP2 (1<<14) +#define SWF10_PIPEB_TV2 (1<<13) +#define SWF10_PIPEB_CRT2 (1<<12) +#define SWF10_PIPEB_LFP (1<<11) +#define SWF10_PIPEB_EFP (1<<10) +#define SWF10_PIPEB_TV (1<<9) +#define SWF10_PIPEB_CRT (1<<8) +#define SWF10_PIPEA_LFP2 (1<<7) +#define SWF10_PIPEA_EFP2 (1<<6) +#define SWF10_PIPEA_TV2 (1<<5) +#define SWF10_PIPEA_CRT2 (1<<4) +#define SWF10_PIPEA_LFP (1<<3) +#define SWF10_PIPEA_EFP (1<<2) +#define SWF10_PIPEA_TV (1<<1) +#define SWF10_PIPEA_CRT (1<<0) + +#define SWF11_MEMORY_SIZE_SHIFT 16 +#define SWF11_SV_TEST_EN (1<<15) +#define SWF11_IS_AGP (1<<14) +#define SWF11_DISPLAY_HOLDOFF (1<<13) +#define SWF11_DPMS_REDUCED (1<<12) +#define SWF11_IS_VBE_MODE (1<<11) +#define SWF11_PIPEB_ACCESS (1<<10) /* 0 here means pipe a */ +#define SWF11_DPMS_MASK 0x07 +#define SWF11_DPMS_OFF (1<<2) +#define SWF11_DPMS_SUSPEND (1<<1) +#define SWF11_DPMS_STANDBY (1<<0) +#define SWF11_DPMS_ON 0 + +#define SWF14_GFX_PFIT_EN (1<<31) +#define SWF14_TEXT_PFIT_EN (1<<30) +#define SWF14_LID_STATUS_CLOSED (1<<29) /* 0 here means open */ +#define SWF14_POPUP_EN (1<<28) +#define SWF14_DISPLAY_HOLDOFF (1<<27) +#define SWF14_DISP_DETECT_EN (1<<26) +#define SWF14_DOCKING_STATUS_DOCKED (1<<25) /* 0 here means undocked */ +#define SWF14_DRIVER_STATUS (1<<24) +#define SWF14_OS_TYPE_WIN9X (1<<23) +#define SWF14_OS_TYPE_WINNT (1<<22) +/* 21:19 rsvd */ +#define SWF14_PM_TYPE_MASK 0x00070000 +#define SWF14_PM_ACPI_VIDEO (0x4 << 16) +#define SWF14_PM_ACPI (0x3 << 16) +#define SWF14_PM_APM_12 (0x2 << 16) +#define SWF14_PM_APM_11 (0x1 << 16) +#define SWF14_HK_REQUEST_MASK 0x0000ffff /* see GR18 6:3 for event type */ + /* if GR18 indicates a display switch */ +#define SWF14_DS_PIPEB_LFP2_EN (1<<15) +#define SWF14_DS_PIPEB_EFP2_EN (1<<14) +#define SWF14_DS_PIPEB_TV2_EN (1<<13) +#define SWF14_DS_PIPEB_CRT2_EN (1<<12) +#define SWF14_DS_PIPEB_LFP_EN (1<<11) +#define SWF14_DS_PIPEB_EFP_EN (1<<10) +#define SWF14_DS_PIPEB_TV_EN (1<<9) +#define SWF14_DS_PIPEB_CRT_EN (1<<8) +#define SWF14_DS_PIPEA_LFP2_EN (1<<7) +#define SWF14_DS_PIPEA_EFP2_EN (1<<6) +#define SWF14_DS_PIPEA_TV2_EN (1<<5) +#define SWF14_DS_PIPEA_CRT2_EN (1<<4) +#define SWF14_DS_PIPEA_LFP_EN (1<<3) +#define SWF14_DS_PIPEA_EFP_EN (1<<2) +#define SWF14_DS_PIPEA_TV_EN (1<<1) +#define SWF14_DS_PIPEA_CRT_EN (1<<0) + /* if GR18 indicates a panel fitting request */ +#define SWF14_PFIT_EN (1<<0) /* 0 means disable */ + /* if GR18 indicates an APM change request */ +#define SWF14_APM_HIBERNATE 0x4 +#define SWF14_APM_SUSPEND 0x3 +#define SWF14_APM_STANDBY 0x1 +#define SWF14_APM_RESTORE 0x0 + +#endif /* _I830_BIOS_H_ */ diff --git a/trunk/drivers/staging/gma500/intel_i2c.c b/trunk/drivers/staging/gma500/intel_i2c.c new file mode 100644 index 000000000000..51cbf65268e6 --- /dev/null +++ b/trunk/drivers/staging/gma500/intel_i2c.c @@ -0,0 +1,170 @@ +/* + * Copyright © 2006-2007 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * 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. + * + * Authors: + * Eric Anholt + */ + +#include +#include +#include + +#include "psb_drv.h" +#include "psb_intel_reg.h" + +/* + * Intel GPIO access functions + */ + +#define I2C_RISEFALL_TIME 20 + +static int get_clock(void *data) +{ + struct psb_intel_i2c_chan *chan = data; + struct drm_device *dev = chan->drm_dev; + u32 val; + + val = REG_READ(chan->reg); + return (val & GPIO_CLOCK_VAL_IN) != 0; +} + +static int get_data(void *data) +{ + struct psb_intel_i2c_chan *chan = data; + struct drm_device *dev = chan->drm_dev; + u32 val; + + val = REG_READ(chan->reg); + return (val & GPIO_DATA_VAL_IN) != 0; +} + +static void set_clock(void *data, int state_high) +{ + struct psb_intel_i2c_chan *chan = data; + struct drm_device *dev = chan->drm_dev; + u32 reserved = 0, clock_bits; + + /* On most chips, these bits must be preserved in software. */ + reserved = + REG_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE | + GPIO_CLOCK_PULLUP_DISABLE); + + if (state_high) + clock_bits = GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK; + else + clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK | + GPIO_CLOCK_VAL_MASK; + REG_WRITE(chan->reg, reserved | clock_bits); + udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */ +} + +static void set_data(void *data, int state_high) +{ + struct psb_intel_i2c_chan *chan = data; + struct drm_device *dev = chan->drm_dev; + u32 reserved = 0, data_bits; + + /* On most chips, these bits must be preserved in software. */ + reserved = + REG_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE | + GPIO_CLOCK_PULLUP_DISABLE); + + if (state_high) + data_bits = GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK; + else + data_bits = + GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK | + GPIO_DATA_VAL_MASK; + + REG_WRITE(chan->reg, reserved | data_bits); + udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */ +} + +/** + * psb_intel_i2c_create - instantiate an Intel i2c bus using the specified GPIO reg + * @dev: DRM device + * @output: driver specific output device + * @reg: GPIO reg to use + * @name: name for this bus + * + * Creates and registers a new i2c bus with the Linux i2c layer, for use + * in output probing and control (e.g. DDC or SDVO control functions). + * + * Possible values for @reg include: + * %GPIOA + * %GPIOB + * %GPIOC + * %GPIOD + * %GPIOE + * %GPIOF + * %GPIOG + * %GPIOH + * see PRM for details on how these different busses are used. + */ +struct psb_intel_i2c_chan *psb_intel_i2c_create(struct drm_device *dev, + const u32 reg, const char *name) +{ + struct psb_intel_i2c_chan *chan; + + chan = kzalloc(sizeof(struct psb_intel_i2c_chan), GFP_KERNEL); + if (!chan) + goto out_free; + + chan->drm_dev = dev; + chan->reg = reg; + snprintf(chan->adapter.name, I2C_NAME_SIZE, "intel drm %s", name); + chan->adapter.owner = THIS_MODULE; + chan->adapter.algo_data = &chan->algo; + chan->adapter.dev.parent = &dev->pdev->dev; + chan->algo.setsda = set_data; + chan->algo.setscl = set_clock; + chan->algo.getsda = get_data; + chan->algo.getscl = get_clock; + chan->algo.udelay = 20; + chan->algo.timeout = usecs_to_jiffies(2200); + chan->algo.data = chan; + + i2c_set_adapdata(&chan->adapter, chan); + + if (i2c_bit_add_bus(&chan->adapter)) + goto out_free; + + /* JJJ: raise SCL and SDA? */ + set_data(chan, 1); + set_clock(chan, 1); + udelay(20); + + return chan; + +out_free: + kfree(chan); + return NULL; +} + +/** + * psb_intel_i2c_destroy - unregister and free i2c bus resources + * @output: channel to free + * + * Unregister the adapter from the i2c layer, then free the structure. + */ +void psb_intel_i2c_destroy(struct psb_intel_i2c_chan *chan) +{ + if (!chan) + return; + + i2c_del_adapter(&chan->adapter); + kfree(chan); +} diff --git a/trunk/drivers/staging/gma500/intel_opregion.c b/trunk/drivers/staging/gma500/intel_opregion.c new file mode 100644 index 000000000000..d946bc1b17bf --- /dev/null +++ b/trunk/drivers/staging/gma500/intel_opregion.c @@ -0,0 +1,81 @@ +/* + * Copyright 2010 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. + * + * FIXME: resolve with the i915 version + */ + +#include "psb_drv.h" + +struct opregion_header { + u8 signature[16]; + u32 size; + u32 opregion_ver; + u8 bios_ver[32]; + u8 vbios_ver[16]; + u8 driver_ver[16]; + u32 mboxes; + u8 reserved[164]; +} __packed; + +struct opregion_apci { + /*FIXME: add it later*/ +} __packed; + +struct opregion_swsci { + /*FIXME: add it later*/ +} __packed; + +struct opregion_acpi { + /*FIXME: add it later*/ +} __packed; + +int gma_intel_opregion_init(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + u32 opregion_phy; + void *base; + u32 *lid_state; + + dev_priv->lid_state = NULL; + + pci_read_config_dword(dev->pdev, 0xfc, &opregion_phy); + if (opregion_phy == 0) + return -ENOTSUPP; + + base = ioremap(opregion_phy, 8*1024); + if (!base) + return -ENOMEM; + + lid_state = base + 0x01ac; + + dev_priv->lid_state = lid_state; + dev_priv->lid_last_state = readl(lid_state); + return 0; +} + +int gma_intel_opregion_exit(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + if (dev_priv->lid_state) + iounmap(dev_priv->lid_state); + return 0; +} diff --git a/trunk/drivers/staging/gma500/mdfld_device.c b/trunk/drivers/staging/gma500/mdfld_device.c new file mode 100644 index 000000000000..f47aeb7a2039 --- /dev/null +++ b/trunk/drivers/staging/gma500/mdfld_device.c @@ -0,0 +1,714 @@ +/************************************************************************** + * 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 +#include +#include +#include "psb_reg.h" +#include "psb_intel_reg.h" +#include "psb_drm.h" +#include "psb_drv.h" +#include "mdfld_output.h" +#include "mdfld_dsi_output.h" +#include "mid_bios.h" + +/* + * Provide the Medfield specific backlight management + */ + +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + +static int mdfld_brightness; +struct backlight_device *mdfld_backlight_device; + +static int mfld_set_brightness(struct backlight_device *bd) +{ + struct drm_device *dev = bl_get_data(mdfld_backlight_device); + struct drm_psb_private *dev_priv = dev->dev_private; + int level = bd->props.brightness; + + /* Percentage 1-100% being valid */ + if (level < 1) + level = 1; + + if (gma_power_begin(dev, 0)) { + /* Calculate and set the brightness value */ + u32 adjusted_level; + + /* Adjust the backlight level with the percent in + * dev_priv->blc_adj2; + */ + adjusted_level = level * dev_priv->blc_adj2; + adjusted_level = adjusted_level / 100; +#if 0 +#ifndef CONFIG_MDFLD_DSI_DPU + if(!(dev_priv->dsr_fb_update & MDFLD_DSR_MIPI_CONTROL) && + (dev_priv->dbi_panel_on || dev_priv->dbi_panel_on2)){ + mdfld_dsi_dbi_exit_dsr(dev,MDFLD_DSR_MIPI_CONTROL, 0, 0); + dev_dbg(dev->dev, "Out of DSR before set brightness to %d.\n",adjusted_level); + } +#endif + mdfld_dsi_brightness_control(dev, 0, adjusted_level); + + if ((dev_priv->dbi_panel_on2) || (dev_priv->dpi_panel_on2)) + mdfld_dsi_brightness_control(dev, 2, adjusted_level); +#endif + gma_power_end(dev); + } + mdfld_brightness = level; + return 0; +} + +int psb_get_brightness(struct backlight_device *bd) +{ + /* return locally cached var instead of HW read (due to DPST etc.) */ + /* FIXME: ideally return actual value in case firmware fiddled with + it */ + return mdfld_brightness; +} + +static const struct backlight_ops mfld_ops = { + .get_brightness = psb_get_brightness, + .update_status = mfld_set_brightness, +}; + +static int mdfld_backlight_init(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct backlight_properties props; + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = 100; + props.type = BACKLIGHT_PLATFORM; + + mdfld_backlight_device = backlight_device_register("mfld-bl", + NULL, (void *)dev, &mfld_ops, &props); + + if (IS_ERR(mdfld_backlight_device)) + return PTR_ERR(mdfld_backlight_device); + + dev_priv->blc_adj1 = 100; + dev_priv->blc_adj2 = 100; + mdfld_backlight_device->props.brightness = 100; + mdfld_backlight_device->props.max_brightness = 100; + backlight_update_status(mdfld_backlight_device); + dev_priv->backlight_device = mdfld_backlight_device; + return 0; +} + +#endif + +/* + * Provide the Medfield specific chip logic and low level methods for + * power management. + */ + +static void mdfld_init_pm(struct drm_device *dev) +{ + /* No work needed here yet */ +} + +/** + * mdfld_save_display_registers - save registers for pipe + * @dev: our device + * @pipe: pipe to save + * + * Save the pipe state of the device before we power it off. Keep everything + * we need to put it back again + */ +static int mdfld_save_display_registers(struct drm_device *dev, int pipe) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + int i; + + /* register */ + u32 dpll_reg = MRST_DPLL_A; + u32 fp_reg = MRST_FPA0; + u32 pipeconf_reg = PIPEACONF; + u32 htot_reg = HTOTAL_A; + u32 hblank_reg = HBLANK_A; + u32 hsync_reg = HSYNC_A; + u32 vtot_reg = VTOTAL_A; + u32 vblank_reg = VBLANK_A; + u32 vsync_reg = VSYNC_A; + u32 pipesrc_reg = PIPEASRC; + u32 dspstride_reg = DSPASTRIDE; + u32 dsplinoff_reg = DSPALINOFF; + u32 dsptileoff_reg = DSPATILEOFF; + u32 dspsize_reg = DSPASIZE; + u32 dsppos_reg = DSPAPOS; + u32 dspsurf_reg = DSPASURF; + u32 mipi_reg = MIPI; + u32 dspcntr_reg = DSPACNTR; + u32 dspstatus_reg = PIPEASTAT; + u32 palette_reg = PALETTE_A; + + /* pointer to values */ + u32 *dpll_val = &dev_priv->saveDPLL_A; + u32 *fp_val = &dev_priv->saveFPA0; + u32 *pipeconf_val = &dev_priv->savePIPEACONF; + u32 *htot_val = &dev_priv->saveHTOTAL_A; + u32 *hblank_val = &dev_priv->saveHBLANK_A; + u32 *hsync_val = &dev_priv->saveHSYNC_A; + u32 *vtot_val = &dev_priv->saveVTOTAL_A; + u32 *vblank_val = &dev_priv->saveVBLANK_A; + u32 *vsync_val = &dev_priv->saveVSYNC_A; + u32 *pipesrc_val = &dev_priv->savePIPEASRC; + u32 *dspstride_val = &dev_priv->saveDSPASTRIDE; + u32 *dsplinoff_val = &dev_priv->saveDSPALINOFF; + u32 *dsptileoff_val = &dev_priv->saveDSPATILEOFF; + u32 *dspsize_val = &dev_priv->saveDSPASIZE; + u32 *dsppos_val = &dev_priv->saveDSPAPOS; + u32 *dspsurf_val = &dev_priv->saveDSPASURF; + u32 *mipi_val = &dev_priv->saveMIPI; + u32 *dspcntr_val = &dev_priv->saveDSPACNTR; + u32 *dspstatus_val = &dev_priv->saveDSPASTATUS; + u32 *palette_val = dev_priv->save_palette_a; + + switch (pipe) { + case 0: + break; + case 1: + /* register */ + dpll_reg = MDFLD_DPLL_B; + fp_reg = MDFLD_DPLL_DIV0; + pipeconf_reg = PIPEBCONF; + htot_reg = HTOTAL_B; + hblank_reg = HBLANK_B; + hsync_reg = HSYNC_B; + vtot_reg = VTOTAL_B; + vblank_reg = VBLANK_B; + vsync_reg = VSYNC_B; + pipesrc_reg = PIPEBSRC; + dspstride_reg = DSPBSTRIDE; + dsplinoff_reg = DSPBLINOFF; + dsptileoff_reg = DSPBTILEOFF; + dspsize_reg = DSPBSIZE; + dsppos_reg = DSPBPOS; + dspsurf_reg = DSPBSURF; + dspcntr_reg = DSPBCNTR; + dspstatus_reg = PIPEBSTAT; + palette_reg = PALETTE_B; + + /* values */ + dpll_val = &dev_priv->saveDPLL_B; + fp_val = &dev_priv->saveFPB0; + pipeconf_val = &dev_priv->savePIPEBCONF; + htot_val = &dev_priv->saveHTOTAL_B; + hblank_val = &dev_priv->saveHBLANK_B; + hsync_val = &dev_priv->saveHSYNC_B; + vtot_val = &dev_priv->saveVTOTAL_B; + vblank_val = &dev_priv->saveVBLANK_B; + vsync_val = &dev_priv->saveVSYNC_B; + pipesrc_val = &dev_priv->savePIPEBSRC; + dspstride_val = &dev_priv->saveDSPBSTRIDE; + dsplinoff_val = &dev_priv->saveDSPBLINOFF; + dsptileoff_val = &dev_priv->saveDSPBTILEOFF; + dspsize_val = &dev_priv->saveDSPBSIZE; + dsppos_val = &dev_priv->saveDSPBPOS; + dspsurf_val = &dev_priv->saveDSPBSURF; + dspcntr_val = &dev_priv->saveDSPBCNTR; + dspstatus_val = &dev_priv->saveDSPBSTATUS; + palette_val = dev_priv->save_palette_b; + break; + case 2: + /* register */ + pipeconf_reg = PIPECCONF; + htot_reg = HTOTAL_C; + hblank_reg = HBLANK_C; + hsync_reg = HSYNC_C; + vtot_reg = VTOTAL_C; + vblank_reg = VBLANK_C; + vsync_reg = VSYNC_C; + pipesrc_reg = PIPECSRC; + dspstride_reg = DSPCSTRIDE; + dsplinoff_reg = DSPCLINOFF; + dsptileoff_reg = DSPCTILEOFF; + dspsize_reg = DSPCSIZE; + dsppos_reg = DSPCPOS; + dspsurf_reg = DSPCSURF; + mipi_reg = MIPI_C; + dspcntr_reg = DSPCCNTR; + dspstatus_reg = PIPECSTAT; + palette_reg = PALETTE_C; + + /* pointer to values */ + pipeconf_val = &dev_priv->savePIPECCONF; + htot_val = &dev_priv->saveHTOTAL_C; + hblank_val = &dev_priv->saveHBLANK_C; + hsync_val = &dev_priv->saveHSYNC_C; + vtot_val = &dev_priv->saveVTOTAL_C; + vblank_val = &dev_priv->saveVBLANK_C; + vsync_val = &dev_priv->saveVSYNC_C; + pipesrc_val = &dev_priv->savePIPECSRC; + dspstride_val = &dev_priv->saveDSPCSTRIDE; + dsplinoff_val = &dev_priv->saveDSPCLINOFF; + dsptileoff_val = &dev_priv->saveDSPCTILEOFF; + dspsize_val = &dev_priv->saveDSPCSIZE; + dsppos_val = &dev_priv->saveDSPCPOS; + dspsurf_val = &dev_priv->saveDSPCSURF; + mipi_val = &dev_priv->saveMIPI_C; + dspcntr_val = &dev_priv->saveDSPCCNTR; + dspstatus_val = &dev_priv->saveDSPCSTATUS; + palette_val = dev_priv->save_palette_c; + break; + default: + DRM_ERROR("%s, invalid pipe number.\n", __func__); + return -EINVAL; + } + + /* Pipe & plane A info */ + *dpll_val = PSB_RVDC32(dpll_reg); + *fp_val = PSB_RVDC32(fp_reg); + *pipeconf_val = PSB_RVDC32(pipeconf_reg); + *htot_val = PSB_RVDC32(htot_reg); + *hblank_val = PSB_RVDC32(hblank_reg); + *hsync_val = PSB_RVDC32(hsync_reg); + *vtot_val = PSB_RVDC32(vtot_reg); + *vblank_val = PSB_RVDC32(vblank_reg); + *vsync_val = PSB_RVDC32(vsync_reg); + *pipesrc_val = PSB_RVDC32(pipesrc_reg); + *dspstride_val = PSB_RVDC32(dspstride_reg); + *dsplinoff_val = PSB_RVDC32(dsplinoff_reg); + *dsptileoff_val = PSB_RVDC32(dsptileoff_reg); + *dspsize_val = PSB_RVDC32(dspsize_reg); + *dsppos_val = PSB_RVDC32(dsppos_reg); + *dspsurf_val = PSB_RVDC32(dspsurf_reg); + *dspcntr_val = PSB_RVDC32(dspcntr_reg); + *dspstatus_val = PSB_RVDC32(dspstatus_reg); + + /*save palette (gamma) */ + for (i = 0; i < 256; i++) + palette_val[i] = PSB_RVDC32(palette_reg + (i<<2)); + + if (pipe == 1) { + dev_priv->savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL); + dev_priv->savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS); + dev_priv->saveHDMIPHYMISCCTL = PSB_RVDC32(HDMIPHYMISCCTL); + dev_priv->saveHDMIB_CONTROL = PSB_RVDC32(HDMIB_CONTROL); + return 0; + } + *mipi_val = PSB_RVDC32(mipi_reg); + return 0; +} + +/** + * mdfld_save_cursor_overlay_registers - save cursor overlay info + * @dev: our device + * + * Save the cursor and overlay register state + */ +static int mdfld_save_cursor_overlay_registers(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + + /* Save cursor regs */ + dev_priv->saveDSPACURSOR_CTRL = PSB_RVDC32(CURACNTR); + dev_priv->saveDSPACURSOR_BASE = PSB_RVDC32(CURABASE); + dev_priv->saveDSPACURSOR_POS = PSB_RVDC32(CURAPOS); + + dev_priv->saveDSPBCURSOR_CTRL = PSB_RVDC32(CURBCNTR); + dev_priv->saveDSPBCURSOR_BASE = PSB_RVDC32(CURBBASE); + dev_priv->saveDSPBCURSOR_POS = PSB_RVDC32(CURBPOS); + + dev_priv->saveDSPCCURSOR_CTRL = PSB_RVDC32(CURCCNTR); + dev_priv->saveDSPCCURSOR_BASE = PSB_RVDC32(CURCBASE); + dev_priv->saveDSPCCURSOR_POS = PSB_RVDC32(CURCPOS); + + /* HW overlay */ + dev_priv->saveOV_OVADD = PSB_RVDC32(OV_OVADD); + dev_priv->saveOV_OGAMC0 = PSB_RVDC32(OV_OGAMC0); + dev_priv->saveOV_OGAMC1 = PSB_RVDC32(OV_OGAMC1); + dev_priv->saveOV_OGAMC2 = PSB_RVDC32(OV_OGAMC2); + dev_priv->saveOV_OGAMC3 = PSB_RVDC32(OV_OGAMC3); + dev_priv->saveOV_OGAMC4 = PSB_RVDC32(OV_OGAMC4); + dev_priv->saveOV_OGAMC5 = PSB_RVDC32(OV_OGAMC5); + + dev_priv->saveOV_OVADD_C = PSB_RVDC32(OV_OVADD + OV_C_OFFSET); + dev_priv->saveOV_OGAMC0_C = PSB_RVDC32(OV_OGAMC0 + OV_C_OFFSET); + dev_priv->saveOV_OGAMC1_C = PSB_RVDC32(OV_OGAMC1 + OV_C_OFFSET); + dev_priv->saveOV_OGAMC2_C = PSB_RVDC32(OV_OGAMC2 + OV_C_OFFSET); + dev_priv->saveOV_OGAMC3_C = PSB_RVDC32(OV_OGAMC3 + OV_C_OFFSET); + dev_priv->saveOV_OGAMC4_C = PSB_RVDC32(OV_OGAMC4 + OV_C_OFFSET); + dev_priv->saveOV_OGAMC5_C = PSB_RVDC32(OV_OGAMC5 + OV_C_OFFSET); + + return 0; +} +/* + * mdfld_restore_display_registers - restore the state of a pipe + * @dev: our device + * @pipe: the pipe to restore + * + * Restore the state of a pipe to that which was saved by the register save + * functions. + */ +static int mdfld_restore_display_registers(struct drm_device *dev, int pipe) +{ + /* To get panel out of ULPS mode */ + struct drm_psb_private *dev_priv = dev->dev_private; + struct mdfld_dsi_config *dsi_config = NULL; + u32 i = 0; + u32 dpll = 0; + u32 timeout = 0; + u32 reg_offset = 0; + + /* register */ + u32 dpll_reg = MRST_DPLL_A; + u32 fp_reg = MRST_FPA0; + u32 pipeconf_reg = PIPEACONF; + u32 htot_reg = HTOTAL_A; + u32 hblank_reg = HBLANK_A; + u32 hsync_reg = HSYNC_A; + u32 vtot_reg = VTOTAL_A; + u32 vblank_reg = VBLANK_A; + u32 vsync_reg = VSYNC_A; + u32 pipesrc_reg = PIPEASRC; + u32 dspstride_reg = DSPASTRIDE; + u32 dsplinoff_reg = DSPALINOFF; + u32 dsptileoff_reg = DSPATILEOFF; + u32 dspsize_reg = DSPASIZE; + u32 dsppos_reg = DSPAPOS; + u32 dspsurf_reg = DSPASURF; + u32 dspstatus_reg = PIPEASTAT; + u32 mipi_reg = MIPI; + u32 dspcntr_reg = DSPACNTR; + u32 palette_reg = PALETTE_A; + + /* values */ + u32 dpll_val = dev_priv->saveDPLL_A & ~DPLL_VCO_ENABLE; + u32 fp_val = dev_priv->saveFPA0; + u32 pipeconf_val = dev_priv->savePIPEACONF; + u32 htot_val = dev_priv->saveHTOTAL_A; + u32 hblank_val = dev_priv->saveHBLANK_A; + u32 hsync_val = dev_priv->saveHSYNC_A; + u32 vtot_val = dev_priv->saveVTOTAL_A; + u32 vblank_val = dev_priv->saveVBLANK_A; + u32 vsync_val = dev_priv->saveVSYNC_A; + u32 pipesrc_val = dev_priv->savePIPEASRC; + u32 dspstride_val = dev_priv->saveDSPASTRIDE; + u32 dsplinoff_val = dev_priv->saveDSPALINOFF; + u32 dsptileoff_val = dev_priv->saveDSPATILEOFF; + u32 dspsize_val = dev_priv->saveDSPASIZE; + u32 dsppos_val = dev_priv->saveDSPAPOS; + u32 dspsurf_val = dev_priv->saveDSPASURF; + u32 dspstatus_val = dev_priv->saveDSPASTATUS; + u32 mipi_val = dev_priv->saveMIPI; + u32 dspcntr_val = dev_priv->saveDSPACNTR; + u32 *palette_val = dev_priv->save_palette_a; + + switch (pipe) { + case 0: + dsi_config = dev_priv->dsi_configs[0]; + break; + case 1: + /* register */ + dpll_reg = MDFLD_DPLL_B; + fp_reg = MDFLD_DPLL_DIV0; + pipeconf_reg = PIPEBCONF; + htot_reg = HTOTAL_B; + hblank_reg = HBLANK_B; + hsync_reg = HSYNC_B; + vtot_reg = VTOTAL_B; + vblank_reg = VBLANK_B; + vsync_reg = VSYNC_B; + pipesrc_reg = PIPEBSRC; + dspstride_reg = DSPBSTRIDE; + dsplinoff_reg = DSPBLINOFF; + dsptileoff_reg = DSPBTILEOFF; + dspsize_reg = DSPBSIZE; + dsppos_reg = DSPBPOS; + dspsurf_reg = DSPBSURF; + dspcntr_reg = DSPBCNTR; + palette_reg = PALETTE_B; + dspstatus_reg = PIPEBSTAT; + + /* values */ + dpll_val = dev_priv->saveDPLL_B & ~DPLL_VCO_ENABLE; + fp_val = dev_priv->saveFPB0; + pipeconf_val = dev_priv->savePIPEBCONF; + htot_val = dev_priv->saveHTOTAL_B; + hblank_val = dev_priv->saveHBLANK_B; + hsync_val = dev_priv->saveHSYNC_B; + vtot_val = dev_priv->saveVTOTAL_B; + vblank_val = dev_priv->saveVBLANK_B; + vsync_val = dev_priv->saveVSYNC_B; + pipesrc_val = dev_priv->savePIPEBSRC; + dspstride_val = dev_priv->saveDSPBSTRIDE; + dsplinoff_val = dev_priv->saveDSPBLINOFF; + dsptileoff_val = dev_priv->saveDSPBTILEOFF; + dspsize_val = dev_priv->saveDSPBSIZE; + dsppos_val = dev_priv->saveDSPBPOS; + dspsurf_val = dev_priv->saveDSPBSURF; + dspcntr_val = dev_priv->saveDSPBCNTR; + dspstatus_val = dev_priv->saveDSPBSTATUS; + palette_val = dev_priv->save_palette_b; + break; + case 2: + reg_offset = MIPIC_REG_OFFSET; + + /* register */ + pipeconf_reg = PIPECCONF; + htot_reg = HTOTAL_C; + hblank_reg = HBLANK_C; + hsync_reg = HSYNC_C; + vtot_reg = VTOTAL_C; + vblank_reg = VBLANK_C; + vsync_reg = VSYNC_C; + pipesrc_reg = PIPECSRC; + dspstride_reg = DSPCSTRIDE; + dsplinoff_reg = DSPCLINOFF; + dsptileoff_reg = DSPCTILEOFF; + dspsize_reg = DSPCSIZE; + dsppos_reg = DSPCPOS; + dspsurf_reg = DSPCSURF; + mipi_reg = MIPI_C; + dspcntr_reg = DSPCCNTR; + palette_reg = PALETTE_C; + dspstatus_reg = PIPECSTAT; + + /* values */ + pipeconf_val = dev_priv->savePIPECCONF; + htot_val = dev_priv->saveHTOTAL_C; + hblank_val = dev_priv->saveHBLANK_C; + hsync_val = dev_priv->saveHSYNC_C; + vtot_val = dev_priv->saveVTOTAL_C; + vblank_val = dev_priv->saveVBLANK_C; + vsync_val = dev_priv->saveVSYNC_C; + pipesrc_val = dev_priv->savePIPECSRC; + dspstride_val = dev_priv->saveDSPCSTRIDE; + dsplinoff_val = dev_priv->saveDSPCLINOFF; + dsptileoff_val = dev_priv->saveDSPCTILEOFF; + dspsize_val = dev_priv->saveDSPCSIZE; + dsppos_val = dev_priv->saveDSPCPOS; + dspsurf_val = dev_priv->saveDSPCSURF; + dspstatus_val = dev_priv->saveDSPCSTATUS; + mipi_val = dev_priv->saveMIPI_C; + dspcntr_val = dev_priv->saveDSPCCNTR; + palette_val = dev_priv->save_palette_c; + + dsi_config = dev_priv->dsi_configs[1]; + break; + default: + DRM_ERROR("%s, invalid pipe number.\n", __func__); + return -EINVAL; + } + + /* Make sure VGA plane is off. it initializes to on after reset!*/ + PSB_WVDC32(0x80000000, VGACNTRL); + if (pipe == 1) { + PSB_WVDC32(dpll_val & ~DPLL_VCO_ENABLE, dpll_reg); + PSB_RVDC32(dpll_reg); + + PSB_WVDC32(fp_val, fp_reg); + } else { + dpll = PSB_RVDC32(dpll_reg); + + if (!(dpll & DPLL_VCO_ENABLE)) { + + /* When ungating power of DPLL, needs to wait 0.5us before enable the VCO */ + if (dpll & MDFLD_PWR_GATE_EN) { + dpll &= ~MDFLD_PWR_GATE_EN; + PSB_WVDC32(dpll, dpll_reg); + udelay(500); /* FIXME: 1 ? */ + } + + PSB_WVDC32(fp_val, fp_reg); + PSB_WVDC32(dpll_val, dpll_reg); + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(500); + + dpll_val |= DPLL_VCO_ENABLE; + PSB_WVDC32(dpll_val, dpll_reg); + PSB_RVDC32(dpll_reg); + + /* wait for DSI PLL to lock */ + while ((timeout < 20000) && !(PSB_RVDC32(pipeconf_reg) & PIPECONF_DSIPLL_LOCK)) { + udelay(150); + timeout++; + } + + if (timeout == 20000) { + DRM_ERROR("%s, can't lock DSIPLL.\n", + __func__); + return -EINVAL; + } + } + } + /* Restore mode */ + PSB_WVDC32(htot_val, htot_reg); + PSB_WVDC32(hblank_val, hblank_reg); + PSB_WVDC32(hsync_val, hsync_reg); + PSB_WVDC32(vtot_val, vtot_reg); + PSB_WVDC32(vblank_val, vblank_reg); + PSB_WVDC32(vsync_val, vsync_reg); + PSB_WVDC32(pipesrc_val, pipesrc_reg); + PSB_WVDC32(dspstatus_val, dspstatus_reg); + + /* Set up the plane */ + PSB_WVDC32(dspstride_val, dspstride_reg); + PSB_WVDC32(dsplinoff_val, dsplinoff_reg); + PSB_WVDC32(dsptileoff_val, dsptileoff_reg); + PSB_WVDC32(dspsize_val, dspsize_reg); + PSB_WVDC32(dsppos_val, dsppos_reg); + PSB_WVDC32(dspsurf_val, dspsurf_reg); + + if (pipe == 1) { + PSB_WVDC32(dev_priv->savePFIT_CONTROL, PFIT_CONTROL); + PSB_WVDC32(dev_priv->savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS); + PSB_WVDC32(dev_priv->saveHDMIPHYMISCCTL, HDMIPHYMISCCTL); + PSB_WVDC32(dev_priv->saveHDMIB_CONTROL, HDMIB_CONTROL); + + } else { + /* Set up pipe related registers */ + PSB_WVDC32(mipi_val, mipi_reg); + /* Setup MIPI adapter + MIPI IP registers */ + mdfld_dsi_controller_init(dsi_config, pipe); + msleep(20); + } + /* Enable the plane */ + PSB_WVDC32(dspcntr_val, dspcntr_reg); + msleep(20); + /* Enable the pipe */ + PSB_WVDC32(pipeconf_val, pipeconf_reg); + + for (i = 0; i < 256; i++) + PSB_WVDC32(palette_val[i], palette_reg + (i<<2)); + if (pipe == 1) + return 0; + if (!mdfld_panel_dpi(dev)) + mdfld_enable_te(dev, pipe); + return 0; +} + +/** + * mdfld_restore_cursor_overlay_registers - restore cursor + * @dev: our device + * + * Restore the cursor and overlay state that was saved earlier + */ +static int mdfld_restore_cursor_overlay_registers(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + + /* Enable Cursor A */ + PSB_WVDC32(dev_priv->saveDSPACURSOR_CTRL, CURACNTR); + PSB_WVDC32(dev_priv->saveDSPACURSOR_POS, CURAPOS); + PSB_WVDC32(dev_priv->saveDSPACURSOR_BASE, CURABASE); + + PSB_WVDC32(dev_priv->saveDSPBCURSOR_CTRL, CURBCNTR); + PSB_WVDC32(dev_priv->saveDSPBCURSOR_POS, CURBPOS); + PSB_WVDC32(dev_priv->saveDSPBCURSOR_BASE, CURBBASE); + + PSB_WVDC32(dev_priv->saveDSPCCURSOR_CTRL, CURCCNTR); + PSB_WVDC32(dev_priv->saveDSPCCURSOR_POS, CURCPOS); + PSB_WVDC32(dev_priv->saveDSPCCURSOR_BASE, CURCBASE); + + /* Restore HW overlay */ + PSB_WVDC32(dev_priv->saveOV_OVADD, OV_OVADD); + PSB_WVDC32(dev_priv->saveOV_OGAMC0, OV_OGAMC0); + PSB_WVDC32(dev_priv->saveOV_OGAMC1, OV_OGAMC1); + PSB_WVDC32(dev_priv->saveOV_OGAMC2, OV_OGAMC2); + PSB_WVDC32(dev_priv->saveOV_OGAMC3, OV_OGAMC3); + PSB_WVDC32(dev_priv->saveOV_OGAMC4, OV_OGAMC4); + PSB_WVDC32(dev_priv->saveOV_OGAMC5, OV_OGAMC5); + + PSB_WVDC32(dev_priv->saveOV_OVADD_C, OV_OVADD + OV_C_OFFSET); + PSB_WVDC32(dev_priv->saveOV_OGAMC0_C, OV_OGAMC0 + OV_C_OFFSET); + PSB_WVDC32(dev_priv->saveOV_OGAMC1_C, OV_OGAMC1 + OV_C_OFFSET); + PSB_WVDC32(dev_priv->saveOV_OGAMC2_C, OV_OGAMC2 + OV_C_OFFSET); + PSB_WVDC32(dev_priv->saveOV_OGAMC3_C, OV_OGAMC3 + OV_C_OFFSET); + PSB_WVDC32(dev_priv->saveOV_OGAMC4_C, OV_OGAMC4 + OV_C_OFFSET); + PSB_WVDC32(dev_priv->saveOV_OGAMC5_C, OV_OGAMC5 + OV_C_OFFSET); + + return 0; +} + +/** + * mdfld_save_display_registers - save registers lost on suspend + * @dev: our DRM device + * + * Save the state we need in order to be able to restore the interface + * upon resume from suspend + */ +static int mdfld_save_registers(struct drm_device *dev) +{ + /* FIXME: We need to shut down panels here if using them + and once the right bits are merged */ + mdfld_save_cursor_overlay_registers(dev); + mdfld_save_display_registers(dev, 0); + mdfld_save_display_registers(dev, 0); + mdfld_save_display_registers(dev, 2); + mdfld_save_display_registers(dev, 1); + mdfld_disable_crtc(dev, 0); + mdfld_disable_crtc(dev, 2); + mdfld_disable_crtc(dev, 1); + return 0; +} + +/** + * mdfld_restore_display_registers - restore lost register state + * @dev: our DRM device + * + * Restore register state that was lost during suspend and resume. + */ +static int mdfld_restore_registers(struct drm_device *dev) +{ + mdfld_restore_display_registers(dev, 1); + mdfld_restore_display_registers(dev, 0); + mdfld_restore_display_registers(dev, 2); + mdfld_restore_cursor_overlay_registers(dev); + return 0; +} + +static int mdfld_power_down(struct drm_device *dev) +{ + /* FIXME */ + return 0; +} + +static int mdfld_power_up(struct drm_device *dev) +{ + /* FIXME */ + return 0; +} + +const struct psb_ops mdfld_chip_ops = { + .name = "Medfield", + .accel_2d = 0, + .pipes = 3, + .crtcs = 2, + .sgx_offset = MRST_SGX_OFFSET, + + .chip_setup = mid_chip_setup, + + .crtc_helper = &mdfld_helper_funcs, + .crtc_funcs = &mdfld_intel_crtc_funcs, + + .output_init = mdfld_output_init, + +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + .backlight_init = mdfld_backlight_init, +#endif + + .init_pm = mdfld_init_pm, + .save_regs = mdfld_save_registers, + .restore_regs = mdfld_restore_registers, + .power_down = mdfld_power_down, + .power_up = mdfld_power_up, +}; + diff --git a/trunk/drivers/staging/gma500/mdfld_dsi_dbi.c b/trunk/drivers/staging/gma500/mdfld_dsi_dbi.c new file mode 100644 index 000000000000..fd211f3467c4 --- /dev/null +++ b/trunk/drivers/staging/gma500/mdfld_dsi_dbi.c @@ -0,0 +1,761 @@ +/* + * Copyright © 2010 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: + * jim liu + * Jackie Li + */ + +#include "mdfld_dsi_dbi.h" +#include "mdfld_dsi_dbi_dpu.h" +#include "mdfld_dsi_pkg_sender.h" + +#include "power.h" +#include + +int enable_gfx_rtpm; + +extern struct drm_device *gpDrmDevice; +extern int gfxrtdelay; +int enter_dsr; +struct mdfld_dsi_dbi_output *gdbi_output; +extern bool gbgfxsuspended; +extern int enable_gfx_rtpm; +extern int gfxrtdelay; + +#define MDFLD_DSR_MAX_IDLE_COUNT 2 + +/* + * set refreshing area + */ +int mdfld_dsi_dbi_update_area(struct mdfld_dsi_dbi_output *dbi_output, + u16 x1, u16 y1, u16 x2, u16 y2) +{ + struct mdfld_dsi_pkg_sender *sender = + mdfld_dsi_encoder_get_pkg_sender(&dbi_output->base); + u8 param[4]; + u8 cmd; + int err; + + if (!sender) { + WARN_ON(1); + return -EINVAL; + } + + /* Set column */ + cmd = DCS_SET_COLUMN_ADDRESS; + param[0] = x1 >> 8; + param[1] = x1; + param[2] = x2 >> 8; + param[3] = x2; + + err = mdfld_dsi_send_dcs(sender, + cmd, + param, + 4, + CMD_DATA_SRC_SYSTEM_MEM, + MDFLD_DSI_QUEUE_PACKAGE); + if (err) { + dev_err(sender->dev->dev, "DCS 0x%x sent failed\n", cmd); + goto err_out; + } + + /* Set page */ + cmd = DCS_SET_PAGE_ADDRESS; + param[0] = y1 >> 8; + param[1] = y1; + param[2] = y2 >> 8; + param[3] = y2; + + err = mdfld_dsi_send_dcs(sender, + cmd, + param, + 4, + CMD_DATA_SRC_SYSTEM_MEM, + MDFLD_DSI_QUEUE_PACKAGE); + if (err) { + dev_err(sender->dev->dev, "DCS 0x%x sent failed\n", cmd); + goto err_out; + } + + /*update screen*/ + err = mdfld_dsi_send_dcs(sender, + write_mem_start, + NULL, + 0, + CMD_DATA_SRC_PIPE, + MDFLD_DSI_QUEUE_PACKAGE); + if (err) { + dev_err(sender->dev->dev, "DCS 0x%x sent failed\n", cmd); + goto err_out; + } + mdfld_dsi_cmds_kick_out(sender); +err_out: + return err; +} + +/* + * set panel's power state + */ +int mdfld_dsi_dbi_update_power(struct mdfld_dsi_dbi_output *dbi_output, + int mode) +{ + struct drm_device *dev = dbi_output->dev; + struct mdfld_dsi_pkg_sender *sender = + mdfld_dsi_encoder_get_pkg_sender(&dbi_output->base); + u8 param = 0; + u32 err = 0; + + if (!sender) { + WARN_ON(1); + return -EINVAL; + } + + if (mode == DRM_MODE_DPMS_ON) { + /* Exit sleep mode */ + err = mdfld_dsi_send_dcs(sender, + DCS_EXIT_SLEEP_MODE, + NULL, + 0, + CMD_DATA_SRC_SYSTEM_MEM, + MDFLD_DSI_QUEUE_PACKAGE); + if (err) { + dev_err(dev->dev, "DCS 0x%x sent failed\n", + DCS_EXIT_SLEEP_MODE); + goto power_err; + } + + /* Set display on */ + err = mdfld_dsi_send_dcs(sender, + DCS_SET_DISPLAY_ON, + NULL, + 0, + CMD_DATA_SRC_SYSTEM_MEM, + MDFLD_DSI_QUEUE_PACKAGE); + if (err) { + dev_err(dev->dev, "DCS 0x%x sent failed\n", + DCS_SET_DISPLAY_ON); + goto power_err; + } + + /* set tear effect on */ + err = mdfld_dsi_send_dcs(sender, + DCS_SET_TEAR_ON, + ¶m, + 1, + CMD_DATA_SRC_SYSTEM_MEM, + MDFLD_DSI_QUEUE_PACKAGE); + if (err) { + dev_err(dev->dev, "DCS 0x%x sent failed\n", + set_tear_on); + goto power_err; + } + + /** + * FIXME: remove this later + */ + err = mdfld_dsi_send_dcs(sender, + DCS_WRITE_MEM_START, + NULL, + 0, + CMD_DATA_SRC_PIPE, + MDFLD_DSI_QUEUE_PACKAGE); + if (err) { + dev_err(dev->dev, "DCS 0x%x sent failed\n", + DCS_WRITE_MEM_START); + goto power_err; + } + } else { + /* Set tear effect off */ + err = mdfld_dsi_send_dcs(sender, + DCS_SET_TEAR_OFF, + NULL, + 0, + CMD_DATA_SRC_SYSTEM_MEM, + MDFLD_DSI_QUEUE_PACKAGE); + if (err) { + dev_err(dev->dev, "DCS 0x%x sent failed\n", + DCS_SET_TEAR_OFF); + goto power_err; + } + + /* Turn display off */ + err = mdfld_dsi_send_dcs(sender, + DCS_SET_DISPLAY_OFF, + NULL, + 0, + CMD_DATA_SRC_SYSTEM_MEM, + MDFLD_DSI_QUEUE_PACKAGE); + if (err) { + dev_err(dev->dev, "DCS 0x%x sent failed\n", + DCS_SET_DISPLAY_OFF); + goto power_err; + } + + /* Now enter sleep mode */ + err = mdfld_dsi_send_dcs(sender, + DCS_ENTER_SLEEP_MODE, + NULL, + 0, + CMD_DATA_SRC_SYSTEM_MEM, + MDFLD_DSI_QUEUE_PACKAGE); + if (err) { + dev_err(dev->dev, "DCS 0x%x sent failed\n", + DCS_ENTER_SLEEP_MODE); + goto power_err; + } + } + mdfld_dsi_cmds_kick_out(sender); +power_err: + return err; +} + +/* + * send a generic DCS command with a parameter list + */ +int mdfld_dsi_dbi_send_dcs(struct mdfld_dsi_dbi_output *dbi_output, + u8 dcs, u8 *param, u32 num, u8 data_src) +{ + struct mdfld_dsi_pkg_sender *sender = + mdfld_dsi_encoder_get_pkg_sender(&dbi_output->base); + int ret; + + if (!sender) { + WARN_ON(1); + return -EINVAL; + } + + ret = mdfld_dsi_send_dcs(sender, + dcs, + param, + num, + data_src, + MDFLD_DSI_SEND_PACKAGE); + + return ret; +} + +/* + * Enter DSR + */ +void mdfld_dsi_dbi_enter_dsr(struct mdfld_dsi_dbi_output *dbi_output, int pipe) +{ + u32 reg_val; + struct drm_device *dev = dbi_output->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc = dbi_output->base.base.crtc; + struct psb_intel_crtc *psb_crtc = (crtc) ? + to_psb_intel_crtc(crtc) : NULL; + u32 dpll_reg = MRST_DPLL_A; + u32 pipeconf_reg = PIPEACONF; + u32 dspcntr_reg = DSPACNTR; + + if (!dbi_output) + return; + + /* FIXME check if can go */ + dev_priv->is_in_idle = true; + + gdbi_output = dbi_output; + if ((dbi_output->mode_flags & MODE_SETTING_ON_GOING) || + (psb_crtc && psb_crtc->mode_flags & MODE_SETTING_ON_GOING)) + return; + + if (pipe == 2) { + dpll_reg = MRST_DPLL_A; + pipeconf_reg = PIPECCONF; + dspcntr_reg = DSPCCNTR; + } + + if (!gma_power_begin(dev, true)) { + dev_err(dev->dev, "hw begin failed\n"); + return; + } + /* Disable te interrupts */ + mdfld_disable_te(dev, pipe); + + /* Disable plane */ + reg_val = REG_READ(dspcntr_reg); + if (!(reg_val & DISPLAY_PLANE_ENABLE)) { + REG_WRITE(dspcntr_reg, reg_val & ~DISPLAY_PLANE_ENABLE); + REG_READ(dspcntr_reg); + } + + /* Disable pipe */ + reg_val = REG_READ(pipeconf_reg); + if (!(reg_val & DISPLAY_PLANE_ENABLE)) { + reg_val &= ~DISPLAY_PLANE_ENABLE; + reg_val |= (PIPECONF_PLANE_OFF | PIPECONF_CURSOR_OFF); + REG_WRITE(pipeconf_reg, reg_val); + REG_READ(pipeconf_reg); + mdfldWaitForPipeDisable(dev, pipe); + } + + /* Disable DPLL */ + reg_val = REG_READ(dpll_reg); + if (!(reg_val & DPLL_VCO_ENABLE)) { + reg_val &= ~DPLL_VCO_ENABLE; + REG_WRITE(dpll_reg, reg_val); + REG_READ(dpll_reg); + udelay(500); + } + + gma_power_end(dev); + dbi_output->mode_flags |= MODE_SETTING_IN_DSR; + if (pipe == 2) { + enter_dsr = 1; + /* pm_schedule_suspend(&dev->pdev->dev, gfxrtdelay); */ + } +} + +static void mdfld_dbi_output_exit_dsr(struct mdfld_dsi_dbi_output *dbi_output, + int pipe) +{ + struct drm_device *dev = dbi_output->dev; + struct drm_crtc *crtc = dbi_output->base.base.crtc; + struct psb_intel_crtc *psb_crtc = (crtc) ? + to_psb_intel_crtc(crtc) : NULL; + u32 reg_val; + u32 dpll_reg = MRST_DPLL_A; + u32 pipeconf_reg = PIPEACONF; + u32 dspcntr_reg = DSPACNTR; + u32 reg_offset = 0; + + /*if mode setting on-going, back off*/ + if ((dbi_output->mode_flags & MODE_SETTING_ON_GOING) || + (psb_crtc && psb_crtc->mode_flags & MODE_SETTING_ON_GOING)) + return; + + if (pipe == 2) { + dpll_reg = MRST_DPLL_A; + pipeconf_reg = PIPECCONF; + dspcntr_reg = DSPCCNTR; + reg_offset = MIPIC_REG_OFFSET; + } + + if (!gma_power_begin(dev, true)) { + dev_err(dev->dev, "hw begin failed\n"); + return; + } + + /* Enable DPLL */ + reg_val = REG_READ(dpll_reg); + if (!(reg_val & DPLL_VCO_ENABLE)) { + if (reg_val & MDFLD_PWR_GATE_EN) { + reg_val &= ~MDFLD_PWR_GATE_EN; + REG_WRITE(dpll_reg, reg_val); + REG_READ(dpll_reg); + udelay(500); + } + + reg_val |= DPLL_VCO_ENABLE; + REG_WRITE(dpll_reg, reg_val); + REG_READ(dpll_reg); + udelay(500); + + /* Add timeout */ + while (!(REG_READ(pipeconf_reg) & PIPECONF_DSIPLL_LOCK)) + cpu_relax(); + } + + /* Enable pipe */ + reg_val = REG_READ(pipeconf_reg); + if (!(reg_val & PIPEACONF_ENABLE)) { + reg_val |= PIPEACONF_ENABLE; + REG_WRITE(pipeconf_reg, reg_val); + REG_READ(pipeconf_reg); + udelay(500); + mdfldWaitForPipeEnable(dev, pipe); + } + + /* Enable plane */ + reg_val = REG_READ(dspcntr_reg); + if (!(reg_val & DISPLAY_PLANE_ENABLE)) { + reg_val |= DISPLAY_PLANE_ENABLE; + REG_WRITE(dspcntr_reg, reg_val); + REG_READ(dspcntr_reg); + udelay(500); + } + + /* Enable TE interrupt on this pipe */ + mdfld_enable_te(dev, pipe); + gma_power_end(dev); + + /*clean IN_DSR flag*/ + dbi_output->mode_flags &= ~MODE_SETTING_IN_DSR; +} + +/* + * Exit from DSR + */ +void mdfld_dsi_dbi_exit_dsr(struct drm_device *dev, u32 update_src) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct mdfld_dbi_dsr_info *dsr_info = dev_priv->dbi_dsr_info; + struct mdfld_dsi_dbi_output **dbi_output; + int i; + int pipe; + + /* FIXME can go ? */ + dev_priv->is_in_idle = false; + dbi_output = dsr_info->dbi_outputs; + +#ifdef CONFIG_PM_RUNTIME + if (!enable_gfx_rtpm) { +/* pm_runtime_allow(&gpDrmDevice->pdev->dev); */ +/* schedule_delayed_work(&rtpm_work, 30 * 1000);*/ /* FIXME: HZ ? */ + } +#endif + + /* For each output, exit dsr */ + for (i = 0; i < dsr_info->dbi_output_num; i++) { + /* If panel has been turned off, skip */ + if (!dbi_output[i] || !dbi_output[i]->dbi_panel_on) + continue; + pipe = dbi_output[i]->channel_num ? 2 : 0; + enter_dsr = 0; + mdfld_dbi_output_exit_dsr(dbi_output[i], pipe); + } + dev_priv->dsr_fb_update |= update_src; +} + +static bool mdfld_dbi_is_in_dsr(struct drm_device *dev) +{ + if (REG_READ(MRST_DPLL_A) & DPLL_VCO_ENABLE) + return false; + if ((REG_READ(PIPEACONF) & PIPEACONF_ENABLE) || + (REG_READ(PIPECCONF) & PIPEACONF_ENABLE)) + return false; + if ((REG_READ(DSPACNTR) & DISPLAY_PLANE_ENABLE) || + (REG_READ(DSPCCNTR) & DISPLAY_PLANE_ENABLE)) + return false; + + return true; +} + +/* Periodically update dbi panel */ +void mdfld_dbi_update_panel(struct drm_device *dev, int pipe) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct mdfld_dbi_dsr_info *dsr_info = dev_priv->dbi_dsr_info; + struct mdfld_dsi_dbi_output **dbi_outputs; + struct mdfld_dsi_dbi_output *dbi_output; + int i; + int can_enter_dsr = 0; + u32 damage_mask; + + dbi_outputs = dsr_info->dbi_outputs; + dbi_output = pipe ? dbi_outputs[1] : dbi_outputs[0]; + + if (!dbi_output) + return; + + if (pipe == 0) + damage_mask = dev_priv->dsr_fb_update & MDFLD_DSR_DAMAGE_MASK_0; + else if (pipe == 2) + damage_mask = dev_priv->dsr_fb_update & MDFLD_DSR_DAMAGE_MASK_2; + else + return; + + /* If FB is damaged and panel is on update on-panel FB */ + if (damage_mask && dbi_output->dbi_panel_on) { + dbi_output->dsr_fb_update_done = false; + + if (dbi_output->p_funcs->update_fb) + dbi_output->p_funcs->update_fb(dbi_output, pipe); + + if (dev_priv->dsr_enable && dbi_output->dsr_fb_update_done) + dev_priv->dsr_fb_update &= ~damage_mask; + + /*clean IN_DSR flag*/ + dbi_output->mode_flags &= ~MODE_SETTING_IN_DSR; + + dbi_output->dsr_idle_count = 0; + } else { + dbi_output->dsr_idle_count++; + } + + switch (dsr_info->dbi_output_num) { + case 1: + if (dbi_output->dsr_idle_count > MDFLD_DSR_MAX_IDLE_COUNT) + can_enter_dsr = 1; + break; + case 2: + if (dbi_outputs[0]->dsr_idle_count > MDFLD_DSR_MAX_IDLE_COUNT + && dbi_outputs[1]->dsr_idle_count > MDFLD_DSR_MAX_IDLE_COUNT) + can_enter_dsr = 1; + break; + default: + DRM_ERROR("Wrong DBI output number\n"); + } + + /* Try to enter DSR */ + if (can_enter_dsr) { + for (i = 0; i < dsr_info->dbi_output_num; i++) { + if (!mdfld_dbi_is_in_dsr(dev) && dbi_outputs[i] && + !(dbi_outputs[i]->mode_flags & MODE_SETTING_ON_GOING)) { + mdfld_dsi_dbi_enter_dsr(dbi_outputs[i], + dbi_outputs[i]->channel_num ? 2 : 0); +#if 0 + enter_dsr = 1; + pr_err("%s: enter_dsr = 1\n", __func__); +#endif + } + } + /*schedule rpm suspend after gfxrtdelay*/ +#ifdef CONFIG_GFX_RTPM + if (!dev_priv->rpm_enabled + || !enter_dsr + /* || (REG_READ(HDMIB_CONTROL) & HDMIB_PORT_EN) */ + || pm_schedule_suspend(&dev->pdev->dev, gfxrtdelay)) + dev_warn(dev->dev, + "Runtime PM schedule suspend failed, rpm %d\n", + dev_priv->rpm_enabled); +#endif + } +} + +int mdfld_dbi_dsr_init(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct mdfld_dbi_dsr_info *dsr_info = dev_priv->dbi_dsr_info; + + if (!dsr_info || IS_ERR(dsr_info)) { + dsr_info = kzalloc(sizeof(struct mdfld_dbi_dsr_info), + GFP_KERNEL); + if (!dsr_info) { + dev_err(dev->dev, "No memory\n"); + return -ENOMEM; + } + dev_priv->dbi_dsr_info = dsr_info; + } + return 0; +} + +void mdfld_dbi_dsr_exit(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct mdfld_dbi_dsr_info *dsr_info = dev_priv->dbi_dsr_info; + + if (dsr_info) { + kfree(dsr_info); + dev_priv->dbi_dsr_info = NULL; + } +} + +void mdfld_dsi_controller_dbi_init(struct mdfld_dsi_config *dsi_config, + int pipe) +{ + struct drm_device *dev = dsi_config->dev; + u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0; + int lane_count = dsi_config->lane_count; + u32 val = 0; + + dev_dbg(dev->dev, "Init DBI interface on pipe %d...\n", pipe); + + /* Un-ready device */ + REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000000); + + /* Init dsi adapter before kicking off */ + REG_WRITE((MIPIA_CONTROL_REG + reg_offset), 0x00000018); + + /* TODO: figure out how to setup these registers */ + REG_WRITE((MIPIA_DPHY_PARAM_REG + reg_offset), 0x150c3408); + REG_WRITE((MIPIA_CLK_LANE_SWITCH_TIME_CNT_REG + reg_offset), + 0x000a0014); + REG_WRITE((MIPIA_DBI_BW_CTRL_REG + reg_offset), 0x00000400); + REG_WRITE((MIPIA_DBI_FIFO_THROTTLE_REG + reg_offset), 0x00000001); + REG_WRITE((MIPIA_HS_LS_DBI_ENABLE_REG + reg_offset), 0x00000000); + + /* Enable all interrupts */ + REG_WRITE((MIPIA_INTR_EN_REG + reg_offset), 0xffffffff); + /* Max value: 20 clock cycles of txclkesc */ + REG_WRITE((MIPIA_TURN_AROUND_TIMEOUT_REG + reg_offset), 0x0000001f); + /* Min 21 txclkesc, max: ffffh */ + REG_WRITE((MIPIA_DEVICE_RESET_TIMER_REG + reg_offset), 0x0000ffff); + /* Min: 7d0 max: 4e20 */ + REG_WRITE((MIPIA_INIT_COUNT_REG + reg_offset), 0x00000fa0); + + /* Set up func_prg */ + val |= lane_count; + val |= (dsi_config->channel_num << DSI_DBI_VIRT_CHANNEL_OFFSET); + val |= DSI_DBI_COLOR_FORMAT_OPTION2; + REG_WRITE((MIPIA_DSI_FUNC_PRG_REG + reg_offset), val); + + REG_WRITE((MIPIA_HS_TX_TIMEOUT_REG + reg_offset), 0x3fffff); + REG_WRITE((MIPIA_LP_RX_TIMEOUT_REG + reg_offset), 0xffff); + + /* De-assert dbi_stall when half of DBI FIFO is empty */ + /* REG_WRITE((MIPIA_DBI_FIFO_THROTTLE_REG + reg_offset), 0x00000000); */ + + REG_WRITE((MIPIA_HIGH_LOW_SWITCH_COUNT_REG + reg_offset), 0x46); + REG_WRITE((MIPIA_EOT_DISABLE_REG + reg_offset), 0x00000000); + REG_WRITE((MIPIA_LP_BYTECLK_REG + reg_offset), 0x00000004); + REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000001); +} + +#if 0 +/*DBI encoder helper funcs*/ +static const struct drm_encoder_helper_funcs mdfld_dsi_dbi_helper_funcs = { + .dpms = mdfld_dsi_dbi_dpms, + .mode_fixup = mdfld_dsi_dbi_mode_fixup, + .prepare = mdfld_dsi_dbi_prepare, + .mode_set = mdfld_dsi_dbi_mode_set, + .commit = mdfld_dsi_dbi_commit, +}; + +/*DBI encoder funcs*/ +static const struct drm_encoder_funcs mdfld_dsi_dbi_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +#endif + +/* + * Init DSI DBI encoder. + * Allocate an mdfld_dsi_encoder and attach it to given @dsi_connector + * return pointer of newly allocated DBI encoder, NULL on error + */ +struct mdfld_dsi_encoder *mdfld_dsi_dbi_init(struct drm_device *dev, + struct mdfld_dsi_connector *dsi_connector, + struct panel_funcs *p_funcs) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct mdfld_dsi_dbi_output *dbi_output = NULL; + struct mdfld_dsi_config *dsi_config; + struct drm_connector *connector = NULL; + struct drm_encoder *encoder = NULL; + struct drm_display_mode *fixed_mode = NULL; + struct psb_gtt *pg = dev_priv ? (&dev_priv->gtt) : NULL; + struct mdfld_dbi_dpu_info *dpu_info = dev_priv ? (dev_priv->dbi_dpu_info) : NULL; + struct mdfld_dbi_dsr_info *dsr_info = dev_priv ? (dev_priv->dbi_dsr_info) : NULL; + u32 data = 0; + int pipe; + int ret; + + if (!pg || !dsi_connector || !p_funcs) { + WARN_ON(1); + return NULL; + } + + dsi_config = mdfld_dsi_get_config(dsi_connector); + pipe = dsi_connector->pipe; + + /*panel hard-reset*/ + if (p_funcs->reset) { + ret = p_funcs->reset(pipe); + if (ret) { + DRM_ERROR("Panel %d hard-reset failed\n", pipe); + return NULL; + } + } + /* Panel drvIC init */ + if (p_funcs->drv_ic_init) + p_funcs->drv_ic_init(dsi_config, pipe); + + /* Panel power mode detect */ + ret = mdfld_dsi_get_power_mode(dsi_config, + &data, + MDFLD_DSI_HS_TRANSMISSION); + if (ret) { + DRM_ERROR("Panel %d get power mode failed\n", pipe); + dsi_connector->status = connector_status_disconnected; + } else { + DRM_INFO("pipe %d power mode 0x%x\n", pipe, data); + dsi_connector->status = connector_status_connected; + } + + /*TODO: get panel info from DDB*/ + + dbi_output = kzalloc(sizeof(struct mdfld_dsi_dbi_output), GFP_KERNEL); + if (!dbi_output) { + dev_err(dev->dev, "No memory\n"); + return NULL; + } + + if (dsi_connector->pipe == 0) { + dbi_output->channel_num = 0; + dev_priv->dbi_output = dbi_output; + } else if (dsi_connector->pipe == 2) { + dbi_output->channel_num = 1; + dev_priv->dbi_output2 = dbi_output; + } else { + dev_err(dev->dev, "only support 2 DSI outputs\n"); + goto out_err1; + } + + dbi_output->dev = dev; + dbi_output->p_funcs = p_funcs; + fixed_mode = dsi_config->fixed_mode; + dbi_output->panel_fixed_mode = fixed_mode; + + /* Create drm encoder object */ + connector = &dsi_connector->base.base; + encoder = &dbi_output->base.base; + /* Review this if we ever get MIPI-HDMI bridges or similar */ + drm_encoder_init(dev, + encoder, + p_funcs->encoder_funcs, + DRM_MODE_ENCODER_LVDS); + drm_encoder_helper_add(encoder, p_funcs->encoder_helper_funcs); + + /* Attach to given connector */ + drm_mode_connector_attach_encoder(connector, encoder); + + /* Set possible CRTCs and clones */ + if (dsi_connector->pipe) { + encoder->possible_crtcs = (1 << 2); + encoder->possible_clones = (1 << 1); + } else { + encoder->possible_crtcs = (1 << 0); + encoder->possible_clones = (1 << 0); + } + + dev_priv->dsr_fb_update = 0; + dev_priv->dsr_enable = false; + dev_priv->exit_idle = mdfld_dsi_dbi_exit_dsr; + + dbi_output->first_boot = true; + dbi_output->mode_flags = MODE_SETTING_IN_ENCODER; + + /* Add this output to dpu_info if in DPU mode */ + if (dpu_info && dsi_connector->status == connector_status_connected) { + if (dsi_connector->pipe == 0) + dpu_info->dbi_outputs[0] = dbi_output; + else + dpu_info->dbi_outputs[1] = dbi_output; + + dpu_info->dbi_output_num++; + } else if (dsi_connector->status == connector_status_connected) { + /* Add this output to dsr_info if not */ + if (dsi_connector->pipe == 0) + dsr_info->dbi_outputs[0] = dbi_output; + else + dsr_info->dbi_outputs[1] = dbi_output; + + dsr_info->dbi_output_num++; + } + return &dbi_output->base; +out_err1: + kfree(dbi_output); + return NULL; +} diff --git a/trunk/drivers/staging/gma500/mdfld_dsi_dbi.h b/trunk/drivers/staging/gma500/mdfld_dsi_dbi.h new file mode 100644 index 000000000000..f0fa986fd934 --- /dev/null +++ b/trunk/drivers/staging/gma500/mdfld_dsi_dbi.h @@ -0,0 +1,173 @@ +/* + * Copyright © 2010 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: + * jim liu + * Jackie Li + */ + +#ifndef __MDFLD_DSI_DBI_H__ +#define __MDFLD_DSI_DBI_H__ + +#include +#include +#include +#include +#include + +#include "psb_drv.h" +#include "psb_intel_drv.h" +#include "psb_intel_reg.h" +#include "power.h" + +#include "mdfld_dsi_output.h" +#include "mdfld_output.h" + +/* + * DBI encoder which inherits from mdfld_dsi_encoder + */ +struct mdfld_dsi_dbi_output { + struct mdfld_dsi_encoder base; + struct drm_display_mode *panel_fixed_mode; + u8 last_cmd; + u8 lane_count; + u8 channel_num; + struct drm_device *dev; + + /* Backlight operations */ + + /* DSR timer */ + u32 dsr_idle_count; + bool dsr_fb_update_done; + + /* Mode setting flags */ + u32 mode_flags; + + /* Panel status */ + bool dbi_panel_on; + bool first_boot; + struct panel_funcs *p_funcs; + + /* DPU */ + u32 *dbi_cb_addr; + u32 dbi_cb_phy; + spinlock_t cb_lock; + u32 cb_write; +}; + +#define MDFLD_DSI_DBI_OUTPUT(dsi_encoder) \ + container_of(dsi_encoder, struct mdfld_dsi_dbi_output, base) + +struct mdfld_dbi_dsr_info { + int dbi_output_num; + struct mdfld_dsi_dbi_output *dbi_outputs[2]; + + u32 dsr_idle_count; +}; + +#define DBI_CB_TIMEOUT_COUNT 0xffff + +/* Offsets */ +#define CMD_MEM_ADDR_OFFSET 0 + +#define CMD_DATA_SRC_SYSTEM_MEM 0 +#define CMD_DATA_SRC_PIPE 1 + +static inline int mdfld_dsi_dbi_fifo_ready(struct mdfld_dsi_dbi_output *dbi_output) +{ + struct drm_device *dev = dbi_output->dev; + u32 retry = DBI_CB_TIMEOUT_COUNT; + int reg_offset = (dbi_output->channel_num == 1) ? MIPIC_REG_OFFSET : 0; + int ret = 0; + + /* Query the dbi fifo status*/ + while (retry--) { + if (REG_READ(MIPIA_GEN_FIFO_STAT_REG + reg_offset) & (1 << 27)) + break; + } + + if (!retry) { + DRM_ERROR("Timeout waiting for DBI FIFO empty\n"); + ret = -EAGAIN; + } + return ret; +} + +static inline int mdfld_dsi_dbi_cmd_sent(struct mdfld_dsi_dbi_output *dbi_output) +{ + struct drm_device *dev = dbi_output->dev; + u32 retry = DBI_CB_TIMEOUT_COUNT; + int reg_offset = (dbi_output->channel_num == 1) ? MIPIC_REG_OFFSET : 0; + int ret = 0; + + /* Query the command execution status */ + while (retry--) + if (!(REG_READ(MIPIA_CMD_ADD_REG + reg_offset) & (1 << 0))) + break; + + if (!retry) { + DRM_ERROR("Timeout waiting for DBI command status\n"); + ret = -EAGAIN; + } + + return ret; +} + +static inline int mdfld_dsi_dbi_cb_ready(struct mdfld_dsi_dbi_output *dbi_output) +{ + int ret = 0; + + /* Query the command execution status*/ + ret = mdfld_dsi_dbi_cmd_sent(dbi_output); + if (ret) { + DRM_ERROR("Peripheral is busy\n"); + ret = -EAGAIN; + } + /* Query the dbi fifo status*/ + ret = mdfld_dsi_dbi_fifo_ready(dbi_output); + if (ret) { + DRM_ERROR("DBI FIFO is not empty\n"); + ret = -EAGAIN; + } + return ret; +} + +extern void mdfld_dsi_dbi_output_init(struct drm_device *dev, + struct psb_intel_mode_device *mode_dev, int pipe); +extern void mdfld_dsi_dbi_exit_dsr(struct drm_device *dev, u32 update_src); +extern void mdfld_dsi_dbi_enter_dsr(struct mdfld_dsi_dbi_output *dbi_output, + int pipe); +extern int mdfld_dbi_dsr_init(struct drm_device *dev); +extern void mdfld_dbi_dsr_exit(struct drm_device *dev); +extern struct mdfld_dsi_encoder *mdfld_dsi_dbi_init(struct drm_device *dev, + struct mdfld_dsi_connector *dsi_connector, + struct panel_funcs *p_funcs); +extern int mdfld_dsi_dbi_send_dcs(struct mdfld_dsi_dbi_output *dbi_output, + u8 dcs, u8 *param, u32 num, u8 data_src); +extern int mdfld_dsi_dbi_update_area(struct mdfld_dsi_dbi_output *dbi_output, + u16 x1, u16 y1, u16 x2, u16 y2); +extern int mdfld_dsi_dbi_update_power(struct mdfld_dsi_dbi_output *dbi_output, + int mode); +extern void mdfld_dsi_controller_dbi_init(struct mdfld_dsi_config *dsi_config, + int pipe); + +#endif /*__MDFLD_DSI_DBI_H__*/ diff --git a/trunk/drivers/staging/gma500/mdfld_dsi_dbi_dpu.c b/trunk/drivers/staging/gma500/mdfld_dsi_dbi_dpu.c new file mode 100644 index 000000000000..a4e2ff442b1f --- /dev/null +++ b/trunk/drivers/staging/gma500/mdfld_dsi_dbi_dpu.c @@ -0,0 +1,778 @@ +/* + * Copyright © 2010-2011 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: + * Jim Liu + * Jackie Li + */ + +#include "mdfld_dsi_dbi_dpu.h" +#include "mdfld_dsi_dbi.h" + +/* + * NOTE: all mdlfd_x_damage funcs should be called by holding dpu_update_lock + */ + +static int mdfld_cursor_damage(struct mdfld_dbi_dpu_info *dpu_info, + mdfld_plane_t plane, + struct psb_drm_dpu_rect *damaged_rect) +{ + int x, y; + int new_x, new_y; + struct psb_drm_dpu_rect *rect; + struct psb_drm_dpu_rect *pipe_rect; + int cursor_size; + struct mdfld_cursor_info *cursor; + mdfld_plane_t fb_plane; + + if (plane == MDFLD_CURSORA) { + cursor = &dpu_info->cursors[0]; + x = dpu_info->cursors[0].x; + y = dpu_info->cursors[0].y; + cursor_size = dpu_info->cursors[0].size; + pipe_rect = &dpu_info->damage_pipea; + fb_plane = MDFLD_PLANEA; + } else { + cursor = &dpu_info->cursors[1]; + x = dpu_info->cursors[1].x; + y = dpu_info->cursors[1].y; + cursor_size = dpu_info->cursors[1].size; + pipe_rect = &dpu_info->damage_pipec; + fb_plane = MDFLD_PLANEC; + } + new_x = damaged_rect->x; + new_y = damaged_rect->y; + + if (x == new_x && y == new_y) + return 0; + + rect = &dpu_info->damaged_rects[plane]; + /* Move to right */ + if (new_x >= x) { + if (new_y > y) { + rect->x = x; + rect->y = y; + rect->width = (new_x + cursor_size) - x; + rect->height = (new_y + cursor_size) - y; + goto cursor_out; + } else { + rect->x = x; + rect->y = new_y; + rect->width = (new_x + cursor_size) - x; + rect->height = (y - new_y); + goto cursor_out; + } + } else { + if (new_y > y) { + rect->x = new_x; + rect->y = y; + rect->width = (x + cursor_size) - new_x; + rect->height = new_y - y; + goto cursor_out; + } else { + rect->x = new_x; + rect->y = new_y; + rect->width = (x + cursor_size) - new_x; + rect->height = (y + cursor_size) - new_y; + } + } +cursor_out: + if (new_x < 0) + cursor->x = 0; + else if (new_x > 864) + cursor->x = 864; + else + cursor->x = new_x; + + if (new_y < 0) + cursor->y = 0; + else if (new_y > 480) + cursor->y = 480; + else + cursor->y = new_y; + + /* + * FIXME: this is a workaround for cursor plane update, + * remove it later! + */ + rect->x = 0; + rect->y = 0; + rect->width = 864; + rect->height = 480; + + mdfld_check_boundary(dpu_info, rect); + mdfld_dpu_region_extent(pipe_rect, rect); + + /* Update pending status of dpu_info */ + dpu_info->pending |= (1 << plane); + /* Update fb panel as well */ + dpu_info->pending |= (1 << fb_plane); + return 0; +} + +static int mdfld_fb_damage(struct mdfld_dbi_dpu_info *dpu_info, + mdfld_plane_t plane, + struct psb_drm_dpu_rect *damaged_rect) +{ + struct psb_drm_dpu_rect *rect; + + if (plane == MDFLD_PLANEA) + rect = &dpu_info->damage_pipea; + else + rect = &dpu_info->damage_pipec; + + mdfld_check_boundary(dpu_info, damaged_rect); + + /* Add fb damage area to this pipe */ + mdfld_dpu_region_extent(rect, damaged_rect); + + /* Update pending status of dpu_info */ + dpu_info->pending |= (1 << plane); + return 0; +} + +/* Do nothing here, right now */ +static int mdfld_overlay_damage(struct mdfld_dbi_dpu_info *dpu_info, + mdfld_plane_t plane, + struct psb_drm_dpu_rect *damaged_rect) +{ + return 0; +} + +int mdfld_dbi_dpu_report_damage(struct drm_device *dev, + mdfld_plane_t plane, + struct psb_drm_dpu_rect *rect) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct mdfld_dbi_dpu_info *dpu_info = dev_priv->dbi_dpu_info; + int ret = 0; + + /* DPU not in use, no damage reporting needed */ + if (dpu_info == NULL) + return 0; + + spin_lock(&dpu_info->dpu_update_lock); + + switch (plane) { + case MDFLD_PLANEA: + case MDFLD_PLANEC: + mdfld_fb_damage(dpu_info, plane, rect); + break; + case MDFLD_CURSORA: + case MDFLD_CURSORC: + mdfld_cursor_damage(dpu_info, plane, rect); + break; + case MDFLD_OVERLAYA: + case MDFLD_OVERLAYC: + mdfld_overlay_damage(dpu_info, plane, rect); + break; + default: + DRM_ERROR("Invalid plane type %d\n", plane); + ret = -EINVAL; + } + spin_unlock(&dpu_info->dpu_update_lock); + return ret; +} + +int mdfld_dbi_dpu_report_fullscreen_damage(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv; + struct mdfld_dbi_dpu_info *dpu_info; + struct mdfld_dsi_config *dsi_config; + struct psb_drm_dpu_rect rect; + int i; + + if (!dev) { + DRM_ERROR("Invalid parameter\n"); + return -EINVAL; + } + + dev_priv = dev->dev_private; + dpu_info = dev_priv->dbi_dpu_info; + + /* This is fine - we may be in non DPU mode */ + if (!dpu_info) + return -EINVAL; + + for (i = 0; i < dpu_info->dbi_output_num; i++) { + dsi_config = dev_priv->dsi_configs[i]; + if (dsi_config) { + rect.x = rect.y = 0; + rect.width = dsi_config->fixed_mode->hdisplay; + rect.height = dsi_config->fixed_mode->vdisplay; + mdfld_dbi_dpu_report_damage(dev, + i ? (MDFLD_PLANEC) : (MDFLD_PLANEA), + &rect); + } + } + /* Exit DSR state */ + mdfld_dpu_exit_dsr(dev); + return 0; +} + +int mdfld_dsi_dbi_dsr_off(struct drm_device *dev, + struct psb_drm_dpu_rect *rect) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct mdfld_dbi_dpu_info *dpu_info = dev_priv->dbi_dpu_info; + + mdfld_dbi_dpu_report_damage(dev, MDFLD_PLANEA, rect); + + /* If dual display mode */ + if (dpu_info->dbi_output_num == 2) + mdfld_dbi_dpu_report_damage(dev, MDFLD_PLANEC, rect); + + /* Force dsi to exit DSR mode */ + mdfld_dpu_exit_dsr(dev); + return 0; +} + +static void mdfld_dpu_cursor_plane_flush(struct mdfld_dbi_dpu_info *dpu_info, + mdfld_plane_t plane) +{ + struct drm_device *dev = dpu_info->dev; + u32 curpos_reg = CURAPOS; + u32 curbase_reg = CURABASE; + u32 curcntr_reg = CURACNTR; + struct mdfld_cursor_info *cursor = &dpu_info->cursors[0]; + + if (plane == MDFLD_CURSORC) { + curpos_reg = CURCPOS; + curbase_reg = CURCBASE; + curcntr_reg = CURCCNTR; + cursor = &dpu_info->cursors[1]; + } + + REG_WRITE(curcntr_reg, REG_READ(curcntr_reg)); + REG_WRITE(curpos_reg, + (((cursor->x & CURSOR_POS_MASK) << CURSOR_X_SHIFT) | + ((cursor->y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT))); + REG_WRITE(curbase_reg, REG_READ(curbase_reg)); +} + +static void mdfld_dpu_fb_plane_flush(struct mdfld_dbi_dpu_info *dpu_info, + mdfld_plane_t plane) +{ + u32 pipesrc_reg = PIPEASRC; + u32 dspsize_reg = DSPASIZE; + u32 dspoff_reg = DSPALINOFF; + u32 dspsurf_reg = DSPASURF; + u32 dspstride_reg = DSPASTRIDE; + u32 stride; + struct psb_drm_dpu_rect *rect = &dpu_info->damage_pipea; + struct drm_device *dev = dpu_info->dev; + + if (plane == MDFLD_PLANEC) { + pipesrc_reg = PIPECSRC; + dspsize_reg = DSPCSIZE; + dspoff_reg = DSPCLINOFF; + dspsurf_reg = DSPCSURF; + dspstride_reg = DSPCSTRIDE; + rect = &dpu_info->damage_pipec; + } + + stride = REG_READ(dspstride_reg); + /* FIXME: should I do the pipe src update here? */ + REG_WRITE(pipesrc_reg, ((rect->width - 1) << 16) | (rect->height - 1)); + /* Flush plane */ + REG_WRITE(dspsize_reg, ((rect->height - 1) << 16) | (rect->width - 1)); + REG_WRITE(dspoff_reg, ((rect->x * 4) + (rect->y * stride))); + REG_WRITE(dspsurf_reg, REG_READ(dspsurf_reg)); + + /* + * TODO: wait for flip finished and restore the pipesrc reg, + * or cursor will be show at a wrong position + */ +} + +static void mdfld_dpu_overlay_plane_flush(struct mdfld_dbi_dpu_info *dpu_info, + mdfld_plane_t plane) +{ +} + +/* + * TODO: we are still in dbi normal mode now, we will try to use partial + * mode later. + */ +static int mdfld_dbi_prepare_cb(struct mdfld_dsi_dbi_output *dbi_output, + struct mdfld_dbi_dpu_info *dpu_info, int pipe) +{ + u8 *cb_addr = (u8 *)dbi_output->dbi_cb_addr; + u32 *index; + struct psb_drm_dpu_rect *rect = pipe ? + (&dpu_info->damage_pipec) : (&dpu_info->damage_pipea); + + /* FIXME: lock command buffer, this may lead to a deadlock, + as we already hold the dpu_update_lock */ + if (!spin_trylock(&dbi_output->cb_lock)) { + DRM_ERROR("lock command buffer failed, try again\n"); + return -EAGAIN; + } + + index = &dbi_output->cb_write; + + if (*index) { + DRM_ERROR("DBI command buffer unclean\n"); + return -EAGAIN; + } + + /* Column address */ + *(cb_addr + ((*index)++)) = set_column_address; + *(cb_addr + ((*index)++)) = rect->x >> 8; + *(cb_addr + ((*index)++)) = rect->x; + *(cb_addr + ((*index)++)) = (rect->x + rect->width - 1) >> 8; + *(cb_addr + ((*index)++)) = (rect->x + rect->width - 1); + + *index = 8; + + /* Page address */ + *(cb_addr + ((*index)++)) = set_page_addr; + *(cb_addr + ((*index)++)) = rect->y >> 8; + *(cb_addr + ((*index)++)) = rect->y; + *(cb_addr + ((*index)++)) = (rect->y + rect->height - 1) >> 8; + *(cb_addr + ((*index)++)) = (rect->y + rect->height - 1); + + *index = 16; + + /*write memory*/ + *(cb_addr + ((*index)++)) = write_mem_start; + + return 0; +} + +static int mdfld_dbi_flush_cb(struct mdfld_dsi_dbi_output *dbi_output, int pipe) +{ + u32 cmd_phy = dbi_output->dbi_cb_phy; + u32 *index = &dbi_output->cb_write; + int reg_offset = pipe ? MIPIC_REG_OFFSET : 0; + struct drm_device *dev = dbi_output->dev; + + if (*index == 0 || !dbi_output) + return 0; + + REG_WRITE((MIPIA_CMD_LEN_REG + reg_offset), 0x010505); + REG_WRITE((MIPIA_CMD_ADD_REG + reg_offset), cmd_phy | 3); + + *index = 0; + + /* FIXME: unlock command buffer */ + spin_unlock(&dbi_output->cb_lock); + return 0; +} + +static int mdfld_dpu_update_pipe(struct mdfld_dsi_dbi_output *dbi_output, + struct mdfld_dbi_dpu_info *dpu_info, int pipe) +{ + struct drm_device *dev = dbi_output->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + mdfld_plane_t cursor_plane = MDFLD_CURSORA; + mdfld_plane_t fb_plane = MDFLD_PLANEA; + mdfld_plane_t overlay_plane = MDFLD_OVERLAYA; + int ret = 0; + u32 plane_mask = MDFLD_PIPEA_PLANE_MASK; + + /* Damaged rects on this pipe */ + if (pipe) { + cursor_plane = MDFLD_CURSORC; + fb_plane = MDFLD_PLANEC; + overlay_plane = MDFLD_OVERLAYC; + plane_mask = MDFLD_PIPEC_PLANE_MASK; + } + + /*update cursor which assigned to @pipe*/ + if (dpu_info->pending & (1 << cursor_plane)) + mdfld_dpu_cursor_plane_flush(dpu_info, cursor_plane); + + /*update fb which assigned to @pipe*/ + if (dpu_info->pending & (1 << fb_plane)) + mdfld_dpu_fb_plane_flush(dpu_info, fb_plane); + + /* TODO: update overlay */ + if (dpu_info->pending & (1 << overlay_plane)) + mdfld_dpu_overlay_plane_flush(dpu_info, overlay_plane); + + /* Flush damage area to panel fb */ + if (dpu_info->pending & plane_mask) { + ret = mdfld_dbi_prepare_cb(dbi_output, dpu_info, pipe); + /* + * TODO: remove b_dsr_enable later, + * added it so that text console could boot smoothly + */ + /* Clean pending flags on this pipe */ + if (!ret && dev_priv->dsr_enable) { + dpu_info->pending &= ~plane_mask; + /* Reset overlay pipe damage rect */ + mdfld_dpu_init_damage(dpu_info, pipe); + } + } + return ret; +} + +static int mdfld_dpu_update_fb(struct drm_device *dev) +{ + struct drm_crtc *crtc; + struct psb_intel_crtc *psb_crtc; + struct mdfld_dsi_dbi_output **dbi_output; + struct drm_psb_private *dev_priv = dev->dev_private; + struct mdfld_dbi_dpu_info *dpu_info = dev_priv->dbi_dpu_info; + bool pipe_updated[2]; + unsigned long irq_flags; + u32 dpll_reg = MRST_DPLL_A; + u32 dspcntr_reg = DSPACNTR; + u32 pipeconf_reg = PIPEACONF; + u32 dsplinoff_reg = DSPALINOFF; + u32 dspsurf_reg = DSPASURF; + u32 mipi_state_reg = MIPIA_INTR_STAT_REG; + u32 reg_offset = 0; + int pipe; + int i; + int ret; + + dbi_output = dpu_info->dbi_outputs; + pipe_updated[0] = pipe_updated[1] = false; + + if (!gma_power_begin(dev, true)) + return -EAGAIN; + + /* Try to prevent any new damage reports */ + if (!spin_trylock_irqsave(&dpu_info->dpu_update_lock, irq_flags)) + return -EAGAIN; + + for (i = 0; i < dpu_info->dbi_output_num; i++) { + crtc = dbi_output[i]->base.base.crtc; + psb_crtc = (crtc) ? to_psb_intel_crtc(crtc) : NULL; + + pipe = dbi_output[i]->channel_num ? 2 : 0; + + if (pipe == 2) { + dspcntr_reg = DSPCCNTR; + pipeconf_reg = PIPECCONF; + dsplinoff_reg = DSPCLINOFF; + dspsurf_reg = DSPCSURF; + reg_offset = MIPIC_REG_OFFSET; + } + + if (!(REG_READ((MIPIA_GEN_FIFO_STAT_REG + reg_offset)) + & (1 << 27)) || + !(REG_READ(dpll_reg) & DPLL_VCO_ENABLE) || + !(REG_READ(dspcntr_reg) & DISPLAY_PLANE_ENABLE) || + !(REG_READ(pipeconf_reg) & DISPLAY_PLANE_ENABLE)) { + dev_err(dev->dev, + "DBI FIFO is busy, DSI %d state %x\n", + pipe, + REG_READ(mipi_state_reg + reg_offset)); + continue; + } + + /* + * If DBI output is in a exclusive state then the pipe + * change won't be updated + */ + if (dbi_output[i]->dbi_panel_on && + !(dbi_output[i]->mode_flags & MODE_SETTING_ON_GOING) && + !(psb_crtc && + psb_crtc->mode_flags & MODE_SETTING_ON_GOING) && + !(dbi_output[i]->mode_flags & MODE_SETTING_IN_DSR)) { + ret = mdfld_dpu_update_pipe(dbi_output[i], + dpu_info, dbi_output[i]->channel_num ? 2 : 0); + if (!ret) + pipe_updated[i] = true; + } + } + + for (i = 0; i < dpu_info->dbi_output_num; i++) + if (pipe_updated[i]) + mdfld_dbi_flush_cb(dbi_output[i], + dbi_output[i]->channel_num ? 2 : 0); + + spin_unlock_irqrestore(&dpu_info->dpu_update_lock, irq_flags); + gma_power_end(dev); + return 0; +} + +static int __mdfld_dbi_exit_dsr(struct mdfld_dsi_dbi_output *dbi_output, + int pipe) +{ + struct drm_device *dev = dbi_output->dev; + struct drm_crtc *crtc = dbi_output->base.base.crtc; + struct psb_intel_crtc *psb_crtc = (crtc) ? to_psb_intel_crtc(crtc) + : NULL; + u32 reg_val; + u32 dpll_reg = MRST_DPLL_A; + u32 pipeconf_reg = PIPEACONF; + u32 dspcntr_reg = DSPACNTR; + u32 dspbase_reg = DSPABASE; + u32 dspsurf_reg = DSPASURF; + u32 reg_offset = 0; + + if (!dbi_output) + return 0; + + /* If mode setting on-going, back off */ + if ((dbi_output->mode_flags & MODE_SETTING_ON_GOING) || + (psb_crtc && psb_crtc->mode_flags & MODE_SETTING_ON_GOING)) + return -EAGAIN; + + if (pipe == 2) { + dpll_reg = MRST_DPLL_A; + pipeconf_reg = PIPECCONF; + dspcntr_reg = DSPCCNTR; + dspbase_reg = MDFLD_DSPCBASE; + dspsurf_reg = DSPCSURF; + + reg_offset = MIPIC_REG_OFFSET; + } + + if (!gma_power_begin(dev, true)) + return -EAGAIN; + + /* Enable DPLL */ + reg_val = REG_READ(dpll_reg); + if (!(reg_val & DPLL_VCO_ENABLE)) { + + if (reg_val & MDFLD_PWR_GATE_EN) { + reg_val &= ~MDFLD_PWR_GATE_EN; + REG_WRITE(dpll_reg, reg_val); + REG_READ(dpll_reg); + udelay(500); + } + + reg_val |= DPLL_VCO_ENABLE; + REG_WRITE(dpll_reg, reg_val); + REG_READ(dpll_reg); + udelay(500); + + /* FIXME: add timeout */ + while (!(REG_READ(pipeconf_reg) & PIPECONF_DSIPLL_LOCK)) + cpu_relax(); + } + + /* Enable pipe */ + reg_val = REG_READ(pipeconf_reg); + if (!(reg_val & PIPEACONF_ENABLE)) { + reg_val |= PIPEACONF_ENABLE; + REG_WRITE(pipeconf_reg, reg_val); + REG_READ(pipeconf_reg); + udelay(500); + mdfldWaitForPipeEnable(dev, pipe); + } + + /* Enable plane */ + reg_val = REG_READ(dspcntr_reg); + if (!(reg_val & DISPLAY_PLANE_ENABLE)) { + reg_val |= DISPLAY_PLANE_ENABLE; + REG_WRITE(dspcntr_reg, reg_val); + REG_READ(dspcntr_reg); + udelay(500); + } + + gma_power_end(dev); + + /* Clean IN_DSR flag */ + dbi_output->mode_flags &= ~MODE_SETTING_IN_DSR; + + return 0; +} + +int mdfld_dpu_exit_dsr(struct drm_device *dev) +{ + struct mdfld_dsi_dbi_output **dbi_output; + struct drm_psb_private *dev_priv = dev->dev_private; + struct mdfld_dbi_dpu_info *dpu_info = dev_priv->dbi_dpu_info; + int i; + int pipe; + + dbi_output = dpu_info->dbi_outputs; + + for (i = 0; i < dpu_info->dbi_output_num; i++) { + /* If this output is not in DSR mode, don't call exit dsr */ + if (dbi_output[i]->mode_flags & MODE_SETTING_IN_DSR) + __mdfld_dbi_exit_dsr(dbi_output[i], + dbi_output[i]->channel_num ? 2 : 0); + } + + /* Enable TE interrupt */ + for (i = 0; i < dpu_info->dbi_output_num; i++) { + /* If this output is not in DSR mode, don't call exit dsr */ + pipe = dbi_output[i]->channel_num ? 2 : 0; + if (dbi_output[i]->dbi_panel_on && pipe) { + mdfld_disable_te(dev, 0); + mdfld_enable_te(dev, 2); + } else if (dbi_output[i]->dbi_panel_on && !pipe) { + mdfld_disable_te(dev, 2); + mdfld_enable_te(dev, 0); + } + } + return 0; +} + +static int mdfld_dpu_enter_dsr(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct mdfld_dbi_dpu_info *dpu_info = dev_priv->dbi_dpu_info; + struct mdfld_dsi_dbi_output **dbi_output; + int i; + + dbi_output = dpu_info->dbi_outputs; + + for (i = 0; i < dpu_info->dbi_output_num; i++) { + /* If output is off or already in DSR state, don't re-enter */ + if (dbi_output[i]->dbi_panel_on && + !(dbi_output[i]->mode_flags & MODE_SETTING_IN_DSR)) { + mdfld_dsi_dbi_enter_dsr(dbi_output[i], + dbi_output[i]->channel_num ? 2 : 0); + } + } + + return 0; +} + +static void mdfld_dbi_dpu_timer_func(unsigned long data) +{ + struct drm_device *dev = (struct drm_device *)data; + struct drm_psb_private *dev_priv = dev->dev_private; + struct mdfld_dbi_dpu_info *dpu_info = dev_priv->dbi_dpu_info; + struct timer_list *dpu_timer = &dpu_info->dpu_timer; + unsigned long flags; + + if (dpu_info->pending) { + dpu_info->idle_count = 0; + /* Update panel fb with damaged area */ + mdfld_dpu_update_fb(dev); + } else { + dpu_info->idle_count++; + } + + if (dpu_info->idle_count >= MDFLD_MAX_IDLE_COUNT) { + mdfld_dpu_enter_dsr(dev); + /* Stop timer by return */ + return; + } + + spin_lock_irqsave(&dpu_info->dpu_timer_lock, flags); + if (!timer_pending(dpu_timer)) { + dpu_timer->expires = jiffies + MDFLD_DSR_DELAY; + add_timer(dpu_timer); + } + spin_unlock_irqrestore(&dpu_info->dpu_timer_lock, flags); +} + +void mdfld_dpu_update_panel(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct mdfld_dbi_dpu_info *dpu_info = dev_priv->dbi_dpu_info; + + if (dpu_info->pending) { + dpu_info->idle_count = 0; + + /*update panel fb with damaged area*/ + mdfld_dpu_update_fb(dev); + } else { + dpu_info->idle_count++; + } + + if (dpu_info->idle_count >= MDFLD_MAX_IDLE_COUNT) { + /*enter dsr*/ + mdfld_dpu_enter_dsr(dev); + } +} + +static int mdfld_dbi_dpu_timer_init(struct drm_device *dev, + struct mdfld_dbi_dpu_info *dpu_info) +{ + struct timer_list *dpu_timer = &dpu_info->dpu_timer; + unsigned long flags; + + spin_lock_init(&dpu_info->dpu_timer_lock); + spin_lock_irqsave(&dpu_info->dpu_timer_lock, flags); + + init_timer(dpu_timer); + + dpu_timer->data = (unsigned long)dev; + dpu_timer->function = mdfld_dbi_dpu_timer_func; + dpu_timer->expires = jiffies + MDFLD_DSR_DELAY; + + spin_unlock_irqrestore(&dpu_info->dpu_timer_lock, flags); + + return 0; +} + +void mdfld_dbi_dpu_timer_start(struct mdfld_dbi_dpu_info *dpu_info) +{ + struct timer_list *dpu_timer = &dpu_info->dpu_timer; + unsigned long flags; + + spin_lock_irqsave(&dpu_info->dpu_timer_lock, flags); + if (!timer_pending(dpu_timer)) { + dpu_timer->expires = jiffies + MDFLD_DSR_DELAY; + add_timer(dpu_timer); + } + spin_unlock_irqrestore(&dpu_info->dpu_timer_lock, flags); +} + +int mdfld_dbi_dpu_init(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct mdfld_dbi_dpu_info *dpu_info = dev_priv->dbi_dpu_info; + + if (!dpu_info || IS_ERR(dpu_info)) { + dpu_info = kzalloc(sizeof(struct mdfld_dbi_dpu_info), + GFP_KERNEL); + if (!dpu_info) { + DRM_ERROR("No memory\n"); + return -ENOMEM; + } + dev_priv->dbi_dpu_info = dpu_info; + } + + dpu_info->dev = dev; + + dpu_info->cursors[0].size = MDFLD_CURSOR_SIZE; + dpu_info->cursors[1].size = MDFLD_CURSOR_SIZE; + + /*init dpu_update_lock*/ + spin_lock_init(&dpu_info->dpu_update_lock); + + /*init dpu refresh timer*/ + mdfld_dbi_dpu_timer_init(dev, dpu_info); + + /*init pipe damage area*/ + mdfld_dpu_init_damage(dpu_info, 0); + mdfld_dpu_init_damage(dpu_info, 2); + + return 0; +} + +void mdfld_dbi_dpu_exit(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct mdfld_dbi_dpu_info *dpu_info = dev_priv->dbi_dpu_info; + + if (!dpu_info) + return; + + del_timer_sync(&dpu_info->dpu_timer); + kfree(dpu_info); + dev_priv->dbi_dpu_info = NULL; +} + + diff --git a/trunk/drivers/staging/gma500/mdfld_dsi_dbi_dpu.h b/trunk/drivers/staging/gma500/mdfld_dsi_dbi_dpu.h new file mode 100644 index 000000000000..42367ed48c08 --- /dev/null +++ b/trunk/drivers/staging/gma500/mdfld_dsi_dbi_dpu.h @@ -0,0 +1,154 @@ +/* + * Copyright © 2010 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: + * jim liu + * Jackie Li + */ + +#ifndef __MDFLD_DSI_DBI_DPU_H__ +#define __MDFLD_DSI_DBI_DPU_H__ + +#include "mdfld_dsi_dbi.h" + +typedef enum { + MDFLD_PLANEA, + MDFLD_PLANEC, + MDFLD_CURSORA, + MDFLD_CURSORC, + MDFLD_OVERLAYA, + MDFLD_OVERLAYC, + MDFLD_PLANE_NUM, +} mdfld_plane_t; + +#define MDFLD_PIPEA_PLANE_MASK 0x15 +#define MDFLD_PIPEC_PLANE_MASK 0x2A + +struct mdfld_cursor_info { + int x, y; + int size; +}; + +#define MDFLD_CURSOR_SIZE 64 + +/* + * enter DSR mode if screen has no update for 2 frames. + */ +#define MDFLD_MAX_IDLE_COUNT 2 + +struct mdfld_dbi_dpu_info { + struct drm_device *dev; + /* Lock */ + spinlock_t dpu_update_lock; + + /* Cursor postion */ + struct mdfld_cursor_info cursors[2]; + + /* Damaged area for each plane */ + struct psb_drm_dpu_rect damaged_rects[MDFLD_PLANE_NUM]; + + /* Final damaged area */ + struct psb_drm_dpu_rect damage_pipea; + struct psb_drm_dpu_rect damage_pipec; + + /* Pending */ + u32 pending; + + /* DPU timer */ + struct timer_list dpu_timer; + spinlock_t dpu_timer_lock; + + /* DPU idle count */ + u32 idle_count; + + /* DSI outputs */ + struct mdfld_dsi_dbi_output *dbi_outputs[2]; + int dbi_output_num; +}; + +static inline int mdfld_dpu_region_extent(struct psb_drm_dpu_rect *origin, + struct psb_drm_dpu_rect *rect) +{ + int x1, y1, x2, y2; + + x1 = origin->x + origin->width; + y1 = origin->y + origin->height; + + x2 = rect->x + rect->width; + y2 = rect->y + rect->height; + + origin->x = min(origin->x, rect->x); + origin->y = min(origin->y, rect->y); + origin->width = max(x1, x2) - origin->x; + origin->height = max(y1, y2) - origin->y; + + return 0; +} + +static inline void mdfld_check_boundary(struct mdfld_dbi_dpu_info *dpu_info, + struct psb_drm_dpu_rect *rect) +{ + if (rect->x < 0) + rect->x = 0; + if (rect->y < 0) + rect->y = 0; + + if (rect->x + rect->width > 864) + rect->width = 864 - rect->x; + if (rect->y + rect->height > 480) + rect->height = 480 - rect->height; + + if (!rect->width) + rect->width = 1; + if (!rect->height) + rect->height = 1; +} + +static inline void mdfld_dpu_init_damage(struct mdfld_dbi_dpu_info *dpu_info, + int pipe) +{ + struct psb_drm_dpu_rect *rect; + + if (pipe == 0) + rect = &dpu_info->damage_pipea; + else + rect = &dpu_info->damage_pipec; + + rect->x = 864; + rect->y = 480; + rect->width = -864; + rect->height = -480; +} + +extern int mdfld_dsi_dbi_dsr_off(struct drm_device *dev, + struct psb_drm_dpu_rect *rect); +extern int mdfld_dbi_dpu_report_damage(struct drm_device *dev, + mdfld_plane_t plane, + struct psb_drm_dpu_rect *rect); +extern int mdfld_dbi_dpu_report_fullscreen_damage(struct drm_device *dev); +extern int mdfld_dpu_exit_dsr(struct drm_device *dev); +extern void mdfld_dbi_dpu_timer_start(struct mdfld_dbi_dpu_info *dpu_info); +extern int mdfld_dbi_dpu_init(struct drm_device *dev); +extern void mdfld_dbi_dpu_exit(struct drm_device *dev); +extern void mdfld_dpu_update_panel(struct drm_device *dev); + +#endif /*__MDFLD_DSI_DBI_DPU_H__*/ diff --git a/trunk/drivers/staging/gma500/mdfld_dsi_dpi.c b/trunk/drivers/staging/gma500/mdfld_dsi_dpi.c new file mode 100644 index 000000000000..e685f1217baa --- /dev/null +++ b/trunk/drivers/staging/gma500/mdfld_dsi_dpi.c @@ -0,0 +1,805 @@ +/* + * Copyright © 2010 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: + * jim liu + * Jackie Li + */ + +#include "mdfld_dsi_dpi.h" +#include "mdfld_output.h" +#include "mdfld_dsi_pkg_sender.h" + + +static void mdfld_wait_for_HS_DATA_FIFO(struct drm_device *dev, u32 pipe) +{ + u32 gen_fifo_stat_reg = MIPIA_GEN_FIFO_STAT_REG; + int timeout = 0; + + if (pipe == 2) + gen_fifo_stat_reg += MIPIC_REG_OFFSET; + + udelay(500); + + /* This will time out after approximately 2+ seconds */ + while ((timeout < 20000) && (REG_READ(gen_fifo_stat_reg) & DSI_FIFO_GEN_HS_DATA_FULL)) { + udelay(100); + timeout++; + } + + if (timeout == 20000) + dev_warn(dev->dev, "MIPI: HS Data FIFO was never cleared!\n"); +} + +static void mdfld_wait_for_HS_CTRL_FIFO(struct drm_device *dev, u32 pipe) +{ + u32 gen_fifo_stat_reg = MIPIA_GEN_FIFO_STAT_REG; + int timeout = 0; + + if (pipe == 2) + gen_fifo_stat_reg += MIPIC_REG_OFFSET; + + udelay(500); + + /* This will time out after approximately 2+ seconds */ + while ((timeout < 20000) && (REG_READ(gen_fifo_stat_reg) & DSI_FIFO_GEN_HS_CTRL_FULL)) { + udelay(100); + timeout++; + } + if (timeout == 20000) + dev_warn(dev->dev, "MIPI: HS CMD FIFO was never cleared!\n"); +} + +static void mdfld_wait_for_DPI_CTRL_FIFO(struct drm_device *dev, u32 pipe) +{ + u32 gen_fifo_stat_reg = MIPIA_GEN_FIFO_STAT_REG; + int timeout = 0; + + if (pipe == 2) + gen_fifo_stat_reg += MIPIC_REG_OFFSET; + + udelay(500); + + /* This will time out after approximately 2+ seconds */ + while ((timeout < 20000) && ((REG_READ(gen_fifo_stat_reg) & DPI_FIFO_EMPTY) + != DPI_FIFO_EMPTY)) { + udelay(100); + timeout++; + } + + if (timeout == 20000) + dev_warn(dev->dev, "MIPI: DPI FIFO was never cleared!\n"); +} + +static void mdfld_wait_for_SPL_PKG_SENT(struct drm_device *dev, u32 pipe) +{ + u32 intr_stat_reg = MIPIA_INTR_STAT_REG; + int timeout = 0; + + if (pipe == 2) + intr_stat_reg += MIPIC_REG_OFFSET; + + udelay(500); + + /* This will time out after approximately 2+ seconds */ + while ((timeout < 20000) && (!(REG_READ(intr_stat_reg) & DSI_INTR_STATE_SPL_PKG_SENT))) { + udelay(100); + timeout++; + } + + if (timeout == 20000) + dev_warn(dev->dev, "MIPI: SPL_PKT_SENT_INTERRUPT was not sent successfully!\n"); +} + + +/* ************************************************************************* *\ + * FUNCTION: mdfld_dsi_tpo_ic_init + * + * DESCRIPTION: This function is called only by mrst_dsi_mode_set and + * restore_display_registers. since this function does not + * acquire the mutex, it is important that the calling function + * does! +\* ************************************************************************* */ +void mdfld_dsi_tpo_ic_init(struct mdfld_dsi_config *dsi_config, u32 pipe) +{ + struct drm_device *dev = dsi_config->dev; + u32 dcsChannelNumber = dsi_config->channel_num; + u32 gen_data_reg = MIPIA_HS_GEN_DATA_REG; + u32 gen_ctrl_reg = MIPIA_HS_GEN_CTRL_REG; + u32 gen_ctrl_val = GEN_LONG_WRITE; + + if (pipe == 2) { + gen_data_reg = HS_GEN_DATA_REG + MIPIC_REG_OFFSET; + gen_ctrl_reg = HS_GEN_CTRL_REG + MIPIC_REG_OFFSET; + } + + gen_ctrl_val |= dcsChannelNumber << DCS_CHANNEL_NUMBER_POS; + + /* Flip page order */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x00008036); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x02 << WORD_COUNTS_POS)); + + /* 0xF0 */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x005a5af0); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS)); + + /* Write protection key */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x005a5af1); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS)); + + /* 0xFC */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x005a5afc); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS)); + + /* 0xB7 */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x770000b7); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x00000044); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x05 << WORD_COUNTS_POS)); + + /* 0xB6 */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x000a0ab6); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS)); + + /* 0xF2 */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x081010f2); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x4a070708); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x000000c5); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x09 << WORD_COUNTS_POS)); + + /* 0xF8 */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x024003f8); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x01030a04); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x0e020220); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x00000004); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x0d << WORD_COUNTS_POS)); + + /* 0xE2 */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x398fc3e2); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x0000916f); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x06 << WORD_COUNTS_POS)); + + /* 0xB0 */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x000000b0); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x02 << WORD_COUNTS_POS)); + + /* 0xF4 */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x240242f4); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x78ee2002); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x2a071050); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x507fee10); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x10300710); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x14 << WORD_COUNTS_POS)); + + /* 0xBA */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x19fe07ba); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x101c0a31); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x00000010); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x09 << WORD_COUNTS_POS)); + + /* 0xBB */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x28ff07bb); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x24280a31); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x00000034); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x09 << WORD_COUNTS_POS)); + + /* 0xFB */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x535d05fb); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x1b1a2130); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x221e180e); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x131d2120); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x535d0508); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x1c1a2131); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x231f160d); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x111b2220); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x535c2008); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x1f1d2433); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x2c251a10); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x2c34372d); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x00000023); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x31 << WORD_COUNTS_POS)); + + /* 0xFA */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x525c0bfa); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x1c1c232f); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x2623190e); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x18212625); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x545d0d0e); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x1e1d2333); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x26231a10); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x1a222725); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x545d280f); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x21202635); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x31292013); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x31393d33); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x00000029); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x31 << WORD_COUNTS_POS)); + + /* Set DM */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x000100f7); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS)); +} + +static u16 mdfld_dsi_dpi_to_byte_clock_count(int pixel_clock_count, + int num_lane, int bpp) +{ + return (u16)((pixel_clock_count * bpp) / (num_lane * 8)); +} + +/* + * Calculate the dpi time basing on a given drm mode @mode + * return 0 on success. + * FIXME: I was using proposed mode value for calculation, may need to + * use crtc mode values later + */ +int mdfld_dsi_dpi_timing_calculation(struct drm_display_mode *mode, + struct mdfld_dsi_dpi_timing *dpi_timing, + int num_lane, int bpp) +{ + int pclk_hsync, pclk_hfp, pclk_hbp, pclk_hactive; + int pclk_vsync, pclk_vfp, pclk_vbp, pclk_vactive; + + if(!mode || !dpi_timing) { + DRM_ERROR("Invalid parameter\n"); + return -EINVAL; + } + + pclk_hactive = mode->hdisplay; + pclk_hfp = mode->hsync_start - mode->hdisplay; + pclk_hsync = mode->hsync_end - mode->hsync_start; + pclk_hbp = mode->htotal - mode->hsync_end; + + pclk_vactive = mode->vdisplay; + pclk_vfp = mode->vsync_start - mode->vdisplay; + pclk_vsync = mode->vsync_end - mode->vsync_start; + pclk_vbp = mode->vtotal - mode->vsync_end; + + /* + * byte clock counts were calculated by following formula + * bclock_count = pclk_count * bpp / num_lane / 8 + */ + dpi_timing->hsync_count = mdfld_dsi_dpi_to_byte_clock_count(pclk_hsync, num_lane, bpp); + dpi_timing->hbp_count = mdfld_dsi_dpi_to_byte_clock_count(pclk_hbp, num_lane, bpp); + dpi_timing->hfp_count = mdfld_dsi_dpi_to_byte_clock_count(pclk_hfp, num_lane, bpp); + dpi_timing->hactive_count = mdfld_dsi_dpi_to_byte_clock_count(pclk_hactive, num_lane, bpp); + dpi_timing->vsync_count = mdfld_dsi_dpi_to_byte_clock_count(pclk_vsync, num_lane, bpp); + dpi_timing->vbp_count = mdfld_dsi_dpi_to_byte_clock_count(pclk_vbp, num_lane, bpp); + dpi_timing->vfp_count = mdfld_dsi_dpi_to_byte_clock_count(pclk_vfp, num_lane, bpp); + + return 0; +} + +void mdfld_dsi_dpi_controller_init(struct mdfld_dsi_config *dsi_config, int pipe) +{ + struct drm_device *dev = dsi_config->dev; + u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0; + int lane_count = dsi_config->lane_count; + struct mdfld_dsi_dpi_timing dpi_timing; + struct drm_display_mode *mode = dsi_config->mode; + u32 val = 0; + + /*un-ready device*/ + REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000000); + + /*init dsi adapter before kicking off*/ + REG_WRITE((MIPIA_CONTROL_REG + reg_offset), 0x00000018); + + /*enable all interrupts*/ + REG_WRITE((MIPIA_INTR_EN_REG + reg_offset), 0xffffffff); + + + /*set up func_prg*/ + val |= lane_count; + val |= dsi_config->channel_num << DSI_DPI_VIRT_CHANNEL_OFFSET; + + switch(dsi_config->bpp) { + case 16: + val |= DSI_DPI_COLOR_FORMAT_RGB565; + break; + case 18: + val |= DSI_DPI_COLOR_FORMAT_RGB666; + break; + case 24: + val |= DSI_DPI_COLOR_FORMAT_RGB888; + break; + default: + DRM_ERROR("unsupported color format, bpp = %d\n", dsi_config->bpp); + } + REG_WRITE((MIPIA_DSI_FUNC_PRG_REG + reg_offset), val); + + REG_WRITE((MIPIA_HS_TX_TIMEOUT_REG + reg_offset), + (mode->vtotal * mode->htotal * dsi_config->bpp / (8 * lane_count)) & DSI_HS_TX_TIMEOUT_MASK); + REG_WRITE((MIPIA_LP_RX_TIMEOUT_REG + reg_offset), 0xffff & DSI_LP_RX_TIMEOUT_MASK); + + /*max value: 20 clock cycles of txclkesc*/ + REG_WRITE((MIPIA_TURN_AROUND_TIMEOUT_REG + reg_offset), 0x14 & DSI_TURN_AROUND_TIMEOUT_MASK); + + /*min 21 txclkesc, max: ffffh*/ + REG_WRITE((MIPIA_DEVICE_RESET_TIMER_REG + reg_offset), 0xffff & DSI_RESET_TIMER_MASK); + + REG_WRITE((MIPIA_DPI_RESOLUTION_REG + reg_offset), mode->vdisplay << 16 | mode->hdisplay); + + /*set DPI timing registers*/ + mdfld_dsi_dpi_timing_calculation(mode, &dpi_timing, dsi_config->lane_count, dsi_config->bpp); + + REG_WRITE((MIPIA_HSYNC_COUNT_REG + reg_offset), dpi_timing.hsync_count & DSI_DPI_TIMING_MASK); + REG_WRITE((MIPIA_HBP_COUNT_REG + reg_offset), dpi_timing.hbp_count & DSI_DPI_TIMING_MASK); + REG_WRITE((MIPIA_HFP_COUNT_REG + reg_offset), dpi_timing.hfp_count & DSI_DPI_TIMING_MASK); + REG_WRITE((MIPIA_HACTIVE_COUNT_REG + reg_offset), dpi_timing.hactive_count & DSI_DPI_TIMING_MASK); + REG_WRITE((MIPIA_VSYNC_COUNT_REG + reg_offset), dpi_timing.vsync_count & DSI_DPI_TIMING_MASK); + REG_WRITE((MIPIA_VBP_COUNT_REG + reg_offset), dpi_timing.vbp_count & DSI_DPI_TIMING_MASK); + REG_WRITE((MIPIA_VFP_COUNT_REG + reg_offset), dpi_timing.vfp_count & DSI_DPI_TIMING_MASK); + + REG_WRITE((MIPIA_HIGH_LOW_SWITCH_COUNT_REG + reg_offset), 0x46); + + /*min: 7d0 max: 4e20*/ + REG_WRITE((MIPIA_INIT_COUNT_REG + reg_offset), 0x000007d0); + + /*set up video mode*/ + val = 0; + val = dsi_config->video_mode | DSI_DPI_COMPLETE_LAST_LINE; + REG_WRITE((MIPIA_VIDEO_MODE_FORMAT_REG + reg_offset), val); + + REG_WRITE((MIPIA_EOT_DISABLE_REG + reg_offset), 0x00000000); + + REG_WRITE((MIPIA_LP_BYTECLK_REG + reg_offset), 0x00000004); + + /*TODO: figure out how to setup these registers*/ + REG_WRITE((MIPIA_DPHY_PARAM_REG + reg_offset), 0x150c3408); + + REG_WRITE((MIPIA_CLK_LANE_SWITCH_TIME_CNT_REG + reg_offset), (0xa << 16) | 0x14); + /*set device ready*/ + REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000001); +} + +void mdfld_dsi_dpi_turn_on(struct mdfld_dsi_dpi_output *output, int pipe) +{ + struct drm_device *dev = output->dev; + u32 reg_offset = 0; + + if(output->panel_on) + return; + + if(pipe) + reg_offset = MIPIC_REG_OFFSET; + + /* clear special packet sent bit */ + if(REG_READ(MIPIA_INTR_STAT_REG + reg_offset) & DSI_INTR_STATE_SPL_PKG_SENT) { + REG_WRITE((MIPIA_INTR_STAT_REG + reg_offset), DSI_INTR_STATE_SPL_PKG_SENT); + } + + /*send turn on package*/ + REG_WRITE((MIPIA_DPI_CONTROL_REG + reg_offset), DSI_DPI_CTRL_HS_TURN_ON); + + /*wait for SPL_PKG_SENT interrupt*/ + mdfld_wait_for_SPL_PKG_SENT(dev, pipe); + + if(REG_READ(MIPIA_INTR_STAT_REG + reg_offset) & DSI_INTR_STATE_SPL_PKG_SENT) { + REG_WRITE((MIPIA_INTR_STAT_REG + reg_offset), DSI_INTR_STATE_SPL_PKG_SENT); + } + + output->panel_on = 1; + + /* FIXME the following is disabled to WA the X slow start issue for TMD panel */ + /* if(pipe == 2) */ + /* dev_priv->dpi_panel_on2 = true; */ + /* else if (pipe == 0) */ + /* dev_priv->dpi_panel_on = true; */ +} + +static void mdfld_dsi_dpi_shut_down(struct mdfld_dsi_dpi_output *output, int pipe) +{ + struct drm_device *dev = output->dev; + u32 reg_offset = 0; + + /*if output is on, or mode setting didn't happen, ignore this*/ + if((!output->panel_on) || output->first_boot) { + output->first_boot = 0; + return; + } + + if(pipe) + reg_offset = MIPIC_REG_OFFSET; + + /* Wait for dpi fifo to empty */ + mdfld_wait_for_DPI_CTRL_FIFO(dev, pipe); + + /* Clear the special packet interrupt bit if set */ + if(REG_READ(MIPIA_INTR_STAT_REG + reg_offset) & DSI_INTR_STATE_SPL_PKG_SENT) { + REG_WRITE((MIPIA_INTR_STAT_REG + reg_offset), DSI_INTR_STATE_SPL_PKG_SENT); + } + + if(REG_READ(MIPIA_DPI_CONTROL_REG + reg_offset) == DSI_DPI_CTRL_HS_SHUTDOWN) { + dev_warn(dev->dev, "try to send the same package again, abort!"); + goto shutdown_out; + } + + REG_WRITE((MIPIA_DPI_CONTROL_REG + reg_offset), DSI_DPI_CTRL_HS_SHUTDOWN); + +shutdown_out: + output->panel_on = 0; + output->first_boot = 0; + + /* FIXME the following is disabled to WA the X slow start issue for TMD panel */ + /* if(pipe == 2) */ + /* dev_priv->dpi_panel_on2 = false; */ + /* else if (pipe == 0) */ + /* dev_priv->dpi_panel_on = false; */ + /* #ifdef CONFIG_PM_RUNTIME*/ + /* if (drm_psb_ospm && !enable_gfx_rtpm) { */ + /* pm_runtime_allow(&gpDrmDevice->pdev->dev); */ + /* schedule_delayed_work(&dev_priv->rtpm_work, 30 * 1000); */ + /* } */ + /*if (enable_gfx_rtpm) */ + /* pm_schedule_suspend(&dev->pdev->dev, gfxrtdelay); */ + /* #endif */ +} + +void mdfld_dsi_dpi_set_power(struct drm_encoder *encoder, bool on) +{ + struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); + struct mdfld_dsi_dpi_output *dpi_output = MDFLD_DSI_DPI_OUTPUT(dsi_encoder); + struct mdfld_dsi_config *dsi_config = mdfld_dsi_encoder_get_config(dsi_encoder); + int pipe = mdfld_dsi_encoder_get_pipe(dsi_encoder); + struct drm_device *dev = dsi_config->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + u32 mipi_reg = MIPI; + u32 pipeconf_reg = PIPEACONF; + + if(pipe) { + mipi_reg = MIPI_C; + pipeconf_reg = PIPECCONF; + } + + /* Start up display island if it was shutdown */ + if (!gma_power_begin(dev, true)) + return; + + if(on) { + if (mdfld_get_panel_type(dev, pipe) == TMD_VID){ + mdfld_dsi_dpi_turn_on(dpi_output, pipe); + } else { + /* Enable mipi port */ + REG_WRITE(mipi_reg, (REG_READ(mipi_reg) | (1 << 31))); + REG_READ(mipi_reg); + + mdfld_dsi_dpi_turn_on(dpi_output, pipe); + mdfld_dsi_tpo_ic_init(dsi_config, pipe); + } + + if(pipe == 2) { + dev_priv->dpi_panel_on2 = true; + } + else { + dev_priv->dpi_panel_on = true; + } + + } else { + if (mdfld_get_panel_type(dev, pipe) == TMD_VID) { + mdfld_dsi_dpi_shut_down(dpi_output, pipe); + } else { + mdfld_dsi_dpi_shut_down(dpi_output, pipe); + /* Disable mipi port */ + REG_WRITE(mipi_reg, (REG_READ(mipi_reg) & ~(1<<31))); + REG_READ(mipi_reg); + } + + if(pipe == 2) + dev_priv->dpi_panel_on2 = false; + else + dev_priv->dpi_panel_on = false; + } + gma_power_end(dev); +} + +void mdfld_dsi_dpi_dpms(struct drm_encoder *encoder, int mode) +{ + dev_dbg(encoder->dev->dev, "DPMS %s\n", + (mode == DRM_MODE_DPMS_ON ? "on":"off")); + + if (mode == DRM_MODE_DPMS_ON) + mdfld_dsi_dpi_set_power(encoder, true); + else { + mdfld_dsi_dpi_set_power(encoder, false); +#if 0 /* FIXME */ +#ifdef CONFIG_PM_RUNTIME + if (enable_gfx_rtpm) + pm_schedule_suspend(&gpDrmDevice->pdev->dev, gfxrtdelay); +#endif +#endif + } +} + +bool mdfld_dsi_dpi_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); + struct mdfld_dsi_config *dsi_config = mdfld_dsi_encoder_get_config(dsi_encoder); + struct drm_display_mode *fixed_mode = dsi_config->fixed_mode; + + if(fixed_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); + } + + return true; +} + +void mdfld_dsi_dpi_prepare(struct drm_encoder *encoder) +{ + mdfld_dsi_dpi_set_power(encoder, false); +} + +void mdfld_dsi_dpi_commit(struct drm_encoder *encoder) +{ + mdfld_dsi_dpi_set_power(encoder, true); +} + +void mdfld_dsi_dpi_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); + struct mdfld_dsi_dpi_output *dpi_output = MDFLD_DSI_DPI_OUTPUT(dsi_encoder); + struct mdfld_dsi_config *dsi_config = mdfld_dsi_encoder_get_config(dsi_encoder); + struct drm_device *dev = dsi_config->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + int pipe = mdfld_dsi_encoder_get_pipe(dsi_encoder); + + u32 pipeconf_reg = PIPEACONF; + u32 dspcntr_reg = DSPACNTR; + u32 mipi_reg = MIPI; + u32 reg_offset = 0; + + u32 pipeconf = dev_priv->pipeconf; + u32 dspcntr = dev_priv->dspcntr; + u32 mipi = MIPI_PORT_EN | PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX; + + dev_dbg(dev->dev, "set mode %dx%d on pipe %d\n", + mode->hdisplay, mode->vdisplay, pipe); + + if(pipe) { + pipeconf_reg = PIPECCONF; + dspcntr_reg = DSPCCNTR; + mipi_reg = MIPI_C; + reg_offset = MIPIC_REG_OFFSET; + } else { + mipi |= 2; + } + + if (!gma_power_begin(dev, true)) + return; + + /* Set up mipi port FIXME: do at init time */ + REG_WRITE(mipi_reg, mipi); + REG_READ(mipi_reg); + + /* Set up DSI controller DPI interface */ + mdfld_dsi_dpi_controller_init(dsi_config, pipe); + + if (mdfld_get_panel_type(dev, pipe) != TMD_VID) { + /* Turn on DPI interface */ + mdfld_dsi_dpi_turn_on(dpi_output, pipe); + } + + /* Set up pipe */ + REG_WRITE(pipeconf_reg, pipeconf); + REG_READ(pipeconf_reg); + + /* Set up display plane */ + REG_WRITE(dspcntr_reg, dspcntr); + REG_READ(dspcntr_reg); + + msleep(20); /* FIXME: this should wait for vblank */ + + dev_dbg(dev->dev, "State %x, power %d\n", + REG_READ(MIPIA_INTR_STAT_REG + reg_offset), + dpi_output->panel_on); + + if (mdfld_get_panel_type(dev, pipe) != TMD_VID) { + /* Init driver ic */ + mdfld_dsi_tpo_ic_init(dsi_config, pipe); + /* Init backlight */ + mdfld_dsi_brightness_init(dsi_config, pipe); + } + gma_power_end(dev); +} + + +/* + * Init DSI DPI encoder. + * Allocate an mdfld_dsi_encoder and attach it to given @dsi_connector + * return pointer of newly allocated DPI encoder, NULL on error + */ +struct mdfld_dsi_encoder *mdfld_dsi_dpi_init(struct drm_device *dev, + struct mdfld_dsi_connector *dsi_connector, + struct panel_funcs *p_funcs) +{ + struct mdfld_dsi_dpi_output *dpi_output = NULL; + struct mdfld_dsi_config *dsi_config; + struct drm_connector *connector = NULL; + struct drm_encoder *encoder = NULL; + struct drm_display_mode *fixed_mode = NULL; + int pipe; + u32 data; + int ret; + + if (!dsi_connector || !p_funcs) { + WARN_ON(1); + return NULL; + } + + dsi_config = mdfld_dsi_get_config(dsi_connector); + pipe = dsi_connector->pipe; + + /* Panel hard-reset */ + if (p_funcs->reset) { + ret = p_funcs->reset(pipe); + if (ret) { + DRM_ERROR("Panel %d hard-reset failed\n", pipe); + return NULL; + } + } + + /* Panel drvIC init */ + if (p_funcs->drv_ic_init) + p_funcs->drv_ic_init(dsi_config, pipe); + + /* Panel power mode detect */ + ret = mdfld_dsi_get_power_mode(dsi_config, + &data, + MDFLD_DSI_LP_TRANSMISSION); + if (ret) { + DRM_ERROR("Panel %d get power mode failed\n", pipe); + dsi_connector->status = connector_status_disconnected; + } else { + DRM_INFO("pipe %d power mode 0x%x\n", pipe, data); + dsi_connector->status = connector_status_connected; + } + + dpi_output = kzalloc(sizeof(struct mdfld_dsi_dpi_output), GFP_KERNEL); + if(!dpi_output) { + dev_err(dev->dev, "No memory for dsi_dpi_output\n"); + return NULL; + } + + if(dsi_connector->pipe) + dpi_output->panel_on = 0; + else + dpi_output->panel_on = 0; + + dpi_output->dev = dev; + dpi_output->p_funcs = p_funcs; + dpi_output->first_boot = 1; + + /* Get fixed mode */ + dsi_config = mdfld_dsi_get_config(dsi_connector); + fixed_mode = dsi_config->fixed_mode; + + /* Create drm encoder object */ + connector = &dsi_connector->base.base; + encoder = &dpi_output->base.base; + /* + * On existing hardware this will be a panel of some form, + * if future devices also have HDMI bridges this will need + * revisiting + */ + drm_encoder_init(dev, + encoder, + p_funcs->encoder_funcs, + DRM_MODE_ENCODER_LVDS); + drm_encoder_helper_add(encoder, + p_funcs->encoder_helper_funcs); + + /* Attach to given connector */ + drm_mode_connector_attach_encoder(connector, encoder); + + /* Set possible crtcs and clones */ + if(dsi_connector->pipe) { + encoder->possible_crtcs = (1 << 2); + encoder->possible_clones = (1 << 1); + } else { + encoder->possible_crtcs = (1 << 0); + encoder->possible_clones = (1 << 0); + } + return &dpi_output->base; +} + diff --git a/trunk/drivers/staging/gma500/mdfld_dsi_dpi.h b/trunk/drivers/staging/gma500/mdfld_dsi_dpi.h new file mode 100644 index 000000000000..ed92d45ee74a --- /dev/null +++ b/trunk/drivers/staging/gma500/mdfld_dsi_dpi.h @@ -0,0 +1,78 @@ +/* + * Copyright © 2010 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: + * jim liu + * Jackie Li + */ + +#ifndef __MDFLD_DSI_DPI_H__ +#define __MDFLD_DSI_DPI_H__ + +#include "mdfld_dsi_output.h" +#include "mdfld_output.h" + +struct mdfld_dsi_dpi_timing { + u16 hsync_count; + u16 hbp_count; + u16 hfp_count; + u16 hactive_count; + u16 vsync_count; + u16 vbp_count; + u16 vfp_count; +}; + +struct mdfld_dsi_dpi_output { + struct mdfld_dsi_encoder base; + struct drm_device *dev; + + int panel_on; + int first_boot; + + struct panel_funcs *p_funcs; +}; + +#define MDFLD_DSI_DPI_OUTPUT(dsi_encoder) \ + container_of(dsi_encoder, struct mdfld_dsi_dpi_output, base) + +extern int mdfld_dsi_dpi_timing_calculation(struct drm_display_mode *mode, + struct mdfld_dsi_dpi_timing *dpi_timing, + int num_lane, int bpp); +extern struct mdfld_dsi_encoder *mdfld_dsi_dpi_init(struct drm_device *dev, + struct mdfld_dsi_connector *dsi_connector, + struct panel_funcs *p_funcs); + +/* Medfield DPI helper functions */ +extern void mdfld_dsi_dpi_dpms(struct drm_encoder *encoder, int mode); +extern bool mdfld_dsi_dpi_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +extern void mdfld_dsi_dpi_prepare(struct drm_encoder *encoder); +extern void mdfld_dsi_dpi_commit(struct drm_encoder *encoder); +extern void mdfld_dsi_dpi_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +extern void mdfld_dsi_dpi_turn_on(struct mdfld_dsi_dpi_output *output, + int pipe); +extern void mdfld_dsi_dpi_controller_init(struct mdfld_dsi_config *si_config, + int pipe); +#endif /*__MDFLD_DSI_DPI_H__*/ diff --git a/trunk/drivers/staging/gma500/mdfld_dsi_output.c b/trunk/drivers/staging/gma500/mdfld_dsi_output.c new file mode 100644 index 000000000000..3f979db2c3a5 --- /dev/null +++ b/trunk/drivers/staging/gma500/mdfld_dsi_output.c @@ -0,0 +1,1014 @@ +/* + * Copyright © 2010 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: + * jim liu + * Jackie Li + */ + +#include "mdfld_dsi_output.h" +#include "mdfld_dsi_dbi.h" +#include "mdfld_dsi_dpi.h" +#include "mdfld_output.h" +#include +#include "mdfld_dsi_pkg_sender.h" +#include +#include + +#define MDFLD_DSI_BRIGHTNESS_MAX_LEVEL 100 + +static int CABC_control = 1; +static int LABC_control = 1; + +module_param (CABC_control, int, 0644); +module_param (LABC_control, int, 0644); + +/** + * make these MCS command global + * we don't need 'movl' everytime we send them. + * FIXME: these datas were provided by OEM, we should get them from GCT. + **/ +static u32 mdfld_dbi_mcs_hysteresis[] = { + 0x42000f57, 0x8c006400, 0xff00bf00, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x38000aff, 0x82005000, 0xff00ab00, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x000000ff, +}; + +static u32 mdfld_dbi_mcs_display_profile[] = { + 0x50281450, 0x0000c882, 0x00000000, 0x00000000, + 0x00000000, +}; + +static u32 mdfld_dbi_mcs_kbbc_profile[] = { + 0x00ffcc60, 0x00000000, 0x00000000, 0x00000000, +}; + +static u32 mdfld_dbi_mcs_gamma_profile[] = { + 0x81111158, 0x88888888, 0x88888888, +}; + +/* + * write hysteresis values. + */ +static void mdfld_dsi_write_hysteresis (struct mdfld_dsi_config *dsi_config, + int pipe) +{ + struct mdfld_dsi_pkg_sender *sender = mdfld_dsi_get_pkg_sender(dsi_config); + + if(!sender) { + WARN_ON(1); + return; + } + mdfld_dsi_send_mcs_long_hs(sender, + mdfld_dbi_mcs_hysteresis, + 17, + MDFLD_DSI_SEND_PACKAGE); +} + +/* + * write display profile values. + */ +static void mdfld_dsi_write_display_profile(struct mdfld_dsi_config *dsi_config, int pipe) +{ + struct mdfld_dsi_pkg_sender *sender = mdfld_dsi_get_pkg_sender(dsi_config); + + if(!sender) { + WARN_ON(1); + return; + } + mdfld_dsi_send_mcs_long_hs(sender, + mdfld_dbi_mcs_display_profile, + 5, + MDFLD_DSI_SEND_PACKAGE); +} + +/* + * write KBBC profile values. + */ +static void mdfld_dsi_write_kbbc_profile (struct mdfld_dsi_config * dsi_config, int pipe) +{ + struct mdfld_dsi_pkg_sender *sender = mdfld_dsi_get_pkg_sender(dsi_config); + + if(!sender) { + WARN_ON(1); + return; + } + mdfld_dsi_send_mcs_long_hs(sender, + mdfld_dbi_mcs_kbbc_profile, + 4, + MDFLD_DSI_SEND_PACKAGE); +} + +/* + * write gamma setting. + */ +static void mdfld_dsi_write_gamma_setting (struct mdfld_dsi_config *dsi_config, int pipe) +{ + struct mdfld_dsi_pkg_sender *sender = mdfld_dsi_get_pkg_sender(dsi_config); + + if(!sender) { + WARN_ON(1); + return; + } + mdfld_dsi_send_mcs_long_hs(sender, + mdfld_dbi_mcs_gamma_profile, + 3, + MDFLD_DSI_SEND_PACKAGE); +} + +/* + * Check and see if the generic control or data buffer is empty and ready. + */ +void mdfld_dsi_gen_fifo_ready (struct drm_device *dev, u32 gen_fifo_stat_reg, u32 fifo_stat) +{ + u32 GEN_BF_time_out_count = 0; + + /* Check MIPI Adatper command registers */ + for (GEN_BF_time_out_count = 0; GEN_BF_time_out_count < GEN_FB_TIME_OUT; GEN_BF_time_out_count++) + { + if ((REG_READ(gen_fifo_stat_reg) & fifo_stat) == fifo_stat) + break; + udelay (100); + } + + if (GEN_BF_time_out_count == GEN_FB_TIME_OUT) + dev_err(dev->dev, + "mdfld_dsi_gen_fifo_ready, Timeout. gen_fifo_stat_reg = 0x%x. \n", + gen_fifo_stat_reg); +} + +/* + * Manage the DSI MIPI keyboard and display brightness. + * FIXME: this is exported to OSPM code. should work out an specific + * display interface to OSPM. + */ +void mdfld_dsi_brightness_init(struct mdfld_dsi_config *dsi_config, int pipe) +{ + struct mdfld_dsi_pkg_sender *sender = mdfld_dsi_get_pkg_sender(dsi_config); + struct drm_device *dev = sender->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + u32 gen_ctrl_val; + + if(!sender) { + WARN_ON(1); + return; + } + /* Set default display backlight value to 85% (0xd8)*/ + mdfld_dsi_send_mcs_short_hs(sender, + write_display_brightness, + 0xd8, + 1, + MDFLD_DSI_SEND_PACKAGE); + + /* Set minimum brightness setting of CABC function to 20% (0x33)*/ + mdfld_dsi_send_mcs_short_hs(sender, + write_cabc_min_bright, + 0x33, + 1, + MDFLD_DSI_SEND_PACKAGE); + + mdfld_dsi_write_hysteresis(dsi_config, pipe); + mdfld_dsi_write_display_profile (dsi_config, pipe); + mdfld_dsi_write_kbbc_profile (dsi_config, pipe); + mdfld_dsi_write_gamma_setting (dsi_config, pipe); + + /* Enable backlight or/and LABC */ + gen_ctrl_val = BRIGHT_CNTL_BLOCK_ON | DISPLAY_DIMMING_ON| BACKLIGHT_ON; + if (LABC_control == 1 || CABC_control == 1) + gen_ctrl_val |= DISPLAY_DIMMING_ON| DISPLAY_BRIGHTNESS_AUTO | GAMMA_AUTO; + + if (LABC_control == 1) + gen_ctrl_val |= AMBIENT_LIGHT_SENSE_ON; + + dev_priv->mipi_ctrl_display = gen_ctrl_val; + + mdfld_dsi_send_mcs_short_hs(sender, + write_ctrl_display, + (u8)gen_ctrl_val, + 1, + MDFLD_DSI_SEND_PACKAGE); + + if (CABC_control == 0) + return; + mdfld_dsi_send_mcs_short_hs(sender, + write_ctrl_cabc, + UI_IMAGE, + 1, + MDFLD_DSI_SEND_PACKAGE); +} + +/* + * Manage the mipi display brightness. + * TODO: refine this interface later + */ +void mdfld_dsi_brightness_control(struct drm_device *dev, int pipe, int level) +{ + struct mdfld_dsi_pkg_sender *sender; + struct drm_psb_private *dev_priv; + struct mdfld_dsi_config *dsi_config; + u32 gen_ctrl_val; + int p_type; + + if (!dev || (pipe != 0 && pipe != 2)) { + dev_err(dev->dev, "Invalid parameter\n"); + return; + } + + p_type = mdfld_get_panel_type(dev, 0); + + dev_priv = dev->dev_private; + + if(pipe) + dsi_config = dev_priv->dsi_configs[1]; + else + dsi_config = dev_priv->dsi_configs[0]; + + sender = mdfld_dsi_get_pkg_sender(dsi_config); + + if(!sender) { + WARN_ON(1); + return; + } + + gen_ctrl_val = ((level * 0xff) / MDFLD_DSI_BRIGHTNESS_MAX_LEVEL) & 0xff; + + dev_dbg(dev->dev, + "pipe = %d, gen_ctrl_val = %d. \n", pipe, gen_ctrl_val); + + if(p_type == TMD_VID || p_type == TMD_CMD){ + /* Set display backlight value */ + mdfld_dsi_send_mcs_short_hs(sender, + tmd_write_display_brightness, + (u8)gen_ctrl_val, + 1, + MDFLD_DSI_SEND_PACKAGE); + } else { + /* Set display backlight value */ + mdfld_dsi_send_mcs_short_hs(sender, + write_display_brightness, + (u8)gen_ctrl_val, + 1, + MDFLD_DSI_SEND_PACKAGE); + + + /* Enable backlight control */ + if (level == 0) + gen_ctrl_val = 0; + else + gen_ctrl_val = dev_priv->mipi_ctrl_display; + + mdfld_dsi_send_mcs_short_hs(sender, + write_ctrl_display, + (u8)gen_ctrl_val, + 1, + MDFLD_DSI_SEND_PACKAGE); + } +} + +/* + * shut down DSI controller + */ +void mdfld_dsi_controller_shutdown(struct mdfld_dsi_config * dsi_config, int pipe) +{ + struct drm_device * dev; + u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0; + int retry = 100; + + if (!dsi_config) { + WARN_ON(1); + return; + } + + dev = dsi_config->dev; + + if (!gma_power_begin(dev, true)) { + dev_err(dev->dev, "hw begin failed\n"); + return; + } + + if(!(REG_READ(MIPIA_DEVICE_READY_REG + reg_offset) & DSI_DEVICE_READY)) + goto shutdown_out; + + /* Send shut down package, clean packet send bit first */ + if(REG_READ(MIPIA_INTR_STAT_REG + reg_offset) & DSI_INTR_STATE_SPL_PKG_SENT) { + REG_WRITE((MIPIA_INTR_STAT_REG + reg_offset), + (REG_READ(MIPIA_INTR_STAT_REG + reg_offset) | DSI_INTR_STATE_SPL_PKG_SENT)); + } + + /*send shut down package in HS*/ + REG_WRITE((MIPIA_DPI_CONTROL_REG + reg_offset), DSI_DPI_CTRL_HS_SHUTDOWN); + + + /* + * make sure shut down is sent. + * FIXME: add max retry counter + */ + while(!(REG_READ(MIPIA_INTR_STAT_REG + reg_offset) & DSI_INTR_STATE_SPL_PKG_SENT)) { + retry--; + + if(!retry) { + dev_err(dev->dev, "timeout\n"); + break; + } + } + + /*sleep 1 ms to ensure shutdown finished*/ + msleep(100); + + /*un-ready device*/ + REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), + (REG_READ(MIPIA_DEVICE_READY_REG + reg_offset) & ~DSI_DEVICE_READY)); + +shutdown_out: + gma_power_end(dev); +} + +void mdfld_dsi_controller_startup(struct mdfld_dsi_config * dsi_config, int pipe) +{ + struct drm_device * dev; + u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0; + int retry = 100; + + + if (!dsi_config) { + WARN_ON(1); + return; + } + + dev = dsi_config->dev; + dev_dbg(dev->dev, "starting up DSI controller on pipe %d...\n", pipe); + + if (!gma_power_begin(dev, true)) { + dev_err(dev->dev, "hw begin failed\n"); + return; + } + + if((REG_READ(MIPIA_DEVICE_READY_REG + reg_offset) & DSI_DEVICE_READY)) + goto startup_out; + + /*if config DPI, turn on DPI interface*/ + if(dsi_config->type == MDFLD_DSI_ENCODER_DPI) { + if(REG_READ(MIPIA_INTR_STAT_REG + reg_offset) & DSI_INTR_STATE_SPL_PKG_SENT) { + REG_WRITE((MIPIA_INTR_STAT_REG + reg_offset), DSI_INTR_STATE_SPL_PKG_SENT); + } + + REG_WRITE((MIPIA_DPI_CONTROL_REG + reg_offset), DSI_DPI_CTRL_HS_TURN_ON); + + /* + * make sure shut down is sent. + * FIXME: add max retry counter + */ + while(!(REG_READ(MIPIA_INTR_STAT_REG + reg_offset) & DSI_INTR_STATE_SPL_PKG_SENT)) { + retry--; + if(!retry) { + dev_err(dev->dev, "timeout\n"); + break; + } + } + + msleep(100); + } + + /*set device ready*/ + REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), + (REG_READ(MIPIA_DEVICE_READY_REG + reg_offset) | DSI_DEVICE_READY)); + +startup_out: + gma_power_end(dev); +} + + +static int mdfld_dsi_get_panel_status(struct mdfld_dsi_config *dsi_config, + u8 dcs, + u32 *data, + u8 transmission) +{ + struct mdfld_dsi_pkg_sender *sender + = mdfld_dsi_get_pkg_sender(dsi_config); + + if (!sender || !data) { + DRM_ERROR("Invalid parameter\n"); + return -EINVAL; + } + + if (transmission == MDFLD_DSI_HS_TRANSMISSION) + return mdfld_dsi_read_mcs_hs(sender, dcs, data, 1); + else if (transmission == MDFLD_DSI_LP_TRANSMISSION) + return mdfld_dsi_read_mcs_lp(sender, dcs, data, 1); + else + return -EINVAL; +} + +int mdfld_dsi_get_power_mode(struct mdfld_dsi_config *dsi_config, + u32 *mode, + u8 transmission) +{ + if (!dsi_config || !mode) { + DRM_ERROR("Invalid parameter\n"); + return -EINVAL; + } + + return mdfld_dsi_get_panel_status(dsi_config, 0x0a, mode, transmission); +} + +int mdfld_dsi_get_diagnostic_result(struct mdfld_dsi_config *dsi_config, + u32 *result, + u8 transmission) +{ + if (!dsi_config || !result) { + DRM_ERROR("Invalid parameter\n"); + return -EINVAL; + } + + return mdfld_dsi_get_panel_status(dsi_config, 0x0f, result, + transmission); +} + +/* + * NOTE: this function was used by OSPM. + * TODO: will be removed later, should work out display interfaces for OSPM + */ +void mdfld_dsi_controller_init(struct mdfld_dsi_config * dsi_config, int pipe) +{ + if(!dsi_config || ((pipe != 0) && (pipe != 2))) { + WARN_ON(1); + return; + } + + if(dsi_config->type) + mdfld_dsi_dpi_controller_init(dsi_config, pipe); + else + mdfld_dsi_controller_dbi_init(dsi_config, pipe); +} + +static void mdfld_dsi_connector_save(struct drm_connector * connector) +{ +} + +static void mdfld_dsi_connector_restore(struct drm_connector * connector) +{ +} + +static enum drm_connector_status mdfld_dsi_connector_detect(struct drm_connector * connector, bool force) +{ + struct psb_intel_output *psb_output + = to_psb_intel_output(connector); + struct mdfld_dsi_connector *dsi_connector + = MDFLD_DSI_CONNECTOR(psb_output); + return dsi_connector->status; +} + +static int mdfld_dsi_connector_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t value) +{ + struct drm_encoder *encoder = connector->encoder; + + if (!strcmp(property->name, "scaling mode") && encoder) { + struct psb_intel_crtc * psb_crtc = to_psb_intel_crtc(encoder->crtc); + bool bTransitionFromToCentered; + uint64_t curValue; + + if (!psb_crtc) + goto set_prop_error; + + switch (value) { + case DRM_MODE_SCALE_FULLSCREEN: + break; + case DRM_MODE_SCALE_NO_SCALE: + break; + case DRM_MODE_SCALE_ASPECT: + break; + default: + goto set_prop_error; + } + + if (drm_connector_property_get_value(connector, property, &curValue)) + goto set_prop_error; + + if (curValue == value) + goto set_prop_done; + + if (drm_connector_property_set_value(connector, property, value)) + goto set_prop_error; + + bTransitionFromToCentered = (curValue == DRM_MODE_SCALE_NO_SCALE) || + (value == DRM_MODE_SCALE_NO_SCALE); + + if (psb_crtc->saved_mode.hdisplay != 0 && + psb_crtc->saved_mode.vdisplay != 0) { + if (bTransitionFromToCentered) { + if (!drm_crtc_helper_set_mode(encoder->crtc, &psb_crtc->saved_mode, + encoder->crtc->x, encoder->crtc->y, encoder->crtc->fb)) + goto set_prop_error; + } else { + struct drm_encoder_helper_funcs *pEncHFuncs = encoder->helper_private; + pEncHFuncs->mode_set(encoder, &psb_crtc->saved_mode, + &psb_crtc->saved_adjusted_mode); + } + } +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + } else if (!strcmp(property->name, "backlight") && encoder) { + struct drm_psb_private *dev_priv = encoder->dev->dev_private; + struct backlight_device *psb_bd = dev_priv->backlight_device; + dev_dbg(encoder->dev->dev, "backlight level = %d\n", (int)value); + if (drm_connector_property_set_value(connector, property, value)) + goto set_prop_error; + else { + dev_dbg(encoder->dev->dev, + "set brightness to %d", (int)value); + if (psb_bd) { + psb_bd->props.brightness = value; + backlight_update_status(psb_bd); + } + } +#endif + } +set_prop_done: + return 0; +set_prop_error: + return -1; +} + +static void mdfld_dsi_connector_destroy(struct drm_connector *connector) +{ + struct psb_intel_output * psb_output = to_psb_intel_output(connector); + struct mdfld_dsi_connector * dsi_connector = MDFLD_DSI_CONNECTOR(psb_output); + struct mdfld_dsi_pkg_sender * sender; + + if(!dsi_connector) + return; + + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + + sender = dsi_connector->pkg_sender; + + mdfld_dsi_pkg_sender_destroy(sender); + + kfree(dsi_connector); +} + +static int mdfld_dsi_connector_get_modes(struct drm_connector * connector) +{ + struct psb_intel_output * psb_output = to_psb_intel_output(connector); + struct mdfld_dsi_connector * dsi_connector = MDFLD_DSI_CONNECTOR(psb_output); + struct mdfld_dsi_config * dsi_config = mdfld_dsi_get_config(dsi_connector); + struct drm_display_mode * fixed_mode = dsi_config->fixed_mode; + struct drm_display_mode * dup_mode = NULL; + struct drm_device * dev = connector->dev; + + connector->display_info.min_vfreq = 0; + connector->display_info.max_vfreq = 200; + connector->display_info.min_hfreq = 0; + connector->display_info.max_hfreq = 200; + + if(fixed_mode) { + dev_dbg(dev->dev, "fixed_mode %dx%d\n", + fixed_mode->hdisplay, fixed_mode->vdisplay); + + dup_mode = drm_mode_duplicate(dev, fixed_mode); + drm_mode_probed_add(connector, dup_mode); + return 1; + } + dev_err(dev->dev, "Didn't get any modes!\n"); + return 0; +} + +static int mdfld_dsi_connector_mode_valid(struct drm_connector * connector, struct drm_display_mode * mode) +{ + struct psb_intel_output * psb_output = to_psb_intel_output(connector); + struct mdfld_dsi_connector * dsi_connector = MDFLD_DSI_CONNECTOR(psb_output); + struct mdfld_dsi_config * dsi_config = mdfld_dsi_get_config(dsi_connector); + struct drm_display_mode * fixed_mode = dsi_config->fixed_mode; + + dev_dbg(connector->dev->dev, "mode %p, fixed mode %p\n", + mode, fixed_mode); + + if(mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + + if(mode->flags & DRM_MODE_FLAG_INTERLACE) + return MODE_NO_INTERLACE; + + /** + * FIXME: current DC has no fitting unit, reject any mode setting request + * will figure out a way to do up-scaling(pannel fitting) later. + **/ + if(fixed_mode) { + if(mode->hdisplay != fixed_mode->hdisplay) + return MODE_PANEL; + + if(mode->vdisplay != fixed_mode->vdisplay) + return MODE_PANEL; + } + dev_dbg(connector->dev->dev, "mode ok\n"); + + return MODE_OK; +} + +static void mdfld_dsi_connector_dpms(struct drm_connector *connector, int mode) +{ +#ifdef CONFIG_PM_RUNTIME + struct drm_device * dev = connector->dev; + struct drm_psb_private * dev_priv = dev->dev_private; + bool panel_on, panel_on2; +#endif + /* First, execute DPMS */ + drm_helper_connector_dpms(connector, mode); + +#ifdef CONFIG_PM_RUNTIME + if(mdfld_panel_dpi(dev)) { + /* DPI panel */ + panel_on = dev_priv->dpi_panel_on; + panel_on2 = dev_priv->dpi_panel_on2; + } else { + /* DBI panel */ + panel_on = dev_priv->dbi_panel_on; + panel_on2 = dev_priv->dbi_panel_on2; + } + + /* Then check all display panels + monitors status */ + /* Make sure that the Display (B) sub-system status isn't i3 when + * R/W the DC register, otherwise "Fabric error" issue would occur + * during S0i3 state. */ + if(!panel_on && !panel_on2 && !(REG_READ(HDMIB_CONTROL) + & HDMIB_PORT_EN)) { + /* Request rpm idle */ + if(dev_priv->rpm_enabled) + pm_request_idle(&dev->pdev->dev); + } + /* + * if rpm wasn't enabled yet, try to allow it + * FIXME: won't enable rpm for DPI since DPI + * CRTC setting is a little messy now. + * Enable it later! + */ +#if 0 + if(!dev_priv->rpm_enabled && !mdfld_panel_dpi(dev)) + ospm_runtime_pm_allow(dev); +#endif +#endif +} + +static struct drm_encoder *mdfld_dsi_connector_best_encoder( + struct drm_connector *connector) +{ + struct psb_intel_output * psb_output = to_psb_intel_output(connector); + struct mdfld_dsi_connector * dsi_connector = MDFLD_DSI_CONNECTOR(psb_output); + struct mdfld_dsi_config * dsi_config = mdfld_dsi_get_config(dsi_connector); + struct mdfld_dsi_encoder * encoder = NULL; + + if(dsi_config->type == MDFLD_DSI_ENCODER_DBI) + encoder = dsi_config->encoders[MDFLD_DSI_ENCODER_DBI]; + else if (dsi_config->type == MDFLD_DSI_ENCODER_DPI) + encoder = dsi_config->encoders[MDFLD_DSI_ENCODER_DPI]; + + dev_dbg(connector->dev->dev, "get encoder %p\n", encoder); + + if(!encoder) { + dev_err(connector->dev->dev, + "Invalid encoder for type %d\n", dsi_config->type); + return NULL; + } + dsi_config->encoder = encoder; + return &encoder->base; +} + +/* DSI connector funcs */ +static const struct drm_connector_funcs mdfld_dsi_connector_funcs = { + .dpms = /*drm_helper_connector_dpms*/mdfld_dsi_connector_dpms, + .save = mdfld_dsi_connector_save, + .restore = mdfld_dsi_connector_restore, + .detect = mdfld_dsi_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = mdfld_dsi_connector_set_property, + .destroy = mdfld_dsi_connector_destroy, +}; + +/* DSI connector helper funcs */ +static const struct drm_connector_helper_funcs mdfld_dsi_connector_helper_funcs = { + .get_modes = mdfld_dsi_connector_get_modes, + .mode_valid = mdfld_dsi_connector_mode_valid, + .best_encoder = mdfld_dsi_connector_best_encoder, +}; + +static int mdfld_dsi_get_default_config(struct drm_device * dev, + struct mdfld_dsi_config * config, int pipe) +{ + if(!dev || !config) { + WARN_ON(1); + return -EINVAL; + } + + config->bpp = 24; + config->type = mdfld_panel_dpi(dev); + config->lane_count = 2; + config->channel_num = 0; + /*NOTE: video mode is ignored when type is MDFLD_DSI_ENCODER_DBI*/ + if (mdfld_get_panel_type(dev, pipe) == TMD_VID) { + config->video_mode = MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_PULSE; + } else { + config->video_mode = MDFLD_DSI_VIDEO_BURST_MODE; + } + + return 0; +} + +/* + * Returns the panel fixed mode from configuration. + */ +struct drm_display_mode * +mdfld_dsi_get_configuration_mode(struct mdfld_dsi_config * dsi_config, int pipe) +{ + struct drm_device *dev = dsi_config->dev; + struct drm_display_mode *mode; + struct drm_psb_private *dev_priv = dev->dev_private; + struct mrst_timing_info *ti = &dev_priv->gct_data.DTD; + bool use_gct = false; + + mode = kzalloc(sizeof(*mode), GFP_KERNEL); + if (!mode) { + dev_err(dev->dev, "Out of memory for mode\n"); + return NULL; + } + if (use_gct) { + dev_dbg(dev->dev, "gct find MIPI panel.\n"); + + mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo; + mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo; + mode->hsync_start = mode->hdisplay + \ + ((ti->hsync_offset_hi << 8) | \ + ti->hsync_offset_lo); + mode->hsync_end = mode->hsync_start + \ + ((ti->hsync_pulse_width_hi << 8) | \ + ti->hsync_pulse_width_lo); + mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) | \ + ti->hblank_lo); + mode->vsync_start = \ + mode->vdisplay + ((ti->vsync_offset_hi << 8) | \ + ti->vsync_offset_lo); + mode->vsync_end = \ + mode->vsync_start + ((ti->vsync_pulse_width_hi << 8) | \ + ti->vsync_pulse_width_lo); + mode->vtotal = mode->vdisplay + \ + ((ti->vblank_hi << 8) | ti->vblank_lo); + mode->clock = ti->pixel_clock * 10; + } else { + if(dsi_config->type == MDFLD_DSI_ENCODER_DPI) { + if (mdfld_get_panel_type(dev, pipe) == TMD_VID) { + mode->hdisplay = 480; + mode->vdisplay = 854; + mode->hsync_start = 487; + mode->hsync_end = 490; + mode->htotal = 499; + mode->vsync_start = 861; + mode->vsync_end = 865; + mode->vtotal = 873; + mode->clock = 33264; + } else { + mode->hdisplay = 864; + mode->vdisplay = 480; + mode->hsync_start = 873; + mode->hsync_end = 876; + mode->htotal = 887; + mode->vsync_start = 487; + mode->vsync_end = 490; + mode->vtotal = 499; + mode->clock = 33264; + } + } else if(dsi_config->type == MDFLD_DSI_ENCODER_DBI) { + mode->hdisplay = 864; + mode->vdisplay = 480; + mode->hsync_start = 872; + mode->hsync_end = 876; + mode->htotal = 884; + mode->vsync_start = 482; + mode->vsync_end = 494; + mode->vtotal = 486; + mode->clock = 25777; + + } + } + + drm_mode_set_name(mode); + drm_mode_set_crtcinfo(mode, 0); + + mode->type |= DRM_MODE_TYPE_PREFERRED; + + return mode; +} + +int mdfld_dsi_panel_reset(int pipe) +{ + unsigned gpio; + int ret = 0; + + switch (pipe) { + case 0: + gpio = 128; + break; + case 2: + gpio = 34; + break; + default: + DRM_ERROR("Invalid output\n"); + return -EINVAL; + } + + ret = gpio_request(gpio, "gfx"); + if (ret) { + DRM_ERROR("gpio_rqueset failed\n"); + return ret; + } + + ret = gpio_direction_output(gpio, 1); + if (ret) { + DRM_ERROR("gpio_direction_output failed\n"); + goto gpio_error; + } + + gpio_get_value(128); + +gpio_error: + if (gpio_is_valid(gpio)) + gpio_free(gpio); + + return ret; +} + +/* + * MIPI output init + * @dev drm device + * @pipe pipe number. 0 or 2 + * @config + * + * Do the initialization of a MIPI output, including create DRM mode objects + * initialization of DSI output on @pipe + */ +void mdfld_dsi_output_init(struct drm_device *dev, + int pipe, + struct mdfld_dsi_config *config, + struct panel_funcs* p_cmd_funcs, + struct panel_funcs* p_vid_funcs) +{ + struct mdfld_dsi_config * dsi_config; + struct mdfld_dsi_connector * dsi_connector; + struct psb_intel_output * psb_output; + struct drm_connector * connector; + struct mdfld_dsi_encoder * encoder; + struct drm_psb_private * dev_priv = dev->dev_private; + struct panel_info dsi_panel_info; + u32 width_mm, height_mm; + + dev_dbg(dev->dev, "init DSI output on pipe %d\n", pipe); + + if(!dev || ((pipe != 0) && (pipe != 2))) { + WARN_ON(1); + return; + } + + /*create a new connetor*/ + dsi_connector = kzalloc(sizeof(struct mdfld_dsi_connector), GFP_KERNEL); + if(!dsi_connector) { + DRM_ERROR("No memory"); + return; + } + + dsi_connector->pipe = pipe; + + /*set DSI config*/ + if(config) { + dsi_config = config; + } else { + dsi_config = kzalloc(sizeof(struct mdfld_dsi_config), GFP_KERNEL); + if(!dsi_config) { + dev_err(dev->dev, + "cannot allocate memory for DSI config\n"); + goto dsi_init_err0; + } + + mdfld_dsi_get_default_config(dev, dsi_config, pipe); + } + + dsi_connector->private = dsi_config; + + dsi_config->changed = 1; + dsi_config->dev = dev; + + /* Init fixed mode basing on DSI config type */ + if(dsi_config->type == MDFLD_DSI_ENCODER_DBI) { + dsi_config->fixed_mode = p_cmd_funcs->get_config_mode(dev); + if(p_cmd_funcs->get_panel_info(dev, pipe, &dsi_panel_info)) + goto dsi_init_err0; + } else if(dsi_config->type == MDFLD_DSI_ENCODER_DPI) { + dsi_config->fixed_mode = p_vid_funcs->get_config_mode(dev); + if(p_vid_funcs->get_panel_info(dev, pipe, &dsi_panel_info)) + goto dsi_init_err0; + } + + width_mm = dsi_panel_info.width_mm; + height_mm = dsi_panel_info.height_mm; + + dsi_config->mode = dsi_config->fixed_mode; + dsi_config->connector = dsi_connector; + + if(!dsi_config->fixed_mode) { + dev_err(dev->dev, "No pannel fixed mode was found\n"); + goto dsi_init_err0; + } + + if(pipe && dev_priv->dsi_configs[0]) { + dsi_config->dvr_ic_inited = 0; + dev_priv->dsi_configs[1] = dsi_config; + } else if(pipe == 0) { + dsi_config->dvr_ic_inited = 1; + dev_priv->dsi_configs[0] = dsi_config; + } else { + dev_err(dev->dev, "Trying to init MIPI1 before MIPI0\n"); + goto dsi_init_err0; + } + + /*init drm connector object*/ + psb_output = &dsi_connector->base; + + psb_output->type = (pipe == 0) ? INTEL_OUTPUT_MIPI : INTEL_OUTPUT_MIPI2; + + connector = &psb_output->base; + /* Revisit type if MIPI/HDMI bridges ever appear on Medfield */ + drm_connector_init(dev, connector, &mdfld_dsi_connector_funcs, + DRM_MODE_CONNECTOR_LVDS); + drm_connector_helper_add(connector, &mdfld_dsi_connector_helper_funcs); + + connector->display_info.subpixel_order = SubPixelHorizontalRGB; + connector->display_info.width_mm = width_mm; + connector->display_info.height_mm = height_mm; + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + + /* Attach properties */ + drm_connector_attach_property(connector, dev->mode_config.scaling_mode_property, DRM_MODE_SCALE_FULLSCREEN); + drm_connector_attach_property(connector, dev_priv->backlight_property, MDFLD_DSI_BRIGHTNESS_MAX_LEVEL); + + /* Init DSI package sender on this output */ + if (mdfld_dsi_pkg_sender_init(dsi_connector, pipe)) { + DRM_ERROR("Package Sender initialization failed on pipe %d\n", pipe); + goto dsi_init_err0; + } + + /* Init DBI & DPI encoders */ + if (p_cmd_funcs) { + encoder = mdfld_dsi_dbi_init(dev, dsi_connector, p_cmd_funcs); + if(!encoder) { + dev_err(dev->dev, "Create DBI encoder failed\n"); + goto dsi_init_err1; + } + encoder->private = dsi_config; + dsi_config->encoders[MDFLD_DSI_ENCODER_DBI] = encoder; + } + + if(p_vid_funcs) { + encoder = mdfld_dsi_dpi_init(dev, dsi_connector, p_vid_funcs); + if(!encoder) { + dev_err(dev->dev, "Create DPI encoder failed\n"); + goto dsi_init_err1; + } + encoder->private = dsi_config; + dsi_config->encoders[MDFLD_DSI_ENCODER_DPI] = encoder; + } + + drm_sysfs_connector_add(connector); + return; + + /*TODO: add code to destroy outputs on error*/ +dsi_init_err1: + /*destroy sender*/ + mdfld_dsi_pkg_sender_destroy(dsi_connector->pkg_sender); + + drm_connector_cleanup(connector); + kfree(dsi_config->fixed_mode); + kfree(dsi_config); +dsi_init_err0: + kfree(dsi_connector); +} diff --git a/trunk/drivers/staging/gma500/mdfld_dsi_output.h b/trunk/drivers/staging/gma500/mdfld_dsi_output.h new file mode 100644 index 000000000000..4699267efd60 --- /dev/null +++ b/trunk/drivers/staging/gma500/mdfld_dsi_output.h @@ -0,0 +1,138 @@ +/* + * Copyright © 2010 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: + * jim liu + * Jackie Li + */ + +#ifndef __MDFLD_DSI_OUTPUT_H__ +#define __MDFLD_DSI_OUTPUT_H__ + +#include +#include +#include +#include +#include + +#include "psb_drv.h" +#include "psb_intel_drv.h" +#include "psb_intel_reg.h" +#include "power.h" +#include "mdfld_output.h" + +#include + + +static inline struct mdfld_dsi_config * + mdfld_dsi_get_config(struct mdfld_dsi_connector *connector) +{ + if (!connector) + return NULL; + return (struct mdfld_dsi_config *)connector->private; +} + +static inline void *mdfld_dsi_get_pkg_sender(struct mdfld_dsi_config *config) +{ + struct mdfld_dsi_connector *dsi_connector; + + if (!config) + return NULL; + + dsi_connector = config->connector; + + if (!dsi_connector) + return NULL; + + return dsi_connector->pkg_sender; +} + +static inline struct mdfld_dsi_config * + mdfld_dsi_encoder_get_config(struct mdfld_dsi_encoder *encoder) +{ + if (!encoder) + return NULL; + return (struct mdfld_dsi_config *)encoder->private; +} + +static inline struct mdfld_dsi_connector * + mdfld_dsi_encoder_get_connector(struct mdfld_dsi_encoder *encoder) +{ + struct mdfld_dsi_config *config; + + if (!encoder) + return NULL; + + config = mdfld_dsi_encoder_get_config(encoder); + if (!config) + return NULL; + + return config->connector; +} + +static inline void *mdfld_dsi_encoder_get_pkg_sender( + struct mdfld_dsi_encoder *encoder) +{ + struct mdfld_dsi_config *dsi_config; + + dsi_config = mdfld_dsi_encoder_get_config(encoder); + if (!dsi_config) + return NULL; + + return mdfld_dsi_get_pkg_sender(dsi_config); +} + +static inline int mdfld_dsi_encoder_get_pipe(struct mdfld_dsi_encoder *encoder) +{ + struct mdfld_dsi_connector *connector; + + if (!encoder) + return -1; + + connector = mdfld_dsi_encoder_get_connector(encoder); + if (!connector) + return -1; + + return connector->pipe; +} + +extern void mdfld_dsi_gen_fifo_ready(struct drm_device *dev, + u32 gen_fifo_stat_reg, u32 fifo_stat); +extern void mdfld_dsi_brightness_init(struct mdfld_dsi_config *dsi_config, + int pipe); +extern void mdfld_dsi_brightness_control(struct drm_device *dev, int pipe, + int level); +extern void mdfld_dsi_output_init(struct drm_device *dev, int pipe, + struct mdfld_dsi_config *config, + struct panel_funcs *p_cmd_funcs, + struct panel_funcs *p_vid_funcs); +extern void mdfld_dsi_controller_init(struct mdfld_dsi_config *dsi_config, + int pipe); +extern int mdfld_dsi_get_power_mode(struct mdfld_dsi_config *dsi_config, + u32 *mode, + u8 transmission); +extern int mdfld_dsi_get_diagnostic_result(struct mdfld_dsi_config *dsi_config, + u32 *result, + u8 transmission); +extern int mdfld_dsi_panel_reset(int pipe); + +#endif /*__MDFLD_DSI_OUTPUT_H__*/ diff --git a/trunk/drivers/staging/gma500/mdfld_dsi_pkg_sender.c b/trunk/drivers/staging/gma500/mdfld_dsi_pkg_sender.c new file mode 100644 index 000000000000..9b96a5c9abcd --- /dev/null +++ b/trunk/drivers/staging/gma500/mdfld_dsi_pkg_sender.c @@ -0,0 +1,1484 @@ +/* + * Copyright © 2010 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: + * Jackie Li + */ + +#include + +#include "mdfld_dsi_output.h" +#include "mdfld_dsi_pkg_sender.h" +#include "mdfld_dsi_dbi.h" +#include "mdfld_dsi_dpi.h" + +#define MDFLD_DSI_DBI_FIFO_TIMEOUT 100 +#define MDFLD_DSI_MAX_RETURN_PACKET_SIZE 512 +#define MDFLD_DSI_READ_MAX_COUNT 5000 + +static const char * const dsi_errors[] = { + "RX SOT Error", + "RX SOT Sync Error", + "RX EOT Sync Error", + "RX Escape Mode Entry Error", + "RX LP TX Sync Error", + "RX HS Receive Timeout Error", + "RX False Control Error", + "RX ECC Single Bit Error", + "RX ECC Multibit Error", + "RX Checksum Error", + "RX DSI Data Type Not Recognised", + "RX DSI VC ID Invalid", + "TX False Control Error", + "TX ECC Single Bit Error", + "TX ECC Multibit Error", + "TX Checksum Error", + "TX DSI Data Type Not Recognised", + "TX DSI VC ID invalid", + "High Contention", + "Low contention", + "DPI FIFO Under run", + "HS TX Timeout", + "LP RX Timeout", + "Turn Around ACK Timeout", + "ACK With No Error", + "RX Invalid TX Length", + "RX Prot Violation", + "HS Generic Write FIFO Full", + "LP Generic Write FIFO Full", + "Generic Read Data Avail", + "Special Packet Sent", + "Tearing Effect", +}; + +static int wait_for_gen_fifo_empty(struct mdfld_dsi_pkg_sender *sender, + u32 mask) +{ + struct drm_device *dev = sender->dev; + u32 gen_fifo_stat_reg = sender->mipi_gen_fifo_stat_reg; + int retry = 0xffff; + + while (retry--) { + if ((mask & REG_READ(gen_fifo_stat_reg)) == mask) + return 0; + udelay(100); + } + dev_err(dev->dev, "fifo is NOT empty 0x%08x\n", + REG_READ(gen_fifo_stat_reg)); + return -EIO; +} + +static int wait_for_all_fifos_empty(struct mdfld_dsi_pkg_sender *sender) +{ + return wait_for_gen_fifo_empty(sender, (1 << 2) | (1 << 10) | (1 << 18) + | (1 << 26) | (1 << 27) | (1 << 28)); +} + +static int wait_for_lp_fifos_empty(struct mdfld_dsi_pkg_sender *sender) +{ + return wait_for_gen_fifo_empty(sender, (1 << 10) | (1 << 26)); +} + +static int wait_for_hs_fifos_empty(struct mdfld_dsi_pkg_sender *sender) +{ + return wait_for_gen_fifo_empty(sender, (1 << 2) | (1 << 18)); +} + +static int wait_for_dbi_fifo_empty(struct mdfld_dsi_pkg_sender *sender) +{ + return wait_for_gen_fifo_empty(sender, (1 << 27)); +} + +static int handle_dsi_error(struct mdfld_dsi_pkg_sender *sender, u32 mask) +{ + u32 intr_stat_reg = sender->mipi_intr_stat_reg; + struct drm_device *dev = sender->dev; + + switch (mask) { + case (1 << 0): + case (1 << 1): + case (1 << 2): + case (1 << 3): + case (1 << 4): + case (1 << 5): + case (1 << 6): + case (1 << 7): + case (1 << 8): + case (1 << 9): + case (1 << 10): + case (1 << 11): + case (1 << 12): + case (1 << 13): + break; + case (1 << 14): + /*wait for all fifo empty*/ + /*wait_for_all_fifos_empty(sender)*/; + break; + case (1 << 15): + break; + case (1 << 16): + break; + case (1 << 17): + break; + case (1 << 18): + case (1 << 19): + /*wait for contention recovery time*/ + /*mdelay(10);*/ + /*wait for all fifo empty*/ + if (0) + wait_for_all_fifos_empty(sender); + break; + case (1 << 20): + break; + case (1 << 21): + /*wait for all fifo empty*/ + /*wait_for_all_fifos_empty(sender);*/ + break; + case (1 << 22): + break; + case (1 << 23): + case (1 << 24): + case (1 << 25): + case (1 << 26): + case (1 << 27): + /* HS Gen fifo full */ + REG_WRITE(intr_stat_reg, mask); + wait_for_hs_fifos_empty(sender); + break; + case (1 << 28): + /* LP Gen fifo full\n */ + REG_WRITE(intr_stat_reg, mask); + wait_for_lp_fifos_empty(sender); + break; + case (1 << 29): + case (1 << 30): + case (1 << 31): + break; + } + + if (mask & REG_READ(intr_stat_reg)) + dev_warn(dev->dev, "Cannot clean interrupt 0x%08x\n", mask); + + return 0; +} + +static int dsi_error_handler(struct mdfld_dsi_pkg_sender *sender) +{ + struct drm_device *dev = sender->dev; + u32 intr_stat_reg = sender->mipi_intr_stat_reg; + u32 mask; + u32 intr_stat; + int i; + int err = 0; + + intr_stat = REG_READ(intr_stat_reg); + + for (i = 0; i < 32; i++) { + mask = (0x00000001UL) << i; + if (intr_stat & mask) { + dev_dbg(dev->dev, "[DSI]: %s\n", dsi_errors[i]); + err = handle_dsi_error(sender, mask); + if (err) + dev_err(dev->dev, "Cannot handle error\n"); + } + } + return err; +} + +static inline int dbi_cmd_sent(struct mdfld_dsi_pkg_sender *sender) +{ + struct drm_device *dev = sender->dev; + u32 retry = 0xffff; + u32 dbi_cmd_addr_reg = sender->mipi_cmd_addr_reg; + + /* Query the command execution status */ + while (retry--) { + if (!(REG_READ(dbi_cmd_addr_reg) & (1 << 0))) + break; + } + + if (!retry) { + dev_err(dev->dev, "Timeout waiting for DBI Command status\n"); + return -EAGAIN; + } + return 0; +} + +/* + * NOTE: this interface is abandoned expect for write_mem_start DCS + * other DCS are sent via generic pkg interfaces + */ +static int send_dcs_pkg(struct mdfld_dsi_pkg_sender *sender, + struct mdfld_dsi_pkg *pkg) +{ + struct drm_device *dev = sender->dev; + struct mdfld_dsi_dcs_pkg *dcs_pkg = &pkg->pkg.dcs_pkg; + u32 dbi_cmd_len_reg = sender->mipi_cmd_len_reg; + u32 dbi_cmd_addr_reg = sender->mipi_cmd_addr_reg; + u32 cb_phy = sender->dbi_cb_phy; + u32 index = 0; + u8 *cb = (u8 *)sender->dbi_cb_addr; + int i; + int ret; + + if (!sender->dbi_pkg_support) { + dev_err(dev->dev, "Trying to send DCS on a non DBI output, abort!\n"); + return -ENOTSUPP; + } + + /*wait for DBI fifo empty*/ + wait_for_dbi_fifo_empty(sender); + + *(cb + (index++)) = dcs_pkg->cmd; + if (dcs_pkg->param_num) { + for (i = 0; i < dcs_pkg->param_num; i++) + *(cb + (index++)) = *(dcs_pkg->param + i); + } + + REG_WRITE(dbi_cmd_len_reg, (1 + dcs_pkg->param_num)); + REG_WRITE(dbi_cmd_addr_reg, + (cb_phy << CMD_MEM_ADDR_OFFSET) + | (1 << 0) + | ((dcs_pkg->data_src == CMD_DATA_SRC_PIPE) ? (1 << 1) : 0)); + + ret = dbi_cmd_sent(sender); + if (ret) { + dev_err(dev->dev, "command 0x%x not complete\n", dcs_pkg->cmd); + return -EAGAIN; + } + return 0; +} + +static int __send_short_pkg(struct mdfld_dsi_pkg_sender *sender, + struct mdfld_dsi_pkg *pkg) +{ + struct drm_device *dev = sender->dev; + u32 hs_gen_ctrl_reg = sender->mipi_hs_gen_ctrl_reg; + u32 lp_gen_ctrl_reg = sender->mipi_lp_gen_ctrl_reg; + u32 gen_ctrl_val = 0; + struct mdfld_dsi_gen_short_pkg *short_pkg = &pkg->pkg.short_pkg; + + gen_ctrl_val |= short_pkg->cmd << MCS_COMMANDS_POS; + gen_ctrl_val |= 0 << DCS_CHANNEL_NUMBER_POS; + gen_ctrl_val |= pkg->pkg_type; + gen_ctrl_val |= short_pkg->param << MCS_PARAMETER_POS; + + if (pkg->transmission_type == MDFLD_DSI_HS_TRANSMISSION) { + /* wait for hs fifo empty */ + /* wait_for_hs_fifos_empty(sender); */ + /* Send pkg */ + REG_WRITE(hs_gen_ctrl_reg, gen_ctrl_val); + } else if (pkg->transmission_type == MDFLD_DSI_LP_TRANSMISSION) { + /* wait_for_lp_fifos_empty(sender); */ + /* Send pkg*/ + REG_WRITE(lp_gen_ctrl_reg, gen_ctrl_val); + } else { + dev_err(dev->dev, "Unknown transmission type %d\n", + pkg->transmission_type); + return -EINVAL; + } + + return 0; +} + +static int __send_long_pkg(struct mdfld_dsi_pkg_sender *sender, + struct mdfld_dsi_pkg *pkg) +{ + struct drm_device *dev = sender->dev; + u32 hs_gen_ctrl_reg = sender->mipi_hs_gen_ctrl_reg; + u32 hs_gen_data_reg = sender->mipi_hs_gen_data_reg; + u32 lp_gen_ctrl_reg = sender->mipi_lp_gen_ctrl_reg; + u32 lp_gen_data_reg = sender->mipi_lp_gen_data_reg; + u32 gen_ctrl_val = 0; + u32 *dp; + int i; + struct mdfld_dsi_gen_long_pkg *long_pkg = &pkg->pkg.long_pkg; + + dp = long_pkg->data; + + /* + * Set up word count for long pkg + * FIXME: double check word count field. + * currently, using the byte counts of the payload as the word count. + * ------------------------------------------------------------ + * | DI | WC | ECC| PAYLOAD |CHECKSUM| + * ------------------------------------------------------------ + */ + gen_ctrl_val |= (long_pkg->len << 2) << WORD_COUNTS_POS; + gen_ctrl_val |= 0 << DCS_CHANNEL_NUMBER_POS; + gen_ctrl_val |= pkg->pkg_type; + + if (pkg->transmission_type == MDFLD_DSI_HS_TRANSMISSION) { + /* Wait for hs ctrl and data fifos to be empty */ + /* wait_for_hs_fifos_empty(sender); */ + for (i = 0; i < long_pkg->len; i++) + REG_WRITE(hs_gen_data_reg, *(dp + i)); + REG_WRITE(hs_gen_ctrl_reg, gen_ctrl_val); + } else if (pkg->transmission_type == MDFLD_DSI_LP_TRANSMISSION) { + /* wait_for_lp_fifos_empty(sender); */ + for (i = 0; i < long_pkg->len; i++) + REG_WRITE(lp_gen_data_reg, *(dp + i)); + REG_WRITE(lp_gen_ctrl_reg, gen_ctrl_val); + } else { + dev_err(dev->dev, "Unknown transmission type %d\n", + pkg->transmission_type); + return -EINVAL; + } + + return 0; + +} + +static int send_mcs_short_pkg(struct mdfld_dsi_pkg_sender *sender, + struct mdfld_dsi_pkg *pkg) +{ + return __send_short_pkg(sender, pkg); +} + +static int send_mcs_long_pkg(struct mdfld_dsi_pkg_sender *sender, + struct mdfld_dsi_pkg *pkg) +{ + return __send_long_pkg(sender, pkg); +} + +static int send_gen_short_pkg(struct mdfld_dsi_pkg_sender *sender, + struct mdfld_dsi_pkg *pkg) +{ + return __send_short_pkg(sender, pkg); +} + +static int send_gen_long_pkg(struct mdfld_dsi_pkg_sender *sender, + struct mdfld_dsi_pkg *pkg) +{ + return __send_long_pkg(sender, pkg); +} + +static int send_pkg_prepare(struct mdfld_dsi_pkg_sender *sender, + struct mdfld_dsi_pkg *pkg) +{ + u8 cmd; + u8 *data; + + switch (pkg->pkg_type) { + case MDFLD_DSI_PKG_DCS: + cmd = pkg->pkg.dcs_pkg.cmd; + break; + case MDFLD_DSI_PKG_MCS_SHORT_WRITE_0: + case MDFLD_DSI_PKG_MCS_SHORT_WRITE_1: + cmd = pkg->pkg.short_pkg.cmd; + break; + case MDFLD_DSI_PKG_MCS_LONG_WRITE: + data = (u8 *)pkg->pkg.long_pkg.data; + cmd = *data; + break; + default: + return 0; + } + + /* This prevents other package sending while doing msleep */ + sender->status = MDFLD_DSI_PKG_SENDER_BUSY; + + /* Check panel mode v.s. sending command */ + if ((sender->panel_mode & MDFLD_DSI_PANEL_MODE_SLEEP) && + cmd != exit_sleep_mode) { + dev_err(sender->dev->dev, + "sending 0x%x when panel sleep in\n", cmd); + sender->status = MDFLD_DSI_PKG_SENDER_FREE; + return -EINVAL; + } + + /* Wait for 120 milliseconds in case exit_sleep_mode just be sent */ + if (cmd == DCS_ENTER_SLEEP_MODE) { + /*TODO: replace it with msleep later*/ + mdelay(120); + } + return 0; +} + +static int send_pkg_done(struct mdfld_dsi_pkg_sender *sender, + struct mdfld_dsi_pkg *pkg) +{ + u8 cmd; + u8 *data; + + switch (pkg->pkg_type) { + case MDFLD_DSI_PKG_DCS: + cmd = pkg->pkg.dcs_pkg.cmd; + break; + case MDFLD_DSI_PKG_MCS_SHORT_WRITE_0: + case MDFLD_DSI_PKG_MCS_SHORT_WRITE_1: + cmd = pkg->pkg.short_pkg.cmd; + break; + case MDFLD_DSI_PKG_MCS_LONG_WRITE: + data = (u8 *)pkg->pkg.long_pkg.data; + cmd = *data; + break; + default: + return 0; + } + + /* Update panel status */ + if (cmd == DCS_ENTER_SLEEP_MODE) { + sender->panel_mode |= MDFLD_DSI_PANEL_MODE_SLEEP; + /*TODO: replace it with msleep later*/ + mdelay(120); + } else if (cmd == DCS_EXIT_SLEEP_MODE) { + sender->panel_mode &= ~MDFLD_DSI_PANEL_MODE_SLEEP; + /*TODO: replace it with msleep later*/ + mdelay(120); + } else if (unlikely(cmd == DCS_SOFT_RESET)) { + /*TODO: replace it with msleep later*/ + mdelay(5); + } + sender->status = MDFLD_DSI_PKG_SENDER_FREE; + return 0; + +} + +static int do_send_pkg(struct mdfld_dsi_pkg_sender *sender, + struct mdfld_dsi_pkg *pkg) +{ + int ret; + + if (sender->status == MDFLD_DSI_PKG_SENDER_BUSY) { + dev_err(sender->dev->dev, "sender is busy\n"); + return -EAGAIN; + } + + ret = send_pkg_prepare(sender, pkg); + if (ret) { + dev_err(sender->dev->dev, "send_pkg_prepare error\n"); + return ret; + } + + switch (pkg->pkg_type) { + case MDFLD_DSI_PKG_DCS: + ret = send_dcs_pkg(sender, pkg); + break; + case MDFLD_DSI_PKG_GEN_SHORT_WRITE_0: + case MDFLD_DSI_PKG_GEN_SHORT_WRITE_1: + case MDFLD_DSI_PKG_GEN_SHORT_WRITE_2: + case MDFLD_DSI_PKG_GEN_READ_0: + case MDFLD_DSI_PKG_GEN_READ_1: + case MDFLD_DSI_PKG_GEN_READ_2: + ret = send_gen_short_pkg(sender, pkg); + break; + case MDFLD_DSI_PKG_GEN_LONG_WRITE: + ret = send_gen_long_pkg(sender, pkg); + break; + case MDFLD_DSI_PKG_MCS_SHORT_WRITE_0: + case MDFLD_DSI_PKG_MCS_SHORT_WRITE_1: + case MDFLD_DSI_PKG_MCS_READ: + ret = send_mcs_short_pkg(sender, pkg); + break; + case MDFLD_DSI_PKG_MCS_LONG_WRITE: + ret = send_mcs_long_pkg(sender, pkg); + break; + default: + dev_err(sender->dev->dev, "Invalid pkg type 0x%x\n", + pkg->pkg_type); + ret = -EINVAL; + } + send_pkg_done(sender, pkg); + return ret; +} + +static int send_pkg(struct mdfld_dsi_pkg_sender *sender, + struct mdfld_dsi_pkg *pkg) +{ + int err ; + + /* Handle DSI error */ + err = dsi_error_handler(sender); + if (err) { + dev_err(sender->dev->dev, "Error handling failed\n"); + err = -EAGAIN; + goto send_pkg_err; + } + + /* Send pkg */ + err = do_send_pkg(sender, pkg); + if (err) { + dev_err(sender->dev->dev, "sent pkg failed\n"); + err = -EAGAIN; + goto send_pkg_err; + } + + /* FIXME: should I query complete and fifo empty here? */ +send_pkg_err: + return err; +} + +static struct mdfld_dsi_pkg *pkg_sender_get_pkg_locked( + struct mdfld_dsi_pkg_sender *sender) +{ + struct mdfld_dsi_pkg *pkg; + + if (list_empty(&sender->free_list)) { + dev_err(sender->dev->dev, "No free pkg left\n"); + return NULL; + } + pkg = list_first_entry(&sender->free_list, struct mdfld_dsi_pkg, entry); + /* Detach from free list */ + list_del_init(&pkg->entry); + return pkg; +} + +static void pkg_sender_put_pkg_locked(struct mdfld_dsi_pkg_sender *sender, + struct mdfld_dsi_pkg *pkg) +{ + memset(pkg, 0, sizeof(struct mdfld_dsi_pkg)); + INIT_LIST_HEAD(&pkg->entry); + list_add_tail(&pkg->entry, &sender->free_list); +} + +static int mdfld_dbi_cb_init(struct mdfld_dsi_pkg_sender *sender, + struct psb_gtt *pg, int pipe) +{ + unsigned long phys; + void *virt_addr = NULL; + + switch (pipe) { + case 0: + /* FIXME: Doesn't this collide with stolen space ? */ + phys = pg->gtt_phys_start - 0x1000; + break; + case 2: + phys = pg->gtt_phys_start - 0x800; + break; + default: + dev_err(sender->dev->dev, "Unsupported channel %d\n", pipe); + return -EINVAL; + } + + virt_addr = ioremap_nocache(phys, 0x800); + if (!virt_addr) { + dev_err(sender->dev->dev, "Map DBI command buffer error\n"); + return -ENOMEM; + } + sender->dbi_cb_phy = phys; + sender->dbi_cb_addr = virt_addr; + return 0; +} + +static void mdfld_dbi_cb_destroy(struct mdfld_dsi_pkg_sender *sender) +{ + if (sender && sender->dbi_cb_addr) + iounmap(sender->dbi_cb_addr); +} + +static void pkg_sender_queue_pkg(struct mdfld_dsi_pkg_sender *sender, + struct mdfld_dsi_pkg *pkg, + int delay) +{ + unsigned long flags; + + spin_lock_irqsave(&sender->lock, flags); + + if (!delay) { + send_pkg(sender, pkg); + pkg_sender_put_pkg_locked(sender, pkg); + } else { + /* Queue it */ + list_add_tail(&pkg->entry, &sender->pkg_list); + } + spin_unlock_irqrestore(&sender->lock, flags); +} + +static void process_pkg_list(struct mdfld_dsi_pkg_sender *sender) +{ + struct mdfld_dsi_pkg *pkg; + unsigned long flags; + + spin_lock_irqsave(&sender->lock, flags); + + while (!list_empty(&sender->pkg_list)) { + pkg = list_first_entry(&sender->pkg_list, + struct mdfld_dsi_pkg, entry); + send_pkg(sender, pkg); + list_del_init(&pkg->entry); + pkg_sender_put_pkg_locked(sender, pkg); + } + + spin_unlock_irqrestore(&sender->lock, flags); +} + +static int mdfld_dsi_send_mcs_long(struct mdfld_dsi_pkg_sender *sender, + u32 *data, u32 len, u8 transmission, int delay) +{ + struct mdfld_dsi_pkg *pkg; + unsigned long flags; + + spin_lock_irqsave(&sender->lock, flags); + pkg = pkg_sender_get_pkg_locked(sender); + spin_unlock_irqrestore(&sender->lock, flags); + + if (!pkg) { + dev_err(sender->dev->dev, "No memory\n"); + return -ENOMEM; + } + pkg->pkg_type = MDFLD_DSI_PKG_MCS_LONG_WRITE; + pkg->transmission_type = transmission; + pkg->pkg.long_pkg.data = data; + pkg->pkg.long_pkg.len = len; + INIT_LIST_HEAD(&pkg->entry); + + pkg_sender_queue_pkg(sender, pkg, delay); + return 0; +} + +static int mdfld_dsi_send_mcs_short(struct mdfld_dsi_pkg_sender *sender, + u8 cmd, u8 param, u8 param_num, + u8 transmission, + int delay) +{ + struct mdfld_dsi_pkg *pkg; + unsigned long flags; + + spin_lock_irqsave(&sender->lock, flags); + pkg = pkg_sender_get_pkg_locked(sender); + spin_unlock_irqrestore(&sender->lock, flags); + + if (!pkg) { + dev_err(sender->dev->dev, "No memory\n"); + return -ENOMEM; + } + + if (param_num) { + pkg->pkg_type = MDFLD_DSI_PKG_MCS_SHORT_WRITE_1; + pkg->pkg.short_pkg.param = param; + } else { + pkg->pkg_type = MDFLD_DSI_PKG_MCS_SHORT_WRITE_0; + pkg->pkg.short_pkg.param = 0; + } + pkg->transmission_type = transmission; + pkg->pkg.short_pkg.cmd = cmd; + INIT_LIST_HEAD(&pkg->entry); + + pkg_sender_queue_pkg(sender, pkg, delay); + return 0; +} + +static int mdfld_dsi_send_gen_short(struct mdfld_dsi_pkg_sender *sender, + u8 param0, u8 param1, u8 param_num, + u8 transmission, + int delay) +{ + struct mdfld_dsi_pkg *pkg; + unsigned long flags; + + spin_lock_irqsave(&sender->lock, flags); + pkg = pkg_sender_get_pkg_locked(sender); + spin_unlock_irqrestore(&sender->lock, flags); + + if (!pkg) { + dev_err(sender->dev->dev, "No pkg memory\n"); + return -ENOMEM; + } + + switch (param_num) { + case 0: + pkg->pkg_type = MDFLD_DSI_PKG_GEN_SHORT_WRITE_0; + pkg->pkg.short_pkg.cmd = 0; + pkg->pkg.short_pkg.param = 0; + break; + case 1: + pkg->pkg_type = MDFLD_DSI_PKG_GEN_SHORT_WRITE_1; + pkg->pkg.short_pkg.cmd = param0; + pkg->pkg.short_pkg.param = 0; + break; + case 2: + pkg->pkg_type = MDFLD_DSI_PKG_GEN_SHORT_WRITE_2; + pkg->pkg.short_pkg.cmd = param0; + pkg->pkg.short_pkg.param = param1; + break; + } + + pkg->transmission_type = transmission; + INIT_LIST_HEAD(&pkg->entry); + + pkg_sender_queue_pkg(sender, pkg, delay); + return 0; +} + +static int mdfld_dsi_send_gen_long(struct mdfld_dsi_pkg_sender *sender, + u32 *data, u32 len, u8 transmission, int delay) +{ + struct mdfld_dsi_pkg *pkg; + unsigned long flags; + + spin_lock_irqsave(&sender->lock, flags); + pkg = pkg_sender_get_pkg_locked(sender); + spin_unlock_irqrestore(&sender->lock, flags); + + if (!pkg) { + dev_err(sender->dev->dev, "No pkg memory\n"); + return -ENOMEM; + } + + pkg->pkg_type = MDFLD_DSI_PKG_GEN_LONG_WRITE; + pkg->transmission_type = transmission; + pkg->pkg.long_pkg.data = data; + pkg->pkg.long_pkg.len = len; + + INIT_LIST_HEAD(&pkg->entry); + + pkg_sender_queue_pkg(sender, pkg, delay); + + return 0; +} + +static int __read_panel_data(struct mdfld_dsi_pkg_sender *sender, + struct mdfld_dsi_pkg *pkg, + u32 *data, + u16 len) +{ + unsigned long flags; + struct drm_device *dev = sender->dev; + int i; + u32 gen_data_reg; + int retry = MDFLD_DSI_READ_MAX_COUNT; + u8 transmission = pkg->transmission_type; + + /* + * do reading. + * 0) send out generic read request + * 1) polling read data avail interrupt + * 2) read data + */ + spin_lock_irqsave(&sender->lock, flags); + + REG_WRITE(sender->mipi_intr_stat_reg, 1 << 29); + + if ((REG_READ(sender->mipi_intr_stat_reg) & (1 << 29))) + DRM_ERROR("Can NOT clean read data valid interrupt\n"); + + /*send out read request*/ + send_pkg(sender, pkg); + + pkg_sender_put_pkg_locked(sender, pkg); + + /*polling read data avail interrupt*/ + while (retry && !(REG_READ(sender->mipi_intr_stat_reg) & (1 << 29))) { + udelay(100); + retry--; + } + + if (!retry) { + spin_unlock_irqrestore(&sender->lock, flags); + return -ETIMEDOUT; + } + + REG_WRITE(sender->mipi_intr_stat_reg, (1 << 29)); + + /*read data*/ + if (transmission == MDFLD_DSI_HS_TRANSMISSION) + gen_data_reg = sender->mipi_hs_gen_data_reg; + else if (transmission == MDFLD_DSI_LP_TRANSMISSION) + gen_data_reg = sender->mipi_lp_gen_data_reg; + else { + DRM_ERROR("Unknown transmission"); + spin_unlock_irqrestore(&sender->lock, flags); + return -EINVAL; + } + + for (i=0; ilock, flags); + + return 0; +} + +static int mdfld_dsi_read_gen(struct mdfld_dsi_pkg_sender *sender, + u8 param0, + u8 param1, + u8 param_num, + u32 *data, + u16 len, + u8 transmission) +{ + struct mdfld_dsi_pkg *pkg; + unsigned long flags; + + spin_lock_irqsave(&sender->lock, flags); + + pkg = pkg_sender_get_pkg_locked(sender); + + spin_unlock_irqrestore(&sender->lock,flags); + + if (!pkg) { + dev_err(sender->dev->dev, "No pkg memory\n"); + return -ENOMEM; + } + + switch (param_num) { + case 0: + pkg->pkg_type = MDFLD_DSI_PKG_GEN_READ_0; + pkg->pkg.short_pkg.cmd = 0; + pkg->pkg.short_pkg.param = 0; + break; + case 1: + pkg->pkg_type = MDFLD_DSI_PKG_GEN_READ_1; + pkg->pkg.short_pkg.cmd = param0; + pkg->pkg.short_pkg.param = 0; + break; + case 2: + pkg->pkg_type = MDFLD_DSI_PKG_GEN_READ_2; + pkg->pkg.short_pkg.cmd = param0; + pkg->pkg.short_pkg.param = param1; + break; + } + + pkg->transmission_type = transmission; + + INIT_LIST_HEAD(&pkg->entry); + + return __read_panel_data(sender, pkg, data, len); +} + +static int mdfld_dsi_read_mcs(struct mdfld_dsi_pkg_sender *sender, + u8 cmd, + u32 *data, + u16 len, + u8 transmission) +{ + struct mdfld_dsi_pkg *pkg; + unsigned long flags; + + spin_lock_irqsave(&sender->lock, flags); + + pkg = pkg_sender_get_pkg_locked(sender); + + spin_unlock_irqrestore(&sender->lock, flags); + + if (!pkg) { + dev_err(sender->dev->dev, "No pkg memory\n"); + return -ENOMEM; + } + + pkg->pkg_type = MDFLD_DSI_PKG_MCS_READ; + pkg->pkg.short_pkg.cmd = cmd; + pkg->pkg.short_pkg.param = 0; + + pkg->transmission_type = transmission; + + INIT_LIST_HEAD(&pkg->entry); + + return __read_panel_data(sender, pkg, data, len); +} + +void dsi_controller_dbi_init(struct mdfld_dsi_config * dsi_config, int pipe) +{ + struct drm_device * dev = dsi_config->dev; + u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0; + int lane_count = dsi_config->lane_count; + u32 val = 0; + + /*un-ready device*/ + REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000000); + + /*init dsi adapter before kicking off*/ + REG_WRITE((MIPIA_CONTROL_REG + reg_offset), 0x00000018); + + /*TODO: figure out how to setup these registers*/ + REG_WRITE((MIPIA_DPHY_PARAM_REG + reg_offset), 0x150c3408); + REG_WRITE((MIPIA_CLK_LANE_SWITCH_TIME_CNT_REG + reg_offset), 0x000a0014); + REG_WRITE((MIPIA_DBI_BW_CTRL_REG + reg_offset), 0x00000400); + REG_WRITE((MIPIA_DBI_FIFO_THROTTLE_REG + reg_offset), 0x00000001); + REG_WRITE((MIPIA_HS_LS_DBI_ENABLE_REG + reg_offset), 0x00000000); + + /*enable all interrupts*/ + REG_WRITE((MIPIA_INTR_EN_REG + reg_offset), 0xffffffff); + /*max value: 20 clock cycles of txclkesc*/ + REG_WRITE((MIPIA_TURN_AROUND_TIMEOUT_REG + reg_offset), 0x0000001f); + /*min 21 txclkesc, max: ffffh*/ + REG_WRITE((MIPIA_DEVICE_RESET_TIMER_REG + reg_offset), 0x0000ffff); + /*min: 7d0 max: 4e20*/ + REG_WRITE((MIPIA_INIT_COUNT_REG + reg_offset), 0x00000fa0); + + /*set up max return packet size*/ + REG_WRITE((MIPIA_MAX_RETURN_PACK_SIZE_REG + reg_offset), + MDFLD_DSI_MAX_RETURN_PACKET_SIZE); + + /*set up func_prg*/ + val |= lane_count; + val |= (dsi_config->channel_num << DSI_DBI_VIRT_CHANNEL_OFFSET); + val |= DSI_DBI_COLOR_FORMAT_OPTION2; + REG_WRITE((MIPIA_DSI_FUNC_PRG_REG + reg_offset), val); + + REG_WRITE((MIPIA_HS_TX_TIMEOUT_REG + reg_offset), 0x3fffff); + REG_WRITE((MIPIA_LP_RX_TIMEOUT_REG + reg_offset), 0xffff); + + REG_WRITE((MIPIA_HIGH_LOW_SWITCH_COUNT_REG + reg_offset), 0x46); + REG_WRITE((MIPIA_EOT_DISABLE_REG + reg_offset), 0x00000000); + REG_WRITE((MIPIA_LP_BYTECLK_REG + reg_offset), 0x00000004); + REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000001); +} + +void dsi_controller_dpi_init(struct mdfld_dsi_config * dsi_config, int pipe) +{ + struct drm_device * dev = dsi_config->dev; + u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0; + int lane_count = dsi_config->lane_count; + struct mdfld_dsi_dpi_timing dpi_timing; + struct drm_display_mode * mode = dsi_config->mode; + u32 val = 0; + + /*un-ready device*/ + REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000000); + + /*init dsi adapter before kicking off*/ + REG_WRITE((MIPIA_CONTROL_REG + reg_offset), 0x00000018); + + /*enable all interrupts*/ + REG_WRITE((MIPIA_INTR_EN_REG + reg_offset), 0xffffffff); + + /*set up func_prg*/ + val |= lane_count; + val |= dsi_config->channel_num << DSI_DPI_VIRT_CHANNEL_OFFSET; + + switch(dsi_config->bpp) { + case 16: + val |= DSI_DPI_COLOR_FORMAT_RGB565; + break; + case 18: + val |= DSI_DPI_COLOR_FORMAT_RGB666; + break; + case 24: + val |= DSI_DPI_COLOR_FORMAT_RGB888; + break; + default: + DRM_ERROR("unsupported color format, bpp = %d\n", dsi_config->bpp); + } + + REG_WRITE((MIPIA_DSI_FUNC_PRG_REG + reg_offset), val); + + REG_WRITE((MIPIA_HS_TX_TIMEOUT_REG + reg_offset), + (mode->vtotal * mode->htotal * dsi_config->bpp / (8 * lane_count)) & DSI_HS_TX_TIMEOUT_MASK); + REG_WRITE((MIPIA_LP_RX_TIMEOUT_REG + reg_offset), 0xffff & DSI_LP_RX_TIMEOUT_MASK); + + /*max value: 20 clock cycles of txclkesc*/ + REG_WRITE((MIPIA_TURN_AROUND_TIMEOUT_REG + reg_offset), 0x14 & DSI_TURN_AROUND_TIMEOUT_MASK); + + /*min 21 txclkesc, max: ffffh*/ + REG_WRITE((MIPIA_DEVICE_RESET_TIMER_REG + reg_offset), 0xffff & DSI_RESET_TIMER_MASK); + + REG_WRITE((MIPIA_DPI_RESOLUTION_REG + reg_offset), mode->vdisplay << 16 | mode->hdisplay); + + /*set DPI timing registers*/ + mdfld_dsi_dpi_timing_calculation(mode, &dpi_timing, dsi_config->lane_count, dsi_config->bpp); + + REG_WRITE((MIPIA_HSYNC_COUNT_REG + reg_offset), dpi_timing.hsync_count & DSI_DPI_TIMING_MASK); + REG_WRITE((MIPIA_HBP_COUNT_REG + reg_offset), dpi_timing.hbp_count & DSI_DPI_TIMING_MASK); + REG_WRITE((MIPIA_HFP_COUNT_REG + reg_offset), dpi_timing.hfp_count & DSI_DPI_TIMING_MASK); + REG_WRITE((MIPIA_HACTIVE_COUNT_REG + reg_offset), dpi_timing.hactive_count & DSI_DPI_TIMING_MASK); + REG_WRITE((MIPIA_VSYNC_COUNT_REG + reg_offset), dpi_timing.vsync_count & DSI_DPI_TIMING_MASK); + REG_WRITE((MIPIA_VBP_COUNT_REG + reg_offset), dpi_timing.vbp_count & DSI_DPI_TIMING_MASK); + REG_WRITE((MIPIA_VFP_COUNT_REG + reg_offset), dpi_timing.vfp_count & DSI_DPI_TIMING_MASK); + + REG_WRITE((MIPIA_HIGH_LOW_SWITCH_COUNT_REG + reg_offset), 0x46); + + /*min: 7d0 max: 4e20*/ + REG_WRITE((MIPIA_INIT_COUNT_REG + reg_offset), 0x000007d0); + + /*set up video mode*/ + val = dsi_config->video_mode | DSI_DPI_COMPLETE_LAST_LINE; + REG_WRITE((MIPIA_VIDEO_MODE_FORMAT_REG + reg_offset), val); + + REG_WRITE((MIPIA_EOT_DISABLE_REG + reg_offset), 0x00000000); + + REG_WRITE((MIPIA_LP_BYTECLK_REG + reg_offset), 0x00000004); + + /*TODO: figure out how to setup these registers*/ + REG_WRITE((MIPIA_DPHY_PARAM_REG + reg_offset), 0x150c3408); + + REG_WRITE((MIPIA_CLK_LANE_SWITCH_TIME_CNT_REG + reg_offset), (0xa << 16) | 0x14); + + /*set device ready*/ + REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000001); +} + +static void dsi_controller_init(struct mdfld_dsi_config * dsi_config, int pipe) +{ + if (!dsi_config || ((pipe != 0) && (pipe != 2))) { + DRM_ERROR("Invalid parameters\n"); + return; + } + + if (dsi_config->type == MDFLD_DSI_ENCODER_DPI) + dsi_controller_dpi_init(dsi_config, pipe); + else if (dsi_config->type == MDFLD_DSI_ENCODER_DBI) + dsi_controller_dbi_init(dsi_config, pipe); + else + DRM_ERROR("Bad DSI encoder type\n"); +} + +void mdfld_dsi_cmds_kick_out(struct mdfld_dsi_pkg_sender *sender) +{ + process_pkg_list(sender); +} + +int mdfld_dsi_send_dcs(struct mdfld_dsi_pkg_sender *sender, + u8 dcs, u8 *param, u32 param_num, u8 data_src, + int delay) +{ + struct mdfld_dsi_pkg *pkg; + u32 cb_phy = sender->dbi_cb_phy; + struct drm_device *dev = sender->dev; + u32 index = 0; + u8 *cb = (u8 *)sender->dbi_cb_addr; + unsigned long flags; + int retry; + u8 *dst = NULL; + u32 len; + + if (!sender) { + WARN_ON(1); + return -EINVAL; + } + + if (!sender->dbi_pkg_support) { + dev_err(dev->dev, "No DBI pkg sending on this sender\n"); + return -ENOTSUPP; + } + + if (param_num > MDFLD_MAX_DCS_PARAM) { + dev_err(dev->dev, "Sender only supports up to %d DCS params\n", + MDFLD_MAX_DCS_PARAM); + return -EINVAL; + } + + /* + * If dcs is write_mem_start, send it directly using DSI adapter + * interface + */ + if (dcs == DCS_WRITE_MEM_START) { + if (!spin_trylock(&sender->lock)) + return -EAGAIN; + + /* + * query whether DBI FIFO is empty, + * if not wait it becoming empty + */ + retry = MDFLD_DSI_DBI_FIFO_TIMEOUT; + while (retry && + !(REG_READ(sender->mipi_gen_fifo_stat_reg) & (1 << 27))) { + udelay(500); + retry--; + } + + /* If DBI FIFO timeout, drop this frame */ + if (!retry) { + spin_unlock(&sender->lock); + return 0; + } + + *(cb + (index++)) = write_mem_start; + + REG_WRITE(sender->mipi_cmd_len_reg, 1); + REG_WRITE(sender->mipi_cmd_addr_reg, + cb_phy | (1 << 0) | (1 << 1)); + + retry = MDFLD_DSI_DBI_FIFO_TIMEOUT; + while (retry && + (REG_READ(sender->mipi_cmd_addr_reg) & (1 << 0))) { + udelay(1); + retry--; + } + + spin_unlock(&sender->lock); + return 0; + } + + /* Get a free pkg */ + spin_lock_irqsave(&sender->lock, flags); + pkg = pkg_sender_get_pkg_locked(sender); + spin_unlock_irqrestore(&sender->lock, flags); + + if (!pkg) { + dev_err(dev->dev, "No packages memory\n"); + return -ENOMEM; + } + + dst = pkg->pkg.dcs_pkg.param; + memcpy(dst, param, param_num); + + pkg->pkg_type = MDFLD_DSI_PKG_DCS; + pkg->transmission_type = MDFLD_DSI_DCS; + pkg->pkg.dcs_pkg.cmd = dcs; + pkg->pkg.dcs_pkg.param_num = param_num; + pkg->pkg.dcs_pkg.data_src = data_src; + + INIT_LIST_HEAD(&pkg->entry); + + if (param_num == 0) + return mdfld_dsi_send_mcs_short_hs(sender, dcs, 0, 0, delay); + else if (param_num == 1) + return mdfld_dsi_send_mcs_short_hs(sender, dcs, + param[0], 1, delay); + else if (param_num > 1) { + len = (param_num + 1) / 4; + if ((param_num + 1) % 4) + len++; + return mdfld_dsi_send_mcs_long_hs(sender, + (u32 *)&pkg->pkg.dcs_pkg, len, delay); + } + return 0; +} + +int mdfld_dsi_send_mcs_short_hs(struct mdfld_dsi_pkg_sender *sender, + u8 cmd, u8 param, u8 param_num, int delay) +{ + if (!sender) { + WARN_ON(1); + return -EINVAL; + } + return mdfld_dsi_send_mcs_short(sender, cmd, param, param_num, + MDFLD_DSI_HS_TRANSMISSION, delay); +} + +int mdfld_dsi_send_mcs_short_lp(struct mdfld_dsi_pkg_sender *sender, + u8 cmd, u8 param, u8 param_num, int delay) +{ + if (!sender) { + WARN_ON(1); + return -EINVAL; + } + return mdfld_dsi_send_mcs_short(sender, cmd, param, param_num, + MDFLD_DSI_LP_TRANSMISSION, delay); +} + +int mdfld_dsi_send_mcs_long_hs(struct mdfld_dsi_pkg_sender *sender, + u32 *data, + u32 len, + int delay) +{ + if (!sender || !data || !len) { + DRM_ERROR("Invalid parameters\n"); + return -EINVAL; + } + return mdfld_dsi_send_mcs_long(sender, data, len, + MDFLD_DSI_HS_TRANSMISSION, delay); +} + +int mdfld_dsi_send_mcs_long_lp(struct mdfld_dsi_pkg_sender *sender, + u32 *data, + u32 len, + int delay) +{ + if (!sender || !data || !len) { + WARN_ON(1); + return -EINVAL; + } + return mdfld_dsi_send_mcs_long(sender, data, len, + MDFLD_DSI_LP_TRANSMISSION, delay); +} + +int mdfld_dsi_send_gen_short_hs(struct mdfld_dsi_pkg_sender *sender, + u8 param0, u8 param1, u8 param_num, int delay) +{ + if (!sender) { + WARN_ON(1); + return -EINVAL; + } + return mdfld_dsi_send_gen_short(sender, param0, param1, param_num, + MDFLD_DSI_HS_TRANSMISSION, delay); +} + +int mdfld_dsi_send_gen_short_lp(struct mdfld_dsi_pkg_sender *sender, + u8 param0, u8 param1, u8 param_num, int delay) +{ + if (!sender || param_num < 0 || param_num > 2) { + WARN_ON(1); + return -EINVAL; + } + return mdfld_dsi_send_gen_short(sender, param0, param1, param_num, + MDFLD_DSI_LP_TRANSMISSION, delay); +} + +int mdfld_dsi_send_gen_long_hs(struct mdfld_dsi_pkg_sender *sender, + u32 *data, + u32 len, + int delay) +{ + if (!sender || !data || !len) { + WARN_ON(1); + return -EINVAL; + } + return mdfld_dsi_send_gen_long(sender, data, len, + MDFLD_DSI_HS_TRANSMISSION, delay); +} + +int mdfld_dsi_send_gen_long_lp(struct mdfld_dsi_pkg_sender *sender, + u32 *data, + u32 len, + int delay) +{ + if (!sender || !data || !len) { + WARN_ON(1); + return -EINVAL; + } + return mdfld_dsi_send_gen_long(sender, data, len, + MDFLD_DSI_LP_TRANSMISSION, delay); +} + +int mdfld_dsi_read_gen_hs(struct mdfld_dsi_pkg_sender *sender, + u8 param0, + u8 param1, + u8 param_num, + u32 *data, + u16 len) +{ + if (!sender || !data || param_num < 0 || param_num > 2 + || !data || !len) { + DRM_ERROR("Invalid parameters\n"); + return -EINVAL; + } + + return mdfld_dsi_read_gen(sender, param0, param1, param_num, + data, len, MDFLD_DSI_HS_TRANSMISSION); + +} + +int mdfld_dsi_read_gen_lp(struct mdfld_dsi_pkg_sender *sender, + u8 param0, + u8 param1, + u8 param_num, + u32 *data, + u16 len) +{ + if (!sender || !data || param_num < 0 || param_num > 2 + || !data || !len) { + DRM_ERROR("Invalid parameters\n"); + return -EINVAL; + } + + return mdfld_dsi_read_gen(sender, param0, param1, param_num, + data, len, MDFLD_DSI_LP_TRANSMISSION); +} + +int mdfld_dsi_read_mcs_hs(struct mdfld_dsi_pkg_sender *sender, + u8 cmd, + u32 *data, + u16 len) +{ + if (!sender || !data || !len) { + DRM_ERROR("Invalid parameters\n"); + return -EINVAL; + } + + return mdfld_dsi_read_mcs(sender, cmd, data, len, + MDFLD_DSI_HS_TRANSMISSION); +} + +int mdfld_dsi_read_mcs_lp(struct mdfld_dsi_pkg_sender *sender, + u8 cmd, + u32 *data, + u16 len) +{ + if (!sender || !data || !len) { + WARN_ON(1); + return -EINVAL; + } + + return mdfld_dsi_read_mcs(sender, cmd, data, len, + MDFLD_DSI_LP_TRANSMISSION); +} + +int mdfld_dsi_pkg_sender_init(struct mdfld_dsi_connector *dsi_connector, + int pipe) +{ + int ret; + struct mdfld_dsi_pkg_sender *pkg_sender; + struct mdfld_dsi_config *dsi_config = + mdfld_dsi_get_config(dsi_connector); + struct drm_device *dev = dsi_config->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_gtt *pg = &dev_priv->gtt; + int i; + struct mdfld_dsi_pkg *pkg, *tmp; + u32 mipi_val = 0; + + if (!dsi_connector) { + WARN_ON(1); + return -EINVAL; + } + + pkg_sender = dsi_connector->pkg_sender; + + if (!pkg_sender || IS_ERR(pkg_sender)) { + pkg_sender = kzalloc(sizeof(struct mdfld_dsi_pkg_sender), + GFP_KERNEL); + if (!pkg_sender) { + dev_err(dev->dev, "Create DSI pkg sender failed\n"); + return -ENOMEM; + } + + dsi_connector->pkg_sender = (void *)pkg_sender; + } + + pkg_sender->dev = dev; + pkg_sender->dsi_connector = dsi_connector; + pkg_sender->pipe = pipe; + pkg_sender->pkg_num = 0; + pkg_sender->panel_mode = 0; + pkg_sender->status = MDFLD_DSI_PKG_SENDER_FREE; + + /* Init dbi command buffer*/ + + if (dsi_config->type == MDFLD_DSI_ENCODER_DBI) { + pkg_sender->dbi_pkg_support = 1; + ret = mdfld_dbi_cb_init(pkg_sender, pg, pipe); + if (ret) { + dev_err(dev->dev, "DBI command buffer map failed\n"); + goto mapping_err; + } + } + + /* Init regs */ + if (pipe == 0) { + pkg_sender->dpll_reg = MRST_DPLL_A; + pkg_sender->dspcntr_reg = DSPACNTR; + pkg_sender->pipeconf_reg = PIPEACONF; + pkg_sender->dsplinoff_reg = DSPALINOFF; + pkg_sender->dspsurf_reg = DSPASURF; + pkg_sender->pipestat_reg = PIPEASTAT; + + pkg_sender->mipi_intr_stat_reg = MIPIA_INTR_STAT_REG; + pkg_sender->mipi_lp_gen_data_reg = MIPIA_LP_GEN_DATA_REG; + pkg_sender->mipi_hs_gen_data_reg = MIPIA_HS_GEN_DATA_REG; + pkg_sender->mipi_lp_gen_ctrl_reg = MIPIA_LP_GEN_CTRL_REG; + pkg_sender->mipi_hs_gen_ctrl_reg = MIPIA_HS_GEN_CTRL_REG; + pkg_sender->mipi_gen_fifo_stat_reg = MIPIA_GEN_FIFO_STAT_REG; + pkg_sender->mipi_data_addr_reg = MIPIA_DATA_ADD_REG; + pkg_sender->mipi_data_len_reg = MIPIA_DATA_LEN_REG; + pkg_sender->mipi_cmd_addr_reg = MIPIA_CMD_ADD_REG; + pkg_sender->mipi_cmd_len_reg = MIPIA_CMD_LEN_REG; + } else if (pipe == 2) { + pkg_sender->dpll_reg = MRST_DPLL_A; + pkg_sender->dspcntr_reg = DSPCCNTR; + pkg_sender->pipeconf_reg = PIPECCONF; + pkg_sender->dsplinoff_reg = DSPCLINOFF; + pkg_sender->dspsurf_reg = DSPCSURF; + pkg_sender->pipestat_reg = PIPECSTAT; + + pkg_sender->mipi_intr_stat_reg = + MIPIA_INTR_STAT_REG + MIPIC_REG_OFFSET; + pkg_sender->mipi_lp_gen_data_reg = + MIPIA_LP_GEN_DATA_REG + MIPIC_REG_OFFSET; + pkg_sender->mipi_hs_gen_data_reg = + MIPIA_HS_GEN_DATA_REG + MIPIC_REG_OFFSET; + pkg_sender->mipi_lp_gen_ctrl_reg = + MIPIA_LP_GEN_CTRL_REG + MIPIC_REG_OFFSET; + pkg_sender->mipi_hs_gen_ctrl_reg = + MIPIA_HS_GEN_CTRL_REG + MIPIC_REG_OFFSET; + pkg_sender->mipi_gen_fifo_stat_reg = + MIPIA_GEN_FIFO_STAT_REG + MIPIC_REG_OFFSET; + pkg_sender->mipi_data_addr_reg = + MIPIA_DATA_ADD_REG + MIPIC_REG_OFFSET; + pkg_sender->mipi_data_len_reg = + MIPIA_DATA_LEN_REG + MIPIC_REG_OFFSET; + pkg_sender->mipi_cmd_addr_reg = + MIPIA_CMD_ADD_REG + MIPIC_REG_OFFSET; + pkg_sender->mipi_cmd_len_reg = + MIPIA_CMD_LEN_REG + MIPIC_REG_OFFSET; + } + + /* Init pkg list */ + INIT_LIST_HEAD(&pkg_sender->pkg_list); + INIT_LIST_HEAD(&pkg_sender->free_list); + + spin_lock_init(&pkg_sender->lock); + + /* Allocate free pkg pool */ + for (i = 0; i < MDFLD_MAX_PKG_NUM; i++) { + pkg = kzalloc(sizeof(struct mdfld_dsi_pkg), GFP_KERNEL); + if (!pkg) { + dev_err(dev->dev, "Out of memory allocating pkg pool"); + ret = -ENOMEM; + goto pkg_alloc_err; + } + INIT_LIST_HEAD(&pkg->entry); + list_add_tail(&pkg->entry, &pkg_sender->free_list); + } + + /* + * For video mode, don't enable DPI timing output here, + * will init the DPI timing output during mode setting. + */ + if (dsi_config->type == MDFLD_DSI_ENCODER_DPI) + mipi_val = PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX; + else if (dsi_config->type == MDFLD_DSI_ENCODER_DBI) + mipi_val = PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX + | TE_TRIGGER_GPIO_PIN; + else + DRM_ERROR("Bad DSI encoder type\n"); + + if (pipe == 0) { + mipi_val |= 0x2; + REG_WRITE(MIPI, mipi_val); + REG_READ(MIPI); + } else if (pipe == 2) { + REG_WRITE(MIPI_C, mipi_val); + REG_READ(MIPI_C); + } + + /*do dsi controller init*/ + dsi_controller_init(dsi_config, pipe); + + return 0; + +pkg_alloc_err: + list_for_each_entry_safe(pkg, tmp, &pkg_sender->free_list, entry) { + list_del(&pkg->entry); + kfree(pkg); + } + + /* Free mapped command buffer */ + mdfld_dbi_cb_destroy(pkg_sender); +mapping_err: + kfree(pkg_sender); + dsi_connector->pkg_sender = NULL; + return ret; +} + +void mdfld_dsi_pkg_sender_destroy(struct mdfld_dsi_pkg_sender *sender) +{ + struct mdfld_dsi_pkg *pkg, *tmp; + + if (!sender || IS_ERR(sender)) + return; + + /* Free pkg pool */ + list_for_each_entry_safe(pkg, tmp, &sender->free_list, entry) { + list_del(&pkg->entry); + kfree(pkg); + } + /* Free pkg list */ + list_for_each_entry_safe(pkg, tmp, &sender->pkg_list, entry) { + list_del(&pkg->entry); + kfree(pkg); + } + mdfld_dbi_cb_destroy(sender); /* free mapped command buffer */ + kfree(sender); +} diff --git a/trunk/drivers/staging/gma500/mdfld_dsi_pkg_sender.h b/trunk/drivers/staging/gma500/mdfld_dsi_pkg_sender.h new file mode 100644 index 000000000000..f24abc700684 --- /dev/null +++ b/trunk/drivers/staging/gma500/mdfld_dsi_pkg_sender.h @@ -0,0 +1,184 @@ +/* + * Copyright © 2010 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: + * Jackie Li + */ +#ifndef __MDFLD_DSI_PKG_SENDER_H__ +#define __MDFLD_DSI_PKG_SENDER_H__ + +#include + +#define MDFLD_MAX_DCS_PARAM 8 +#define MDFLD_MAX_PKG_NUM 2048 + +enum { + MDFLD_DSI_PKG_DCS, + MDFLD_DSI_PKG_GEN_SHORT_WRITE_0 = 0x03, + MDFLD_DSI_PKG_GEN_SHORT_WRITE_1 = 0x13, + MDFLD_DSI_PKG_GEN_SHORT_WRITE_2 = 0x23, + MDFLD_DSI_PKG_GEN_READ_0 = 0x04, + MDFLD_DSI_PKG_GEN_READ_1 = 0x14, + MDFLD_DSI_PKG_GEN_READ_2 = 0x24, + MDFLD_DSI_PKG_GEN_LONG_WRITE = 0x29, + MDFLD_DSI_PKG_MCS_SHORT_WRITE_0 = 0x05, + MDFLD_DSI_PKG_MCS_SHORT_WRITE_1 = 0x15, + MDFLD_DSI_PKG_MCS_READ = 0x06, + MDFLD_DSI_PKG_MCS_LONG_WRITE = 0x39, +}; + +enum { + MDFLD_DSI_LP_TRANSMISSION, + MDFLD_DSI_HS_TRANSMISSION, + MDFLD_DSI_DCS, +}; + +enum { + MDFLD_DSI_PANEL_MODE_SLEEP = 0x1, +}; + +enum { + MDFLD_DSI_PKG_SENDER_FREE = 0x0, + MDFLD_DSI_PKG_SENDER_BUSY = 0x1, +}; + +enum { + MDFLD_DSI_SEND_PACKAGE, + MDFLD_DSI_QUEUE_PACKAGE, +}; + +struct mdfld_dsi_gen_short_pkg { + u8 cmd; + u8 param; +}; + +struct mdfld_dsi_gen_long_pkg { + u32 *data; + u32 len; +}; + +struct mdfld_dsi_dcs_pkg { + u8 cmd; + u8 param[MDFLD_MAX_DCS_PARAM]; + u32 param_num; + u8 data_src; +}; + +struct mdfld_dsi_pkg { + u8 pkg_type; + u8 transmission_type; + + union { + struct mdfld_dsi_gen_short_pkg short_pkg; + struct mdfld_dsi_gen_long_pkg long_pkg; + struct mdfld_dsi_dcs_pkg dcs_pkg; + } pkg; + + struct list_head entry; +}; + +struct mdfld_dsi_pkg_sender { + struct drm_device *dev; + struct mdfld_dsi_connector *dsi_connector; + u32 status; + + u32 panel_mode; + + int pipe; + + spinlock_t lock; + struct list_head pkg_list; + struct list_head free_list; + + u32 pkg_num; + + int dbi_pkg_support; + + u32 dbi_cb_phy; + void *dbi_cb_addr; + + /* Registers */ + u32 dpll_reg; + u32 dspcntr_reg; + u32 pipeconf_reg; + u32 pipestat_reg; + u32 dsplinoff_reg; + u32 dspsurf_reg; + + u32 mipi_intr_stat_reg; + u32 mipi_lp_gen_data_reg; + u32 mipi_hs_gen_data_reg; + u32 mipi_lp_gen_ctrl_reg; + u32 mipi_hs_gen_ctrl_reg; + u32 mipi_gen_fifo_stat_reg; + u32 mipi_data_addr_reg; + u32 mipi_data_len_reg; + u32 mipi_cmd_addr_reg; + u32 mipi_cmd_len_reg; +}; + +/* DCS definitions */ +#define DCS_SOFT_RESET 0x01 +#define DCS_ENTER_SLEEP_MODE 0x10 +#define DCS_EXIT_SLEEP_MODE 0x11 +#define DCS_SET_DISPLAY_OFF 0x28 +#define DCS_SET_DISPLAY_ON 0x29 +#define DCS_SET_COLUMN_ADDRESS 0x2a +#define DCS_SET_PAGE_ADDRESS 0x2b +#define DCS_WRITE_MEM_START 0x2c +#define DCS_SET_TEAR_OFF 0x34 +#define DCS_SET_TEAR_ON 0x35 + +extern int mdfld_dsi_pkg_sender_init(struct mdfld_dsi_connector *dsi_connector, + int pipe); +extern void mdfld_dsi_pkg_sender_destroy(struct mdfld_dsi_pkg_sender *sender); +extern int mdfld_dsi_send_dcs(struct mdfld_dsi_pkg_sender *sender, u8 dcs, + u8 *param, u32 param_num, u8 data_src, int delay); +extern int mdfld_dsi_send_mcs_short_hs(struct mdfld_dsi_pkg_sender *sender, + u8 cmd, u8 param, u8 param_num, int delay); +extern int mdfld_dsi_send_mcs_short_lp(struct mdfld_dsi_pkg_sender *sender, + u8 cmd, u8 param, u8 param_num, int delay); +extern int mdfld_dsi_send_mcs_long_hs(struct mdfld_dsi_pkg_sender *sender, + u32 *data, u32 len, int delay); +extern int mdfld_dsi_send_mcs_long_lp(struct mdfld_dsi_pkg_sender *sender, + u32 *data, u32 len, int delay); +extern int mdfld_dsi_send_gen_short_hs(struct mdfld_dsi_pkg_sender *sender, + u8 param0, u8 param1, u8 param_num, int delay); +extern int mdfld_dsi_send_gen_short_lp(struct mdfld_dsi_pkg_sender *sender, + u8 param0, u8 param1, u8 param_num, int delay); +extern int mdfld_dsi_send_gen_long_hs(struct mdfld_dsi_pkg_sender *sender, + u32 *data, u32 len, int delay); +extern int mdfld_dsi_send_gen_long_lp(struct mdfld_dsi_pkg_sender *sender, + u32 *data, u32 len, int delay); + +extern int mdfld_dsi_read_gen_hs(struct mdfld_dsi_pkg_sender *sender, + u8 param0, u8 param1, u8 param_num, u32 *data, u16 len); +extern int mdfld_dsi_read_gen_lp(struct mdfld_dsi_pkg_sender *sender, + u8 param0, u8 param1, u8 param_num, u32 *data, u16 len); +extern int mdfld_dsi_read_mcs_hs(struct mdfld_dsi_pkg_sender *sender, + u8 cmd, u32 *data, u16 len); +extern int mdfld_dsi_read_mcs_lp(struct mdfld_dsi_pkg_sender *sender, + u8 cmd, u32 *data, u16 len); + +extern void mdfld_dsi_cmds_kick_out(struct mdfld_dsi_pkg_sender *sender); + +#endif /* __MDFLD_DSI_PKG_SENDER_H__ */ diff --git a/trunk/drivers/staging/gma500/mdfld_intel_display.c b/trunk/drivers/staging/gma500/mdfld_intel_display.c new file mode 100644 index 000000000000..0b37b7b6b02a --- /dev/null +++ b/trunk/drivers/staging/gma500/mdfld_intel_display.c @@ -0,0 +1,1404 @@ +/* + * Copyright © 2006-2011 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: + * Eric Anholt + */ + +#include "framebuffer.h" +#include "psb_intel_display.h" +#include "mdfld_dsi_dbi.h" +#include "mdfld_dsi_dpi.h" +#include "mdfld_dsi_dbi_dpu.h" + +#include + +#ifdef MIN +#undef MIN +#endif + +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) + +/* Hardcoded currently */ +static int ksel = KSEL_CRYSTAL_19; + +extern void mdfld_save_display(struct drm_device *dev); +extern bool gbgfxsuspended; + +struct psb_intel_range_t { + int min, max; +}; + +struct mdfld_limit_t { + struct psb_intel_range_t dot, m, p1; +}; + +struct mdfld_intel_clock_t { + /* given values */ + int n; + int m1, m2; + int p1, p2; + /* derived values */ + int dot; + int vco; + int m; + int p; +}; + + + +#define COUNT_MAX 0x10000000 + +void mdfldWaitForPipeDisable(struct drm_device *dev, int pipe) +{ + int count, temp; + u32 pipeconf_reg = PIPEACONF; + + switch (pipe) { + case 0: + break; + case 1: + pipeconf_reg = PIPEBCONF; + break; + case 2: + pipeconf_reg = PIPECCONF; + break; + default: + DRM_ERROR("Illegal Pipe Number. \n"); + return; + } + + /* FIXME JLIU7_PO */ + psb_intel_wait_for_vblank(dev); + return; + + /* Wait for for the pipe disable to take effect. */ + for (count = 0; count < COUNT_MAX; count++) { + temp = REG_READ(pipeconf_reg); + if ((temp & PIPEACONF_PIPE_STATE) == 0) + break; + } +} + +void mdfldWaitForPipeEnable(struct drm_device *dev, int pipe) +{ + int count, temp; + u32 pipeconf_reg = PIPEACONF; + + switch (pipe) { + case 0: + break; + case 1: + pipeconf_reg = PIPEBCONF; + break; + case 2: + pipeconf_reg = PIPECCONF; + break; + default: + dev_err(dev->dev, "Illegal Pipe Number.\n"); + return; + } + + /* FIXME JLIU7_PO */ + psb_intel_wait_for_vblank(dev); + return; + + /* Wait for for the pipe enable to take effect. */ + for (count = 0; count < COUNT_MAX; count++) { + temp = REG_READ(pipeconf_reg); + if ((temp & PIPEACONF_PIPE_STATE) == 1) + break; + } +} + + +static int mdfld_intel_crtc_cursor_set(struct drm_crtc *crtc, + struct drm_file *file_priv, + uint32_t handle, + uint32_t width, uint32_t height) +{ + struct drm_device *dev = crtc->dev; + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + int pipe = psb_intel_crtc->pipe; + uint32_t control = CURACNTR; + uint32_t base = CURABASE; + uint32_t temp; + size_t addr = 0; + struct gtt_range *gt; + struct drm_gem_object *obj; + int ret; + + switch (pipe) { + case 0: + break; + case 1: + control = CURBCNTR; + base = CURBBASE; + break; + case 2: + control = CURCCNTR; + base = CURCBASE; + break; + default: + dev_err(dev->dev, "Illegal Pipe Number. \n"); + return -EINVAL; + } + +#if 1 /* FIXME_JLIU7 can't enalbe cursorB/C HW issue. need to remove after HW fix */ + if (pipe != 0) + return 0; +#endif + /* if we want to turn of the cursor ignore width and height */ + if (!handle) { + dev_dbg(dev->dev, "cursor off\n"); + /* turn off the cursor */ + temp = 0; + temp |= CURSOR_MODE_DISABLE; + + if (gma_power_begin(dev, true)) { + REG_WRITE(control, temp); + REG_WRITE(base, 0); + gma_power_end(dev); + } + /* Unpin the old GEM object */ + if (psb_intel_crtc->cursor_obj) { + gt = container_of(psb_intel_crtc->cursor_obj, + struct gtt_range, gem); + psb_gtt_unpin(gt); + drm_gem_object_unreference(psb_intel_crtc->cursor_obj); + psb_intel_crtc->cursor_obj = NULL; + } + return 0; + } + + /* Currently we only support 64x64 cursors */ + if (width != 64 || height != 64) { + DRM_ERROR("we currently only support 64x64 cursors\n"); + return -EINVAL; + } + + obj = drm_gem_object_lookup(dev, file_priv, handle); + if (!obj) + return -ENOENT; + + if (obj->size < width * height * 4) { + dev_dbg(dev->dev, "buffer is to small\n"); + return -ENOMEM; + } + + gt = container_of(obj, struct gtt_range, gem); + + /* Pin the memory into the GTT */ + ret = psb_gtt_pin(gt); + if (ret) { + dev_err(dev->dev, "Can not pin down handle 0x%x\n", handle); + return ret; + } + + + addr = gt->offset; /* Or resource.start ??? */ + + psb_intel_crtc->cursor_addr = addr; + + temp = 0; + /* set the pipe for the cursor */ + temp |= (pipe << 28); + temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE; + + if (gma_power_begin(dev, true)) { + REG_WRITE(control, temp); + REG_WRITE(base, addr); + gma_power_end(dev); + } + /* unpin the old GEM object */ + if (psb_intel_crtc->cursor_obj) { + gt = container_of(psb_intel_crtc->cursor_obj, + struct gtt_range, gem); + psb_gtt_unpin(gt); + drm_gem_object_unreference(psb_intel_crtc->cursor_obj); + psb_intel_crtc->cursor_obj = obj; + } + return 0; +} + +static int mdfld_intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) +{ + struct drm_device *dev = crtc->dev; + struct drm_psb_private * dev_priv = (struct drm_psb_private *)dev->dev_private; + struct mdfld_dbi_dpu_info *dpu_info = dev_priv->dbi_dpu_info; + struct psb_drm_dpu_rect rect; + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + int pipe = psb_intel_crtc->pipe; + uint32_t pos = CURAPOS; + uint32_t base = CURABASE; + uint32_t temp = 0; + uint32_t addr; + + switch (pipe) { + case 0: + if (dpu_info) { + rect.x = x; + rect.y = y; + + mdfld_dbi_dpu_report_damage(dev, MDFLD_CURSORA, &rect); + mdfld_dpu_exit_dsr(dev); + } else if (!(dev_priv->dsr_fb_update & MDFLD_DSR_CURSOR_0)) + mdfld_dsi_dbi_exit_dsr(dev, MDFLD_DSR_CURSOR_0); + break; + case 1: + pos = CURBPOS; + base = CURBBASE; + break; + case 2: + if (dpu_info) { + mdfld_dbi_dpu_report_damage(dev, MDFLD_CURSORC, &rect); + mdfld_dpu_exit_dsr(dev); + } else if (!(dev_priv->dsr_fb_update & MDFLD_DSR_CURSOR_2)) + mdfld_dsi_dbi_exit_dsr(dev, MDFLD_DSR_CURSOR_2); + pos = CURCPOS; + base = CURCBASE; + break; + default: + DRM_ERROR("Illegal Pipe Number. \n"); + return -EINVAL; + } + +#if 1 /* FIXME_JLIU7 can't enable cursorB/C HW issue. need to remove after HW fix */ + if (pipe != 0) + return 0; +#endif + if (x < 0) { + temp |= (CURSOR_POS_SIGN << CURSOR_X_SHIFT); + x = -x; + } + if (y < 0) { + temp |= (CURSOR_POS_SIGN << CURSOR_Y_SHIFT); + y = -y; + } + + temp |= ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT); + temp |= ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT); + + addr = psb_intel_crtc->cursor_addr; + + if (gma_power_begin(dev, true)) { + REG_WRITE(pos, temp); + REG_WRITE(base, addr); + gma_power_end(dev); + } + + return 0; +} + +const struct drm_crtc_funcs mdfld_intel_crtc_funcs = { + .cursor_set = mdfld_intel_crtc_cursor_set, + .cursor_move = mdfld_intel_crtc_cursor_move, + .gamma_set = psb_intel_crtc_gamma_set, + .set_config = drm_crtc_helper_set_config, + .destroy = psb_intel_crtc_destroy, +}; + +static struct drm_device globle_dev; + +void mdfld__intel_plane_set_alpha(int enable) +{ + struct drm_device *dev = &globle_dev; + int dspcntr_reg = DSPACNTR; + u32 dspcntr; + + dspcntr = REG_READ(dspcntr_reg); + + if (enable) { + dspcntr &= ~DISPPLANE_32BPP_NO_ALPHA; + dspcntr |= DISPPLANE_32BPP; + } else { + dspcntr &= ~DISPPLANE_32BPP; + dspcntr |= DISPPLANE_32BPP_NO_ALPHA; + } + + REG_WRITE(dspcntr_reg, dspcntr); +} + +int mdfld__intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) +{ + struct drm_device *dev = crtc->dev; + /* struct drm_i915_master_private *master_priv; */ + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb); + int pipe = psb_intel_crtc->pipe; + unsigned long start, offset; + int dsplinoff = DSPALINOFF; + int dspsurf = DSPASURF; + int dspstride = DSPASTRIDE; + int dspcntr_reg = DSPACNTR; + u32 dspcntr; + int ret = 0; + + memcpy(&globle_dev, dev, sizeof(struct drm_device)); + + if (!gma_power_begin(dev, true)) + return 0; + + /* no fb bound */ + if (!crtc->fb) { + dev_err(dev->dev, "No FB bound\n"); + goto psb_intel_pipe_cleaner; + } + + switch (pipe) { + case 0: + dsplinoff = DSPALINOFF; + break; + case 1: + dsplinoff = DSPBLINOFF; + dspsurf = DSPBSURF; + dspstride = DSPBSTRIDE; + dspcntr_reg = DSPBCNTR; + break; + case 2: + dsplinoff = DSPCLINOFF; + dspsurf = DSPCSURF; + dspstride = DSPCSTRIDE; + dspcntr_reg = DSPCCNTR; + break; + default: + dev_err(dev->dev, "Illegal Pipe Number.\n"); + return -EINVAL; + } + + ret = psb_gtt_pin(psbfb->gtt); + if (ret < 0) + goto psb_intel_pipe_set_base_exit; + + start = psbfb->gtt->offset; + offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8); + + REG_WRITE(dspstride, crtc->fb->pitches[0]); + dspcntr = REG_READ(dspcntr_reg); + dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; + + switch (crtc->fb->bits_per_pixel) { + case 8: + dspcntr |= DISPPLANE_8BPP; + break; + case 16: + if (crtc->fb->depth == 15) + dspcntr |= DISPPLANE_15_16BPP; + else + dspcntr |= DISPPLANE_16BPP; + break; + case 24: + case 32: + dspcntr |= DISPPLANE_32BPP_NO_ALPHA; + break; + default: + dev_err(dev->dev, "Unknown color depth\n"); + ret = -EINVAL; + goto psb_intel_pipe_set_base_exit; + } + REG_WRITE(dspcntr_reg, dspcntr); + + dev_dbg(dev->dev, "Writing base %08lX %08lX %d %d\n", + start, offset, x, y); + + REG_WRITE(dsplinoff, offset); + REG_READ(dsplinoff); + REG_WRITE(dspsurf, start); + REG_READ(dspsurf); + +psb_intel_pipe_cleaner: + /* If there was a previous display we can now unpin it */ + if (old_fb) + psb_gtt_unpin(to_psb_fb(old_fb)->gtt); + +psb_intel_pipe_set_base_exit: + gma_power_end(dev); + return ret; +} + +/** + * Disable the pipe, plane and pll. + * + */ +void mdfld_disable_crtc (struct drm_device *dev, int pipe) +{ + int dpll_reg = MRST_DPLL_A; + int dspcntr_reg = DSPACNTR; + int dspbase_reg = MRST_DSPABASE; + int pipeconf_reg = PIPEACONF; + u32 gen_fifo_stat_reg = GEN_FIFO_STAT_REG; + u32 temp; + + switch (pipe) { + case 0: + break; + case 1: + dpll_reg = MDFLD_DPLL_B; + dspcntr_reg = DSPBCNTR; + dspbase_reg = DSPBSURF; + pipeconf_reg = PIPEBCONF; + break; + case 2: + dpll_reg = MRST_DPLL_A; + dspcntr_reg = DSPCCNTR; + dspbase_reg = MDFLD_DSPCBASE; + pipeconf_reg = PIPECCONF; + gen_fifo_stat_reg = GEN_FIFO_STAT_REG + MIPIC_REG_OFFSET; + break; + default: + dev_err(dev->dev, "Illegal Pipe Number. \n"); + return; + } + + if (pipe != 1) + mdfld_dsi_gen_fifo_ready (dev, gen_fifo_stat_reg, HS_CTRL_FIFO_EMPTY | HS_DATA_FIFO_EMPTY); + + /* Disable display plane */ + temp = REG_READ(dspcntr_reg); + if ((temp & DISPLAY_PLANE_ENABLE) != 0) { + REG_WRITE(dspcntr_reg, + temp & ~DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + REG_READ(dspbase_reg); + } + + /* FIXME_JLIU7 MDFLD_PO revisit */ + /* Wait for vblank for the disable to take effect */ +/* MDFLD_PO_JLIU7 psb_intel_wait_for_vblank(dev); */ + + /* Next, disable display pipes */ + temp = REG_READ(pipeconf_reg); + if ((temp & PIPEACONF_ENABLE) != 0) { + temp &= ~PIPEACONF_ENABLE; + temp |= PIPECONF_PLANE_OFF | PIPECONF_CURSOR_OFF; + REG_WRITE(pipeconf_reg, temp); + REG_READ(pipeconf_reg); + + /* Wait for for the pipe disable to take effect. */ + mdfldWaitForPipeDisable(dev, pipe); + } + + temp = REG_READ(dpll_reg); + if (temp & DPLL_VCO_ENABLE) { + if (((pipe != 1) && !((REG_READ(PIPEACONF) | REG_READ(PIPECCONF)) & PIPEACONF_ENABLE)) + || (pipe == 1)){ + temp &= ~(DPLL_VCO_ENABLE); + REG_WRITE(dpll_reg, temp); + REG_READ(dpll_reg); + /* Wait for the clocks to turn off. */ + /* FIXME_MDFLD PO may need more delay */ + udelay(500); + + if (!(temp & MDFLD_PWR_GATE_EN)) { + /* gating power of DPLL */ + REG_WRITE(dpll_reg, temp | MDFLD_PWR_GATE_EN); + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(5000); + } + } + } + +} + +/** + * Sets the power management mode of the pipe and plane. + * + * This code should probably grow support for turning the cursor off and back + * on appropriately at the same time as we're turning the pipe off/on. + */ +static void mdfld_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + int pipe = psb_intel_crtc->pipe; + int dpll_reg = MRST_DPLL_A; + int dspcntr_reg = DSPACNTR; + int dspbase_reg = MRST_DSPABASE; + int pipeconf_reg = PIPEACONF; + u32 pipestat_reg = PIPEASTAT; + u32 gen_fifo_stat_reg = GEN_FIFO_STAT_REG; + u32 pipeconf = dev_priv->pipeconf; + u32 dspcntr = dev_priv->dspcntr; + u32 mipi_enable_reg = MIPIA_DEVICE_READY_REG; + u32 temp; + bool enabled; + int timeout = 0; + + if (!gma_power_begin(dev, true)) + return; + + /* Ignore if system is already in DSR and in suspended state. */ + if(/*gbgfxsuspended */0 && dev_priv->dispstatus == false && mode == 3){ + if(dev_priv->rpm_enabled && pipe == 1){ + // dev_priv->is_mipi_on = false; + pm_request_idle(&dev->pdev->dev); + } + return; + }else if(mode == 0) { + //do not need to set gbdispstatus=true in crtc. + //this will be set in encoder such as mdfld_dsi_dbi_dpms + //gbdispstatus = true; + } + +/* FIXME_JLIU7 MDFLD_PO replaced w/ the following function */ +/* mdfld_dbi_dpms (struct drm_device *dev, int pipe, bool enabled) */ + + switch (pipe) { + case 0: + break; + case 1: + dpll_reg = DPLL_B; + dspcntr_reg = DSPBCNTR; + dspbase_reg = MRST_DSPBBASE; + pipeconf_reg = PIPEBCONF; + pipeconf = dev_priv->pipeconf1; + dspcntr = dev_priv->dspcntr1; + dpll_reg = MDFLD_DPLL_B; + break; + case 2: + dpll_reg = MRST_DPLL_A; + dspcntr_reg = DSPCCNTR; + dspbase_reg = MDFLD_DSPCBASE; + pipeconf_reg = PIPECCONF; + pipestat_reg = PIPECSTAT; + pipeconf = dev_priv->pipeconf2; + dspcntr = dev_priv->dspcntr2; + gen_fifo_stat_reg = GEN_FIFO_STAT_REG + MIPIC_REG_OFFSET; + mipi_enable_reg = MIPIA_DEVICE_READY_REG + MIPIC_REG_OFFSET; + break; + default: + dev_err(dev->dev, "Illegal Pipe Number.\n"); + return; + } + + /* 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: + /* Enable the DPLL */ + temp = REG_READ(dpll_reg); + + if ((temp & DPLL_VCO_ENABLE) == 0) { + /* When ungating power of DPLL, needs to wait 0.5us before enable the VCO */ + if (temp & MDFLD_PWR_GATE_EN) { + temp &= ~MDFLD_PWR_GATE_EN; + REG_WRITE(dpll_reg, temp); + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(500); + } + + REG_WRITE(dpll_reg, temp); + REG_READ(dpll_reg); + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(500); + + REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); + REG_READ(dpll_reg); + + /** + * wait for DSI PLL to lock + * NOTE: only need to poll status of pipe 0 and pipe 1, + * since both MIPI pipes share the same PLL. + */ + while ((pipe != 2) && (timeout < 20000) && !(REG_READ(pipeconf_reg) & PIPECONF_DSIPLL_LOCK)) { + udelay(150); + timeout ++; + } + } + + /* Enable the plane */ + temp = REG_READ(dspcntr_reg); + if ((temp & DISPLAY_PLANE_ENABLE) == 0) { + REG_WRITE(dspcntr_reg, + temp | DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + } + + /* Enable the pipe */ + temp = REG_READ(pipeconf_reg); + if ((temp & PIPEACONF_ENABLE) == 0) { + REG_WRITE(pipeconf_reg, pipeconf); + + /* Wait for for the pipe enable to take effect. */ + mdfldWaitForPipeEnable(dev, pipe); + } + + /*workaround for sighting 3741701 Random X blank display*/ + /*perform w/a in video mode only on pipe A or C*/ + if ((pipe == 0 || pipe == 2) && + (mdfld_panel_dpi(dev) == true)) { + REG_WRITE(pipestat_reg, REG_READ(pipestat_reg)); + msleep(100); + if(PIPE_VBLANK_STATUS & REG_READ(pipestat_reg)) { + printk(KERN_ALERT "OK"); + } else { + printk(KERN_ALERT "STUCK!!!!"); + /*shutdown controller*/ + temp = REG_READ(dspcntr_reg); + REG_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE); + REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + /*mdfld_dsi_dpi_shut_down(dev, pipe);*/ + REG_WRITE(0xb048, 1); + msleep(100); + temp = REG_READ(pipeconf_reg); + temp &= ~PIPEACONF_ENABLE; + REG_WRITE(pipeconf_reg, temp); + msleep(100); /*wait for pipe disable*/ + /*printk(KERN_ALERT "70008 is %x\n", REG_READ(0x70008)); + printk(KERN_ALERT "b074 is %x\n", REG_READ(0xb074));*/ + REG_WRITE(mipi_enable_reg, 0); + msleep(100); + printk(KERN_ALERT "70008 is %x\n", REG_READ(0x70008)); + printk(KERN_ALERT "b074 is %x\n", REG_READ(0xb074)); + REG_WRITE(0xb004, REG_READ(0xb004)); + /* try to bring the controller back up again*/ + REG_WRITE(mipi_enable_reg, 1); + temp = REG_READ(dspcntr_reg); + REG_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE); + REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + /*mdfld_dsi_dpi_turn_on(dev, pipe);*/ + REG_WRITE(0xb048, 2); + msleep(100); + temp = REG_READ(pipeconf_reg); + temp |= PIPEACONF_ENABLE; + REG_WRITE(pipeconf_reg, temp); + } + } + + psb_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 */ + + break; + case DRM_MODE_DPMS_OFF: + /* Give the overlay scaler a chance to disable + * if it's on this pipe */ + /* psb_intel_crtc_dpms_video(crtc, FALSE); TODO */ + if (pipe != 1) + mdfld_dsi_gen_fifo_ready (dev, gen_fifo_stat_reg, HS_CTRL_FIFO_EMPTY | HS_DATA_FIFO_EMPTY); + + /* Disable the VGA plane that we never use */ + REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); + + /* Disable display plane */ + temp = REG_READ(dspcntr_reg); + if ((temp & DISPLAY_PLANE_ENABLE) != 0) { + REG_WRITE(dspcntr_reg, + temp & ~DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + REG_READ(dspbase_reg); + } + + /* FIXME_JLIU7 MDFLD_PO revisit */ + /* Wait for vblank for the disable to take effect */ +// MDFLD_PO_JLIU7 psb_intel_wait_for_vblank(dev); + + /* Next, disable display pipes */ + temp = REG_READ(pipeconf_reg); + if ((temp & PIPEACONF_ENABLE) != 0) { + temp &= ~PIPEACONF_ENABLE; + temp |= PIPECONF_PLANE_OFF | PIPECONF_CURSOR_OFF; + REG_WRITE(pipeconf_reg, temp); +// REG_WRITE(pipeconf_reg, 0); + REG_READ(pipeconf_reg); + + /* Wait for for the pipe disable to take effect. */ + mdfldWaitForPipeDisable(dev, pipe); + } + + temp = REG_READ(dpll_reg); + if (temp & DPLL_VCO_ENABLE) { + if (((pipe != 1) && !((REG_READ(PIPEACONF) | REG_READ(PIPECCONF)) & PIPEACONF_ENABLE)) + || (pipe == 1)){ + temp &= ~(DPLL_VCO_ENABLE); + REG_WRITE(dpll_reg, temp); + REG_READ(dpll_reg); + /* Wait for the clocks to turn off. */ + /* FIXME_MDFLD PO may need more delay */ + udelay(500); +#if 0 /* MDFLD_PO_JLIU7 */ + if (!(temp & MDFLD_PWR_GATE_EN)) { + /* gating power of DPLL */ + REG_WRITE(dpll_reg, temp | MDFLD_PWR_GATE_EN); + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(5000); + } +#endif /* MDFLD_PO_JLIU7 */ + } + } + break; + } + + enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF; + +#if 0 /* JB: Add vblank support later */ + if (enabled) + dev_priv->vblank_pipe |= (1 << pipe); + else + dev_priv->vblank_pipe &= ~(1 << pipe); +#endif + + gma_power_end(dev); +} + + +#define MDFLD_LIMT_DPLL_19 0 +#define MDFLD_LIMT_DPLL_25 1 +#define MDFLD_LIMT_DPLL_83 2 +#define MDFLD_LIMT_DPLL_100 3 +#define MDFLD_LIMT_DSIPLL_19 4 +#define MDFLD_LIMT_DSIPLL_25 5 +#define MDFLD_LIMT_DSIPLL_83 6 +#define MDFLD_LIMT_DSIPLL_100 7 + +#define MDFLD_DOT_MIN 19750 /* FIXME_MDFLD JLIU7 need to find out min & max for MDFLD */ +#define MDFLD_DOT_MAX 120000 +#define MDFLD_DPLL_M_MIN_19 113 +#define MDFLD_DPLL_M_MAX_19 155 +#define MDFLD_DPLL_P1_MIN_19 2 +#define MDFLD_DPLL_P1_MAX_19 10 +#define MDFLD_DPLL_M_MIN_25 101 +#define MDFLD_DPLL_M_MAX_25 130 +#define MDFLD_DPLL_P1_MIN_25 2 +#define MDFLD_DPLL_P1_MAX_25 10 +#define MDFLD_DPLL_M_MIN_83 64 +#define MDFLD_DPLL_M_MAX_83 64 +#define MDFLD_DPLL_P1_MIN_83 2 +#define MDFLD_DPLL_P1_MAX_83 2 +#define MDFLD_DPLL_M_MIN_100 64 +#define MDFLD_DPLL_M_MAX_100 64 +#define MDFLD_DPLL_P1_MIN_100 2 +#define MDFLD_DPLL_P1_MAX_100 2 +#define MDFLD_DSIPLL_M_MIN_19 131 +#define MDFLD_DSIPLL_M_MAX_19 175 +#define MDFLD_DSIPLL_P1_MIN_19 3 +#define MDFLD_DSIPLL_P1_MAX_19 8 +#define MDFLD_DSIPLL_M_MIN_25 97 +#define MDFLD_DSIPLL_M_MAX_25 140 +#define MDFLD_DSIPLL_P1_MIN_25 3 +#define MDFLD_DSIPLL_P1_MAX_25 9 +#define MDFLD_DSIPLL_M_MIN_83 33 +#define MDFLD_DSIPLL_M_MAX_83 92 +#define MDFLD_DSIPLL_P1_MIN_83 2 +#define MDFLD_DSIPLL_P1_MAX_83 3 +#define MDFLD_DSIPLL_M_MIN_100 97 +#define MDFLD_DSIPLL_M_MAX_100 140 +#define MDFLD_DSIPLL_P1_MIN_100 3 +#define MDFLD_DSIPLL_P1_MAX_100 9 + +static const struct mdfld_limit_t mdfld_limits[] = { + { /* MDFLD_LIMT_DPLL_19 */ + .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, + .m = {.min = MDFLD_DPLL_M_MIN_19, .max = MDFLD_DPLL_M_MAX_19}, + .p1 = {.min = MDFLD_DPLL_P1_MIN_19, .max = MDFLD_DPLL_P1_MAX_19}, + }, + { /* MDFLD_LIMT_DPLL_25 */ + .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, + .m = {.min = MDFLD_DPLL_M_MIN_25, .max = MDFLD_DPLL_M_MAX_25}, + .p1 = {.min = MDFLD_DPLL_P1_MIN_25, .max = MDFLD_DPLL_P1_MAX_25}, + }, + { /* MDFLD_LIMT_DPLL_83 */ + .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, + .m = {.min = MDFLD_DPLL_M_MIN_83, .max = MDFLD_DPLL_M_MAX_83}, + .p1 = {.min = MDFLD_DPLL_P1_MIN_83, .max = MDFLD_DPLL_P1_MAX_83}, + }, + { /* MDFLD_LIMT_DPLL_100 */ + .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, + .m = {.min = MDFLD_DPLL_M_MIN_100, .max = MDFLD_DPLL_M_MAX_100}, + .p1 = {.min = MDFLD_DPLL_P1_MIN_100, .max = MDFLD_DPLL_P1_MAX_100}, + }, + { /* MDFLD_LIMT_DSIPLL_19 */ + .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, + .m = {.min = MDFLD_DSIPLL_M_MIN_19, .max = MDFLD_DSIPLL_M_MAX_19}, + .p1 = {.min = MDFLD_DSIPLL_P1_MIN_19, .max = MDFLD_DSIPLL_P1_MAX_19}, + }, + { /* MDFLD_LIMT_DSIPLL_25 */ + .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, + .m = {.min = MDFLD_DSIPLL_M_MIN_25, .max = MDFLD_DSIPLL_M_MAX_25}, + .p1 = {.min = MDFLD_DSIPLL_P1_MIN_25, .max = MDFLD_DSIPLL_P1_MAX_25}, + }, + { /* MDFLD_LIMT_DSIPLL_83 */ + .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, + .m = {.min = MDFLD_DSIPLL_M_MIN_83, .max = MDFLD_DSIPLL_M_MAX_83}, + .p1 = {.min = MDFLD_DSIPLL_P1_MIN_83, .max = MDFLD_DSIPLL_P1_MAX_83}, + }, + { /* MDFLD_LIMT_DSIPLL_100 */ + .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, + .m = {.min = MDFLD_DSIPLL_M_MIN_100, .max = MDFLD_DSIPLL_M_MAX_100}, + .p1 = {.min = MDFLD_DSIPLL_P1_MIN_100, .max = MDFLD_DSIPLL_P1_MAX_100}, + }, +}; + +#define MDFLD_M_MIN 21 +#define MDFLD_M_MAX 180 +static const u32 mdfld_m_converts[] = { +/* M configuration table from 9-bit LFSR table */ + 224, 368, 440, 220, 366, 439, 219, 365, 182, 347, /* 21 - 30 */ + 173, 342, 171, 85, 298, 149, 74, 37, 18, 265, /* 31 - 40 */ + 388, 194, 353, 432, 216, 108, 310, 155, 333, 166, /* 41 - 50 */ + 83, 41, 276, 138, 325, 162, 337, 168, 340, 170, /* 51 - 60 */ + 341, 426, 469, 234, 373, 442, 221, 110, 311, 411, /* 61 - 70 */ + 461, 486, 243, 377, 188, 350, 175, 343, 427, 213, /* 71 - 80 */ + 106, 53, 282, 397, 354, 227, 113, 56, 284, 142, /* 81 - 90 */ + 71, 35, 273, 136, 324, 418, 465, 488, 500, 506, /* 91 - 100 */ + 253, 126, 63, 287, 399, 455, 483, 241, 376, 444, /* 101 - 110 */ + 478, 495, 503, 251, 381, 446, 479, 239, 375, 443, /* 111 - 120 */ + 477, 238, 119, 315, 157, 78, 295, 147, 329, 420, /* 121 - 130 */ + 210, 105, 308, 154, 77, 38, 275, 137, 68, 290, /* 131 - 140 */ + 145, 328, 164, 82, 297, 404, 458, 485, 498, 249, /* 141 - 150 */ + 380, 190, 351, 431, 471, 235, 117, 314, 413, 206, /* 151 - 160 */ + 103, 51, 25, 12, 262, 387, 193, 96, 48, 280, /* 161 - 170 */ + 396, 198, 99, 305, 152, 76, 294, 403, 457, 228, /* 171 - 180 */ +}; + +static const struct mdfld_limit_t *mdfld_limit(struct drm_crtc *crtc) +{ + const struct mdfld_limit_t *limit = NULL; + struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + + if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_MIPI) + || psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_MIPI2)) { + if ((ksel == KSEL_CRYSTAL_19) || (ksel == KSEL_BYPASS_19)) + limit = &mdfld_limits[MDFLD_LIMT_DSIPLL_19]; + else if (ksel == KSEL_BYPASS_25) + limit = &mdfld_limits[MDFLD_LIMT_DSIPLL_25]; + else if ((ksel == KSEL_BYPASS_83_100) && (dev_priv->core_freq == 166)) + limit = &mdfld_limits[MDFLD_LIMT_DSIPLL_83]; + else if ((ksel == KSEL_BYPASS_83_100) && + (dev_priv->core_freq == 100 || dev_priv->core_freq == 200)) + limit = &mdfld_limits[MDFLD_LIMT_DSIPLL_100]; + } else if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI)) { + if ((ksel == KSEL_CRYSTAL_19) || (ksel == KSEL_BYPASS_19)) + limit = &mdfld_limits[MDFLD_LIMT_DPLL_19]; + else if (ksel == KSEL_BYPASS_25) + limit = &mdfld_limits[MDFLD_LIMT_DPLL_25]; + else if ((ksel == KSEL_BYPASS_83_100) && (dev_priv->core_freq == 166)) + limit = &mdfld_limits[MDFLD_LIMT_DPLL_83]; + else if ((ksel == KSEL_BYPASS_83_100) && + (dev_priv->core_freq == 100 || dev_priv->core_freq == 200)) + limit = &mdfld_limits[MDFLD_LIMT_DPLL_100]; + } else { + limit = NULL; + dev_err(dev->dev, "mdfld_limit Wrong display type.\n"); + } + + return limit; +} + +/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */ +static void mdfld_clock(int refclk, struct mdfld_intel_clock_t *clock) +{ + clock->dot = (refclk * clock->m) / clock->p1; +} + +/** + * Returns a set of divisors for the desired target clock with the given refclk, + * or FALSE. Divisor values are the actual divisors for + */ +static bool +mdfldFindBestPLL(struct drm_crtc *crtc, int target, int refclk, + struct mdfld_intel_clock_t *best_clock) +{ + struct mdfld_intel_clock_t clock; + const struct mdfld_limit_t *limit = mdfld_limit(crtc); + int err = target; + + memset(best_clock, 0, sizeof(*best_clock)); + + for (clock.m = limit->m.min; clock.m <= limit->m.max; clock.m++) { + for (clock.p1 = limit->p1.min; clock.p1 <= limit->p1.max; + clock.p1++) { + int this_err; + + mdfld_clock(refclk, &clock); + + this_err = abs(clock.dot - target); + if (this_err < err) { + *best_clock = clock; + err = this_err; + } + } + } + return err != target; +} + +/** + * Return the pipe currently connected to the panel fitter, + * or -1 if the panel fitter is not present or not in use + */ +static int mdfld_panel_fitter_pipe(struct drm_device *dev) +{ + u32 pfit_control; + + pfit_control = REG_READ(PFIT_CONTROL); + + /* See if the panel fitter is in use */ + if ((pfit_control & PFIT_ENABLE) == 0) + return -1; + return (pfit_control >> 29) & 3; +} + +static int mdfld_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, + struct drm_framebuffer *old_fb) +{ + struct drm_device *dev = crtc->dev; + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + struct drm_psb_private *dev_priv = dev->dev_private; + int pipe = psb_intel_crtc->pipe; + int fp_reg = MRST_FPA0; + int dpll_reg = MRST_DPLL_A; + int dspcntr_reg = DSPACNTR; + int pipeconf_reg = PIPEACONF; + int htot_reg = HTOTAL_A; + int hblank_reg = HBLANK_A; + int hsync_reg = HSYNC_A; + int vtot_reg = VTOTAL_A; + int vblank_reg = VBLANK_A; + int vsync_reg = VSYNC_A; + int dspsize_reg = DSPASIZE; + int dsppos_reg = DSPAPOS; + int pipesrc_reg = PIPEASRC; + u32 *pipeconf = &dev_priv->pipeconf; + u32 *dspcntr = &dev_priv->dspcntr; + int refclk = 0; + int clk_n = 0, clk_p2 = 0, clk_byte = 1, clk = 0, m_conv = 0, clk_tmp = 0; + struct mdfld_intel_clock_t clock; + bool ok; + u32 dpll = 0, fp = 0; + bool is_crt = false, is_lvds = false, is_tv = false; + bool is_mipi = false, is_mipi2 = false, is_hdmi = false; + struct drm_mode_config *mode_config = &dev->mode_config; + struct psb_intel_output *psb_intel_output = NULL; + uint64_t scalingType = DRM_MODE_SCALE_FULLSCREEN; + struct drm_encoder *encoder; + struct drm_connector *connector; + int timeout = 0; + + dev_dbg(dev->dev, "pipe = 0x%x \n", pipe); + + switch (pipe) { + case 0: + break; + case 1: + fp_reg = FPB0; + dpll_reg = DPLL_B; + dspcntr_reg = DSPBCNTR; + pipeconf_reg = PIPEBCONF; + htot_reg = HTOTAL_B; + hblank_reg = HBLANK_B; + hsync_reg = HSYNC_B; + vtot_reg = VTOTAL_B; + vblank_reg = VBLANK_B; + vsync_reg = VSYNC_B; + dspsize_reg = DSPBSIZE; + dsppos_reg = DSPBPOS; + pipesrc_reg = PIPEBSRC; + pipeconf = &dev_priv->pipeconf1; + dspcntr = &dev_priv->dspcntr1; + fp_reg = MDFLD_DPLL_DIV0; + dpll_reg = MDFLD_DPLL_B; + break; + case 2: + dpll_reg = MRST_DPLL_A; + dspcntr_reg = DSPCCNTR; + pipeconf_reg = PIPECCONF; + htot_reg = HTOTAL_C; + hblank_reg = HBLANK_C; + hsync_reg = HSYNC_C; + vtot_reg = VTOTAL_C; + vblank_reg = VBLANK_C; + vsync_reg = VSYNC_C; + dspsize_reg = DSPCSIZE; + dsppos_reg = DSPCPOS; + pipesrc_reg = PIPECSRC; + pipeconf = &dev_priv->pipeconf2; + dspcntr = &dev_priv->dspcntr2; + break; + default: + DRM_ERROR("Illegal Pipe Number. \n"); + return 0; + } + + dev_dbg(dev->dev, "adjusted_hdisplay = %d\n", + adjusted_mode->hdisplay); + dev_dbg(dev->dev, "adjusted_vdisplay = %d\n", + adjusted_mode->vdisplay); + dev_dbg(dev->dev, "adjusted_hsync_start = %d\n", + adjusted_mode->hsync_start); + dev_dbg(dev->dev, "adjusted_hsync_end = %d\n", + adjusted_mode->hsync_end); + dev_dbg(dev->dev, "adjusted_htotal = %d\n", + adjusted_mode->htotal); + dev_dbg(dev->dev, "adjusted_vsync_start = %d\n", + adjusted_mode->vsync_start); + dev_dbg(dev->dev, "adjusted_vsync_end = %d\n", + adjusted_mode->vsync_end); + dev_dbg(dev->dev, "adjusted_vtotal = %d\n", + adjusted_mode->vtotal); + dev_dbg(dev->dev, "adjusted_clock = %d\n", + adjusted_mode->clock); + dev_dbg(dev->dev, "hdisplay = %d\n", + mode->hdisplay); + dev_dbg(dev->dev, "vdisplay = %d\n", + mode->vdisplay); + + if (!gma_power_begin(dev, true)) + return 0; + + memcpy(&psb_intel_crtc->saved_mode, mode, sizeof(struct drm_display_mode)); + memcpy(&psb_intel_crtc->saved_adjusted_mode, adjusted_mode, sizeof(struct drm_display_mode)); + + list_for_each_entry(connector, &mode_config->connector_list, head) { + + encoder = connector->encoder; + + if(!encoder) + continue; + + if (encoder->crtc != crtc) + continue; + + psb_intel_output = to_psb_intel_output(connector); + + dev_dbg(dev->dev, "output->type = 0x%x \n", psb_intel_output->type); + + switch (psb_intel_output->type) { + case INTEL_OUTPUT_LVDS: + is_lvds = true; + break; + case INTEL_OUTPUT_TVOUT: + is_tv = true; + break; + case INTEL_OUTPUT_ANALOG: + is_crt = true; + break; + case INTEL_OUTPUT_MIPI: + is_mipi = true; + break; + case INTEL_OUTPUT_MIPI2: + is_mipi2 = true; + break; + case INTEL_OUTPUT_HDMI: + is_hdmi = true; + break; + } + } + + /* Disable the VGA plane that we never use */ + REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); + + /* Disable the panel fitter if it was on our pipe */ + if (mdfld_panel_fitter_pipe(dev) == pipe) + REG_WRITE(PFIT_CONTROL, 0); + + /* pipesrc and dspsize control the size that is scaled from, + * which should always be the user's requested size. + */ + if (pipe == 1) { + /* FIXME: To make HDMI display with 864x480 (TPO), 480x864 (PYR) or 480x854 (TMD), set the sprite + * width/height and souce image size registers with the adjusted mode for pipe B. */ + + /* The defined sprite rectangle must always be completely contained within the displayable + * area of the screen image (frame buffer). */ + REG_WRITE(dspsize_reg, ((MIN(mode->crtc_vdisplay, adjusted_mode->crtc_vdisplay) - 1) << 16) + | (MIN(mode->crtc_hdisplay, adjusted_mode->crtc_hdisplay) - 1)); + /* Set the CRTC with encoder mode. */ + REG_WRITE(pipesrc_reg, ((mode->crtc_hdisplay - 1) << 16) + | (mode->crtc_vdisplay - 1)); + } else { + REG_WRITE(dspsize_reg, ((mode->crtc_vdisplay - 1) << 16) | (mode->crtc_hdisplay - 1)); + REG_WRITE(pipesrc_reg, ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); + } + + REG_WRITE(dsppos_reg, 0); + + if (psb_intel_output) + drm_connector_property_get_value(&psb_intel_output->base, + dev->mode_config.scaling_mode_property, &scalingType); + + if (scalingType == DRM_MODE_SCALE_NO_SCALE) { + /* + * Medfield doesn't have register support for centering so + * we need to mess with the h/vblank and h/vsync start and + * ends to get central + */ + int offsetX = 0, offsetY = 0; + + offsetX = (adjusted_mode->crtc_hdisplay - mode->crtc_hdisplay) / 2; + offsetY = (adjusted_mode->crtc_vdisplay - mode->crtc_vdisplay) / 2; + + REG_WRITE(htot_reg, (mode->crtc_hdisplay - 1) | + ((adjusted_mode->crtc_htotal - 1) << 16)); + REG_WRITE(vtot_reg, (mode->crtc_vdisplay - 1) | + ((adjusted_mode->crtc_vtotal - 1) << 16)); + REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - offsetX - 1) | + ((adjusted_mode->crtc_hblank_end - offsetX - 1) << 16)); + REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - offsetX - 1) | + ((adjusted_mode->crtc_hsync_end - offsetX - 1) << 16)); + REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - offsetY - 1) | + ((adjusted_mode->crtc_vblank_end - offsetY - 1) << 16)); + REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - offsetY - 1) | + ((adjusted_mode->crtc_vsync_end - offsetY - 1) << 16)); + } else { + REG_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) | + ((adjusted_mode->crtc_htotal - 1) << 16)); + REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | + ((adjusted_mode->crtc_vtotal - 1) << 16)); + REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | + ((adjusted_mode->crtc_hblank_end - 1) << 16)); + REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | + ((adjusted_mode->crtc_hsync_end - 1) << 16)); + REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | + ((adjusted_mode->crtc_vblank_end - 1) << 16)); + REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | + ((adjusted_mode->crtc_vsync_end - 1) << 16)); + } + + /* Flush the plane changes */ + { + struct drm_crtc_helper_funcs *crtc_funcs = + crtc->helper_private; + crtc_funcs->mode_set_base(crtc, x, y, old_fb); + } + + /* setup pipeconf */ + *pipeconf = PIPEACONF_ENABLE; /* FIXME_JLIU7 REG_READ(pipeconf_reg); */ + + /* Set up the display plane register */ + *dspcntr = REG_READ(dspcntr_reg); + *dspcntr |= pipe << DISPPLANE_SEL_PIPE_POS; + *dspcntr |= DISPLAY_PLANE_ENABLE; +/* MDFLD_PO_JLIU7 dspcntr |= DISPPLANE_BOTTOM; */ +/* MDFLD_PO_JLIU7 dspcntr |= DISPPLANE_GAMMA_ENABLE; */ + + if (is_mipi2) + { + goto mrst_crtc_mode_set_exit; + } +/* FIXME JLIU7 Add MDFLD HDMI supports */ +/* FIXME_MDFLD JLIU7 DSIPLL clock *= 8? */ +/* FIXME_MDFLD JLIU7 need to revist for dual MIPI supports */ + clk = adjusted_mode->clock; + + if (is_hdmi) { + if ((ksel == KSEL_CRYSTAL_19) || (ksel == KSEL_BYPASS_19)) + { + refclk = 19200; + + if (is_mipi || is_mipi2) + { + clk_n = 1, clk_p2 = 8; + } else if (is_hdmi) { + clk_n = 1, clk_p2 = 10; + } + } else if (ksel == KSEL_BYPASS_25) { + refclk = 25000; + + if (is_mipi || is_mipi2) + { + clk_n = 1, clk_p2 = 8; + } else if (is_hdmi) { + clk_n = 1, clk_p2 = 10; + } + } else if ((ksel == KSEL_BYPASS_83_100) && (dev_priv->core_freq == 166)) { + refclk = 83000; + + if (is_mipi || is_mipi2) + { + clk_n = 4, clk_p2 = 8; + } else if (is_hdmi) { + clk_n = 4, clk_p2 = 10; + } + } else if ((ksel == KSEL_BYPASS_83_100) && + (dev_priv->core_freq == 100 || dev_priv->core_freq == 200)) { + refclk = 100000; + if (is_mipi || is_mipi2) + { + clk_n = 4, clk_p2 = 8; + } else if (is_hdmi) { + clk_n = 4, clk_p2 = 10; + } + } + + if (is_mipi) + clk_byte = dev_priv->bpp / 8; + else if (is_mipi2) + clk_byte = dev_priv->bpp2 / 8; + + clk_tmp = clk * clk_n * clk_p2 * clk_byte; + + dev_dbg(dev->dev, "clk = %d, clk_n = %d, clk_p2 = %d. \n", clk, clk_n, clk_p2); + dev_dbg(dev->dev, "adjusted_mode->clock = %d, clk_tmp = %d. \n", adjusted_mode->clock, clk_tmp); + + ok = mdfldFindBestPLL(crtc, clk_tmp, refclk, &clock); + + if (!ok) { + dev_err(dev->dev, + "mdfldFindBestPLL fail in mdfld_crtc_mode_set. \n"); + } else { + m_conv = mdfld_m_converts[(clock.m - MDFLD_M_MIN)]; + + dev_dbg(dev->dev, "dot clock = %d," + "m = %d, p1 = %d, m_conv = %d. \n", clock.dot, clock.m, + clock.p1, m_conv); + } + + dpll = REG_READ(dpll_reg); + + if (dpll & DPLL_VCO_ENABLE) { + dpll &= ~DPLL_VCO_ENABLE; + REG_WRITE(dpll_reg, dpll); + REG_READ(dpll_reg); + + /* FIXME jliu7 check the DPLL lock bit PIPEACONF[29] */ + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(500); + + /* reset M1, N1 & P1 */ + REG_WRITE(fp_reg, 0); + dpll &= ~MDFLD_P1_MASK; + REG_WRITE(dpll_reg, dpll); + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(500); + } + + /* When ungating power of DPLL, needs to wait 0.5us before enable the VCO */ + if (dpll & MDFLD_PWR_GATE_EN) { + dpll &= ~MDFLD_PWR_GATE_EN; + REG_WRITE(dpll_reg, dpll); + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(500); + } + + dpll = 0; + +#if 0 /* FIXME revisit later */ + if ((ksel == KSEL_CRYSTAL_19) || (ksel == KSEL_BYPASS_19) || (ksel == KSEL_BYPASS_25)) { + dpll &= ~MDFLD_INPUT_REF_SEL; + } else if (ksel == KSEL_BYPASS_83_100) { + dpll |= MDFLD_INPUT_REF_SEL; + } +#endif /* FIXME revisit later */ + + if (is_hdmi) + dpll |= MDFLD_VCO_SEL; + + fp = (clk_n / 2) << 16; + fp |= m_conv; + + /* compute bitmask from p1 value */ + dpll |= (1 << (clock.p1 - 2)) << 17; + +#if 0 /* 1080p30 & 720p */ + dpll = 0x00050000; + fp = 0x000001be; +#endif +#if 0 /* 480p */ + dpll = 0x02010000; + fp = 0x000000d2; +#endif + } else { +#if 0 /*DBI_TPO_480x864*/ + dpll = 0x00020000; + fp = 0x00000156; +#endif /* DBI_TPO_480x864 */ /* get from spec. */ + + dpll = 0x00800000; + fp = 0x000000c1; +} + + REG_WRITE(fp_reg, fp); + REG_WRITE(dpll_reg, dpll); + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(500); + + dpll |= DPLL_VCO_ENABLE; + REG_WRITE(dpll_reg, dpll); + REG_READ(dpll_reg); + + /* wait for DSI PLL to lock */ + while ((timeout < 20000) && !(REG_READ(pipeconf_reg) & PIPECONF_DSIPLL_LOCK)) { + udelay(150); + timeout ++; + } + + if (is_mipi) + goto mrst_crtc_mode_set_exit; + + dev_dbg(dev->dev, "is_mipi = 0x%x \n", is_mipi); + + REG_WRITE(pipeconf_reg, *pipeconf); + REG_READ(pipeconf_reg); + + /* Wait for for the pipe enable to take effect. */ +//FIXME_JLIU7 HDMI mrstWaitForPipeEnable(dev); + + REG_WRITE(dspcntr_reg, *dspcntr); + psb_intel_wait_for_vblank(dev); + +mrst_crtc_mode_set_exit: + + gma_power_end(dev); + + return 0; +} + +static void mdfld_crtc_prepare(struct drm_crtc *crtc) +{ + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); +} + +static void mdfld_crtc_commit(struct drm_crtc *crtc) +{ + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); +} + +static bool mdfld_crtc_mode_fixup(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +const struct drm_crtc_helper_funcs mdfld_helper_funcs = { + .dpms = mdfld_crtc_dpms, + .mode_fixup = mdfld_crtc_mode_fixup, + .mode_set = mdfld_crtc_mode_set, + .mode_set_base = mdfld__intel_pipe_set_base, + .prepare = mdfld_crtc_prepare, + .commit = mdfld_crtc_commit, +}; diff --git a/trunk/drivers/staging/gma500/mdfld_msic.h b/trunk/drivers/staging/gma500/mdfld_msic.h new file mode 100644 index 000000000000..a7ad65472491 --- /dev/null +++ b/trunk/drivers/staging/gma500/mdfld_msic.h @@ -0,0 +1,31 @@ +/* + * Copyright © 2010 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: + * Jim Liu + */ + +#define MSIC_PCI_DEVICE_ID 0x831 + +int msic_regsiter_driver(void); +int msic_unregister_driver(void); +extern void hpd_notify_um(void); diff --git a/trunk/drivers/staging/gma500/mdfld_output.c b/trunk/drivers/staging/gma500/mdfld_output.c new file mode 100644 index 000000000000..eabf53d58f92 --- /dev/null +++ b/trunk/drivers/staging/gma500/mdfld_output.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2010 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, sublicensen + * 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: + * Thomas Eaton + * Scott Rowe +*/ + +#include +#include +#include "mdfld_dsi_dbi.h" +#include "mdfld_dsi_dpi.h" +#include "mdfld_dsi_output.h" +#include "mdfld_output.h" +#include "mdfld_dsi_dbi_dpu.h" + +#include "displays/tpo_cmd.h" +#include "displays/tpo_vid.h" +#include "displays/tmd_cmd.h" +#include "displays/tmd_vid.h" +#include "displays/pyr_cmd.h" +#include "displays/pyr_vid.h" +/* #include "displays/hdmi.h" */ + +static int mdfld_dual_mipi; +static int mdfld_hdmi; +static int mdfld_dpu; + +module_param(mdfld_dual_mipi, int, 0600); +MODULE_PARM_DESC(mdfld_dual_mipi, "Enable dual MIPI configuration"); +module_param(mdfld_hdmi, int, 0600); +MODULE_PARM_DESC(mdfld_hdmi, "Enable Medfield HDMI"); +module_param(mdfld_dpu, int, 0600); +MODULE_PARM_DESC(mdfld_dpu, "Enable Medfield DPU"); + +/* For now a single type per device is all we cope with */ +int mdfld_get_panel_type(struct drm_device *dev, int pipe) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + return dev_priv->panel_id; +} + +int mdfld_panel_dpi(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + + switch (dev_priv->panel_id) { + case TMD_VID: + case TPO_VID: + case PYR_VID: + return true; + case TMD_CMD: + case TPO_CMD: + case PYR_CMD: + default: + return false; + } +} + +static int init_panel(struct drm_device *dev, int mipi_pipe, int p_type) +{ + struct panel_funcs *p_cmd_funcs; + struct panel_funcs *p_vid_funcs; + + /* Oh boy ... FIXME */ + p_cmd_funcs = kzalloc(sizeof(struct panel_funcs), GFP_KERNEL); + if (p_cmd_funcs == NULL) + return -ENODEV; + p_vid_funcs = kzalloc(sizeof(struct panel_funcs), GFP_KERNEL); + if (p_vid_funcs == NULL) { + kfree(p_cmd_funcs); + return -ENODEV; + } + + switch (p_type) { + case TPO_CMD: + tpo_cmd_init(dev, p_cmd_funcs); + mdfld_dsi_output_init(dev, mipi_pipe, NULL, p_cmd_funcs, NULL); + break; + case TPO_VID: + tpo_vid_init(dev, p_vid_funcs); + mdfld_dsi_output_init(dev, mipi_pipe, NULL, NULL, p_vid_funcs); + break; + case TMD_CMD: + /*tmd_cmd_init(dev, p_cmd_funcs); */ + mdfld_dsi_output_init(dev, mipi_pipe, NULL, p_cmd_funcs, NULL); + break; + case TMD_VID: + tmd_vid_init(dev, p_vid_funcs); + mdfld_dsi_output_init(dev, mipi_pipe, NULL, NULL, p_vid_funcs); + break; + case PYR_CMD: + pyr_cmd_init(dev, p_cmd_funcs); + mdfld_dsi_output_init(dev, mipi_pipe, NULL, p_cmd_funcs, NULL); + break; + case PYR_VID: + mdfld_dsi_output_init(dev, mipi_pipe, NULL, NULL, p_vid_funcs); + break; + case TPO: /* TPO panel supports both cmd & vid interfaces */ + tpo_cmd_init(dev, p_cmd_funcs); + tpo_vid_init(dev, p_vid_funcs); + mdfld_dsi_output_init(dev, mipi_pipe, NULL, p_cmd_funcs, + p_vid_funcs); + break; + case TMD: + break; + case PYR: + break; +#if 0 + case HDMI: + dev_dbg(dev->dev, "Initializing HDMI"); + mdfld_hdmi_init(dev, &dev_priv->mode_dev); + break; +#endif + default: + dev_err(dev->dev, "Unsupported interface %d", p_type); + return -ENODEV; + } + return 0; +} + +int mdfld_output_init(struct drm_device *dev) +{ + int type; + + /* MIPI panel 1 */ + type = mdfld_get_panel_type(dev, 0); + dev_info(dev->dev, "panel 1: type is %d\n", type); + init_panel(dev, 0, type); + + if (mdfld_dual_mipi) { + /* MIPI panel 2 */ + type = mdfld_get_panel_type(dev, 2); + dev_info(dev->dev, "panel 2: type is %d\n", type); + init_panel(dev, 2, type); + } + if (mdfld_hdmi) + /* HDMI panel */ + init_panel(dev, 0, HDMI); + return 0; +} + +void mdfld_output_setup(struct drm_device *dev) +{ + /* FIXME: this is not the right place for this stuff ! */ + if (IS_MFLD(dev)) { + if (mdfld_dpu) + mdfld_dbi_dpu_init(dev); + else + mdfld_dbi_dsr_init(dev); + } +} diff --git a/trunk/drivers/staging/gma500/mdfld_output.h b/trunk/drivers/staging/gma500/mdfld_output.h new file mode 100644 index 000000000000..daf33e7df9d5 --- /dev/null +++ b/trunk/drivers/staging/gma500/mdfld_output.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2010 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, sublicensen + * 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: + * Thomas Eaton + * Scott Rowe +*/ + +#ifndef MDFLD_OUTPUT_H +#define MDFLD_OUTPUT_H + +int mdfld_output_init(struct drm_device *dev); +int mdfld_panel_dpi(struct drm_device *dev); +int mdfld_get_panel_type(struct drm_device *dev, int pipe); +void mdfld_disable_crtc (struct drm_device *dev, int pipe); + +extern const struct drm_crtc_helper_funcs mdfld_helper_funcs; +extern const struct drm_crtc_funcs mdfld_intel_crtc_funcs; + +extern void mdfld_output_setup(struct drm_device *dev); + +#endif diff --git a/trunk/drivers/staging/gma500/mdfld_pyr_cmd.c b/trunk/drivers/staging/gma500/mdfld_pyr_cmd.c new file mode 100644 index 000000000000..523f2d8fe4f1 --- /dev/null +++ b/trunk/drivers/staging/gma500/mdfld_pyr_cmd.c @@ -0,0 +1,558 @@ +/* + * Copyright (c) 2010 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, sublicensen + * 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: + * Thomas Eaton + * Scott Rowe +*/ + +#include "mdfld_dsi_dbi.h" +#include "mdfld_dsi_dpi.h" +#include "mdfld_dsi_output.h" +#include "mdfld_output.h" +#include "mdfld_dsi_dbi_dpu.h" +#include "mdfld_dsi_pkg_sender.h" + +#include "displays/pyr_cmd.h" + +static struct drm_display_mode *pyr_cmd_get_config_mode(struct drm_device *dev) +{ + struct drm_display_mode *mode; + + mode = kzalloc(sizeof(*mode), GFP_KERNEL); + if (!mode) { + dev_err(dev->dev, "Out of memory\n"); + return NULL; + } + + dev_dbg(dev->dev, "hdisplay is %d\n", mode->hdisplay); + dev_dbg(dev->dev, "vdisplay is %d\n", mode->vdisplay); + dev_dbg(dev->dev, "HSS is %d\n", mode->hsync_start); + dev_dbg(dev->dev, "HSE is %d\n", mode->hsync_end); + dev_dbg(dev->dev, "htotal is %d\n", mode->htotal); + dev_dbg(dev->dev, "VSS is %d\n", mode->vsync_start); + dev_dbg(dev->dev, "VSE is %d\n", mode->vsync_end); + dev_dbg(dev->dev, "vtotal is %d\n", mode->vtotal); + dev_dbg(dev->dev, "clock is %d\n", mode->clock); + + mode->hdisplay = 480; + mode->vdisplay = 864; + mode->hsync_start = 487; + mode->hsync_end = 490; + mode->htotal = 499; + mode->vsync_start = 874; + mode->vsync_end = 878; + mode->vtotal = 886; + mode->clock = 25777; + + drm_mode_set_name(mode); + drm_mode_set_crtcinfo(mode, 0); + + mode->type |= DRM_MODE_TYPE_PREFERRED; + + return mode; +} + +static bool pyr_dsi_dbi_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_display_mode *fixed_mode = pyr_cmd_get_config_mode(dev); + + if (fixed_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); + kfree(fixed_mode); + } + return true; +} + +static void pyr_dsi_dbi_set_power(struct drm_encoder *encoder, bool on) +{ + int ret = 0; + struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); + struct mdfld_dsi_dbi_output *dbi_output = + MDFLD_DSI_DBI_OUTPUT(dsi_encoder); + struct drm_device *dev = encoder->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + u32 reg_offset = 0; + int pipe = (dbi_output->channel_num == 0) ? 0 : 2; + + dev_dbg(dev->dev, "pipe %d : %s, panel on: %s\n", pipe, + on ? "On" : "Off", + dbi_output->dbi_panel_on ? "True" : "False"); + + if (pipe == 2) { + if (on) + dev_priv->dual_mipi = true; + else + dev_priv->dual_mipi = false; + + reg_offset = MIPIC_REG_OFFSET; + } else { + if (!on) + dev_priv->dual_mipi = false; + } + + if (!gma_power_begin(dev, true)) { + dev_err(dev->dev, "hw begin failed\n"); + return; + } + + + if (on) { + if (dbi_output->dbi_panel_on) + goto out_err; + + ret = mdfld_dsi_dbi_update_power(dbi_output, DRM_MODE_DPMS_ON); + if (ret) { + dev_err(dev->dev, "power on error\n"); + goto out_err; + } + + dbi_output->dbi_panel_on = true; + + if (pipe == 2) { + dev_priv->dbi_panel_on2 = true; + } else { + dev_priv->dbi_panel_on = true; + mdfld_enable_te(dev, 0); + } + } else { + if (!dbi_output->dbi_panel_on && !dbi_output->first_boot) + goto out_err; + + dbi_output->dbi_panel_on = false; + dbi_output->first_boot = false; + + if (pipe == 2) { + dev_priv->dbi_panel_on2 = false; + mdfld_disable_te(dev, 2); + } else { + dev_priv->dbi_panel_on = false; + mdfld_disable_te(dev, 0); + + if (dev_priv->dbi_panel_on2) + mdfld_enable_te(dev, 2); + } + + ret = mdfld_dsi_dbi_update_power(dbi_output, DRM_MODE_DPMS_OFF); + if (ret) { + dev_err(dev->dev, "power on error\n"); + goto out_err; + } + } + +out_err: + gma_power_end(dev); + + if (ret) + dev_err(dev->dev, "failed\n"); +} + +static void pyr_dsi_controller_dbi_init(struct mdfld_dsi_config *dsi_config, + int pipe) +{ + struct drm_device *dev = dsi_config->dev; + u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0; + int lane_count = dsi_config->lane_count; + u32 val = 0; + + dev_dbg(dev->dev, "Init DBI interface on pipe %d...\n", pipe); + + /* Un-ready device */ + REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000000); + + /* Init dsi adapter before kicking off */ + REG_WRITE((MIPIA_CONTROL_REG + reg_offset), 0x00000018); + + /* TODO: figure out how to setup these registers */ + REG_WRITE((MIPIA_DPHY_PARAM_REG + reg_offset), 0x150c600F); + REG_WRITE((MIPIA_CLK_LANE_SWITCH_TIME_CNT_REG + reg_offset), + 0x000a0014); + REG_WRITE((MIPIA_DBI_BW_CTRL_REG + reg_offset), 0x00000400); + REG_WRITE((MIPIA_HS_LS_DBI_ENABLE_REG + reg_offset), 0x00000000); + + /* Enable all interrupts */ + REG_WRITE((MIPIA_INTR_EN_REG + reg_offset), 0xffffffff); + /* Max value: 20 clock cycles of txclkesc */ + REG_WRITE((MIPIA_TURN_AROUND_TIMEOUT_REG + reg_offset), 0x0000001f); + /* Min 21 txclkesc, max: ffffh */ + REG_WRITE((MIPIA_DEVICE_RESET_TIMER_REG + reg_offset), 0x0000ffff); + /* Min: 7d0 max: 4e20 */ + REG_WRITE((MIPIA_INIT_COUNT_REG + reg_offset), 0x00000fa0); + + /* Set up func_prg */ + val |= lane_count; + val |= (dsi_config->channel_num << DSI_DBI_VIRT_CHANNEL_OFFSET); + val |= DSI_DBI_COLOR_FORMAT_OPTION2; + REG_WRITE((MIPIA_DSI_FUNC_PRG_REG + reg_offset), val); + + REG_WRITE((MIPIA_HS_TX_TIMEOUT_REG + reg_offset), 0x3fffff); + REG_WRITE((MIPIA_LP_RX_TIMEOUT_REG + reg_offset), 0xffff); + + /* De-assert dbi_stall when half of DBI FIFO is empty */ + /* REG_WRITE((MIPIA_DBI_FIFO_THROTTLE_REG + reg_offset), 0x00000000); */ + + REG_WRITE((MIPIA_HIGH_LOW_SWITCH_COUNT_REG + reg_offset), 0x46); + REG_WRITE((MIPIA_EOT_DISABLE_REG + reg_offset), 0x00000002); + REG_WRITE((MIPIA_LP_BYTECLK_REG + reg_offset), 0x00000004); + REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000001); +} + +static void pyr_dsi_dbi_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + int ret = 0; + struct drm_device *dev = encoder->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); + struct mdfld_dsi_dbi_output *dsi_output = + MDFLD_DSI_DBI_OUTPUT(dsi_encoder); + struct mdfld_dsi_config *dsi_config = + mdfld_dsi_encoder_get_config(dsi_encoder); + struct mdfld_dsi_connector *dsi_connector = dsi_config->connector; + int pipe = dsi_connector->pipe; + u8 param = 0; + + /* Regs */ + u32 mipi_reg = MIPI; + u32 dspcntr_reg = DSPACNTR; + u32 pipeconf_reg = PIPEACONF; + u32 reg_offset = 0; + + /* Values */ + u32 dspcntr_val = dev_priv->dspcntr; + u32 pipeconf_val = dev_priv->pipeconf; + u32 h_active_area = mode->hdisplay; + u32 v_active_area = mode->vdisplay; + u32 mipi_val = (PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX | + TE_TRIGGER_GPIO_PIN); + + dev_dbg(dev->dev, "mipi_val =0x%x\n", mipi_val); + + dev_dbg(dev->dev, "type %s\n", (pipe == 2) ? "MIPI2" : "MIPI"); + dev_dbg(dev->dev, "h %d v %d\n", mode->hdisplay, mode->vdisplay); + + if (pipe == 2) { + mipi_reg = MIPI_C; + dspcntr_reg = DSPCCNTR; + pipeconf_reg = PIPECCONF; + + reg_offset = MIPIC_REG_OFFSET; + + dspcntr_val = dev_priv->dspcntr2; + pipeconf_val = dev_priv->pipeconf2; + } else { + mipi_val |= 0x2; /* Two lanes for port A and C respectively */ + } + + if (!gma_power_begin(dev, true)) { + dev_err(dev->dev, "hw begin failed\n"); + return; + } + + /* Set up pipe related registers */ + REG_WRITE(mipi_reg, mipi_val); + REG_READ(mipi_reg); + + pyr_dsi_controller_dbi_init(dsi_config, pipe); + + msleep(20); + + REG_WRITE(dspcntr_reg, dspcntr_val); + REG_READ(dspcntr_reg); + + /* 20ms delay before sending exit_sleep_mode */ + msleep(20); + + /* Send exit_sleep_mode DCS */ + ret = mdfld_dsi_dbi_send_dcs(dsi_output, exit_sleep_mode, NULL, + 0, CMD_DATA_SRC_SYSTEM_MEM); + if (ret) { + dev_err(dev->dev, "sent exit_sleep_mode faild\n"); + goto out_err; + } + + /*send set_tear_on DCS*/ + ret = mdfld_dsi_dbi_send_dcs(dsi_output, set_tear_on, + ¶m, 1, CMD_DATA_SRC_SYSTEM_MEM); + if (ret) { + dev_err(dev->dev, "%s - sent set_tear_on faild\n", __func__); + goto out_err; + } + + /* Do some init stuff */ + mdfld_dsi_brightness_init(dsi_config, pipe); + mdfld_dsi_gen_fifo_ready(dev, (MIPIA_GEN_FIFO_STAT_REG + reg_offset), + HS_CTRL_FIFO_EMPTY | HS_DATA_FIFO_EMPTY); + + REG_WRITE(pipeconf_reg, pipeconf_val | PIPEACONF_DSR); + REG_READ(pipeconf_reg); + + /* TODO: this looks ugly, try to move it to CRTC mode setting */ + if (pipe == 2) + dev_priv->pipeconf2 |= PIPEACONF_DSR; + else + dev_priv->pipeconf |= PIPEACONF_DSR; + + dev_dbg(dev->dev, "pipeconf %x\n", REG_READ(pipeconf_reg)); + + ret = mdfld_dsi_dbi_update_area(dsi_output, 0, 0, + h_active_area - 1, v_active_area - 1); + if (ret) { + dev_err(dev->dev, "update area failed\n"); + goto out_err; + } + +out_err: + gma_power_end(dev); + + if (ret) + dev_err(dev->dev, "mode set failed\n"); + else + dev_dbg(dev->dev, "mode set done successfully\n"); +} + +static void pyr_dsi_dbi_prepare(struct drm_encoder *encoder) +{ + struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); + struct mdfld_dsi_dbi_output *dbi_output = + MDFLD_DSI_DBI_OUTPUT(dsi_encoder); + + dbi_output->mode_flags |= MODE_SETTING_IN_ENCODER; + dbi_output->mode_flags &= ~MODE_SETTING_ENCODER_DONE; + + pyr_dsi_dbi_set_power(encoder, false); +} + +static void pyr_dsi_dbi_commit(struct drm_encoder *encoder) +{ + struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); + struct mdfld_dsi_dbi_output *dbi_output = + MDFLD_DSI_DBI_OUTPUT(dsi_encoder); + struct drm_device *dev = dbi_output->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_drm_dpu_rect rect; + + pyr_dsi_dbi_set_power(encoder, true); + + dbi_output->mode_flags &= ~MODE_SETTING_IN_ENCODER; + + rect.x = rect.y = 0; + rect.width = 864; + rect.height = 480; + + if (dbi_output->channel_num == 1) { + dev_priv->dsr_fb_update |= MDFLD_DSR_2D_3D_2; + /* If DPU enabled report a fullscreen damage */ + mdfld_dbi_dpu_report_damage(dev, MDFLD_PLANEC, &rect); + } else { + dev_priv->dsr_fb_update |= MDFLD_DSR_2D_3D_0; + mdfld_dbi_dpu_report_damage(dev, MDFLD_PLANEA, &rect); + } + dbi_output->mode_flags |= MODE_SETTING_ENCODER_DONE; +} + +static void pyr_dsi_dbi_dpms(struct drm_encoder *encoder, int mode) +{ + struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); + struct mdfld_dsi_dbi_output *dbi_output = + MDFLD_DSI_DBI_OUTPUT(dsi_encoder); + struct drm_device *dev = dbi_output->dev; + + dev_dbg(dev->dev, "%s\n", (mode == DRM_MODE_DPMS_ON ? "on" : "off")); + + if (mode == DRM_MODE_DPMS_ON) + pyr_dsi_dbi_set_power(encoder, true); + else + pyr_dsi_dbi_set_power(encoder, false); +} + +/* + * Update the DBI MIPI Panel Frame Buffer. + */ +static void pyr_dsi_dbi_update_fb(struct mdfld_dsi_dbi_output *dbi_output, + int pipe) +{ + struct mdfld_dsi_pkg_sender *sender = + mdfld_dsi_encoder_get_pkg_sender(&dbi_output->base); + struct drm_device *dev = dbi_output->dev; + struct drm_crtc *crtc = dbi_output->base.base.crtc; + struct psb_intel_crtc *psb_crtc = (crtc) ? + to_psb_intel_crtc(crtc) : NULL; + + u32 dpll_reg = MRST_DPLL_A; + u32 dspcntr_reg = DSPACNTR; + u32 pipeconf_reg = PIPEACONF; + u32 dsplinoff_reg = DSPALINOFF; + u32 dspsurf_reg = DSPASURF; + u32 hs_gen_ctrl_reg = HS_GEN_CTRL_REG; + u32 gen_fifo_stat_reg = GEN_FIFO_STAT_REG; + u32 reg_offset = 0; + + u32 intr_status; + u32 fifo_stat_reg_val; + u32 dpll_reg_val; + u32 dspcntr_reg_val; + u32 pipeconf_reg_val; + + /* If mode setting on-going, back off */ + if ((dbi_output->mode_flags & MODE_SETTING_ON_GOING) || + (psb_crtc && psb_crtc->mode_flags & MODE_SETTING_ON_GOING) || + !(dbi_output->mode_flags & MODE_SETTING_ENCODER_DONE)) + return; + + /* + * Look for errors here. In particular we're checking for whatever + * error status might have appeared during the last frame transmit + * (memory write). + * + * Normally, the bits we're testing here would be set infrequently, + * if at all. However, one panel (at least) returns at least one + * error bit on most frames. So we've disabled the kernel message + * for now. + * + * Still clear whatever error bits are set, except don't clear the + * ones that would make the Penwell DSI controller reset if we + * cleared them. + */ + intr_status = REG_READ(INTR_STAT_REG); + if ((intr_status & 0x26FFFFFF) != 0) { + /* dev_err(dev->dev, "DSI status: 0x%08X\n", intr_status); */ + intr_status &= 0x26F3FFFF; + REG_WRITE(INTR_STAT_REG, intr_status); + } + + if (pipe == 2) { + dspcntr_reg = DSPCCNTR; + pipeconf_reg = PIPECCONF; + dsplinoff_reg = DSPCLINOFF; + dspsurf_reg = DSPCSURF; + + hs_gen_ctrl_reg = HS_GEN_CTRL_REG + MIPIC_REG_OFFSET; + gen_fifo_stat_reg = GEN_FIFO_STAT_REG + MIPIC_REG_OFFSET, + + reg_offset = MIPIC_REG_OFFSET; + } + + if (!gma_power_begin(dev, true)) { + dev_err(dev->dev, "hw begin failed\n"); + return; + } + + fifo_stat_reg_val = REG_READ(MIPIA_GEN_FIFO_STAT_REG + reg_offset); + dpll_reg_val = REG_READ(dpll_reg); + dspcntr_reg_val = REG_READ(dspcntr_reg); + pipeconf_reg_val = REG_READ(pipeconf_reg); + + if (!(fifo_stat_reg_val & (1 << 27)) || + (dpll_reg_val & DPLL_VCO_ENABLE) || + !(dspcntr_reg_val & DISPLAY_PLANE_ENABLE) || + !(pipeconf_reg_val & DISPLAY_PLANE_ENABLE)) { + goto update_fb_out0; + } + + /* Refresh plane changes */ + REG_WRITE(dsplinoff_reg, REG_READ(dsplinoff_reg)); + REG_WRITE(dspsurf_reg, REG_READ(dspsurf_reg)); + REG_READ(dspsurf_reg); + + mdfld_dsi_send_dcs(sender, + write_mem_start, + NULL, + 0, + CMD_DATA_SRC_PIPE, + MDFLD_DSI_SEND_PACKAGE); + + /* + * The idea here is to transmit a Generic Read command after the + * Write Memory Start/Continue commands finish. This asks for + * the panel to return an "ACK No Errors," or (if it has errors + * to report) an Error Report. This allows us to monitor the + * panel's perception of the health of the DSI. + */ + mdfld_dsi_gen_fifo_ready(dev, gen_fifo_stat_reg, + HS_CTRL_FIFO_EMPTY | HS_DATA_FIFO_EMPTY); + REG_WRITE(hs_gen_ctrl_reg, (1 << WORD_COUNTS_POS) | GEN_READ_0); + + dbi_output->dsr_fb_update_done = true; +update_fb_out0: + gma_power_end(dev); +} + +/* + * TODO: will be removed later, should work out display interfaces for power + */ +void pyr_dsi_adapter_init(struct mdfld_dsi_config *dsi_config, int pipe) +{ + if (!dsi_config || (pipe != 0 && pipe != 2)) { + WARN_ON(1); + return; + } + pyr_dsi_controller_dbi_init(dsi_config, pipe); +} + +static int pyr_cmd_get_panel_info(struct drm_device *dev, int pipe, + struct panel_info *pi) +{ + if (!dev || !pi) + return -EINVAL; + + pi->width_mm = PYR_PANEL_WIDTH; + pi->height_mm = PYR_PANEL_HEIGHT; + + return 0; +} + +/* PYR DBI encoder helper funcs */ +static const struct drm_encoder_helper_funcs pyr_dsi_dbi_helper_funcs = { + .dpms = pyr_dsi_dbi_dpms, + .mode_fixup = pyr_dsi_dbi_mode_fixup, + .prepare = pyr_dsi_dbi_prepare, + .mode_set = pyr_dsi_dbi_mode_set, + .commit = pyr_dsi_dbi_commit, +}; + +/* PYR DBI encoder funcs */ +static const struct drm_encoder_funcs mdfld_dsi_dbi_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +void pyr_cmd_init(struct drm_device *dev, struct panel_funcs *p_funcs) +{ + p_funcs->encoder_funcs = &mdfld_dsi_dbi_encoder_funcs; + p_funcs->encoder_helper_funcs = &pyr_dsi_dbi_helper_funcs; + p_funcs->get_config_mode = &pyr_cmd_get_config_mode; + p_funcs->update_fb = pyr_dsi_dbi_update_fb; + p_funcs->get_panel_info = pyr_cmd_get_panel_info; +} diff --git a/trunk/drivers/staging/gma500/mdfld_tmd_vid.c b/trunk/drivers/staging/gma500/mdfld_tmd_vid.c new file mode 100644 index 000000000000..affdc09c6769 --- /dev/null +++ b/trunk/drivers/staging/gma500/mdfld_tmd_vid.c @@ -0,0 +1,206 @@ +/* + * Copyright © 2010 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: + * Jim Liu + * Jackie Li + * Gideon Eaton + */ + +#include "mdfld_dsi_dbi.h" +#include "mdfld_dsi_dpi.h" +#include "mdfld_dsi_output.h" +#include "mdfld_output.h" + +#include "mdfld_dsi_pkg_sender.h" + +#include "displays/tmd_vid.h" + +/* FIXME: static ? */ +struct drm_display_mode *tmd_vid_get_config_mode(struct drm_device *dev) +{ + struct drm_display_mode *mode; + struct drm_psb_private *dev_priv = dev->dev_private; + struct mrst_timing_info *ti = &dev_priv->gct_data.DTD; + bool use_gct = false; /*Disable GCT for now*/ + + mode = kzalloc(sizeof(*mode), GFP_KERNEL); + if (!mode) { + dev_err(dev->dev, "Out of memory\n"); + return NULL; + } + + if (use_gct) { + dev_dbg(dev->dev, "gct find MIPI panel.\n"); + + mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo; + mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo; + mode->hsync_start = mode->hdisplay + + ((ti->hsync_offset_hi << 8) | + ti->hsync_offset_lo); + mode->hsync_end = mode->hsync_start + + ((ti->hsync_pulse_width_hi << 8) | + ti->hsync_pulse_width_lo); + mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) | + ti->hblank_lo); + mode->vsync_start = \ + mode->vdisplay + ((ti->vsync_offset_hi << 8) | + ti->vsync_offset_lo); + mode->vsync_end = \ + mode->vsync_start + ((ti->vsync_pulse_width_hi << 8) | \ + ti->vsync_pulse_width_lo); + mode->vtotal = mode->vdisplay + + ((ti->vblank_hi << 8) | ti->vblank_lo); + mode->clock = ti->pixel_clock * 10; + + dev_dbg(dev->dev, "hdisplay is %d\n", mode->hdisplay); + dev_dbg(dev->dev, "vdisplay is %d\n", mode->vdisplay); + dev_dbg(dev->dev, "HSS is %d\n", mode->hsync_start); + dev_dbg(dev->dev, "HSE is %d\n", mode->hsync_end); + dev_dbg(dev->dev, "htotal is %d\n", mode->htotal); + dev_dbg(dev->dev, "VSS is %d\n", mode->vsync_start); + dev_dbg(dev->dev, "VSE is %d\n", mode->vsync_end); + dev_dbg(dev->dev, "vtotal is %d\n", mode->vtotal); + dev_dbg(dev->dev, "clock is %d\n", mode->clock); + } else { + mode->hdisplay = 480; + mode->vdisplay = 854; + mode->hsync_start = 487; + mode->hsync_end = 490; + mode->htotal = 499; + mode->vsync_start = 861; + mode->vsync_end = 865; + mode->vtotal = 873; + mode->clock = 33264; + } + drm_mode_set_name(mode); + drm_mode_set_crtcinfo(mode, 0); + + mode->type |= DRM_MODE_TYPE_PREFERRED; + + return mode; +} + +static int tmd_vid_get_panel_info(struct drm_device *dev, + int pipe, + struct panel_info *pi) +{ + if (!dev || !pi) + return -EINVAL; + + pi->width_mm = TMD_PANEL_WIDTH; + pi->height_mm = TMD_PANEL_HEIGHT; + + return 0; +} + +/* + * mdfld_init_TMD_MIPI - initialise a TMD interface + * @dsi_config: configuration + * @pipe: pipe to configure + * + * This function is called only by mrst_dsi_mode_set and + * restore_display_registers. since this function does not + * acquire the mutex, it is important that the calling function + * does! + */ + + +static void mdfld_dsi_tmd_drv_ic_init(struct mdfld_dsi_config *dsi_config, + int pipe) +{ + static u32 tmd_cmd_mcap_off[] = {0x000000b2}; + static u32 tmd_cmd_enable_lane_switch[] = {0x000101ef}; + static u32 tmd_cmd_set_lane_num[] = {0x006360ef}; + static u32 tmd_cmd_pushing_clock0[] = {0x00cc2fef}; + static u32 tmd_cmd_pushing_clock1[] = {0x00dd6eef}; + static u32 tmd_cmd_set_mode[] = {0x000000b3}; + static u32 tmd_cmd_set_sync_pulse_mode[] = {0x000961ef}; + static u32 tmd_cmd_set_column[] = {0x0100002a, 0x000000df}; + static u32 tmd_cmd_set_page[] = {0x0300002b, 0x00000055}; + static u32 tmd_cmd_set_video_mode[] = {0x00000153}; + /*no auto_bl,need add in furture*/ + static u32 tmd_cmd_enable_backlight[] = {0x00005ab4}; + static u32 tmd_cmd_set_backlight_dimming[] = {0x00000ebd}; + + struct mdfld_dsi_pkg_sender *sender + = mdfld_dsi_get_pkg_sender(dsi_config); + + DRM_INFO("Enter mdfld init TMD MIPI display.\n"); + + if (!sender) { + DRM_ERROR("Cannot get sender\n"); + return; + } + + if (dsi_config->dvr_ic_inited) + return; + + msleep(3); + + mdfld_dsi_send_gen_long_lp(sender, tmd_cmd_mcap_off, 1, 0); + mdfld_dsi_send_gen_long_lp(sender, tmd_cmd_enable_lane_switch, 1, 0); + mdfld_dsi_send_gen_long_lp(sender, tmd_cmd_set_lane_num, 1, 0); + mdfld_dsi_send_gen_long_lp(sender, tmd_cmd_pushing_clock0, 1, 0); + mdfld_dsi_send_gen_long_lp(sender, tmd_cmd_pushing_clock1, 1, 0); + mdfld_dsi_send_gen_long_lp(sender, tmd_cmd_set_mode, 1, 0); + mdfld_dsi_send_gen_long_lp(sender, tmd_cmd_set_sync_pulse_mode, 1, 0); + mdfld_dsi_send_mcs_long_lp(sender, tmd_cmd_set_column, 2, 0); + mdfld_dsi_send_mcs_long_lp(sender, tmd_cmd_set_page, 2, 0); + mdfld_dsi_send_gen_long_lp(sender, tmd_cmd_set_video_mode, 1, 0); + mdfld_dsi_send_gen_long_lp(sender, tmd_cmd_enable_backlight, 1, 0); + mdfld_dsi_send_gen_long_lp(sender, tmd_cmd_set_backlight_dimming, 1, 0); + + dsi_config->dvr_ic_inited = 1; +} + +/* TMD DPI encoder helper funcs */ +static const struct drm_encoder_helper_funcs + mdfld_tpo_dpi_encoder_helper_funcs = { + .dpms = mdfld_dsi_dpi_dpms, + .mode_fixup = mdfld_dsi_dpi_mode_fixup, + .prepare = mdfld_dsi_dpi_prepare, + .mode_set = mdfld_dsi_dpi_mode_set, + .commit = mdfld_dsi_dpi_commit, +}; + +/* TMD DPI encoder funcs */ +static const struct drm_encoder_funcs mdfld_tpo_dpi_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +void tmd_vid_init(struct drm_device *dev, struct panel_funcs *p_funcs) +{ + if (!dev || !p_funcs) { + dev_err(dev->dev, "Invalid parameters\n"); + return; + } + + p_funcs->encoder_funcs = &mdfld_tpo_dpi_encoder_funcs; + p_funcs->encoder_helper_funcs = &mdfld_tpo_dpi_encoder_helper_funcs; + p_funcs->get_config_mode = &tmd_vid_get_config_mode; + p_funcs->update_fb = NULL; + p_funcs->get_panel_info = tmd_vid_get_panel_info; + p_funcs->reset = mdfld_dsi_panel_reset; + p_funcs->drv_ic_init = mdfld_dsi_tmd_drv_ic_init; +} diff --git a/trunk/drivers/staging/gma500/mdfld_tpo_cmd.c b/trunk/drivers/staging/gma500/mdfld_tpo_cmd.c new file mode 100644 index 000000000000..c7f7c9c19bc1 --- /dev/null +++ b/trunk/drivers/staging/gma500/mdfld_tpo_cmd.c @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2010 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, sublicensen + * 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: + * Thomas Eaton + * Scott Rowe + */ + +#include "mdfld_dsi_dbi.h" +#include "mdfld_dsi_dpi.h" +#include "mdfld_dsi_output.h" +#include "mdfld_output.h" +#include "mdfld_dsi_dbi_dpu.h" +#include "mdfld_dsi_pkg_sender.h" + +#include "displays/tpo_cmd.h" + +static struct drm_display_mode *tpo_cmd_get_config_mode(struct drm_device *dev) +{ + struct drm_display_mode *mode; + struct drm_psb_private *dev_priv = dev->dev_private; + struct mrst_timing_info *ti = &dev_priv->gct_data.DTD; + bool use_gct = false; + + mode = kzalloc(sizeof(*mode), GFP_KERNEL); + if (!mode) + return NULL; + + if (use_gct) { + dev_dbg(dev->dev, "gct find MIPI panel.\n"); + + mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo; + mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo; + mode->hsync_start = mode->hdisplay + \ + ((ti->hsync_offset_hi << 8) | \ + ti->hsync_offset_lo); + mode->hsync_end = mode->hsync_start + \ + ((ti->hsync_pulse_width_hi << 8) | \ + ti->hsync_pulse_width_lo); + mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) | \ + ti->hblank_lo); + mode->vsync_start = \ + mode->vdisplay + ((ti->vsync_offset_hi << 8) | \ + ti->vsync_offset_lo); + mode->vsync_end = \ + mode->vsync_start + ((ti->vsync_pulse_width_hi << 8) | \ + ti->vsync_pulse_width_lo); + mode->vtotal = mode->vdisplay + \ + ((ti->vblank_hi << 8) | ti->vblank_lo); + mode->clock = ti->pixel_clock * 10; + + dev_dbg(dev->dev, "hdisplay is %d\n", mode->hdisplay); + dev_dbg(dev->dev, "vdisplay is %d\n", mode->vdisplay); + dev_dbg(dev->dev, "HSS is %d\n", mode->hsync_start); + dev_dbg(dev->dev, "HSE is %d\n", mode->hsync_end); + dev_dbg(dev->dev, "htotal is %d\n", mode->htotal); + dev_dbg(dev->dev, "VSS is %d\n", mode->vsync_start); + dev_dbg(dev->dev, "VSE is %d\n", mode->vsync_end); + dev_dbg(dev->dev, "vtotal is %d\n", mode->vtotal); + dev_dbg(dev->dev, "clock is %d\n", mode->clock); + } else { + mode->hdisplay = 864; + mode->vdisplay = 480; + mode->hsync_start = 872; + mode->hsync_end = 876; + mode->htotal = 884; + mode->vsync_start = 482; + mode->vsync_end = 494; + mode->vtotal = 486; + mode->clock = 25777; + } + + drm_mode_set_name(mode); + drm_mode_set_crtcinfo(mode, 0); + + mode->type |= DRM_MODE_TYPE_PREFERRED; + + return mode; +} + +static bool mdfld_dsi_dbi_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_display_mode *fixed_mode = tpo_cmd_get_config_mode(dev); + + if (fixed_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); + kfree(fixed_mode); + } + return true; +} + +static void mdfld_dsi_dbi_set_power(struct drm_encoder *encoder, bool on) +{ + int ret = 0; + struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); + struct mdfld_dsi_dbi_output *dbi_output = + MDFLD_DSI_DBI_OUTPUT(dsi_encoder); + struct mdfld_dsi_config *dsi_config = + mdfld_dsi_encoder_get_config(dsi_encoder); + struct mdfld_dsi_pkg_sender *sender = + mdfld_dsi_encoder_get_pkg_sender(dsi_encoder); + struct drm_device *dev = encoder->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + u32 reg_offset = 0; + int pipe = (dbi_output->channel_num == 0) ? 0 : 2; + u32 data = 0; + + dev_dbg(dev->dev, "pipe %d : %s, panel on: %s\n", + pipe, on ? "On" : "Off", + dbi_output->dbi_panel_on ? "True" : "False"); + + if (pipe == 2) { + if (on) + dev_priv->dual_mipi = true; + else + dev_priv->dual_mipi = false; + reg_offset = MIPIC_REG_OFFSET; + } else { + if (!on) + dev_priv->dual_mipi = false; + } + + if (!gma_power_begin(dev, true)) { + dev_err(dev->dev, "hw begin failed\n"); + return; + } + + if (on) { + if (dbi_output->dbi_panel_on) + goto out_err; + + ret = mdfld_dsi_dbi_update_power(dbi_output, DRM_MODE_DPMS_ON); + if (ret) { + dev_err(dev->dev, "power on error\n"); + goto out_err; + } + + dbi_output->dbi_panel_on = true; + + if (pipe == 2) + dev_priv->dbi_panel_on2 = true; + else + dev_priv->dbi_panel_on = true; + mdfld_enable_te(dev, pipe); + } else { + if (!dbi_output->dbi_panel_on && !dbi_output->first_boot) + goto out_err; + + dbi_output->dbi_panel_on = false; + dbi_output->first_boot = false; + + if (pipe == 2) + dev_priv->dbi_panel_on2 = false; + else + dev_priv->dbi_panel_on = false; + + mdfld_disable_te(dev, pipe); + + ret = mdfld_dsi_dbi_update_power(dbi_output, DRM_MODE_DPMS_OFF); + if (ret) { + dev_err(dev->dev, "power on error\n"); + goto out_err; + } + } + + /* + * FIXME: this is a WA for TPO panel crash on DPMS on & off around + * 83 times. the root cause of this issue is that Booster in + * drvIC crashed. Add this WA so that we can resume the driver IC + * once we found that booster has a fault + */ + mdfld_dsi_get_power_mode(dsi_config, + &data, + MDFLD_DSI_HS_TRANSMISSION); + + if (on && data && !(data & (1 << 7))) { + /* Soft reset */ + mdfld_dsi_send_dcs(sender, + DCS_SOFT_RESET, + NULL, + 0, + CMD_DATA_SRC_PIPE, + MDFLD_DSI_SEND_PACKAGE); + + /* Init drvIC */ + if (dbi_output->p_funcs->drv_ic_init) + dbi_output->p_funcs->drv_ic_init(dsi_config, + pipe); + } + +out_err: + gma_power_end(dev); + if (ret) + dev_err(dev->dev, "failed\n"); +} + + +static void mdfld_dsi_dbi_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + int ret = 0; + struct drm_device *dev = encoder->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); + struct mdfld_dsi_dbi_output *dsi_output = + MDFLD_DSI_DBI_OUTPUT(dsi_encoder); + struct mdfld_dsi_config *dsi_config = + mdfld_dsi_encoder_get_config(dsi_encoder); + struct mdfld_dsi_connector *dsi_connector = dsi_config->connector; + int pipe = dsi_connector->pipe; + u8 param = 0; + + /* Regs */ + u32 mipi_reg = MIPI; + u32 dspcntr_reg = DSPACNTR; + u32 pipeconf_reg = PIPEACONF; + u32 reg_offset = 0; + + /* Values */ + u32 dspcntr_val = dev_priv->dspcntr; + u32 pipeconf_val = dev_priv->pipeconf; + u32 h_active_area = mode->hdisplay; + u32 v_active_area = mode->vdisplay; + u32 mipi_val; + + mipi_val = (PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX | + TE_TRIGGER_GPIO_PIN); + + dev_dbg(dev->dev, "mipi_val =0x%x\n", mipi_val); + + dev_dbg(dev->dev, "type %s\n", (pipe == 2) ? "MIPI2" : "MIPI"); + dev_dbg(dev->dev, "h %d v %d\n", mode->hdisplay, mode->vdisplay); + + if (pipe == 2) { + mipi_reg = MIPI_C; + dspcntr_reg = DSPCCNTR; + pipeconf_reg = PIPECCONF; + + reg_offset = MIPIC_REG_OFFSET; + + dspcntr_val = dev_priv->dspcntr2; + pipeconf_val = dev_priv->pipeconf2; + } else { + mipi_val |= 0x2; /*two lanes for port A and C respectively*/ + } + + if (!gma_power_begin(dev, true)) { + dev_err(dev->dev, "hw begin failed\n"); + return; + } + + REG_WRITE(dspcntr_reg, dspcntr_val); + REG_READ(dspcntr_reg); + + /* 20ms delay before sending exit_sleep_mode */ + msleep(20); + + /* Send exit_sleep_mode DCS */ + ret = mdfld_dsi_dbi_send_dcs(dsi_output, DCS_EXIT_SLEEP_MODE, + NULL, 0, CMD_DATA_SRC_SYSTEM_MEM); + if (ret) { + dev_err(dev->dev, "sent exit_sleep_mode faild\n"); + goto out_err; + } + + /* Send set_tear_on DCS */ + ret = mdfld_dsi_dbi_send_dcs(dsi_output, DCS_SET_TEAR_ON, + ¶m, 1, CMD_DATA_SRC_SYSTEM_MEM); + if (ret) { + dev_err(dev->dev, "%s - sent set_tear_on faild\n", __func__); + goto out_err; + } + + /* Do some init stuff */ + REG_WRITE(pipeconf_reg, pipeconf_val | PIPEACONF_DSR); + REG_READ(pipeconf_reg); + + /* TODO: this looks ugly, try to move it to CRTC mode setting*/ + if (pipe == 2) + dev_priv->pipeconf2 |= PIPEACONF_DSR; + else + dev_priv->pipeconf |= PIPEACONF_DSR; + + dev_dbg(dev->dev, "pipeconf %x\n", REG_READ(pipeconf_reg)); + + ret = mdfld_dsi_dbi_update_area(dsi_output, 0, 0, + h_active_area - 1, v_active_area - 1); + if (ret) { + dev_err(dev->dev, "update area failed\n"); + goto out_err; + } + +out_err: + gma_power_end(dev); + + if (ret) + dev_err(dev->dev, "mode set failed\n"); +} + +static void mdfld_dsi_dbi_prepare(struct drm_encoder *encoder) +{ + struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); + struct mdfld_dsi_dbi_output *dbi_output + = MDFLD_DSI_DBI_OUTPUT(dsi_encoder); + + dbi_output->mode_flags |= MODE_SETTING_IN_ENCODER; + dbi_output->mode_flags &= ~MODE_SETTING_ENCODER_DONE; + + mdfld_dsi_dbi_set_power(encoder, false); +} + +static void mdfld_dsi_dbi_commit(struct drm_encoder *encoder) +{ + struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); + struct mdfld_dsi_dbi_output *dbi_output = + MDFLD_DSI_DBI_OUTPUT(dsi_encoder); + struct drm_device *dev = dbi_output->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_drm_dpu_rect rect; + + mdfld_dsi_dbi_set_power(encoder, true); + dbi_output->mode_flags &= ~MODE_SETTING_IN_ENCODER; + + rect.x = rect.y = 0; + rect.width = 864; + rect.height = 480; + + if (dbi_output->channel_num == 1) { + dev_priv->dsr_fb_update |= MDFLD_DSR_2D_3D_2; + /*if dpu enabled report a fullscreen damage*/ + mdfld_dbi_dpu_report_damage(dev, MDFLD_PLANEC, &rect); + } else { + dev_priv->dsr_fb_update |= MDFLD_DSR_2D_3D_0; + mdfld_dbi_dpu_report_damage(dev, MDFLD_PLANEA, &rect); + } + dbi_output->mode_flags |= MODE_SETTING_ENCODER_DONE; +} + +static void mdfld_dsi_dbi_dpms(struct drm_encoder *encoder, int mode) +{ + struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); + struct mdfld_dsi_dbi_output *dbi_output + = MDFLD_DSI_DBI_OUTPUT(dsi_encoder); + struct drm_device *dev = dbi_output->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + static bool bdispoff; + + dev_dbg(dev->dev, "%s\n", (mode == DRM_MODE_DPMS_ON ? "on" : "off")); + + if (mode == DRM_MODE_DPMS_ON) { + /* + * FIXME: in case I am wrong! + * we don't need to exit dsr here to wake up plane/pipe/pll + * if everything goes right, hw_begin will resume them all + * during set_power. + */ + if (bdispoff /* FIXME && gbgfxsuspended */) { + mdfld_dsi_dbi_exit_dsr(dev, MDFLD_DSR_2D_3D); + bdispoff = false; + dev_priv->dispstatus = true; + } + + mdfld_dsi_dbi_set_power(encoder, true); + /* FIXME if (gbgfxsuspended) + gbgfxsuspended = false; */ + } else { + /* + * I am not sure whether this is the perfect place to + * turn rpm on since we still have a lot of CRTC turnning + * on work to do. + */ + bdispoff = true; + dev_priv->dispstatus = false; + mdfld_dsi_dbi_set_power(encoder, false); + } +} + + +/* + * Update the DBI MIPI Panel Frame Buffer. + */ +static void mdfld_dsi_dbi_update_fb(struct mdfld_dsi_dbi_output *dbi_output, + int pipe) +{ + struct mdfld_dsi_pkg_sender *sender = + mdfld_dsi_encoder_get_pkg_sender(&dbi_output->base); + struct drm_device *dev = dbi_output->dev; + struct drm_crtc *crtc = dbi_output->base.base.crtc; + struct psb_intel_crtc *psb_crtc = (crtc) ? + to_psb_intel_crtc(crtc) : NULL; + u32 dpll_reg = MRST_DPLL_A; + u32 dspcntr_reg = DSPACNTR; + u32 pipeconf_reg = PIPEACONF; + u32 dsplinoff_reg = DSPALINOFF; + u32 dspsurf_reg = DSPASURF; + u32 reg_offset = 0; + + /* If mode setting on-going, back off */ + if ((dbi_output->mode_flags & MODE_SETTING_ON_GOING) || + (psb_crtc && psb_crtc->mode_flags & MODE_SETTING_ON_GOING) || + !(dbi_output->mode_flags & MODE_SETTING_ENCODER_DONE)) + return; + + if (pipe == 2) { + dspcntr_reg = DSPCCNTR; + pipeconf_reg = PIPECCONF; + dsplinoff_reg = DSPCLINOFF; + dspsurf_reg = DSPCSURF; + reg_offset = MIPIC_REG_OFFSET; + } + + if (!gma_power_begin(dev, true)) { + dev_err(dev->dev, "hw begin failed\n"); + return; + } + + /* Check DBI FIFO status */ + if (!(REG_READ(dpll_reg) & DPLL_VCO_ENABLE) || + !(REG_READ(dspcntr_reg) & DISPLAY_PLANE_ENABLE) || + !(REG_READ(pipeconf_reg) & DISPLAY_PLANE_ENABLE)) + goto update_fb_out0; + + /* Refresh plane changes */ + REG_WRITE(dsplinoff_reg, REG_READ(dsplinoff_reg)); + REG_WRITE(dspsurf_reg, REG_READ(dspsurf_reg)); + REG_READ(dspsurf_reg); + + mdfld_dsi_send_dcs(sender, + DCS_WRITE_MEM_START, + NULL, + 0, + CMD_DATA_SRC_PIPE, + MDFLD_DSI_SEND_PACKAGE); + + dbi_output->dsr_fb_update_done = true; +update_fb_out0: + gma_power_end(dev); +} + +static int tpo_cmd_get_panel_info(struct drm_device *dev, + int pipe, + struct panel_info *pi) +{ + if (!dev || !pi) + return -EINVAL; + + pi->width_mm = TPO_PANEL_WIDTH; + pi->height_mm = TPO_PANEL_HEIGHT; + + return 0; +} + + +/* TPO DBI encoder helper funcs */ +static const struct drm_encoder_helper_funcs mdfld_dsi_dbi_helper_funcs = { + .dpms = mdfld_dsi_dbi_dpms, + .mode_fixup = mdfld_dsi_dbi_mode_fixup, + .prepare = mdfld_dsi_dbi_prepare, + .mode_set = mdfld_dsi_dbi_mode_set, + .commit = mdfld_dsi_dbi_commit, +}; + +/* TPO DBI encoder funcs */ +static const struct drm_encoder_funcs mdfld_dsi_dbi_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +void tpo_cmd_init(struct drm_device *dev, struct panel_funcs *p_funcs) +{ + p_funcs->encoder_funcs = &mdfld_dsi_dbi_encoder_funcs; + p_funcs->encoder_helper_funcs = &mdfld_dsi_dbi_helper_funcs; + p_funcs->get_config_mode = &tpo_cmd_get_config_mode; + p_funcs->update_fb = mdfld_dsi_dbi_update_fb; + p_funcs->get_panel_info = tpo_cmd_get_panel_info; + p_funcs->reset = mdfld_dsi_panel_reset; + p_funcs->drv_ic_init = mdfld_dsi_brightness_init; +} diff --git a/trunk/drivers/staging/gma500/mdfld_tpo_vid.c b/trunk/drivers/staging/gma500/mdfld_tpo_vid.c new file mode 100644 index 000000000000..954901751760 --- /dev/null +++ b/trunk/drivers/staging/gma500/mdfld_tpo_vid.c @@ -0,0 +1,140 @@ +/* + * Copyright © 2010 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: + * jim liu + * Jackie Li + */ + +#include "mdfld_dsi_dbi.h" +#include "mdfld_dsi_dpi.h" +#include "mdfld_dsi_output.h" +#include "mdfld_output.h" + +#include "mdfld_dsi_pkg_sender.h" + +#include "displays/tpo_vid.h" + +static struct drm_display_mode *tpo_vid_get_config_mode(struct drm_device *dev) +{ + struct drm_display_mode *mode; + struct drm_psb_private *dev_priv = dev->dev_private; + struct mrst_timing_info *ti = &dev_priv->gct_data.DTD; + bool use_gct = false; + + mode = kzalloc(sizeof(*mode), GFP_KERNEL); + if (!mode) { + dev_err(dev->dev, "out of memory\n"); + return NULL; + } + + if (use_gct) { + mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo; + mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo; + mode->hsync_start = mode->hdisplay + \ + ((ti->hsync_offset_hi << 8) | \ + ti->hsync_offset_lo); + mode->hsync_end = mode->hsync_start + \ + ((ti->hsync_pulse_width_hi << 8) | \ + ti->hsync_pulse_width_lo); + mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) | \ + ti->hblank_lo); + mode->vsync_start = \ + mode->vdisplay + ((ti->vsync_offset_hi << 8) | \ + ti->vsync_offset_lo); + mode->vsync_end = \ + mode->vsync_start + ((ti->vsync_pulse_width_hi << 8) | \ + ti->vsync_pulse_width_lo); + mode->vtotal = mode->vdisplay + \ + ((ti->vblank_hi << 8) | ti->vblank_lo); + mode->clock = ti->pixel_clock * 10; + + dev_dbg(dev->dev, "hdisplay is %d\n", mode->hdisplay); + dev_dbg(dev->dev, "vdisplay is %d\n", mode->vdisplay); + dev_dbg(dev->dev, "HSS is %d\n", mode->hsync_start); + dev_dbg(dev->dev, "HSE is %d\n", mode->hsync_end); + dev_dbg(dev->dev, "htotal is %d\n", mode->htotal); + dev_dbg(dev->dev, "VSS is %d\n", mode->vsync_start); + dev_dbg(dev->dev, "VSE is %d\n", mode->vsync_end); + dev_dbg(dev->dev, "vtotal is %d\n", mode->vtotal); + dev_dbg(dev->dev, "clock is %d\n", mode->clock); + } else { + mode->hdisplay = 864; + mode->vdisplay = 480; + mode->hsync_start = 873; + mode->hsync_end = 876; + mode->htotal = 887; + mode->vsync_start = 487; + mode->vsync_end = 490; + mode->vtotal = 499; + mode->clock = 33264; + } + + drm_mode_set_name(mode); + drm_mode_set_crtcinfo(mode, 0); + + mode->type |= DRM_MODE_TYPE_PREFERRED; + + return mode; +} + +static int tpo_vid_get_panel_info(struct drm_device *dev, + int pipe, + struct panel_info *pi) +{ + if (!dev || !pi) + return -EINVAL; + + pi->width_mm = TPO_PANEL_WIDTH; + pi->height_mm = TPO_PANEL_HEIGHT; + + return 0; +} + +/*TPO DPI encoder helper funcs*/ +static const struct drm_encoder_helper_funcs + mdfld_tpo_dpi_encoder_helper_funcs = { + .dpms = mdfld_dsi_dpi_dpms, + .mode_fixup = mdfld_dsi_dpi_mode_fixup, + .prepare = mdfld_dsi_dpi_prepare, + .mode_set = mdfld_dsi_dpi_mode_set, + .commit = mdfld_dsi_dpi_commit, +}; + +/*TPO DPI encoder funcs*/ +static const struct drm_encoder_funcs mdfld_tpo_dpi_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +void tpo_vid_init(struct drm_device *dev, struct panel_funcs *p_funcs) +{ + if (!dev || !p_funcs) { + dev_err(dev->dev, "tpo_vid_init: Invalid parameters\n"); + return; + } + + p_funcs->encoder_funcs = &mdfld_tpo_dpi_encoder_funcs; + p_funcs->encoder_helper_funcs = &mdfld_tpo_dpi_encoder_helper_funcs; + p_funcs->get_config_mode = &tpo_vid_get_config_mode; + p_funcs->update_fb = NULL; + p_funcs->get_panel_info = tpo_vid_get_panel_info; +} diff --git a/trunk/drivers/staging/gma500/medfield.h b/trunk/drivers/staging/gma500/medfield.h new file mode 100644 index 000000000000..09e9687431f1 --- /dev/null +++ b/trunk/drivers/staging/gma500/medfield.h @@ -0,0 +1,268 @@ +/* + * Copyright © 2011 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. + */ + +/* Medfield DSI controller registers */ + +#define MIPIA_DEVICE_READY_REG 0xb000 +#define MIPIA_INTR_STAT_REG 0xb004 +#define MIPIA_INTR_EN_REG 0xb008 +#define MIPIA_DSI_FUNC_PRG_REG 0xb00c +#define MIPIA_HS_TX_TIMEOUT_REG 0xb010 +#define MIPIA_LP_RX_TIMEOUT_REG 0xb014 +#define MIPIA_TURN_AROUND_TIMEOUT_REG 0xb018 +#define MIPIA_DEVICE_RESET_TIMER_REG 0xb01c +#define MIPIA_DPI_RESOLUTION_REG 0xb020 +#define MIPIA_DBI_FIFO_THROTTLE_REG 0xb024 +#define MIPIA_HSYNC_COUNT_REG 0xb028 +#define MIPIA_HBP_COUNT_REG 0xb02c +#define MIPIA_HFP_COUNT_REG 0xb030 +#define MIPIA_HACTIVE_COUNT_REG 0xb034 +#define MIPIA_VSYNC_COUNT_REG 0xb038 +#define MIPIA_VBP_COUNT_REG 0xb03c +#define MIPIA_VFP_COUNT_REG 0xb040 +#define MIPIA_HIGH_LOW_SWITCH_COUNT_REG 0xb044 +#define MIPIA_DPI_CONTROL_REG 0xb048 +#define MIPIA_DPI_DATA_REG 0xb04c +#define MIPIA_INIT_COUNT_REG 0xb050 +#define MIPIA_MAX_RETURN_PACK_SIZE_REG 0xb054 +#define MIPIA_VIDEO_MODE_FORMAT_REG 0xb058 +#define MIPIA_EOT_DISABLE_REG 0xb05c +#define MIPIA_LP_BYTECLK_REG 0xb060 +#define MIPIA_LP_GEN_DATA_REG 0xb064 +#define MIPIA_HS_GEN_DATA_REG 0xb068 +#define MIPIA_LP_GEN_CTRL_REG 0xb06c +#define MIPIA_HS_GEN_CTRL_REG 0xb070 +#define MIPIA_GEN_FIFO_STAT_REG 0xb074 +#define MIPIA_HS_LS_DBI_ENABLE_REG 0xb078 +#define MIPIA_DPHY_PARAM_REG 0xb080 +#define MIPIA_DBI_BW_CTRL_REG 0xb084 +#define MIPIA_CLK_LANE_SWITCH_TIME_CNT_REG 0xb088 + +#define DSI_DEVICE_READY (0x1) +#define DSI_POWER_STATE_ULPS_ENTER (0x2 << 1) +#define DSI_POWER_STATE_ULPS_EXIT (0x1 << 1) +#define DSI_POWER_STATE_ULPS_OFFSET (0x1) + + +#define DSI_ONE_DATA_LANE (0x1) +#define DSI_TWO_DATA_LANE (0x2) +#define DSI_THREE_DATA_LANE (0X3) +#define DSI_FOUR_DATA_LANE (0x4) +#define DSI_DPI_VIRT_CHANNEL_OFFSET (0x3) +#define DSI_DBI_VIRT_CHANNEL_OFFSET (0x5) +#define DSI_DPI_COLOR_FORMAT_RGB565 (0x01 << 7) +#define DSI_DPI_COLOR_FORMAT_RGB666 (0x02 << 7) +#define DSI_DPI_COLOR_FORMAT_RGB666_UNPACK (0x03 << 7) +#define DSI_DPI_COLOR_FORMAT_RGB888 (0x04 << 7) +#define DSI_DBI_COLOR_FORMAT_OPTION2 (0x05 << 13) + +#define DSI_INTR_STATE_RXSOTERROR 1 + +#define DSI_INTR_STATE_SPL_PKG_SENT (1 << 30) +#define DSI_INTR_STATE_TE (1 << 31) + +#define DSI_HS_TX_TIMEOUT_MASK (0xffffff) + +#define DSI_LP_RX_TIMEOUT_MASK (0xffffff) + +#define DSI_TURN_AROUND_TIMEOUT_MASK (0x3f) + +#define DSI_RESET_TIMER_MASK (0xffff) + +#define DSI_DBI_FIFO_WM_HALF (0x0) +#define DSI_DBI_FIFO_WM_QUARTER (0x1) +#define DSI_DBI_FIFO_WM_LOW (0x2) + +#define DSI_DPI_TIMING_MASK (0xffff) + +#define DSI_INIT_TIMER_MASK (0xffff) + +#define DSI_DBI_RETURN_PACK_SIZE_MASK (0x3ff) + +#define DSI_LP_BYTECLK_MASK (0x0ffff) + +#define DSI_HS_CTRL_GEN_SHORT_W0 (0x03) +#define DSI_HS_CTRL_GEN_SHORT_W1 (0x13) +#define DSI_HS_CTRL_GEN_SHORT_W2 (0x23) +#define DSI_HS_CTRL_GEN_R0 (0x04) +#define DSI_HS_CTRL_GEN_R1 (0x14) +#define DSI_HS_CTRL_GEN_R2 (0x24) +#define DSI_HS_CTRL_GEN_LONG_W (0x29) +#define DSI_HS_CTRL_MCS_SHORT_W0 (0x05) +#define DSI_HS_CTRL_MCS_SHORT_W1 (0x15) +#define DSI_HS_CTRL_MCS_R0 (0x06) +#define DSI_HS_CTRL_MCS_LONG_W (0x39) +#define DSI_HS_CTRL_VC_OFFSET (0x06) +#define DSI_HS_CTRL_WC_OFFSET (0x08) + +#define DSI_FIFO_GEN_HS_DATA_FULL (1 << 0) +#define DSI_FIFO_GEN_HS_DATA_HALF_EMPTY (1 << 1) +#define DSI_FIFO_GEN_HS_DATA_EMPTY (1 << 2) +#define DSI_FIFO_GEN_LP_DATA_FULL (1 << 8) +#define DSI_FIFO_GEN_LP_DATA_HALF_EMPTY (1 << 9) +#define DSI_FIFO_GEN_LP_DATA_EMPTY (1 << 10) +#define DSI_FIFO_GEN_HS_CTRL_FULL (1 << 16) +#define DSI_FIFO_GEN_HS_CTRL_HALF_EMPTY (1 << 17) +#define DSI_FIFO_GEN_HS_CTRL_EMPTY (1 << 18) +#define DSI_FIFO_GEN_LP_CTRL_FULL (1 << 24) +#define DSI_FIFO_GEN_LP_CTRL_HALF_EMPTY (1 << 25) +#define DSI_FIFO_GEN_LP_CTRL_EMPTY (1 << 26) +#define DSI_FIFO_DBI_EMPTY (1 << 27) +#define DSI_FIFO_DPI_EMPTY (1 << 28) + +#define DSI_DBI_HS_LP_SWITCH_MASK (0x1) + +#define DSI_HS_LP_SWITCH_COUNTER_OFFSET (0x0) +#define DSI_LP_HS_SWITCH_COUNTER_OFFSET (0x16) + +#define DSI_DPI_CTRL_HS_SHUTDOWN (0x00000001) +#define DSI_DPI_CTRL_HS_TURN_ON (0x00000002) + +/* Medfield DSI adapter registers */ +#define MIPIA_CONTROL_REG 0xb104 +#define MIPIA_DATA_ADD_REG 0xb108 +#define MIPIA_DATA_LEN_REG 0xb10c +#define MIPIA_CMD_ADD_REG 0xb110 +#define MIPIA_CMD_LEN_REG 0xb114 + +/*dsi power modes*/ +#define DSI_POWER_MODE_DISPLAY_ON (1 << 2) +#define DSI_POWER_MODE_NORMAL_ON (1 << 3) +#define DSI_POWER_MODE_SLEEP_OUT (1 << 4) +#define DSI_POWER_MODE_PARTIAL_ON (1 << 5) +#define DSI_POWER_MODE_IDLE_ON (1 << 6) + +enum { + MDFLD_DSI_ENCODER_DBI = 0, + MDFLD_DSI_ENCODER_DPI, +}; + +enum { + MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_PULSE = 1, + MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_EVENTS = 2, + MDFLD_DSI_VIDEO_BURST_MODE = 3, +}; + +#define DSI_DPI_COMPLETE_LAST_LINE (1 << 2) +#define DSI_DPI_DISABLE_BTA (1 << 3) +/* Panel types */ +enum { + TPO_CMD, + TPO_VID, + TMD_CMD, + TMD_VID, + PYR_CMD, + PYR_VID, + TPO, + TMD, + PYR, + HDMI, + GCT_DETECT +}; + +/* Junk that belongs elsewhere */ +#define TPO_PANEL_WIDTH 84 +#define TPO_PANEL_HEIGHT 46 +#define TMD_PANEL_WIDTH 39 +#define TMD_PANEL_HEIGHT 71 +#define PYR_PANEL_WIDTH 53 +#define PYR_PANEL_HEIGHT 95 + +/* Panel interface */ +struct panel_info { + u32 width_mm; + u32 height_mm; +}; + +struct mdfld_dsi_dbi_output; + +struct mdfld_dsi_connector_state { + u32 mipi_ctrl_reg; +}; + +struct mdfld_dsi_encoder_state { + +}; + +struct mdfld_dsi_connector { + /* + * This is ugly, but I have to use connector in it! :-( + * FIXME: use drm_connector instead. + */ + struct psb_intel_output base; + + int pipe; + void *private; + void *pkg_sender; + + /* Connection status */ + enum drm_connector_status status; +}; + +struct mdfld_dsi_encoder { + struct drm_encoder base; + void *private; +}; + +/* + * DSI config, consists of one DSI connector, two DSI encoders. + * DRM will pick up on DSI encoder basing on differents configs. + */ +struct mdfld_dsi_config { + struct drm_device *dev; + struct drm_display_mode *fixed_mode; + struct drm_display_mode *mode; + + struct mdfld_dsi_connector *connector; + struct mdfld_dsi_encoder *encoders[DRM_CONNECTOR_MAX_ENCODER]; + struct mdfld_dsi_encoder *encoder; + + int changed; + + int bpp; + int type; + int lane_count; + /*Virtual channel number for this encoder*/ + int channel_num; + /*video mode configure*/ + int video_mode; + + int dvr_ic_inited; +}; + +#define MDFLD_DSI_CONNECTOR(psb_output) \ + (container_of(psb_output, struct mdfld_dsi_connector, base)) + +#define MDFLD_DSI_ENCODER(encoder) \ + (container_of(encoder, struct mdfld_dsi_encoder, base)) + +struct panel_funcs { + const struct drm_encoder_funcs *encoder_funcs; + const struct drm_encoder_helper_funcs *encoder_helper_funcs; + struct drm_display_mode *(*get_config_mode) (struct drm_device *); + void (*update_fb) (struct mdfld_dsi_dbi_output *, int); + int (*get_panel_info) (struct drm_device *, int, struct panel_info *); + int (*reset)(int pipe); + void (*drv_ic_init)(struct mdfld_dsi_config *dsi_config, int pipe); +}; + diff --git a/trunk/drivers/staging/gma500/mid_bios.c b/trunk/drivers/staging/gma500/mid_bios.c new file mode 100644 index 000000000000..ee3c0368e320 --- /dev/null +++ b/trunk/drivers/staging/gma500/mid_bios.c @@ -0,0 +1,270 @@ +/************************************************************************** + * 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. + * + **************************************************************************/ + +/* TODO + * - Split functions by vbt type + * - Make them all take drm_device + * - Check ioremap failures + */ + +#include +#include +#include +#include "psb_drm.h" +#include "psb_drv.h" +#include "mid_bios.h" +#include "mdfld_output.h" + +static int panel_id = GCT_DETECT; +module_param_named(panel_id, panel_id, int, 0600); +MODULE_PARM_DESC(panel_id, "Panel Identifier"); + + +static void mid_get_fuse_settings(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0); + uint32_t fuse_value = 0; + uint32_t fuse_value_tmp = 0; + +#define FB_REG06 0xD0810600 +#define FB_MIPI_DISABLE (1 << 11) +#define FB_REG09 0xD0810900 +#define FB_REG09 0xD0810900 +#define FB_SKU_MASK 0x7000 +#define FB_SKU_SHIFT 12 +#define FB_SKU_100 0 +#define FB_SKU_100L 1 +#define FB_SKU_83 2 + pci_write_config_dword(pci_root, 0xD0, FB_REG06); + pci_read_config_dword(pci_root, 0xD4, &fuse_value); + + /* FB_MIPI_DISABLE doesn't mean LVDS on with Medfield */ + if (IS_MRST(dev)) + dev_priv->iLVDS_enable = fuse_value & FB_MIPI_DISABLE; + + DRM_INFO("internal display is %s\n", + dev_priv->iLVDS_enable ? "LVDS display" : "MIPI display"); + + /* Prevent runtime suspend at start*/ + if (dev_priv->iLVDS_enable) { + dev_priv->is_lvds_on = true; + dev_priv->is_mipi_on = false; + } else { + dev_priv->is_mipi_on = true; + dev_priv->is_lvds_on = false; + } + + dev_priv->video_device_fuse = fuse_value; + + pci_write_config_dword(pci_root, 0xD0, FB_REG09); + pci_read_config_dword(pci_root, 0xD4, &fuse_value); + + dev_dbg(dev->dev, "SKU values is 0x%x.\n", fuse_value); + fuse_value_tmp = (fuse_value & FB_SKU_MASK) >> FB_SKU_SHIFT; + + dev_priv->fuse_reg_value = fuse_value; + + switch (fuse_value_tmp) { + case FB_SKU_100: + dev_priv->core_freq = 200; + break; + case FB_SKU_100L: + dev_priv->core_freq = 100; + break; + case FB_SKU_83: + dev_priv->core_freq = 166; + break; + default: + dev_warn(dev->dev, "Invalid SKU values, SKU value = 0x%08x\n", + fuse_value_tmp); + dev_priv->core_freq = 0; + } + dev_dbg(dev->dev, "LNC core clk is %dMHz.\n", dev_priv->core_freq); + pci_dev_put(pci_root); +} + +/* + * Get the revison ID, B0:D2:F0;0x08 + */ +static void mid_get_pci_revID(struct drm_psb_private *dev_priv) +{ + uint32_t platform_rev_id = 0; + struct pci_dev *pci_gfx_root = pci_get_bus_and_slot(0, PCI_DEVFN(2, 0)); + + pci_read_config_dword(pci_gfx_root, 0x08, &platform_rev_id); + dev_priv->platform_rev_id = (uint8_t) platform_rev_id; + pci_dev_put(pci_gfx_root); + dev_dbg(dev_priv->dev->dev, "platform_rev_id is %x\n", + dev_priv->platform_rev_id); +} + +static void mid_get_vbt_data(struct drm_psb_private *dev_priv) +{ + struct drm_device *dev = dev_priv->dev; + struct mrst_vbt *vbt = &dev_priv->vbt_data; + u32 addr; + u16 new_size; + u8 *vbt_virtual; + u8 bpi; + u8 number_desc = 0; + struct mrst_timing_info *dp_ti = &dev_priv->gct_data.DTD; + struct gct_r10_timing_info ti; + void *pGCT; + struct pci_dev *pci_gfx_root = pci_get_bus_and_slot(0, PCI_DEVFN(2, 0)); + + /* Get the address of the platform config vbt, B0:D2:F0;0xFC */ + pci_read_config_dword(pci_gfx_root, 0xFC, &addr); + pci_dev_put(pci_gfx_root); + + dev_dbg(dev->dev, "drm platform config address is %x\n", addr); + + /* check for platform config address == 0. */ + /* this means fw doesn't support vbt */ + + if (addr == 0) { + vbt->size = 0; + return; + } + + /* get the virtual address of the vbt */ + vbt_virtual = ioremap(addr, sizeof(*vbt)); + + memcpy(vbt, vbt_virtual, sizeof(*vbt)); + iounmap(vbt_virtual); /* Free virtual address space */ + + dev_dbg(dev->dev, "GCT revision is %x\n", vbt->revision); + + switch (vbt->revision) { + case 0: + vbt->mrst_gct = ioremap(addr + sizeof(*vbt) - 4, + vbt->size - sizeof(*vbt) + 4); + pGCT = vbt->mrst_gct; + bpi = ((struct mrst_gct_v1 *)pGCT)->PD.BootPanelIndex; + dev_priv->gct_data.bpi = bpi; + dev_priv->gct_data.pt = + ((struct mrst_gct_v1 *)pGCT)->PD.PanelType; + memcpy(&dev_priv->gct_data.DTD, + &((struct mrst_gct_v1 *)pGCT)->panel[bpi].DTD, + sizeof(struct mrst_timing_info)); + dev_priv->gct_data.Panel_Port_Control = + ((struct mrst_gct_v1 *)pGCT)->panel[bpi].Panel_Port_Control; + dev_priv->gct_data.Panel_MIPI_Display_Descriptor = + ((struct mrst_gct_v1 *)pGCT)->panel[bpi].Panel_MIPI_Display_Descriptor; + break; + case 1: + vbt->mrst_gct = ioremap(addr + sizeof(*vbt) - 4, + vbt->size - sizeof(*vbt) + 4); + pGCT = vbt->mrst_gct; + bpi = ((struct mrst_gct_v2 *)pGCT)->PD.BootPanelIndex; + dev_priv->gct_data.bpi = bpi; + dev_priv->gct_data.pt = + ((struct mrst_gct_v2 *)pGCT)->PD.PanelType; + memcpy(&dev_priv->gct_data.DTD, + &((struct mrst_gct_v2 *)pGCT)->panel[bpi].DTD, + sizeof(struct mrst_timing_info)); + dev_priv->gct_data.Panel_Port_Control = + ((struct mrst_gct_v2 *)pGCT)->panel[bpi].Panel_Port_Control; + dev_priv->gct_data.Panel_MIPI_Display_Descriptor = + ((struct mrst_gct_v2 *)pGCT)->panel[bpi].Panel_MIPI_Display_Descriptor; + break; + case 0x10: + /*header definition changed from rev 01 (v2) to rev 10h. */ + /*so, some values have changed location*/ + new_size = vbt->checksum; /*checksum contains lo size byte*/ + /*LSB of mrst_gct contains hi size byte*/ + new_size |= ((0xff & (unsigned int)vbt->mrst_gct)) << 8; + + vbt->checksum = vbt->size; /*size contains the checksum*/ + if (new_size > 0xff) + vbt->size = 0xff; /*restrict size to 255*/ + else + vbt->size = new_size; + + /* number of descriptors defined in the GCT */ + number_desc = ((0xff00 & (unsigned int)vbt->mrst_gct)) >> 8; + bpi = ((0xff0000 & (unsigned int)vbt->mrst_gct)) >> 16; + vbt->mrst_gct = ioremap(addr + GCT_R10_HEADER_SIZE, + GCT_R10_DISPLAY_DESC_SIZE * number_desc); + pGCT = vbt->mrst_gct; + pGCT = (u8 *)pGCT + (bpi*GCT_R10_DISPLAY_DESC_SIZE); + dev_priv->gct_data.bpi = bpi; /*save boot panel id*/ + + /*copy the GCT display timings into a temp structure*/ + memcpy(&ti, pGCT, sizeof(struct gct_r10_timing_info)); + + /*now copy the temp struct into the dev_priv->gct_data*/ + dp_ti->pixel_clock = ti.pixel_clock; + dp_ti->hactive_hi = ti.hactive_hi; + dp_ti->hactive_lo = ti.hactive_lo; + dp_ti->hblank_hi = ti.hblank_hi; + dp_ti->hblank_lo = ti.hblank_lo; + dp_ti->hsync_offset_hi = ti.hsync_offset_hi; + dp_ti->hsync_offset_lo = ti.hsync_offset_lo; + dp_ti->hsync_pulse_width_hi = ti.hsync_pulse_width_hi; + dp_ti->hsync_pulse_width_lo = ti.hsync_pulse_width_lo; + dp_ti->vactive_hi = ti.vactive_hi; + dp_ti->vactive_lo = ti.vactive_lo; + dp_ti->vblank_hi = ti.vblank_hi; + dp_ti->vblank_lo = ti.vblank_lo; + dp_ti->vsync_offset_hi = ti.vsync_offset_hi; + dp_ti->vsync_offset_lo = ti.vsync_offset_lo; + dp_ti->vsync_pulse_width_hi = ti.vsync_pulse_width_hi; + dp_ti->vsync_pulse_width_lo = ti.vsync_pulse_width_lo; + + /* Move the MIPI_Display_Descriptor data from GCT to dev priv */ + dev_priv->gct_data.Panel_MIPI_Display_Descriptor = + *((u8 *)pGCT + 0x0d); + dev_priv->gct_data.Panel_MIPI_Display_Descriptor |= + (*((u8 *)pGCT + 0x0e)) << 8; + break; + default: + dev_err(dev->dev, "Unknown revision of GCT!\n"); + vbt->size = 0; + } + if (IS_MFLD(dev_priv->dev)) { + if (panel_id == GCT_DETECT) { + if (dev_priv->gct_data.bpi == 2) { + dev_info(dev->dev, "[GFX] PYR Panel Detected\n"); + dev_priv->panel_id = PYR_CMD; + panel_id = PYR_CMD; + } else if (dev_priv->gct_data.bpi == 0) { + dev_info(dev->dev, "[GFX] TMD Panel Detected.\n"); + dev_priv->panel_id = TMD_VID; + panel_id = TMD_VID; + } else { + dev_info(dev->dev, "[GFX] Default Panel (TPO)\n"); + dev_priv->panel_id = TPO_CMD; + panel_id = TPO_CMD; + } + } else { + dev_info(dev->dev, "[GFX] Panel Parameter Passed in through cmd line\n"); + dev_priv->panel_id = panel_id; + } + } +} + +int mid_chip_setup(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + mid_get_fuse_settings(dev); + mid_get_vbt_data(dev_priv); + mid_get_pci_revID(dev_priv); + return 0; +} diff --git a/trunk/drivers/staging/gma500/mid_bios.h b/trunk/drivers/staging/gma500/mid_bios.h new file mode 100644 index 000000000000..00e7d564b7eb --- /dev/null +++ b/trunk/drivers/staging/gma500/mid_bios.h @@ -0,0 +1,21 @@ +/************************************************************************** + * 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. + * + **************************************************************************/ + +extern int mid_chip_setup(struct drm_device *dev); + diff --git a/trunk/drivers/staging/gma500/mmu.c b/trunk/drivers/staging/gma500/mmu.c new file mode 100644 index 000000000000..c904d73b1de3 --- /dev/null +++ b/trunk/drivers/staging/gma500/mmu.c @@ -0,0 +1,858 @@ +/************************************************************************** + * Copyright (c) 2007, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * 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 +#include "psb_drv.h" +#include "psb_reg.h" + +/* + * Code for the SGX MMU: + */ + +/* + * clflush on one processor only: + * clflush should apparently flush the cache line on all processors in an + * SMP system. + */ + +/* + * kmap atomic: + * The usage of the slots must be completely encapsulated within a spinlock, and + * no other functions that may be using the locks for other purposed may be + * called from within the locked region. + * Since the slots are per processor, this will guarantee that we are the only + * user. + */ + +/* + * TODO: Inserting ptes from an interrupt handler: + * This may be desirable for some SGX functionality where the GPU can fault in + * needed pages. For that, we need to make an atomic insert_pages function, that + * may fail. + * If it fails, the caller need to insert the page using a workqueue function, + * but on average it should be fast. + */ + +struct psb_mmu_driver { + /* protects driver- and pd structures. Always take in read mode + * before taking the page table spinlock. + */ + struct rw_semaphore sem; + + /* protects page tables, directory tables and pt tables. + * and pt structures. + */ + spinlock_t lock; + + atomic_t needs_tlbflush; + + uint8_t __iomem *register_map; + struct psb_mmu_pd *default_pd; + /*uint32_t bif_ctrl;*/ + int has_clflush; + int clflush_add; + unsigned long clflush_mask; + + struct drm_psb_private *dev_priv; +}; + +struct psb_mmu_pd; + +struct psb_mmu_pt { + struct psb_mmu_pd *pd; + uint32_t index; + uint32_t count; + struct page *p; + uint32_t *v; +}; + +struct psb_mmu_pd { + struct psb_mmu_driver *driver; + int hw_context; + struct psb_mmu_pt **tables; + struct page *p; + struct page *dummy_pt; + struct page *dummy_page; + uint32_t pd_mask; + uint32_t invalid_pde; + uint32_t invalid_pte; +}; + +static inline uint32_t psb_mmu_pt_index(uint32_t offset) +{ + return (offset >> PSB_PTE_SHIFT) & 0x3FF; +} + +static inline uint32_t psb_mmu_pd_index(uint32_t offset) +{ + return offset >> PSB_PDE_SHIFT; +} + +static inline void psb_clflush(void *addr) +{ + __asm__ __volatile__("clflush (%0)\n" : : "r"(addr) : "memory"); +} + +static inline void psb_mmu_clflush(struct psb_mmu_driver *driver, + void *addr) +{ + if (!driver->has_clflush) + return; + + mb(); + psb_clflush(addr); + mb(); +} + +static void psb_page_clflush(struct psb_mmu_driver *driver, struct page* page) +{ + uint32_t clflush_add = driver->clflush_add >> PAGE_SHIFT; + uint32_t clflush_count = PAGE_SIZE / clflush_add; + int i; + uint8_t *clf; + + clf = kmap_atomic(page, KM_USER0); + mb(); + for (i = 0; i < clflush_count; ++i) { + psb_clflush(clf); + clf += clflush_add; + } + mb(); + kunmap_atomic(clf, KM_USER0); +} + +static void psb_pages_clflush(struct psb_mmu_driver *driver, + struct page *page[], unsigned long num_pages) +{ + int i; + + if (!driver->has_clflush) + return ; + + for (i = 0; i < num_pages; i++) + psb_page_clflush(driver, *page++); +} + +static void psb_mmu_flush_pd_locked(struct psb_mmu_driver *driver, + int force) +{ + atomic_set(&driver->needs_tlbflush, 0); +} + +static void psb_mmu_flush_pd(struct psb_mmu_driver *driver, int force) +{ + down_write(&driver->sem); + psb_mmu_flush_pd_locked(driver, force); + up_write(&driver->sem); +} + +void psb_mmu_flush(struct psb_mmu_driver *driver, int rc_prot) +{ + if (rc_prot) + down_write(&driver->sem); + if (rc_prot) + up_write(&driver->sem); +} + +void psb_mmu_set_pd_context(struct psb_mmu_pd *pd, int hw_context) +{ + /*ttm_tt_cache_flush(&pd->p, 1);*/ + psb_pages_clflush(pd->driver, &pd->p, 1); + down_write(&pd->driver->sem); + wmb(); + psb_mmu_flush_pd_locked(pd->driver, 1); + pd->hw_context = hw_context; + up_write(&pd->driver->sem); + +} + +static inline unsigned long psb_pd_addr_end(unsigned long addr, + unsigned long end) +{ + + addr = (addr + PSB_PDE_MASK + 1) & ~PSB_PDE_MASK; + return (addr < end) ? addr : end; +} + +static inline uint32_t psb_mmu_mask_pte(uint32_t pfn, int type) +{ + uint32_t mask = PSB_PTE_VALID; + + if (type & PSB_MMU_CACHED_MEMORY) + mask |= PSB_PTE_CACHED; + if (type & PSB_MMU_RO_MEMORY) + mask |= PSB_PTE_RO; + if (type & PSB_MMU_WO_MEMORY) + mask |= PSB_PTE_WO; + + return (pfn << PAGE_SHIFT) | mask; +} + +struct psb_mmu_pd *psb_mmu_alloc_pd(struct psb_mmu_driver *driver, + int trap_pagefaults, int invalid_type) +{ + struct psb_mmu_pd *pd = kmalloc(sizeof(*pd), GFP_KERNEL); + uint32_t *v; + int i; + + if (!pd) + return NULL; + + pd->p = alloc_page(GFP_DMA32); + if (!pd->p) + goto out_err1; + pd->dummy_pt = alloc_page(GFP_DMA32); + if (!pd->dummy_pt) + goto out_err2; + pd->dummy_page = alloc_page(GFP_DMA32); + if (!pd->dummy_page) + goto out_err3; + + if (!trap_pagefaults) { + pd->invalid_pde = + psb_mmu_mask_pte(page_to_pfn(pd->dummy_pt), + invalid_type); + pd->invalid_pte = + psb_mmu_mask_pte(page_to_pfn(pd->dummy_page), + invalid_type); + } else { + pd->invalid_pde = 0; + pd->invalid_pte = 0; + } + + v = kmap(pd->dummy_pt); + for (i = 0; i < (PAGE_SIZE / sizeof(uint32_t)); ++i) + v[i] = pd->invalid_pte; + + kunmap(pd->dummy_pt); + + v = kmap(pd->p); + for (i = 0; i < (PAGE_SIZE / sizeof(uint32_t)); ++i) + v[i] = pd->invalid_pde; + + kunmap(pd->p); + + clear_page(kmap(pd->dummy_page)); + kunmap(pd->dummy_page); + + pd->tables = vmalloc_user(sizeof(struct psb_mmu_pt *) * 1024); + if (!pd->tables) + goto out_err4; + + pd->hw_context = -1; + pd->pd_mask = PSB_PTE_VALID; + pd->driver = driver; + + return pd; + +out_err4: + __free_page(pd->dummy_page); +out_err3: + __free_page(pd->dummy_pt); +out_err2: + __free_page(pd->p); +out_err1: + kfree(pd); + return NULL; +} + +void psb_mmu_free_pt(struct psb_mmu_pt *pt) +{ + __free_page(pt->p); + kfree(pt); +} + +void psb_mmu_free_pagedir(struct psb_mmu_pd *pd) +{ + struct psb_mmu_driver *driver = pd->driver; + struct psb_mmu_pt *pt; + int i; + + down_write(&driver->sem); + if (pd->hw_context != -1) + psb_mmu_flush_pd_locked(driver, 1); + + /* Should take the spinlock here, but we don't need to do that + since we have the semaphore in write mode. */ + + for (i = 0; i < 1024; ++i) { + pt = pd->tables[i]; + if (pt) + psb_mmu_free_pt(pt); + } + + vfree(pd->tables); + __free_page(pd->dummy_page); + __free_page(pd->dummy_pt); + __free_page(pd->p); + kfree(pd); + up_write(&driver->sem); +} + +static struct psb_mmu_pt *psb_mmu_alloc_pt(struct psb_mmu_pd *pd) +{ + struct psb_mmu_pt *pt = kmalloc(sizeof(*pt), GFP_KERNEL); + void *v; + uint32_t clflush_add = pd->driver->clflush_add >> PAGE_SHIFT; + uint32_t clflush_count = PAGE_SIZE / clflush_add; + spinlock_t *lock = &pd->driver->lock; + uint8_t *clf; + uint32_t *ptes; + int i; + + if (!pt) + return NULL; + + pt->p = alloc_page(GFP_DMA32); + if (!pt->p) { + kfree(pt); + return NULL; + } + + spin_lock(lock); + + v = kmap_atomic(pt->p, KM_USER0); + clf = (uint8_t *) v; + ptes = (uint32_t *) v; + for (i = 0; i < (PAGE_SIZE / sizeof(uint32_t)); ++i) + *ptes++ = pd->invalid_pte; + + + if (pd->driver->has_clflush && pd->hw_context != -1) { + mb(); + for (i = 0; i < clflush_count; ++i) { + psb_clflush(clf); + clf += clflush_add; + } + mb(); + } + + kunmap_atomic(v, KM_USER0); + spin_unlock(lock); + + pt->count = 0; + pt->pd = pd; + pt->index = 0; + + return pt; +} + +struct psb_mmu_pt *psb_mmu_pt_alloc_map_lock(struct psb_mmu_pd *pd, + unsigned long addr) +{ + uint32_t index = psb_mmu_pd_index(addr); + struct psb_mmu_pt *pt; + uint32_t *v; + spinlock_t *lock = &pd->driver->lock; + + spin_lock(lock); + pt = pd->tables[index]; + while (!pt) { + spin_unlock(lock); + pt = psb_mmu_alloc_pt(pd); + if (!pt) + return NULL; + spin_lock(lock); + + if (pd->tables[index]) { + spin_unlock(lock); + psb_mmu_free_pt(pt); + spin_lock(lock); + pt = pd->tables[index]; + continue; + } + + v = kmap_atomic(pd->p, KM_USER0); + pd->tables[index] = pt; + v[index] = (page_to_pfn(pt->p) << 12) | pd->pd_mask; + pt->index = index; + kunmap_atomic((void *) v, KM_USER0); + + if (pd->hw_context != -1) { + psb_mmu_clflush(pd->driver, (void *) &v[index]); + atomic_set(&pd->driver->needs_tlbflush, 1); + } + } + pt->v = kmap_atomic(pt->p, KM_USER0); + return pt; +} + +static struct psb_mmu_pt *psb_mmu_pt_map_lock(struct psb_mmu_pd *pd, + unsigned long addr) +{ + uint32_t index = psb_mmu_pd_index(addr); + struct psb_mmu_pt *pt; + spinlock_t *lock = &pd->driver->lock; + + spin_lock(lock); + pt = pd->tables[index]; + if (!pt) { + spin_unlock(lock); + return NULL; + } + pt->v = kmap_atomic(pt->p, KM_USER0); + return pt; +} + +static void psb_mmu_pt_unmap_unlock(struct psb_mmu_pt *pt) +{ + struct psb_mmu_pd *pd = pt->pd; + uint32_t *v; + + kunmap_atomic(pt->v, KM_USER0); + if (pt->count == 0) { + v = kmap_atomic(pd->p, KM_USER0); + v[pt->index] = pd->invalid_pde; + pd->tables[pt->index] = NULL; + + if (pd->hw_context != -1) { + psb_mmu_clflush(pd->driver, + (void *) &v[pt->index]); + atomic_set(&pd->driver->needs_tlbflush, 1); + } + kunmap_atomic(pt->v, KM_USER0); + spin_unlock(&pd->driver->lock); + psb_mmu_free_pt(pt); + return; + } + spin_unlock(&pd->driver->lock); +} + +static inline void psb_mmu_set_pte(struct psb_mmu_pt *pt, + unsigned long addr, uint32_t pte) +{ + pt->v[psb_mmu_pt_index(addr)] = pte; +} + +static inline void psb_mmu_invalidate_pte(struct psb_mmu_pt *pt, + unsigned long addr) +{ + pt->v[psb_mmu_pt_index(addr)] = pt->pd->invalid_pte; +} + + +void psb_mmu_mirror_gtt(struct psb_mmu_pd *pd, + uint32_t mmu_offset, uint32_t gtt_start, + uint32_t gtt_pages) +{ + uint32_t *v; + uint32_t start = psb_mmu_pd_index(mmu_offset); + struct psb_mmu_driver *driver = pd->driver; + int num_pages = gtt_pages; + + down_read(&driver->sem); + spin_lock(&driver->lock); + + v = kmap_atomic(pd->p, KM_USER0); + v += start; + + while (gtt_pages--) { + *v++ = gtt_start | pd->pd_mask; + gtt_start += PAGE_SIZE; + } + + /*ttm_tt_cache_flush(&pd->p, num_pages);*/ + psb_pages_clflush(pd->driver, &pd->p, num_pages); + kunmap_atomic(v, KM_USER0); + spin_unlock(&driver->lock); + + if (pd->hw_context != -1) + atomic_set(&pd->driver->needs_tlbflush, 1); + + up_read(&pd->driver->sem); + psb_mmu_flush_pd(pd->driver, 0); +} + +struct psb_mmu_pd *psb_mmu_get_default_pd(struct psb_mmu_driver *driver) +{ + struct psb_mmu_pd *pd; + + /* down_read(&driver->sem); */ + pd = driver->default_pd; + /* up_read(&driver->sem); */ + + return pd; +} + +/* Returns the physical address of the PD shared by sgx/msvdx */ +uint32_t psb_get_default_pd_addr(struct psb_mmu_driver *driver) +{ + struct psb_mmu_pd *pd; + + pd = psb_mmu_get_default_pd(driver); + return page_to_pfn(pd->p) << PAGE_SHIFT; +} + +void psb_mmu_driver_takedown(struct psb_mmu_driver *driver) +{ + psb_mmu_free_pagedir(driver->default_pd); + kfree(driver); +} + +struct psb_mmu_driver *psb_mmu_driver_init(uint8_t __iomem * registers, + int trap_pagefaults, + int invalid_type, + struct drm_psb_private *dev_priv) +{ + struct psb_mmu_driver *driver; + + driver = kmalloc(sizeof(*driver), GFP_KERNEL); + + if (!driver) + return NULL; + driver->dev_priv = dev_priv; + + driver->default_pd = psb_mmu_alloc_pd(driver, trap_pagefaults, + invalid_type); + if (!driver->default_pd) + goto out_err1; + + spin_lock_init(&driver->lock); + init_rwsem(&driver->sem); + down_write(&driver->sem); + driver->register_map = registers; + atomic_set(&driver->needs_tlbflush, 1); + + driver->has_clflush = 0; + + if (boot_cpu_has(X86_FEATURE_CLFLSH)) { + uint32_t tfms, misc, cap0, cap4, clflush_size; + + /* + * clflush size is determined at kernel setup for x86_64 + * but not for i386. We have to do it here. + */ + + cpuid(0x00000001, &tfms, &misc, &cap0, &cap4); + clflush_size = ((misc >> 8) & 0xff) * 8; + driver->has_clflush = 1; + driver->clflush_add = + PAGE_SIZE * clflush_size / sizeof(uint32_t); + driver->clflush_mask = driver->clflush_add - 1; + driver->clflush_mask = ~driver->clflush_mask; + } + + up_write(&driver->sem); + return driver; + +out_err1: + kfree(driver); + return NULL; +} + +static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd, + unsigned long address, uint32_t num_pages, + uint32_t desired_tile_stride, + uint32_t hw_tile_stride) +{ + struct psb_mmu_pt *pt; + uint32_t rows = 1; + uint32_t i; + unsigned long addr; + unsigned long end; + unsigned long next; + unsigned long add; + unsigned long row_add; + unsigned long clflush_add = pd->driver->clflush_add; + unsigned long clflush_mask = pd->driver->clflush_mask; + + if (!pd->driver->has_clflush) { + /*ttm_tt_cache_flush(&pd->p, num_pages);*/ + psb_pages_clflush(pd->driver, &pd->p, num_pages); + return; + } + + if (hw_tile_stride) + rows = num_pages / desired_tile_stride; + else + desired_tile_stride = num_pages; + + add = desired_tile_stride << PAGE_SHIFT; + row_add = hw_tile_stride << PAGE_SHIFT; + mb(); + for (i = 0; i < rows; ++i) { + + addr = address; + end = addr + add; + + do { + next = psb_pd_addr_end(addr, end); + pt = psb_mmu_pt_map_lock(pd, addr); + if (!pt) + continue; + do { + psb_clflush(&pt->v + [psb_mmu_pt_index(addr)]); + } while (addr += + clflush_add, + (addr & clflush_mask) < next); + + psb_mmu_pt_unmap_unlock(pt); + } while (addr = next, next != end); + address += row_add; + } + mb(); +} + +void psb_mmu_remove_pfn_sequence(struct psb_mmu_pd *pd, + unsigned long address, uint32_t num_pages) +{ + struct psb_mmu_pt *pt; + unsigned long addr; + unsigned long end; + unsigned long next; + unsigned long f_address = address; + + down_read(&pd->driver->sem); + + addr = address; + end = addr + (num_pages << PAGE_SHIFT); + + do { + next = psb_pd_addr_end(addr, end); + pt = psb_mmu_pt_alloc_map_lock(pd, addr); + if (!pt) + goto out; + do { + psb_mmu_invalidate_pte(pt, addr); + --pt->count; + } while (addr += PAGE_SIZE, addr < next); + psb_mmu_pt_unmap_unlock(pt); + + } while (addr = next, next != end); + +out: + if (pd->hw_context != -1) + psb_mmu_flush_ptes(pd, f_address, num_pages, 1, 1); + + up_read(&pd->driver->sem); + + if (pd->hw_context != -1) + psb_mmu_flush(pd->driver, 0); + + return; +} + +void psb_mmu_remove_pages(struct psb_mmu_pd *pd, unsigned long address, + uint32_t num_pages, uint32_t desired_tile_stride, + uint32_t hw_tile_stride) +{ + struct psb_mmu_pt *pt; + uint32_t rows = 1; + uint32_t i; + unsigned long addr; + unsigned long end; + unsigned long next; + unsigned long add; + unsigned long row_add; + unsigned long f_address = address; + + if (hw_tile_stride) + rows = num_pages / desired_tile_stride; + else + desired_tile_stride = num_pages; + + add = desired_tile_stride << PAGE_SHIFT; + row_add = hw_tile_stride << PAGE_SHIFT; + + /* down_read(&pd->driver->sem); */ + + /* Make sure we only need to flush this processor's cache */ + + for (i = 0; i < rows; ++i) { + + addr = address; + end = addr + add; + + do { + next = psb_pd_addr_end(addr, end); + pt = psb_mmu_pt_map_lock(pd, addr); + if (!pt) + continue; + do { + psb_mmu_invalidate_pte(pt, addr); + --pt->count; + + } while (addr += PAGE_SIZE, addr < next); + psb_mmu_pt_unmap_unlock(pt); + + } while (addr = next, next != end); + address += row_add; + } + if (pd->hw_context != -1) + psb_mmu_flush_ptes(pd, f_address, num_pages, + desired_tile_stride, hw_tile_stride); + + /* up_read(&pd->driver->sem); */ + + if (pd->hw_context != -1) + psb_mmu_flush(pd->driver, 0); +} + +int psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd, uint32_t start_pfn, + unsigned long address, uint32_t num_pages, + int type) +{ + struct psb_mmu_pt *pt; + uint32_t pte; + unsigned long addr; + unsigned long end; + unsigned long next; + unsigned long f_address = address; + int ret = 0; + + down_read(&pd->driver->sem); + + addr = address; + end = addr + (num_pages << PAGE_SHIFT); + + do { + next = psb_pd_addr_end(addr, end); + pt = psb_mmu_pt_alloc_map_lock(pd, addr); + if (!pt) { + ret = -ENOMEM; + goto out; + } + do { + pte = psb_mmu_mask_pte(start_pfn++, type); + psb_mmu_set_pte(pt, addr, pte); + pt->count++; + } while (addr += PAGE_SIZE, addr < next); + psb_mmu_pt_unmap_unlock(pt); + + } while (addr = next, next != end); + +out: + if (pd->hw_context != -1) + psb_mmu_flush_ptes(pd, f_address, num_pages, 1, 1); + + up_read(&pd->driver->sem); + + if (pd->hw_context != -1) + psb_mmu_flush(pd->driver, 1); + + return ret; +} + +int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages, + unsigned long address, uint32_t num_pages, + uint32_t desired_tile_stride, + uint32_t hw_tile_stride, int type) +{ + struct psb_mmu_pt *pt; + uint32_t rows = 1; + uint32_t i; + uint32_t pte; + unsigned long addr; + unsigned long end; + unsigned long next; + unsigned long add; + unsigned long row_add; + unsigned long f_address = address; + int ret = 0; + + if (hw_tile_stride) { + if (num_pages % desired_tile_stride != 0) + return -EINVAL; + rows = num_pages / desired_tile_stride; + } else { + desired_tile_stride = num_pages; + } + + add = desired_tile_stride << PAGE_SHIFT; + row_add = hw_tile_stride << PAGE_SHIFT; + + down_read(&pd->driver->sem); + + for (i = 0; i < rows; ++i) { + + addr = address; + end = addr + add; + + do { + next = psb_pd_addr_end(addr, end); + pt = psb_mmu_pt_alloc_map_lock(pd, addr); + if (!pt) { + ret = -ENOMEM; + goto out; + } + do { + pte = + psb_mmu_mask_pte(page_to_pfn(*pages++), + type); + psb_mmu_set_pte(pt, addr, pte); + pt->count++; + } while (addr += PAGE_SIZE, addr < next); + psb_mmu_pt_unmap_unlock(pt); + + } while (addr = next, next != end); + + address += row_add; + } +out: + if (pd->hw_context != -1) + psb_mmu_flush_ptes(pd, f_address, num_pages, + desired_tile_stride, hw_tile_stride); + + up_read(&pd->driver->sem); + + if (pd->hw_context != -1) + psb_mmu_flush(pd->driver, 1); + + return ret; +} + +int psb_mmu_virtual_to_pfn(struct psb_mmu_pd *pd, uint32_t virtual, + unsigned long *pfn) +{ + int ret; + struct psb_mmu_pt *pt; + uint32_t tmp; + spinlock_t *lock = &pd->driver->lock; + + down_read(&pd->driver->sem); + pt = psb_mmu_pt_map_lock(pd, virtual); + if (!pt) { + uint32_t *v; + + spin_lock(lock); + v = kmap_atomic(pd->p, KM_USER0); + tmp = v[psb_mmu_pd_index(virtual)]; + kunmap_atomic(v, KM_USER0); + spin_unlock(lock); + + if (tmp != pd->invalid_pde || !(tmp & PSB_PTE_VALID) || + !(pd->invalid_pte & PSB_PTE_VALID)) { + ret = -EINVAL; + goto out; + } + ret = 0; + *pfn = pd->invalid_pte >> PAGE_SHIFT; + goto out; + } + tmp = pt->v[psb_mmu_pt_index(virtual)]; + if (!(tmp & PSB_PTE_VALID)) { + ret = -EINVAL; + } else { + ret = 0; + *pfn = tmp >> PAGE_SHIFT; + } + psb_mmu_pt_unmap_unlock(pt); +out: + up_read(&pd->driver->sem); + return ret; +} diff --git a/trunk/drivers/staging/gma500/mrst.h b/trunk/drivers/staging/gma500/mrst.h new file mode 100644 index 000000000000..b563dbc73104 --- /dev/null +++ b/trunk/drivers/staging/gma500/mrst.h @@ -0,0 +1,252 @@ +/************************************************************************** + * Copyright (c) 2007-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. + * + **************************************************************************/ + +/* MID device specific descriptors */ + +struct mrst_vbt { + s8 signature[4]; /*4 bytes,"$GCT" */ + u8 revision; + u8 size; + u8 checksum; + void *mrst_gct; +} __packed; + +struct mrst_timing_info { + u16 pixel_clock; + u8 hactive_lo; + u8 hblank_lo; + u8 hblank_hi:4; + u8 hactive_hi:4; + u8 vactive_lo; + u8 vblank_lo; + u8 vblank_hi:4; + u8 vactive_hi:4; + u8 hsync_offset_lo; + u8 hsync_pulse_width_lo; + u8 vsync_pulse_width_lo:4; + u8 vsync_offset_lo:4; + u8 vsync_pulse_width_hi:2; + u8 vsync_offset_hi:2; + u8 hsync_pulse_width_hi:2; + u8 hsync_offset_hi:2; + u8 width_mm_lo; + u8 height_mm_lo; + u8 height_mm_hi:4; + u8 width_mm_hi:4; + u8 hborder; + u8 vborder; + u8 unknown0:1; + u8 hsync_positive:1; + u8 vsync_positive:1; + u8 separate_sync:2; + u8 stereo:1; + u8 unknown6:1; + u8 interlaced:1; +} __packed; + +struct gct_r10_timing_info { + u16 pixel_clock; + u32 hactive_lo:8; + u32 hactive_hi:4; + u32 hblank_lo:8; + u32 hblank_hi:4; + u32 hsync_offset_lo:8; + u16 hsync_offset_hi:2; + u16 hsync_pulse_width_lo:8; + u16 hsync_pulse_width_hi:2; + u16 hsync_positive:1; + u16 rsvd_1:3; + u8 vactive_lo:8; + u16 vactive_hi:4; + u16 vblank_lo:8; + u16 vblank_hi:4; + u16 vsync_offset_lo:4; + u16 vsync_offset_hi:2; + u16 vsync_pulse_width_lo:4; + u16 vsync_pulse_width_hi:2; + u16 vsync_positive:1; + u16 rsvd_2:3; +} __packed; + +struct mrst_panel_descriptor_v1 { + u32 Panel_Port_Control; /* 1 dword, Register 0x61180 if LVDS */ + /* 0x61190 if MIPI */ + u32 Panel_Power_On_Sequencing;/*1 dword,Register 0x61208,*/ + u32 Panel_Power_Off_Sequencing;/*1 dword,Register 0x6120C,*/ + u32 Panel_Power_Cycle_Delay_and_Reference_Divisor;/* 1 dword */ + /* Register 0x61210 */ + struct mrst_timing_info DTD;/*18 bytes, Standard definition */ + u16 Panel_Backlight_Inverter_Descriptor;/* 16 bits, as follows */ + /* Bit 0, Frequency, 15 bits,0 - 32767Hz */ + /* Bit 15, Polarity, 1 bit, 0: Normal, 1: Inverted */ + u16 Panel_MIPI_Display_Descriptor; + /*16 bits, Defined as follows: */ + /* if MIPI, 0x0000 if LVDS */ + /* Bit 0, Type, 2 bits, */ + /* 0: Type-1, */ + /* 1: Type-2, */ + /* 2: Type-3, */ + /* 3: Type-4 */ + /* Bit 2, Pixel Format, 4 bits */ + /* Bit0: 16bpp (not supported in LNC), */ + /* Bit1: 18bpp loosely packed, */ + /* Bit2: 18bpp packed, */ + /* Bit3: 24bpp */ + /* Bit 6, Reserved, 2 bits, 00b */ + /* Bit 8, Minimum Supported Frame Rate, 6 bits, 0 - 63Hz */ + /* Bit 14, Reserved, 2 bits, 00b */ +} __packed; + +struct mrst_panel_descriptor_v2 { + u32 Panel_Port_Control; /* 1 dword, Register 0x61180 if LVDS */ + /* 0x61190 if MIPI */ + u32 Panel_Power_On_Sequencing;/*1 dword,Register 0x61208,*/ + u32 Panel_Power_Off_Sequencing;/*1 dword,Register 0x6120C,*/ + u8 Panel_Power_Cycle_Delay_and_Reference_Divisor;/* 1 byte */ + /* Register 0x61210 */ + struct mrst_timing_info DTD;/*18 bytes, Standard definition */ + u16 Panel_Backlight_Inverter_Descriptor;/*16 bits, as follows*/ + /*Bit 0, Frequency, 16 bits, 0 - 32767Hz*/ + u8 Panel_Initial_Brightness;/* [7:0] 0 - 100% */ + /*Bit 7, Polarity, 1 bit,0: Normal, 1: Inverted*/ + u16 Panel_MIPI_Display_Descriptor; + /*16 bits, Defined as follows: */ + /* if MIPI, 0x0000 if LVDS */ + /* Bit 0, Type, 2 bits, */ + /* 0: Type-1, */ + /* 1: Type-2, */ + /* 2: Type-3, */ + /* 3: Type-4 */ + /* Bit 2, Pixel Format, 4 bits */ + /* Bit0: 16bpp (not supported in LNC), */ + /* Bit1: 18bpp loosely packed, */ + /* Bit2: 18bpp packed, */ + /* Bit3: 24bpp */ + /* Bit 6, Reserved, 2 bits, 00b */ + /* Bit 8, Minimum Supported Frame Rate, 6 bits, 0 - 63Hz */ + /* Bit 14, Reserved, 2 bits, 00b */ +} __packed; + +union mrst_panel_rx { + struct { + u16 NumberOfLanes:2; /*Num of Lanes, 2 bits,0 = 1 lane,*/ + /* 1 = 2 lanes, 2 = 3 lanes, 3 = 4 lanes. */ + u16 MaxLaneFreq:3; /* 0: 100MHz, 1: 200MHz, 2: 300MHz, */ + /*3: 400MHz, 4: 500MHz, 5: 600MHz, 6: 700MHz, 7: 800MHz.*/ + u16 SupportedVideoTransferMode:2; /*0: Non-burst only */ + /* 1: Burst and non-burst */ + /* 2/3: Reserved */ + u16 HSClkBehavior:1; /*0: Continuous, 1: Non-continuous*/ + u16 DuoDisplaySupport:1; /*1 bit,0: No, 1: Yes*/ + u16 ECC_ChecksumCapabilities:1;/*1 bit,0: No, 1: Yes*/ + u16 BidirectionalCommunication:1;/*1 bit,0: No, 1: Yes */ + u16 Rsvd:5;/*5 bits,00000b */ + } panelrx; + u16 panel_receiver; +} __packed; + +struct mrst_gct_v1 { + union { /*8 bits,Defined as follows: */ + struct { + u8 PanelType:4; /*4 bits, Bit field for panels*/ + /* 0 - 3: 0 = LVDS, 1 = MIPI*/ + /*2 bits,Specifies which of the*/ + u8 BootPanelIndex:2; + /* 4 panels to use by default*/ + u8 BootMIPI_DSI_RxIndex:2;/*Specifies which of*/ + /* the 4 MIPI DSI receivers to use*/ + } PD; + u8 PanelDescriptor; + }; + struct mrst_panel_descriptor_v1 panel[4];/*panel descrs,38 bytes each*/ + union mrst_panel_rx panelrx[4]; /* panel receivers*/ +} __packed; + +struct mrst_gct_v2 { + union { /*8 bits,Defined as follows: */ + struct { + u8 PanelType:4; /*4 bits, Bit field for panels*/ + /* 0 - 3: 0 = LVDS, 1 = MIPI*/ + /*2 bits,Specifies which of the*/ + u8 BootPanelIndex:2; + /* 4 panels to use by default*/ + u8 BootMIPI_DSI_RxIndex:2;/*Specifies which of*/ + /* the 4 MIPI DSI receivers to use*/ + } PD; + u8 PanelDescriptor; + }; + struct mrst_panel_descriptor_v2 panel[4];/*panel descrs,38 bytes each*/ + union mrst_panel_rx panelrx[4]; /* panel receivers*/ +} __packed; + +struct mrst_gct_data { + u8 bpi; /* boot panel index, number of panel used during boot */ + u8 pt; /* panel type, 4 bit field, 0=lvds, 1=mipi */ + struct mrst_timing_info DTD; /* timing info for the selected panel */ + u32 Panel_Port_Control; + u32 PP_On_Sequencing;/*1 dword,Register 0x61208,*/ + u32 PP_Off_Sequencing;/*1 dword,Register 0x6120C,*/ + u32 PP_Cycle_Delay; + u16 Panel_Backlight_Inverter_Descriptor; + u16 Panel_MIPI_Display_Descriptor; +} __packed; + +#define MODE_SETTING_IN_CRTC 0x1 +#define MODE_SETTING_IN_ENCODER 0x2 +#define MODE_SETTING_ON_GOING 0x3 +#define MODE_SETTING_IN_DSR 0x4 +#define MODE_SETTING_ENCODER_DONE 0x8 + +#define GCT_R10_HEADER_SIZE 16 +#define GCT_R10_DISPLAY_DESC_SIZE 28 + +/* + * Moorestown HDMI interfaces + */ + +struct mrst_hdmi_dev { + struct pci_dev *dev; + void __iomem *regs; + unsigned int mmio, mmio_len; + int dpms_mode; + struct hdmi_i2c_dev *i2c_dev; + + /* register state */ + u32 saveDPLL_CTRL; + u32 saveDPLL_DIV_CTRL; + u32 saveDPLL_ADJUST; + u32 saveDPLL_UPDATE; + u32 saveDPLL_CLK_ENABLE; + u32 savePCH_HTOTAL_B; + u32 savePCH_HBLANK_B; + u32 savePCH_HSYNC_B; + u32 savePCH_VTOTAL_B; + u32 savePCH_VBLANK_B; + u32 savePCH_VSYNC_B; + u32 savePCH_PIPEBCONF; + u32 savePCH_PIPEBSRC; +}; + +extern void mrst_hdmi_setup(struct drm_device *dev); +extern void mrst_hdmi_teardown(struct drm_device *dev); +extern int mrst_hdmi_i2c_init(struct pci_dev *dev); +extern void mrst_hdmi_i2c_exit(struct pci_dev *dev); +extern void mrst_hdmi_save(struct drm_device *dev); +extern void mrst_hdmi_restore(struct drm_device *dev); +extern void mrst_hdmi_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev); diff --git a/trunk/drivers/staging/gma500/mrst_crtc.c b/trunk/drivers/staging/gma500/mrst_crtc.c new file mode 100644 index 000000000000..980837e37d80 --- /dev/null +++ b/trunk/drivers/staging/gma500/mrst_crtc.c @@ -0,0 +1,604 @@ +/* + * Copyright © 2009 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * 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 +#include + +#include +#include "framebuffer.h" +#include "psb_drv.h" +#include "psb_intel_drv.h" +#include "psb_intel_reg.h" +#include "psb_intel_display.h" +#include "power.h" + +struct psb_intel_range_t { + int min, max; +}; + +struct mrst_limit_t { + struct psb_intel_range_t dot, m, p1; +}; + +struct mrst_clock_t { + /* derived values */ + int dot; + int m; + int p1; +}; + +#define MRST_LIMIT_LVDS_100L 0 +#define MRST_LIMIT_LVDS_83 1 +#define MRST_LIMIT_LVDS_100 2 + +#define MRST_DOT_MIN 19750 +#define MRST_DOT_MAX 120000 +#define MRST_M_MIN_100L 20 +#define MRST_M_MIN_100 10 +#define MRST_M_MIN_83 12 +#define MRST_M_MAX_100L 34 +#define MRST_M_MAX_100 17 +#define MRST_M_MAX_83 20 +#define MRST_P1_MIN 2 +#define MRST_P1_MAX_0 7 +#define MRST_P1_MAX_1 8 + +static const struct mrst_limit_t mrst_limits[] = { + { /* MRST_LIMIT_LVDS_100L */ + .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX}, + .m = {.min = MRST_M_MIN_100L, .max = MRST_M_MAX_100L}, + .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_1}, + }, + { /* MRST_LIMIT_LVDS_83L */ + .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX}, + .m = {.min = MRST_M_MIN_83, .max = MRST_M_MAX_83}, + .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_0}, + }, + { /* MRST_LIMIT_LVDS_100 */ + .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX}, + .m = {.min = MRST_M_MIN_100, .max = MRST_M_MAX_100}, + .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_1}, + }, +}; + +#define MRST_M_MIN 10 +static const u32 mrst_m_converts[] = { + 0x2B, 0x15, 0x2A, 0x35, 0x1A, 0x0D, 0x26, 0x33, 0x19, 0x2C, + 0x36, 0x3B, 0x1D, 0x2E, 0x37, 0x1B, 0x2D, 0x16, 0x0B, 0x25, + 0x12, 0x09, 0x24, 0x32, 0x39, 0x1c, +}; + +static const struct mrst_limit_t *mrst_limit(struct drm_crtc *crtc) +{ + const struct mrst_limit_t *limit = NULL; + struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + + if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) + || psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_MIPI)) { + switch (dev_priv->core_freq) { + case 100: + limit = &mrst_limits[MRST_LIMIT_LVDS_100L]; + break; + case 166: + limit = &mrst_limits[MRST_LIMIT_LVDS_83]; + break; + case 200: + limit = &mrst_limits[MRST_LIMIT_LVDS_100]; + break; + } + } else { + limit = NULL; + dev_err(dev->dev, "mrst_limit Wrong display type.\n"); + } + + return limit; +} + +/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */ +static void mrst_clock(int refclk, struct mrst_clock_t *clock) +{ + clock->dot = (refclk * clock->m) / (14 * clock->p1); +} + +void mrstPrintPll(char *prefix, struct mrst_clock_t *clock) +{ + pr_debug("%s: dotclock = %d, m = %d, p1 = %d.\n", + prefix, clock->dot, clock->m, clock->p1); +} + +/** + * Returns a set of divisors for the desired target clock with the given refclk, + * or FALSE. Divisor values are the actual divisors for + */ +static bool +mrstFindBestPLL(struct drm_crtc *crtc, int target, int refclk, + struct mrst_clock_t *best_clock) +{ + struct mrst_clock_t clock; + const struct mrst_limit_t *limit = mrst_limit(crtc); + int err = target; + + memset(best_clock, 0, sizeof(*best_clock)); + + for (clock.m = limit->m.min; clock.m <= limit->m.max; clock.m++) { + for (clock.p1 = limit->p1.min; clock.p1 <= limit->p1.max; + clock.p1++) { + int this_err; + + mrst_clock(refclk, &clock); + + this_err = abs(clock.dot - target); + if (this_err < err) { + *best_clock = clock; + err = this_err; + } + } + } + dev_dbg(crtc->dev->dev, "mrstFindBestPLL err = %d.\n", err); + return err != target; +} + +/** + * Sets the power management mode of the pipe and plane. + * + * This code should probably grow support for turning the cursor off and back + * on appropriately at the same time as we're turning the pipe off/on. + */ +static void mrst_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct drm_device *dev = crtc->dev; + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + int pipe = psb_intel_crtc->pipe; + int dpll_reg = (pipe == 0) ? MRST_DPLL_A : DPLL_B; + int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; + int dspbase_reg = (pipe == 0) ? MRST_DSPABASE : DSPBBASE; + int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; + u32 temp; + bool enabled; + + if (!gma_power_begin(dev, true)) + return; + + /* 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: + /* Enable the DPLL */ + temp = REG_READ(dpll_reg); + if ((temp & DPLL_VCO_ENABLE) == 0) { + REG_WRITE(dpll_reg, temp); + REG_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + udelay(150); + REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); + REG_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + udelay(150); + REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); + REG_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + udelay(150); + } + /* Enable the pipe */ + temp = REG_READ(pipeconf_reg); + if ((temp & PIPEACONF_ENABLE) == 0) + REG_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE); + /* Enable the plane */ + temp = REG_READ(dspcntr_reg); + if ((temp & DISPLAY_PLANE_ENABLE) == 0) { + REG_WRITE(dspcntr_reg, + temp | DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + } + + psb_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 */ + break; + case DRM_MODE_DPMS_OFF: + /* Give the overlay scaler a chance to disable + * if it's on this pipe */ + /* psb_intel_crtc_dpms_video(crtc, FALSE); TODO */ + + /* Disable the VGA plane that we never use */ + REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); + /* Disable display plane */ + temp = REG_READ(dspcntr_reg); + if ((temp & DISPLAY_PLANE_ENABLE) != 0) { + REG_WRITE(dspcntr_reg, + temp & ~DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + REG_READ(dspbase_reg); + } + + /* Next, disable display pipes */ + temp = REG_READ(pipeconf_reg); + if ((temp & PIPEACONF_ENABLE) != 0) { + REG_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE); + REG_READ(pipeconf_reg); + } + /* Wait for for the pipe disable to take effect. */ + psb_intel_wait_for_vblank(dev); + + temp = REG_READ(dpll_reg); + if ((temp & DPLL_VCO_ENABLE) != 0) { + REG_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE); + REG_READ(dpll_reg); + } + + /* Wait for the clocks to turn off. */ + udelay(150); + break; + } + + enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF; + + /*Set FIFO Watermarks*/ + REG_WRITE(DSPARB, 0x3FFF); + REG_WRITE(DSPFW1, 0x3F88080A); + REG_WRITE(DSPFW2, 0x0b060808); + REG_WRITE(DSPFW3, 0x0); + REG_WRITE(DSPFW4, 0x08030404); + REG_WRITE(DSPFW5, 0x04040404); + REG_WRITE(DSPFW6, 0x78); + REG_WRITE(0x70400, REG_READ(0x70400) | 0x4000); + /* Must write Bit 14 of the Chicken Bit Register */ + + gma_power_end(dev); +} + +/** + * Return the pipe currently connected to the panel fitter, + * or -1 if the panel fitter is not present or not in use + */ +static int mrst_panel_fitter_pipe(struct drm_device *dev) +{ + u32 pfit_control; + + pfit_control = REG_READ(PFIT_CONTROL); + + /* See if the panel fitter is in use */ + if ((pfit_control & PFIT_ENABLE) == 0) + return -1; + return (pfit_control >> 29) & 3; +} + +static int mrst_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, + struct drm_framebuffer *old_fb) +{ + struct drm_device *dev = crtc->dev; + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + struct drm_psb_private *dev_priv = dev->dev_private; + int pipe = psb_intel_crtc->pipe; + int fp_reg = (pipe == 0) ? MRST_FPA0 : FPB0; + int dpll_reg = (pipe == 0) ? MRST_DPLL_A : DPLL_B; + int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; + int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; + int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; + int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; + int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; + int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B; + int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B; + int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B; + int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; + int refclk = 0; + struct mrst_clock_t clock; + u32 dpll = 0, fp = 0, dspcntr, pipeconf; + bool ok, is_sdvo = false; + bool is_crt = false, is_lvds = false, is_tv = false; + bool is_mipi = false; + struct drm_mode_config *mode_config = &dev->mode_config; + struct psb_intel_output *psb_intel_output = NULL; + uint64_t scalingType = DRM_MODE_SCALE_FULLSCREEN; + struct drm_encoder *encoder; + + if (!gma_power_begin(dev, true)) + return 0; + + memcpy(&psb_intel_crtc->saved_mode, + mode, + sizeof(struct drm_display_mode)); + memcpy(&psb_intel_crtc->saved_adjusted_mode, + adjusted_mode, + sizeof(struct drm_display_mode)); + + list_for_each_entry(encoder, &mode_config->encoder_list, head) { + + if (encoder->crtc != crtc) + continue; + + psb_intel_output = enc_to_psb_intel_output(encoder); + switch (psb_intel_output->type) { + case INTEL_OUTPUT_LVDS: + is_lvds = true; + break; + case INTEL_OUTPUT_SDVO: + is_sdvo = true; + break; + case INTEL_OUTPUT_TVOUT: + is_tv = true; + break; + case INTEL_OUTPUT_ANALOG: + is_crt = true; + break; + case INTEL_OUTPUT_MIPI: + is_mipi = true; + break; + } + } + + /* Disable the VGA plane that we never use */ + REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); + + /* Disable the panel fitter if it was on our pipe */ + if (mrst_panel_fitter_pipe(dev) == pipe) + REG_WRITE(PFIT_CONTROL, 0); + + REG_WRITE(pipesrc_reg, + ((mode->crtc_hdisplay - 1) << 16) | + (mode->crtc_vdisplay - 1)); + + if (psb_intel_output) + drm_connector_property_get_value(&psb_intel_output->base, + dev->mode_config.scaling_mode_property, &scalingType); + + if (scalingType == DRM_MODE_SCALE_NO_SCALE) { + /* Moorestown doesn't have register support for centering so + * we need to mess with the h/vblank and h/vsync start and + * ends to get centering */ + int offsetX = 0, offsetY = 0; + + offsetX = (adjusted_mode->crtc_hdisplay - + mode->crtc_hdisplay) / 2; + offsetY = (adjusted_mode->crtc_vdisplay - + mode->crtc_vdisplay) / 2; + + REG_WRITE(htot_reg, (mode->crtc_hdisplay - 1) | + ((adjusted_mode->crtc_htotal - 1) << 16)); + REG_WRITE(vtot_reg, (mode->crtc_vdisplay - 1) | + ((adjusted_mode->crtc_vtotal - 1) << 16)); + REG_WRITE(hblank_reg, + (adjusted_mode->crtc_hblank_start - offsetX - 1) | + ((adjusted_mode->crtc_hblank_end - offsetX - 1) << 16)); + REG_WRITE(hsync_reg, + (adjusted_mode->crtc_hsync_start - offsetX - 1) | + ((adjusted_mode->crtc_hsync_end - offsetX - 1) << 16)); + REG_WRITE(vblank_reg, + (adjusted_mode->crtc_vblank_start - offsetY - 1) | + ((adjusted_mode->crtc_vblank_end - offsetY - 1) << 16)); + REG_WRITE(vsync_reg, + (adjusted_mode->crtc_vsync_start - offsetY - 1) | + ((adjusted_mode->crtc_vsync_end - offsetY - 1) << 16)); + } else { + REG_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) | + ((adjusted_mode->crtc_htotal - 1) << 16)); + REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | + ((adjusted_mode->crtc_vtotal - 1) << 16)); + REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | + ((adjusted_mode->crtc_hblank_end - 1) << 16)); + REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | + ((adjusted_mode->crtc_hsync_end - 1) << 16)); + REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | + ((adjusted_mode->crtc_vblank_end - 1) << 16)); + REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | + ((adjusted_mode->crtc_vsync_end - 1) << 16)); + } + + /* Flush the plane changes */ + { + struct drm_crtc_helper_funcs *crtc_funcs = + crtc->helper_private; + crtc_funcs->mode_set_base(crtc, x, y, old_fb); + } + + /* setup pipeconf */ + pipeconf = REG_READ(pipeconf_reg); + + /* Set up the display plane register */ + dspcntr = REG_READ(dspcntr_reg); + dspcntr |= DISPPLANE_GAMMA_ENABLE; + + if (pipe == 0) + dspcntr |= DISPPLANE_SEL_PIPE_A; + else + dspcntr |= DISPPLANE_SEL_PIPE_B; + + dev_priv->dspcntr = dspcntr |= DISPLAY_PLANE_ENABLE; + dev_priv->pipeconf = pipeconf |= PIPEACONF_ENABLE; + + if (is_mipi) + goto mrst_crtc_mode_set_exit; + + refclk = dev_priv->core_freq * 1000; + + dpll = 0; /*BIT16 = 0 for 100MHz reference */ + + ok = mrstFindBestPLL(crtc, adjusted_mode->clock, refclk, &clock); + + if (!ok) { + dev_dbg(dev->dev, "mrstFindBestPLL fail in mrst_crtc_mode_set.\n"); + } else { + dev_dbg(dev->dev, "mrst_crtc_mode_set pixel clock = %d," + "m = %x, p1 = %x.\n", clock.dot, clock.m, + clock.p1); + } + + fp = mrst_m_converts[(clock.m - MRST_M_MIN)] << 8; + + dpll |= DPLL_VGA_MODE_DIS; + + + dpll |= DPLL_VCO_ENABLE; + + if (is_lvds) + dpll |= DPLLA_MODE_LVDS; + else + dpll |= DPLLB_MODE_DAC_SERIAL; + + if (is_sdvo) { + int sdvo_pixel_multiply = + adjusted_mode->clock / mode->clock; + + dpll |= DPLL_DVO_HIGH_SPEED; + dpll |= + (sdvo_pixel_multiply - + 1) << SDVO_MULTIPLIER_SHIFT_HIRES; + } + + + /* compute bitmask from p1 value */ + dpll |= (1 << (clock.p1 - 2)) << 17; + + dpll |= DPLL_VCO_ENABLE; + + mrstPrintPll("chosen", &clock); + + if (dpll & DPLL_VCO_ENABLE) { + REG_WRITE(fp_reg, fp); + REG_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE); + REG_READ(dpll_reg); + /* Check the DPLLA lock bit PIPEACONF[29] */ + udelay(150); + } + + REG_WRITE(fp_reg, fp); + REG_WRITE(dpll_reg, dpll); + REG_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + udelay(150); + + /* write it again -- the BIOS does, after all */ + REG_WRITE(dpll_reg, dpll); + REG_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + udelay(150); + + REG_WRITE(pipeconf_reg, pipeconf); + REG_READ(pipeconf_reg); + psb_intel_wait_for_vblank(dev); + + REG_WRITE(dspcntr_reg, dspcntr); + psb_intel_wait_for_vblank(dev); + +mrst_crtc_mode_set_exit: + gma_power_end(dev); + return 0; +} + +static bool mrst_crtc_mode_fixup(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +int mrst_pipe_set_base(struct drm_crtc *crtc, + int x, int y, struct drm_framebuffer *old_fb) +{ + struct drm_device *dev = crtc->dev; + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb); + int pipe = psb_intel_crtc->pipe; + unsigned long start, offset; + + int dspbase = (pipe == 0 ? DSPALINOFF : DSPBBASE); + int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF); + int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE; + int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; + u32 dspcntr; + int ret = 0; + + /* no fb bound */ + if (!crtc->fb) { + dev_dbg(dev->dev, "No FB bound\n"); + return 0; + } + + if (!gma_power_begin(dev, true)) + return 0; + + start = psbfb->gtt->offset; + offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8); + + REG_WRITE(dspstride, crtc->fb->pitches[0]); + + dspcntr = REG_READ(dspcntr_reg); + dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; + + switch (crtc->fb->bits_per_pixel) { + case 8: + dspcntr |= DISPPLANE_8BPP; + break; + case 16: + if (crtc->fb->depth == 15) + dspcntr |= DISPPLANE_15_16BPP; + else + dspcntr |= DISPPLANE_16BPP; + break; + case 24: + case 32: + dspcntr |= DISPPLANE_32BPP_NO_ALPHA; + break; + default: + dev_err(dev->dev, "Unknown color depth\n"); + ret = -EINVAL; + goto pipe_set_base_exit; + } + REG_WRITE(dspcntr_reg, dspcntr); + + REG_WRITE(dspbase, offset); + REG_READ(dspbase); + REG_WRITE(dspsurf, start); + REG_READ(dspsurf); + +pipe_set_base_exit: + gma_power_end(dev); + return ret; +} + +static void mrst_crtc_prepare(struct drm_crtc *crtc) +{ + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); +} + +static void mrst_crtc_commit(struct drm_crtc *crtc) +{ + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); +} + +const struct drm_crtc_helper_funcs mrst_helper_funcs = { + .dpms = mrst_crtc_dpms, + .mode_fixup = mrst_crtc_mode_fixup, + .mode_set = mrst_crtc_mode_set, + .mode_set_base = mrst_pipe_set_base, + .prepare = mrst_crtc_prepare, + .commit = mrst_crtc_commit, +}; + diff --git a/trunk/drivers/staging/gma500/mrst_device.c b/trunk/drivers/staging/gma500/mrst_device.c new file mode 100644 index 000000000000..6707fafbfa1e --- /dev/null +++ b/trunk/drivers/staging/gma500/mrst_device.c @@ -0,0 +1,634 @@ +/************************************************************************** + * 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 +#include +#include +#include +#include +#include "psb_drm.h" +#include "psb_drv.h" +#include "psb_reg.h" +#include "psb_intel_reg.h" +#include +#include +#include "mid_bios.h" + +static int devtype; + +module_param_named(type, devtype, int, 0600); +MODULE_PARM_DESC(type, "Moorestown/Oaktrail device type"); + +#define DEVICE_MOORESTOWN 1 +#define DEVICE_OAKTRAIL 2 +#define DEVICE_MOORESTOWN_MM 3 + +static int mrst_device_ident(struct drm_device *dev) +{ + /* User forced */ + if (devtype) + return devtype; + if (dmi_match(DMI_PRODUCT_NAME, "OakTrail") || + dmi_match(DMI_PRODUCT_NAME, "OakTrail platform")) + return DEVICE_OAKTRAIL; +#if defined(CONFIG_X86_MRST) + if (dmi_match(DMI_PRODUCT_NAME, "MM") || + dmi_match(DMI_PRODUCT_NAME, "MM 10")) + return DEVICE_MOORESTOWN_MM; + if (mrst_identify_cpu()) + return DEVICE_MOORESTOWN; +#endif + return DEVICE_OAKTRAIL; +} + + +/* IPC message and command defines used to enable/disable mipi panel voltages */ +#define IPC_MSG_PANEL_ON_OFF 0xE9 +#define IPC_CMD_PANEL_ON 1 +#define IPC_CMD_PANEL_OFF 0 + +static int mrst_output_init(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + if (dev_priv->iLVDS_enable) + mrst_lvds_init(dev, &dev_priv->mode_dev); + else + dev_err(dev->dev, "DSI is not supported\n"); + if (dev_priv->hdmi_priv) + mrst_hdmi_init(dev, &dev_priv->mode_dev); + return 0; +} + +/* + * Provide the low level interfaces for the Moorestown backlight + */ + +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + +#define MRST_BLC_MAX_PWM_REG_FREQ 0xFFFF +#define BLC_PWM_PRECISION_FACTOR 100 /* 10000000 */ +#define BLC_PWM_FREQ_CALC_CONSTANT 32 +#define MHz 1000000 +#define BLC_ADJUSTMENT_MAX 100 + +static struct backlight_device *mrst_backlight_device; +static int mrst_brightness; + +static int mrst_set_brightness(struct backlight_device *bd) +{ + struct drm_device *dev = bl_get_data(mrst_backlight_device); + struct drm_psb_private *dev_priv = dev->dev_private; + int level = bd->props.brightness; + u32 blc_pwm_ctl; + u32 max_pwm_blc; + + /* Percentage 1-100% being valid */ + if (level < 1) + level = 1; + + if (gma_power_begin(dev, 0)) { + /* Calculate and set the brightness value */ + max_pwm_blc = REG_READ(BLC_PWM_CTL) >> 16; + blc_pwm_ctl = level * max_pwm_blc / 100; + + /* Adjust the backlight level with the percent in + * dev_priv->blc_adj1; + */ + blc_pwm_ctl = blc_pwm_ctl * dev_priv->blc_adj1; + blc_pwm_ctl = blc_pwm_ctl / 100; + + /* Adjust the backlight level with the percent in + * dev_priv->blc_adj2; + */ + blc_pwm_ctl = blc_pwm_ctl * dev_priv->blc_adj2; + blc_pwm_ctl = blc_pwm_ctl / 100; + + /* force PWM bit on */ + REG_WRITE(BLC_PWM_CTL2, (0x80000000 | REG_READ(BLC_PWM_CTL2))); + REG_WRITE(BLC_PWM_CTL, (max_pwm_blc << 16) | blc_pwm_ctl); + gma_power_end(dev); + } + mrst_brightness = level; + return 0; +} + +static int mrst_get_brightness(struct backlight_device *bd) +{ + /* return locally cached var instead of HW read (due to DPST etc.) */ + /* FIXME: ideally return actual value in case firmware fiddled with + it */ + return mrst_brightness; +} + +static int device_backlight_init(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + unsigned long core_clock; + u16 bl_max_freq; + uint32_t value; + uint32_t blc_pwm_precision_factor; + + dev_priv->blc_adj1 = BLC_ADJUSTMENT_MAX; + dev_priv->blc_adj2 = BLC_ADJUSTMENT_MAX; + bl_max_freq = 256; + /* this needs to be set elsewhere */ + blc_pwm_precision_factor = BLC_PWM_PRECISION_FACTOR; + + core_clock = dev_priv->core_freq; + + value = (core_clock * MHz) / BLC_PWM_FREQ_CALC_CONSTANT; + value *= blc_pwm_precision_factor; + value /= bl_max_freq; + value /= blc_pwm_precision_factor; + + if (value > (unsigned long long)MRST_BLC_MAX_PWM_REG_FREQ) + return -ERANGE; + + if (gma_power_begin(dev, false)) { + REG_WRITE(BLC_PWM_CTL2, (0x80000000 | REG_READ(BLC_PWM_CTL2))); + REG_WRITE(BLC_PWM_CTL, value | (value << 16)); + gma_power_end(dev); + } + return 0; +} + +static const struct backlight_ops mrst_ops = { + .get_brightness = mrst_get_brightness, + .update_status = mrst_set_brightness, +}; + +int mrst_backlight_init(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + int ret; + struct backlight_properties props; + + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = 100; + props.type = BACKLIGHT_PLATFORM; + + mrst_backlight_device = backlight_device_register("mrst-bl", + NULL, (void *)dev, &mrst_ops, &props); + + if (IS_ERR(mrst_backlight_device)) + return PTR_ERR(mrst_backlight_device); + + ret = device_backlight_init(dev); + if (ret < 0) { + backlight_device_unregister(mrst_backlight_device); + return ret; + } + mrst_backlight_device->props.brightness = 100; + mrst_backlight_device->props.max_brightness = 100; + backlight_update_status(mrst_backlight_device); + dev_priv->backlight_device = mrst_backlight_device; + return 0; +} + +#endif + +/* + * Provide the Moorestown specific chip logic and low level methods + * for power management + */ + +static void mrst_init_pm(struct drm_device *dev) +{ +} + +/** + * mrst_save_display_registers - save registers lost on suspend + * @dev: our DRM device + * + * Save the state we need in order to be able to restore the interface + * upon resume from suspend + */ +static int mrst_save_display_registers(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + int i; + u32 pp_stat; + + /* Display arbitration control + watermarks */ + dev_priv->saveDSPARB = PSB_RVDC32(DSPARB); + dev_priv->saveDSPFW1 = PSB_RVDC32(DSPFW1); + dev_priv->saveDSPFW2 = PSB_RVDC32(DSPFW2); + dev_priv->saveDSPFW3 = PSB_RVDC32(DSPFW3); + dev_priv->saveDSPFW4 = PSB_RVDC32(DSPFW4); + dev_priv->saveDSPFW5 = PSB_RVDC32(DSPFW5); + dev_priv->saveDSPFW6 = PSB_RVDC32(DSPFW6); + dev_priv->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT); + + /* Pipe & plane A info */ + dev_priv->savePIPEACONF = PSB_RVDC32(PIPEACONF); + dev_priv->savePIPEASRC = PSB_RVDC32(PIPEASRC); + dev_priv->saveFPA0 = PSB_RVDC32(MRST_FPA0); + dev_priv->saveFPA1 = PSB_RVDC32(MRST_FPA1); + dev_priv->saveDPLL_A = PSB_RVDC32(MRST_DPLL_A); + dev_priv->saveHTOTAL_A = PSB_RVDC32(HTOTAL_A); + dev_priv->saveHBLANK_A = PSB_RVDC32(HBLANK_A); + dev_priv->saveHSYNC_A = PSB_RVDC32(HSYNC_A); + dev_priv->saveVTOTAL_A = PSB_RVDC32(VTOTAL_A); + dev_priv->saveVBLANK_A = PSB_RVDC32(VBLANK_A); + dev_priv->saveVSYNC_A = PSB_RVDC32(VSYNC_A); + dev_priv->saveBCLRPAT_A = PSB_RVDC32(BCLRPAT_A); + dev_priv->saveDSPACNTR = PSB_RVDC32(DSPACNTR); + dev_priv->saveDSPASTRIDE = PSB_RVDC32(DSPASTRIDE); + dev_priv->saveDSPAADDR = PSB_RVDC32(DSPABASE); + dev_priv->saveDSPASURF = PSB_RVDC32(DSPASURF); + dev_priv->saveDSPALINOFF = PSB_RVDC32(DSPALINOFF); + dev_priv->saveDSPATILEOFF = PSB_RVDC32(DSPATILEOFF); + + /* Save cursor regs */ + dev_priv->saveDSPACURSOR_CTRL = PSB_RVDC32(CURACNTR); + dev_priv->saveDSPACURSOR_BASE = PSB_RVDC32(CURABASE); + dev_priv->saveDSPACURSOR_POS = PSB_RVDC32(CURAPOS); + + /* Save palette (gamma) */ + for (i = 0; i < 256; i++) + dev_priv->save_palette_a[i] = PSB_RVDC32(PALETTE_A + (i << 2)); + + if (dev_priv->hdmi_priv) + mrst_hdmi_save(dev); + + /* Save performance state */ + dev_priv->savePERF_MODE = PSB_RVDC32(MRST_PERF_MODE); + + /* LVDS state */ + dev_priv->savePP_CONTROL = PSB_RVDC32(PP_CONTROL); + dev_priv->savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS); + dev_priv->savePFIT_AUTO_RATIOS = PSB_RVDC32(PFIT_AUTO_RATIOS); + dev_priv->saveBLC_PWM_CTL = PSB_RVDC32(BLC_PWM_CTL); + dev_priv->saveBLC_PWM_CTL2 = PSB_RVDC32(BLC_PWM_CTL2); + dev_priv->saveLVDS = PSB_RVDC32(LVDS); + dev_priv->savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL); + dev_priv->savePP_ON_DELAYS = PSB_RVDC32(LVDSPP_ON); + dev_priv->savePP_OFF_DELAYS = PSB_RVDC32(LVDSPP_OFF); + dev_priv->savePP_DIVISOR = PSB_RVDC32(PP_CYCLE); + + /* HW overlay */ + dev_priv->saveOV_OVADD = PSB_RVDC32(OV_OVADD); + dev_priv->saveOV_OGAMC0 = PSB_RVDC32(OV_OGAMC0); + dev_priv->saveOV_OGAMC1 = PSB_RVDC32(OV_OGAMC1); + dev_priv->saveOV_OGAMC2 = PSB_RVDC32(OV_OGAMC2); + dev_priv->saveOV_OGAMC3 = PSB_RVDC32(OV_OGAMC3); + dev_priv->saveOV_OGAMC4 = PSB_RVDC32(OV_OGAMC4); + dev_priv->saveOV_OGAMC5 = PSB_RVDC32(OV_OGAMC5); + + /* DPST registers */ + dev_priv->saveHISTOGRAM_INT_CONTROL_REG = + PSB_RVDC32(HISTOGRAM_INT_CONTROL); + dev_priv->saveHISTOGRAM_LOGIC_CONTROL_REG = + PSB_RVDC32(HISTOGRAM_LOGIC_CONTROL); + dev_priv->savePWM_CONTROL_LOGIC = PSB_RVDC32(PWM_CONTROL_LOGIC); + + if (dev_priv->iLVDS_enable) { + /* Shut down the panel */ + PSB_WVDC32(0, PP_CONTROL); + + do { + pp_stat = PSB_RVDC32(PP_STATUS); + } while (pp_stat & 0x80000000); + + /* Turn off the plane */ + PSB_WVDC32(0x58000000, DSPACNTR); + /* Trigger the plane disable */ + PSB_WVDC32(0, DSPASURF); + + /* Wait ~4 ticks */ + msleep(4); + + /* Turn off pipe */ + PSB_WVDC32(0x0, PIPEACONF); + /* Wait ~8 ticks */ + msleep(8); + + /* Turn off PLLs */ + PSB_WVDC32(0, MRST_DPLL_A); + } + return 0; +} + +/** + * mrst_restore_display_registers - restore lost register state + * @dev: our DRM device + * + * Restore register state that was lost during suspend and resume. + */ +static int mrst_restore_display_registers(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + u32 pp_stat; + int i; + + /* Display arbitration + watermarks */ + PSB_WVDC32(dev_priv->saveDSPARB, DSPARB); + PSB_WVDC32(dev_priv->saveDSPFW1, DSPFW1); + PSB_WVDC32(dev_priv->saveDSPFW2, DSPFW2); + PSB_WVDC32(dev_priv->saveDSPFW3, DSPFW3); + PSB_WVDC32(dev_priv->saveDSPFW4, DSPFW4); + PSB_WVDC32(dev_priv->saveDSPFW5, DSPFW5); + PSB_WVDC32(dev_priv->saveDSPFW6, DSPFW6); + PSB_WVDC32(dev_priv->saveCHICKENBIT, DSPCHICKENBIT); + + /* Make sure VGA plane is off. it initializes to on after reset!*/ + PSB_WVDC32(0x80000000, VGACNTRL); + + /* set the plls */ + PSB_WVDC32(dev_priv->saveFPA0, MRST_FPA0); + PSB_WVDC32(dev_priv->saveFPA1, MRST_FPA1); + + /* Actually enable it */ + PSB_WVDC32(dev_priv->saveDPLL_A, MRST_DPLL_A); + DRM_UDELAY(150); + + /* Restore mode */ + PSB_WVDC32(dev_priv->saveHTOTAL_A, HTOTAL_A); + PSB_WVDC32(dev_priv->saveHBLANK_A, HBLANK_A); + PSB_WVDC32(dev_priv->saveHSYNC_A, HSYNC_A); + PSB_WVDC32(dev_priv->saveVTOTAL_A, VTOTAL_A); + PSB_WVDC32(dev_priv->saveVBLANK_A, VBLANK_A); + PSB_WVDC32(dev_priv->saveVSYNC_A, VSYNC_A); + PSB_WVDC32(dev_priv->savePIPEASRC, PIPEASRC); + PSB_WVDC32(dev_priv->saveBCLRPAT_A, BCLRPAT_A); + + /* Restore performance mode*/ + PSB_WVDC32(dev_priv->savePERF_MODE, MRST_PERF_MODE); + + /* Enable the pipe*/ + if (dev_priv->iLVDS_enable) + PSB_WVDC32(dev_priv->savePIPEACONF, PIPEACONF); + + /* Set up the plane*/ + PSB_WVDC32(dev_priv->saveDSPALINOFF, DSPALINOFF); + PSB_WVDC32(dev_priv->saveDSPASTRIDE, DSPASTRIDE); + PSB_WVDC32(dev_priv->saveDSPATILEOFF, DSPATILEOFF); + + /* Enable the plane */ + PSB_WVDC32(dev_priv->saveDSPACNTR, DSPACNTR); + PSB_WVDC32(dev_priv->saveDSPASURF, DSPASURF); + + /* Enable Cursor A */ + PSB_WVDC32(dev_priv->saveDSPACURSOR_CTRL, CURACNTR); + PSB_WVDC32(dev_priv->saveDSPACURSOR_POS, CURAPOS); + PSB_WVDC32(dev_priv->saveDSPACURSOR_BASE, CURABASE); + + /* Restore palette (gamma) */ + for (i = 0; i < 256; i++) + PSB_WVDC32(dev_priv->save_palette_a[i], PALETTE_A + (i << 2)); + + if (dev_priv->hdmi_priv) + mrst_hdmi_restore(dev); + + if (dev_priv->iLVDS_enable) { + PSB_WVDC32(dev_priv->saveBLC_PWM_CTL2, BLC_PWM_CTL2); + PSB_WVDC32(dev_priv->saveLVDS, LVDS); /*port 61180h*/ + PSB_WVDC32(dev_priv->savePFIT_CONTROL, PFIT_CONTROL); + PSB_WVDC32(dev_priv->savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS); + PSB_WVDC32(dev_priv->savePFIT_AUTO_RATIOS, PFIT_AUTO_RATIOS); + PSB_WVDC32(dev_priv->saveBLC_PWM_CTL, BLC_PWM_CTL); + PSB_WVDC32(dev_priv->savePP_ON_DELAYS, LVDSPP_ON); + PSB_WVDC32(dev_priv->savePP_OFF_DELAYS, LVDSPP_OFF); + PSB_WVDC32(dev_priv->savePP_DIVISOR, PP_CYCLE); + PSB_WVDC32(dev_priv->savePP_CONTROL, PP_CONTROL); + } + + /* Wait for cycle delay */ + do { + pp_stat = PSB_RVDC32(PP_STATUS); + } while (pp_stat & 0x08000000); + + /* Wait for panel power up */ + do { + pp_stat = PSB_RVDC32(PP_STATUS); + } while (pp_stat & 0x10000000); + + /* Restore HW overlay */ + PSB_WVDC32(dev_priv->saveOV_OVADD, OV_OVADD); + PSB_WVDC32(dev_priv->saveOV_OGAMC0, OV_OGAMC0); + PSB_WVDC32(dev_priv->saveOV_OGAMC1, OV_OGAMC1); + PSB_WVDC32(dev_priv->saveOV_OGAMC2, OV_OGAMC2); + PSB_WVDC32(dev_priv->saveOV_OGAMC3, OV_OGAMC3); + PSB_WVDC32(dev_priv->saveOV_OGAMC4, OV_OGAMC4); + PSB_WVDC32(dev_priv->saveOV_OGAMC5, OV_OGAMC5); + + /* DPST registers */ + PSB_WVDC32(dev_priv->saveHISTOGRAM_INT_CONTROL_REG, + HISTOGRAM_INT_CONTROL); + PSB_WVDC32(dev_priv->saveHISTOGRAM_LOGIC_CONTROL_REG, + HISTOGRAM_LOGIC_CONTROL); + PSB_WVDC32(dev_priv->savePWM_CONTROL_LOGIC, PWM_CONTROL_LOGIC); + + return 0; +} + +/** + * mrst_power_down - power down the display island + * @dev: our DRM device + * + * Power down the display interface of our device + */ +static int mrst_power_down(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + u32 pwr_mask ; + u32 pwr_sts; + + pwr_mask = PSB_PWRGT_DISPLAY_MASK; + outl(pwr_mask, dev_priv->ospm_base + PSB_PM_SSC); + + while (true) { + pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS); + if ((pwr_sts & pwr_mask) == pwr_mask) + break; + else + udelay(10); + } + return 0; +} + +/* + * mrst_power_up + * + * Restore power to the specified island(s) (powergating) + */ +static int mrst_power_up(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + u32 pwr_mask = PSB_PWRGT_DISPLAY_MASK; + u32 pwr_sts, pwr_cnt; + + pwr_cnt = inl(dev_priv->ospm_base + PSB_PM_SSC); + pwr_cnt &= ~pwr_mask; + outl(pwr_cnt, (dev_priv->ospm_base + PSB_PM_SSC)); + + while (true) { + pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS); + if ((pwr_sts & pwr_mask) == 0) + break; + else + udelay(10); + } + return 0; +} + +#if defined(CONFIG_X86_MRST) +static void mrst_lvds_cache_bl(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + + intel_scu_ipc_ioread8(0x28, &(dev_priv->saveBKLTCNT)); + intel_scu_ipc_ioread8(0x29, &(dev_priv->saveBKLTREQ)); + intel_scu_ipc_ioread8(0x2A, &(dev_priv->saveBKLTBRTL)); +} + +static void mrst_mm_bl_power(struct drm_device *dev, bool on) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + + if (on) { + intel_scu_ipc_iowrite8(0x2A, dev_priv->saveBKLTBRTL); + intel_scu_ipc_iowrite8(0x28, dev_priv->saveBKLTCNT); + intel_scu_ipc_iowrite8(0x29, dev_priv->saveBKLTREQ); + } else { + intel_scu_ipc_iowrite8(0x2A, 0); + intel_scu_ipc_iowrite8(0x28, 0); + intel_scu_ipc_iowrite8(0x29, 0); + } +} + +static const struct psb_ops mrst_mm_chip_ops = { + .name = "Moorestown MM ", + .accel_2d = 1, + .pipes = 1, + .crtcs = 1, + .sgx_offset = MRST_SGX_OFFSET, + + .crtc_helper = &mrst_helper_funcs, + .crtc_funcs = &psb_intel_crtc_funcs, + + .output_init = mrst_output_init, + + .lvds_bl_power = mrst_mm_bl_power, +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + .backlight_init = mrst_backlight_init, +#endif + + .init_pm = mrst_init_pm, + .save_regs = mrst_save_display_registers, + .restore_regs = mrst_restore_display_registers, + .power_down = mrst_power_down, + .power_up = mrst_power_up, + + .i2c_bus = 0, +}; + +#endif + +static void oaktrail_teardown(struct drm_device *dev) +{ + mrst_hdmi_teardown(dev); +} + +static const struct psb_ops oaktrail_chip_ops = { + .name = "Oaktrail", + .accel_2d = 1, + .pipes = 2, + .crtcs = 2, + .sgx_offset = MRST_SGX_OFFSET, + + .chip_setup = mid_chip_setup, + .chip_teardown = oaktrail_teardown, + .crtc_helper = &mrst_helper_funcs, + .crtc_funcs = &psb_intel_crtc_funcs, + + .output_init = mrst_output_init, + +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + .backlight_init = mrst_backlight_init, +#endif + + .init_pm = mrst_init_pm, + .save_regs = mrst_save_display_registers, + .restore_regs = mrst_restore_display_registers, + .power_down = mrst_power_down, + .power_up = mrst_power_up, + + .i2c_bus = 1, +}; + +/** + * mrst_chip_setup - perform the initial chip init + * @dev: Our drm_device + * + * Figure out which incarnation we are and then scan the firmware for + * tables and information. + */ +static int mrst_chip_setup(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + + switch (mrst_device_ident(dev)) { + case DEVICE_OAKTRAIL: + /* Dual CRTC, PC compatible, HDMI, I2C #2 */ + dev_priv->ops = &oaktrail_chip_ops; + mrst_hdmi_setup(dev); + return mid_chip_setup(dev); +#if defined(CONFIG_X86_MRST) + case DEVICE_MOORESTOWN_MM: + /* Single CRTC, No HDMI, I2C #0, BL control */ + mrst_lvds_cache_bl(dev); + dev_priv->ops = &mrst_mm_chip_ops; + return mid_chip_setup(dev); + case DEVICE_MOORESTOWN: + /* Dual CRTC, No HDMI(?), I2C #1 */ + return mid_chip_setup(dev); +#endif + default: + dev_err(dev->dev, "unsupported device type.\n"); + return -ENODEV; + } +} + +const struct psb_ops mrst_chip_ops = { + .name = "Moorestown", + .accel_2d = 1, + .pipes = 2, + .crtcs = 2, + .sgx_offset = MRST_SGX_OFFSET, + + .chip_setup = mrst_chip_setup, + .crtc_helper = &mrst_helper_funcs, + .crtc_funcs = &psb_intel_crtc_funcs, + + .output_init = mrst_output_init, + +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + .backlight_init = mrst_backlight_init, +#endif + + .init_pm = mrst_init_pm, + .save_regs = mrst_save_display_registers, + .restore_regs = mrst_restore_display_registers, + .power_down = mrst_power_down, + .power_up = mrst_power_up, + + .i2c_bus = 2, +}; + diff --git a/trunk/drivers/staging/gma500/mrst_hdmi.c b/trunk/drivers/staging/gma500/mrst_hdmi.c new file mode 100644 index 000000000000..e66607eb3d3e --- /dev/null +++ b/trunk/drivers/staging/gma500/mrst_hdmi.c @@ -0,0 +1,852 @@ +/* + * Copyright © 2010 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: + * Li Peng + */ + +#include +#include +#include "psb_intel_drv.h" +#include "psb_intel_reg.h" +#include "psb_drv.h" + +#define HDMI_READ(reg) readl(hdmi_dev->regs + (reg)) +#define HDMI_WRITE(reg, val) writel(val, hdmi_dev->regs + (reg)) + +#define HDMI_HCR 0x1000 +#define HCR_ENABLE_HDCP (1 << 5) +#define HCR_ENABLE_AUDIO (1 << 2) +#define HCR_ENABLE_PIXEL (1 << 1) +#define HCR_ENABLE_TMDS (1 << 0) + +#define HDMI_HICR 0x1004 +#define HDMI_HSR 0x1008 +#define HDMI_HISR 0x100C +#define HDMI_DETECT_HDP (1 << 0) + +#define HDMI_VIDEO_REG 0x3000 +#define HDMI_UNIT_EN (1 << 7) +#define HDMI_MODE_OUTPUT (1 << 0) +#define HDMI_HBLANK_A 0x3100 + +#define HDMI_AUDIO_CTRL 0x4000 +#define HDMI_ENABLE_AUDIO (1 << 0) + +#define PCH_HTOTAL_B 0x3100 +#define PCH_HBLANK_B 0x3104 +#define PCH_HSYNC_B 0x3108 +#define PCH_VTOTAL_B 0x310C +#define PCH_VBLANK_B 0x3110 +#define PCH_VSYNC_B 0x3114 +#define PCH_PIPEBSRC 0x311C + +#define PCH_PIPEB_DSL 0x3800 +#define PCH_PIPEB_SLC 0x3804 +#define PCH_PIPEBCONF 0x3808 +#define PCH_PIPEBSTAT 0x3824 + +#define CDVO_DFT 0x5000 +#define CDVO_SLEWRATE 0x5004 +#define CDVO_STRENGTH 0x5008 +#define CDVO_RCOMP 0x500C + +#define DPLL_CTRL 0x6000 +#define DPLL_PDIV_SHIFT 16 +#define DPLL_PDIV_MASK (0xf << 16) +#define DPLL_PWRDN (1 << 4) +#define DPLL_RESET (1 << 3) +#define DPLL_FASTEN (1 << 2) +#define DPLL_ENSTAT (1 << 1) +#define DPLL_DITHEN (1 << 0) + +#define DPLL_DIV_CTRL 0x6004 +#define DPLL_CLKF_MASK 0xffffffc0 +#define DPLL_CLKR_MASK (0x3f) + +#define DPLL_CLK_ENABLE 0x6008 +#define DPLL_EN_DISP (1 << 31) +#define DPLL_SEL_HDMI (1 << 8) +#define DPLL_EN_HDMI (1 << 1) +#define DPLL_EN_VGA (1 << 0) + +#define DPLL_ADJUST 0x600C +#define DPLL_STATUS 0x6010 +#define DPLL_UPDATE 0x6014 +#define DPLL_DFT 0x6020 + +struct intel_range { + int min, max; +}; + +struct mrst_hdmi_limit { + struct intel_range vco, np, nr, nf; +}; + +struct mrst_hdmi_clock { + int np; + int nr; + int nf; + int dot; +}; + +#define VCO_MIN 320000 +#define VCO_MAX 1650000 +#define NP_MIN 1 +#define NP_MAX 15 +#define NR_MIN 1 +#define NR_MAX 64 +#define NF_MIN 2 +#define NF_MAX 4095 + +static const struct mrst_hdmi_limit mrst_hdmi_limit = { + .vco = { .min = VCO_MIN, .max = VCO_MAX }, + .np = { .min = NP_MIN, .max = NP_MAX }, + .nr = { .min = NR_MIN, .max = NR_MAX }, + .nf = { .min = NF_MIN, .max = NF_MAX }, +}; + +static void wait_for_vblank(struct drm_device *dev) +{ + /* FIXME: Can we do this as a sleep ? */ + /* Wait for 20ms, i.e. one cycle at 50hz. */ + mdelay(20); +} + +static void scu_busy_loop(void *scu_base) +{ + u32 status = 0; + u32 loop_count = 0; + + status = readl(scu_base + 0x04); + while (status & 1) { + udelay(1); /* scu processing time is in few u secods */ + status = readl(scu_base + 0x04); + loop_count++; + /* break if scu doesn't reset busy bit after huge retry */ + if (loop_count > 1000) { + DRM_DEBUG_KMS("SCU IPC timed out"); + return; + } + } +} + +static void mrst_hdmi_reset(struct drm_device *dev) +{ + void *base; + /* FIXME: at least make these defines */ + unsigned int scu_ipc_mmio = 0xff11c000; + int scu_len = 1024; + + base = ioremap((resource_size_t)scu_ipc_mmio, scu_len); + if (base == NULL) { + DRM_ERROR("failed to map SCU mmio\n"); + return; + } + + /* scu ipc: assert hdmi controller reset */ + writel(0xff11d118, base + 0x0c); + writel(0x7fffffdf, base + 0x80); + writel(0x42005, base + 0x0); + scu_busy_loop(base); + + /* scu ipc: de-assert hdmi controller reset */ + writel(0xff11d118, base + 0x0c); + writel(0x7fffffff, base + 0x80); + writel(0x42005, base + 0x0); + scu_busy_loop(base); + + iounmap(base); +} + +static void mrst_hdmi_audio_enable(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; + + HDMI_WRITE(HDMI_HCR, 0x67); + HDMI_READ(HDMI_HCR); + + HDMI_WRITE(0x51a8, 0x10); + HDMI_READ(0x51a8); + + HDMI_WRITE(HDMI_AUDIO_CTRL, 0x1); + HDMI_READ(HDMI_AUDIO_CTRL); +} + +static void mrst_hdmi_audio_disable(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; + + HDMI_WRITE(0x51a8, 0x0); + HDMI_READ(0x51a8); + + HDMI_WRITE(HDMI_AUDIO_CTRL, 0x0); + HDMI_READ(HDMI_AUDIO_CTRL); + + HDMI_WRITE(HDMI_HCR, 0x47); + HDMI_READ(HDMI_HCR); +} + +void mrst_crtc_hdmi_dpms(struct drm_crtc *crtc, int mode) +{ + struct drm_device *dev = crtc->dev; + u32 temp; + + switch (mode) { + case DRM_MODE_DPMS_OFF: + /* Disable VGACNTRL */ + REG_WRITE(VGACNTRL, 0x80000000); + + /* Disable plane */ + temp = REG_READ(DSPBCNTR); + if ((temp & DISPLAY_PLANE_ENABLE) != 0) { + REG_WRITE(DSPBCNTR, temp & ~DISPLAY_PLANE_ENABLE); + REG_READ(DSPBCNTR); + /* Flush the plane changes */ + REG_WRITE(DSPBSURF, REG_READ(DSPBSURF)); + REG_READ(DSPBSURF); + } + + /* Disable pipe B */ + temp = REG_READ(PIPEBCONF); + if ((temp & PIPEACONF_ENABLE) != 0) { + REG_WRITE(PIPEBCONF, temp & ~PIPEACONF_ENABLE); + REG_READ(PIPEBCONF); + } + + /* Disable LNW Pipes, etc */ + temp = REG_READ(PCH_PIPEBCONF); + if ((temp & PIPEACONF_ENABLE) != 0) { + REG_WRITE(PCH_PIPEBCONF, temp & ~PIPEACONF_ENABLE); + REG_READ(PCH_PIPEBCONF); + } + /* wait for pipe off */ + udelay(150); + /* Disable dpll */ + temp = REG_READ(DPLL_CTRL); + if ((temp & DPLL_PWRDN) == 0) { + REG_WRITE(DPLL_CTRL, temp | (DPLL_PWRDN | DPLL_RESET)); + REG_WRITE(DPLL_STATUS, 0x1); + } + /* wait for dpll off */ + udelay(150); + break; + case DRM_MODE_DPMS_ON: + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + /* Enable dpll */ + temp = REG_READ(DPLL_CTRL); + if ((temp & DPLL_PWRDN) != 0) { + REG_WRITE(DPLL_CTRL, temp & ~(DPLL_PWRDN | DPLL_RESET)); + temp = REG_READ(DPLL_CLK_ENABLE); + REG_WRITE(DPLL_CLK_ENABLE, temp | DPLL_EN_DISP | DPLL_SEL_HDMI | DPLL_EN_HDMI); + REG_READ(DPLL_CLK_ENABLE); + } + /* wait for dpll warm up */ + udelay(150); + + /* Enable pipe B */ + temp = REG_READ(PIPEBCONF); + if ((temp & PIPEACONF_ENABLE) == 0) { + REG_WRITE(PIPEBCONF, temp | PIPEACONF_ENABLE); + REG_READ(PIPEBCONF); + } + + /* Enable LNW Pipe B */ + temp = REG_READ(PCH_PIPEBCONF); + if ((temp & PIPEACONF_ENABLE) == 0) { + REG_WRITE(PCH_PIPEBCONF, temp | PIPEACONF_ENABLE); + REG_READ(PCH_PIPEBCONF); + } + wait_for_vblank(dev); + + /* Enable plane */ + temp = REG_READ(DSPBCNTR); + if ((temp & DISPLAY_PLANE_ENABLE) == 0) { + REG_WRITE(DSPBCNTR, temp | DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + REG_WRITE(DSPBSURF, REG_READ(DSPBSURF)); + REG_READ(DSPBSURF); + } + psb_intel_crtc_load_lut(crtc); + } + /* DSPARB */ + REG_WRITE(DSPARB, 0x00003fbf); + /* FW1 */ + REG_WRITE(0x70034, 0x3f880a0a); + /* FW2 */ + REG_WRITE(0x70038, 0x0b060808); + /* FW4 */ + REG_WRITE(0x70050, 0x08030404); + /* FW5 */ + REG_WRITE(0x70054, 0x04040404); + /* LNC Chicken Bits */ + REG_WRITE(0x70400, 0x4000); +} + + +static void mrst_hdmi_dpms(struct drm_encoder *encoder, int mode) +{ + static int dpms_mode = -1; + + struct drm_device *dev = encoder->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; + u32 temp; + + if (dpms_mode == mode) + return; + + if (mode != DRM_MODE_DPMS_ON) + temp = 0x0; + else + temp = 0x99; + + dpms_mode = mode; + HDMI_WRITE(HDMI_VIDEO_REG, temp); +} + +static unsigned int htotal_calculate(struct drm_display_mode *mode) +{ + u32 htotal, new_crtc_htotal; + + htotal = (mode->crtc_hdisplay - 1) | ((mode->crtc_htotal - 1) << 16); + + /* + * 1024 x 768 new_crtc_htotal = 0x1024; + * 1280 x 1024 new_crtc_htotal = 0x0c34; + */ + new_crtc_htotal = (mode->crtc_htotal - 1) * 200 * 1000 / mode->clock; + + return (mode->crtc_hdisplay - 1) | (new_crtc_htotal << 16); +} + +static void mrst_hdmi_find_dpll(struct drm_crtc *crtc, int target, + int refclk, struct mrst_hdmi_clock *best_clock) +{ + int np_min, np_max, nr_min, nr_max; + int np, nr, nf; + + np_min = DIV_ROUND_UP(mrst_hdmi_limit.vco.min, target * 10); + np_max = mrst_hdmi_limit.vco.max / (target * 10); + if (np_min < mrst_hdmi_limit.np.min) + np_min = mrst_hdmi_limit.np.min; + if (np_max > mrst_hdmi_limit.np.max) + np_max = mrst_hdmi_limit.np.max; + + nr_min = DIV_ROUND_UP((refclk * 1000), (target * 10 * np_max)); + nr_max = DIV_ROUND_UP((refclk * 1000), (target * 10 * np_min)); + if (nr_min < mrst_hdmi_limit.nr.min) + nr_min = mrst_hdmi_limit.nr.min; + if (nr_max > mrst_hdmi_limit.nr.max) + nr_max = mrst_hdmi_limit.nr.max; + + np = DIV_ROUND_UP((refclk * 1000), (target * 10 * nr_max)); + nr = DIV_ROUND_UP((refclk * 1000), (target * 10 * np)); + nf = DIV_ROUND_CLOSEST((target * 10 * np * nr), refclk); + DRM_DEBUG_KMS("np, nr, nf %d %d %d\n", np, nr, nf); + + /* + * 1024 x 768 np = 1; nr = 0x26; nf = 0x0fd8000; + * 1280 x 1024 np = 1; nr = 0x17; nf = 0x1034000; + */ + best_clock->np = np; + best_clock->nr = nr - 1; + best_clock->nf = (nf << 14); +} + +int mrst_crtc_hdmi_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, + struct drm_framebuffer *old_fb) +{ + struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; + int pipe = 1; + int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; + int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; + int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; + int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B; + int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B; + int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B; + int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE; + int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS; + int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; + int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; + int refclk; + struct mrst_hdmi_clock clock; + u32 dspcntr, pipeconf, dpll, temp; + int dspcntr_reg = DSPBCNTR; + + /* Disable the VGA plane that we never use */ + REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); + + /* XXX: Disable the panel fitter if it was on our pipe */ + + /* Disable dpll if necessary */ + dpll = REG_READ(DPLL_CTRL); + if ((dpll & DPLL_PWRDN) == 0) { + REG_WRITE(DPLL_CTRL, dpll | (DPLL_PWRDN | DPLL_RESET)); + REG_WRITE(DPLL_DIV_CTRL, 0x00000000); + REG_WRITE(DPLL_STATUS, 0x1); + } + udelay(150); + + /* reset controller: FIXME - can we sort out the ioremap mess ? */ + iounmap(hdmi_dev->regs); + mrst_hdmi_reset(dev); + + /* program and enable dpll */ + refclk = 25000; + mrst_hdmi_find_dpll(crtc, adjusted_mode->clock, refclk, &clock); + + /* Setting DPLL */ + dpll = REG_READ(DPLL_CTRL); + dpll &= ~DPLL_PDIV_MASK; + dpll &= ~(DPLL_PWRDN | DPLL_RESET); + REG_WRITE(DPLL_CTRL, 0x00000008); + REG_WRITE(DPLL_DIV_CTRL, ((clock.nf << 6) | clock.nr)); + REG_WRITE(DPLL_ADJUST, ((clock.nf >> 14) - 1)); + REG_WRITE(DPLL_CTRL, (dpll | (clock.np << DPLL_PDIV_SHIFT) | DPLL_ENSTAT | DPLL_DITHEN)); + REG_WRITE(DPLL_UPDATE, 0x80000000); + REG_WRITE(DPLL_CLK_ENABLE, 0x80050102); + udelay(150); + + hdmi_dev->regs = ioremap(hdmi_dev->mmio, hdmi_dev->mmio_len); + if (hdmi_dev->regs == NULL) { + DRM_ERROR("failed to do hdmi mmio mapping\n"); + return -ENOMEM; + } + + /* configure HDMI */ + HDMI_WRITE(0x1004, 0x1fd); + HDMI_WRITE(0x2000, 0x1); + HDMI_WRITE(0x2008, 0x0); + HDMI_WRITE(0x3130, 0x8); + HDMI_WRITE(0x101c, 0x1800810); + + temp = htotal_calculate(adjusted_mode); + REG_WRITE(htot_reg, temp); + REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16)); + REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16)); + REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16)); + REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16)); + REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16)); + REG_WRITE(pipesrc_reg, + ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); + + REG_WRITE(PCH_HTOTAL_B, (adjusted_mode->crtc_hdisplay - 1) | ((adjusted_mode->crtc_htotal - 1) << 16)); + REG_WRITE(PCH_HBLANK_B, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16)); + REG_WRITE(PCH_HSYNC_B, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16)); + REG_WRITE(PCH_VTOTAL_B, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16)); + REG_WRITE(PCH_VBLANK_B, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16)); + REG_WRITE(PCH_VSYNC_B, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16)); + REG_WRITE(PCH_PIPEBSRC, + ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); + + temp = adjusted_mode->crtc_hblank_end - adjusted_mode->crtc_hblank_start; + HDMI_WRITE(HDMI_HBLANK_A, ((adjusted_mode->crtc_hdisplay - 1) << 16) | temp); + + REG_WRITE(dspsize_reg, + ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); + REG_WRITE(dsppos_reg, 0); + + /* Flush the plane changes */ + { + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + crtc_funcs->mode_set_base(crtc, x, y, old_fb); + } + + /* Set up the display plane register */ + dspcntr = REG_READ(dspcntr_reg); + dspcntr |= DISPPLANE_GAMMA_ENABLE; + dspcntr |= DISPPLANE_SEL_PIPE_B; + dspcntr |= DISPLAY_PLANE_ENABLE; + + /* setup pipeconf */ + pipeconf = REG_READ(pipeconf_reg); + pipeconf |= PIPEACONF_ENABLE; + + REG_WRITE(pipeconf_reg, pipeconf); + REG_READ(pipeconf_reg); + + REG_WRITE(PCH_PIPEBCONF, pipeconf); + REG_READ(PCH_PIPEBCONF); + wait_for_vblank(dev); + + REG_WRITE(dspcntr_reg, dspcntr); + wait_for_vblank(dev); + + return 0; +} + +static int mrst_hdmi_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + if (mode->clock > 165000) + return MODE_CLOCK_HIGH; + if (mode->clock < 20000) + return MODE_CLOCK_LOW; + + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + + return MODE_OK; +} + +static bool mrst_hdmi_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static enum drm_connector_status +mrst_hdmi_detect(struct drm_connector *connector, bool force) +{ + enum drm_connector_status status; + struct drm_device *dev = connector->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; + u32 temp; + + temp = HDMI_READ(HDMI_HSR); + DRM_DEBUG_KMS("HDMI_HSR %x\n", temp); + + if ((temp & HDMI_DETECT_HDP) != 0) + status = connector_status_connected; + else + status = connector_status_disconnected; + + return status; +} + +static const unsigned char raw_edid[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x10, 0xac, 0x2f, 0xa0, + 0x53, 0x55, 0x33, 0x30, 0x16, 0x13, 0x01, 0x03, 0x0e, 0x3a, 0x24, 0x78, + 0xea, 0xe9, 0xf5, 0xac, 0x51, 0x30, 0xb4, 0x25, 0x11, 0x50, 0x54, 0xa5, + 0x4b, 0x00, 0x81, 0x80, 0xa9, 0x40, 0x71, 0x4f, 0xb3, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x28, 0x3c, 0x80, 0xa0, 0x70, 0xb0, + 0x23, 0x40, 0x30, 0x20, 0x36, 0x00, 0x46, 0x6c, 0x21, 0x00, 0x00, 0x1a, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x47, 0x4e, 0x37, 0x32, 0x31, 0x39, 0x35, + 0x52, 0x30, 0x33, 0x55, 0x53, 0x0a, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x44, + 0x45, 0x4c, 0x4c, 0x20, 0x32, 0x37, 0x30, 0x39, 0x57, 0x0a, 0x20, 0x20, + 0x00, 0x00, 0x00, 0xfd, 0x00, 0x38, 0x4c, 0x1e, 0x53, 0x11, 0x00, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x8d +}; + +static int mrst_hdmi_get_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct i2c_adapter *i2c_adap; + struct edid *edid; + struct drm_display_mode *mode, *t; + int i = 0, ret = 0; + + i2c_adap = i2c_get_adapter(3); + if (i2c_adap == NULL) { + DRM_ERROR("No ddc adapter available!\n"); + edid = (struct edid *)raw_edid; + } else { + edid = (struct edid *)raw_edid; + /* FIXME ? edid = drm_get_edid(connector, i2c_adap); */ + } + + if (edid) { + drm_mode_connector_update_edid_property(connector, edid); + ret = drm_add_edid_modes(connector, edid); + connector->display_info.raw_edid = NULL; + } + + /* + * prune modes that require frame buffer bigger than stolen mem + */ + list_for_each_entry_safe(mode, t, &connector->probed_modes, head) { + if ((mode->hdisplay * mode->vdisplay * 4) >= dev_priv->vram_stolen_size) { + i++; + drm_mode_remove(connector, mode); + } + } + return ret - i; +} + +static void mrst_hdmi_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + + mrst_hdmi_audio_enable(dev); + return; +} + +static void mrst_hdmi_destroy(struct drm_connector *connector) +{ + return; +} + +static const struct drm_encoder_helper_funcs mrst_hdmi_helper_funcs = { + .dpms = mrst_hdmi_dpms, + .mode_fixup = mrst_hdmi_mode_fixup, + .prepare = psb_intel_encoder_prepare, + .mode_set = mrst_hdmi_mode_set, + .commit = psb_intel_encoder_commit, +}; + +static const struct drm_connector_helper_funcs + mrst_hdmi_connector_helper_funcs = { + .get_modes = mrst_hdmi_get_modes, + .mode_valid = mrst_hdmi_mode_valid, + .best_encoder = psb_intel_best_encoder, +}; + +static const struct drm_connector_funcs mrst_hdmi_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = mrst_hdmi_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = mrst_hdmi_destroy, +}; + +static void mrst_hdmi_enc_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs mrst_hdmi_enc_funcs = { + .destroy = mrst_hdmi_enc_destroy, +}; + +void mrst_hdmi_init(struct drm_device *dev, + struct psb_intel_mode_device *mode_dev) +{ + struct psb_intel_output *psb_intel_output; + struct drm_connector *connector; + struct drm_encoder *encoder; + + psb_intel_output = kzalloc(sizeof(struct psb_intel_output), GFP_KERNEL); + if (!psb_intel_output) + return; + + psb_intel_output->mode_dev = mode_dev; + connector = &psb_intel_output->base; + encoder = &psb_intel_output->enc; + drm_connector_init(dev, &psb_intel_output->base, + &mrst_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_DVID); + + drm_encoder_init(dev, &psb_intel_output->enc, + &mrst_hdmi_enc_funcs, + DRM_MODE_ENCODER_TMDS); + + drm_mode_connector_attach_encoder(&psb_intel_output->base, + &psb_intel_output->enc); + + psb_intel_output->type = INTEL_OUTPUT_HDMI; + drm_encoder_helper_add(encoder, &mrst_hdmi_helper_funcs); + drm_connector_helper_add(connector, &mrst_hdmi_connector_helper_funcs); + + connector->display_info.subpixel_order = SubPixelHorizontalRGB; + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + drm_sysfs_connector_add(connector); + + return; +} + +static DEFINE_PCI_DEVICE_TABLE(hdmi_ids) = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080d) }, + {} +}; + +void mrst_hdmi_setup(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct pci_dev *pdev; + struct mrst_hdmi_dev *hdmi_dev; + int ret; + + pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x080d, NULL); + if (!pdev) + return; + + hdmi_dev = kzalloc(sizeof(struct mrst_hdmi_dev), GFP_KERNEL); + if (!hdmi_dev) { + dev_err(dev->dev, "failed to allocate memory\n"); + goto out; + } + + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(dev->dev, "failed to enable hdmi controller\n"); + goto free; + } + + hdmi_dev->mmio = pci_resource_start(pdev, 0); + hdmi_dev->mmio_len = pci_resource_len(pdev, 0); + hdmi_dev->regs = ioremap(hdmi_dev->mmio, hdmi_dev->mmio_len); + if (!hdmi_dev->regs) { + dev_err(dev->dev, "failed to map hdmi mmio\n"); + goto free; + } + + hdmi_dev->dev = pdev; + pci_set_drvdata(pdev, hdmi_dev); + + /* Initialize i2c controller */ + ret = mrst_hdmi_i2c_init(hdmi_dev->dev); + if (ret) + dev_err(dev->dev, "HDMI I2C initialization failed\n"); + + dev_priv->hdmi_priv = hdmi_dev; + mrst_hdmi_audio_disable(dev); + return; + +free: + kfree(hdmi_dev); +out: + return; +} + +void mrst_hdmi_teardown(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; + struct pci_dev *pdev; + + if (hdmi_dev) { + pdev = hdmi_dev->dev; + pci_set_drvdata(pdev, NULL); + mrst_hdmi_i2c_exit(pdev); + iounmap(hdmi_dev->regs); + kfree(hdmi_dev); + pci_dev_put(pdev); + } +} + +/* save HDMI register state */ +void mrst_hdmi_save(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; + int i; + + /* dpll */ + hdmi_dev->saveDPLL_CTRL = PSB_RVDC32(DPLL_CTRL); + hdmi_dev->saveDPLL_DIV_CTRL = PSB_RVDC32(DPLL_DIV_CTRL); + hdmi_dev->saveDPLL_ADJUST = PSB_RVDC32(DPLL_ADJUST); + hdmi_dev->saveDPLL_UPDATE = PSB_RVDC32(DPLL_UPDATE); + hdmi_dev->saveDPLL_CLK_ENABLE = PSB_RVDC32(DPLL_CLK_ENABLE); + + /* pipe B */ + dev_priv->savePIPEBCONF = PSB_RVDC32(PIPEBCONF); + dev_priv->savePIPEBSRC = PSB_RVDC32(PIPEBSRC); + dev_priv->saveHTOTAL_B = PSB_RVDC32(HTOTAL_B); + dev_priv->saveHBLANK_B = PSB_RVDC32(HBLANK_B); + dev_priv->saveHSYNC_B = PSB_RVDC32(HSYNC_B); + dev_priv->saveVTOTAL_B = PSB_RVDC32(VTOTAL_B); + dev_priv->saveVBLANK_B = PSB_RVDC32(VBLANK_B); + dev_priv->saveVSYNC_B = PSB_RVDC32(VSYNC_B); + + hdmi_dev->savePCH_PIPEBCONF = PSB_RVDC32(PCH_PIPEBCONF); + hdmi_dev->savePCH_PIPEBSRC = PSB_RVDC32(PCH_PIPEBSRC); + hdmi_dev->savePCH_HTOTAL_B = PSB_RVDC32(PCH_HTOTAL_B); + hdmi_dev->savePCH_HBLANK_B = PSB_RVDC32(PCH_HBLANK_B); + hdmi_dev->savePCH_HSYNC_B = PSB_RVDC32(PCH_HSYNC_B); + hdmi_dev->savePCH_VTOTAL_B = PSB_RVDC32(PCH_VTOTAL_B); + hdmi_dev->savePCH_VBLANK_B = PSB_RVDC32(PCH_VBLANK_B); + hdmi_dev->savePCH_VSYNC_B = PSB_RVDC32(PCH_VSYNC_B); + + /* plane */ + dev_priv->saveDSPBCNTR = PSB_RVDC32(DSPBCNTR); + dev_priv->saveDSPBSTRIDE = PSB_RVDC32(DSPBSTRIDE); + dev_priv->saveDSPBADDR = PSB_RVDC32(DSPBBASE); + dev_priv->saveDSPBSURF = PSB_RVDC32(DSPBSURF); + dev_priv->saveDSPBLINOFF = PSB_RVDC32(DSPBLINOFF); + dev_priv->saveDSPBTILEOFF = PSB_RVDC32(DSPBTILEOFF); + + /* cursor B */ + dev_priv->saveDSPBCURSOR_CTRL = PSB_RVDC32(CURBCNTR); + dev_priv->saveDSPBCURSOR_BASE = PSB_RVDC32(CURBBASE); + dev_priv->saveDSPBCURSOR_POS = PSB_RVDC32(CURBPOS); + + /* save palette */ + for (i = 0; i < 256; i++) + dev_priv->save_palette_b[i] = PSB_RVDC32(PALETTE_B + (i << 2)); +} + +/* restore HDMI register state */ +void mrst_hdmi_restore(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; + int i; + + /* dpll */ + PSB_WVDC32(hdmi_dev->saveDPLL_CTRL, DPLL_CTRL); + PSB_WVDC32(hdmi_dev->saveDPLL_DIV_CTRL, DPLL_DIV_CTRL); + PSB_WVDC32(hdmi_dev->saveDPLL_ADJUST, DPLL_ADJUST); + PSB_WVDC32(hdmi_dev->saveDPLL_UPDATE, DPLL_UPDATE); + PSB_WVDC32(hdmi_dev->saveDPLL_CLK_ENABLE, DPLL_CLK_ENABLE); + DRM_UDELAY(150); + + /* pipe */ + PSB_WVDC32(dev_priv->savePIPEBSRC, PIPEBSRC); + PSB_WVDC32(dev_priv->saveHTOTAL_B, HTOTAL_B); + PSB_WVDC32(dev_priv->saveHBLANK_B, HBLANK_B); + PSB_WVDC32(dev_priv->saveHSYNC_B, HSYNC_B); + PSB_WVDC32(dev_priv->saveVTOTAL_B, VTOTAL_B); + PSB_WVDC32(dev_priv->saveVBLANK_B, VBLANK_B); + PSB_WVDC32(dev_priv->saveVSYNC_B, VSYNC_B); + + PSB_WVDC32(hdmi_dev->savePCH_PIPEBSRC, PCH_PIPEBSRC); + PSB_WVDC32(hdmi_dev->savePCH_HTOTAL_B, PCH_HTOTAL_B); + PSB_WVDC32(hdmi_dev->savePCH_HBLANK_B, PCH_HBLANK_B); + PSB_WVDC32(hdmi_dev->savePCH_HSYNC_B, PCH_HSYNC_B); + PSB_WVDC32(hdmi_dev->savePCH_VTOTAL_B, PCH_VTOTAL_B); + PSB_WVDC32(hdmi_dev->savePCH_VBLANK_B, PCH_VBLANK_B); + PSB_WVDC32(hdmi_dev->savePCH_VSYNC_B, PCH_VSYNC_B); + + PSB_WVDC32(dev_priv->savePIPEBCONF, PIPEBCONF); + PSB_WVDC32(hdmi_dev->savePCH_PIPEBCONF, PCH_PIPEBCONF); + + /* plane */ + PSB_WVDC32(dev_priv->saveDSPBLINOFF, DSPBLINOFF); + PSB_WVDC32(dev_priv->saveDSPBSTRIDE, DSPBSTRIDE); + PSB_WVDC32(dev_priv->saveDSPBTILEOFF, DSPBTILEOFF); + PSB_WVDC32(dev_priv->saveDSPBCNTR, DSPBCNTR); + PSB_WVDC32(dev_priv->saveDSPBSURF, DSPBSURF); + + /* cursor B */ + PSB_WVDC32(dev_priv->saveDSPBCURSOR_CTRL, CURBCNTR); + PSB_WVDC32(dev_priv->saveDSPBCURSOR_POS, CURBPOS); + PSB_WVDC32(dev_priv->saveDSPBCURSOR_BASE, CURBBASE); + + /* restore palette */ + for (i = 0; i < 256; i++) + PSB_WVDC32(dev_priv->save_palette_b[i], PALETTE_B + (i << 2)); +} diff --git a/trunk/drivers/staging/gma500/mrst_hdmi_i2c.c b/trunk/drivers/staging/gma500/mrst_hdmi_i2c.c new file mode 100644 index 000000000000..36e7edc4d14c --- /dev/null +++ b/trunk/drivers/staging/gma500/mrst_hdmi_i2c.c @@ -0,0 +1,328 @@ +/* + * Copyright © 2010 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: + * Li Peng + */ + +#include +#include +#include +#include +#include +#include +#include "psb_drv.h" + +#define HDMI_READ(reg) readl(hdmi_dev->regs + (reg)) +#define HDMI_WRITE(reg, val) writel(val, hdmi_dev->regs + (reg)) + +#define HDMI_HCR 0x1000 +#define HCR_DETECT_HDP (1 << 6) +#define HCR_ENABLE_HDCP (1 << 5) +#define HCR_ENABLE_AUDIO (1 << 2) +#define HCR_ENABLE_PIXEL (1 << 1) +#define HCR_ENABLE_TMDS (1 << 0) +#define HDMI_HICR 0x1004 +#define HDMI_INTR_I2C_ERROR (1 << 4) +#define HDMI_INTR_I2C_FULL (1 << 3) +#define HDMI_INTR_I2C_DONE (1 << 2) +#define HDMI_INTR_HPD (1 << 0) +#define HDMI_HSR 0x1008 +#define HDMI_HISR 0x100C +#define HDMI_HI2CRDB0 0x1200 +#define HDMI_HI2CHCR 0x1240 +#define HI2C_HDCP_WRITE (0 << 2) +#define HI2C_HDCP_RI_READ (1 << 2) +#define HI2C_HDCP_READ (2 << 2) +#define HI2C_EDID_READ (3 << 2) +#define HI2C_READ_CONTINUE (1 << 1) +#define HI2C_ENABLE_TRANSACTION (1 << 0) + +#define HDMI_ICRH 0x1100 +#define HDMI_HI2CTDR0 0x1244 +#define HDMI_HI2CTDR1 0x1248 + +#define I2C_STAT_INIT 0 +#define I2C_READ_DONE 1 +#define I2C_TRANSACTION_DONE 2 + +struct hdmi_i2c_dev { + struct i2c_adapter *adap; + struct mutex i2c_lock; + struct completion complete; + int status; + struct i2c_msg *msg; + int buf_offset; +}; + +static void hdmi_i2c_irq_enable(struct mrst_hdmi_dev *hdmi_dev) +{ + u32 temp; + + temp = HDMI_READ(HDMI_HICR); + temp |= (HDMI_INTR_I2C_ERROR | HDMI_INTR_I2C_FULL | HDMI_INTR_I2C_DONE); + HDMI_WRITE(HDMI_HICR, temp); + HDMI_READ(HDMI_HICR); +} + +static void hdmi_i2c_irq_disable(struct mrst_hdmi_dev *hdmi_dev) +{ + HDMI_WRITE(HDMI_HICR, 0x0); + HDMI_READ(HDMI_HICR); +} + +static int xfer_read(struct i2c_adapter *adap, struct i2c_msg *pmsg) +{ + struct mrst_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap); + struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; + u32 temp; + + i2c_dev->status = I2C_STAT_INIT; + i2c_dev->msg = pmsg; + i2c_dev->buf_offset = 0; + INIT_COMPLETION(i2c_dev->complete); + + /* Enable I2C transaction */ + temp = ((pmsg->len) << 20) | HI2C_EDID_READ | HI2C_ENABLE_TRANSACTION; + HDMI_WRITE(HDMI_HI2CHCR, temp); + HDMI_READ(HDMI_HI2CHCR); + + while (i2c_dev->status != I2C_TRANSACTION_DONE) + wait_for_completion_interruptible_timeout(&i2c_dev->complete, + 10 * HZ); + + return 0; +} + +static int xfer_write(struct i2c_adapter *adap, struct i2c_msg *pmsg) +{ + /* + * XXX: i2c write seems isn't useful for EDID probe, don't do anything + */ + return 0; +} + +static int mrst_hdmi_i2c_access(struct i2c_adapter *adap, + struct i2c_msg *pmsg, + int num) +{ + struct mrst_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap); + struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; + int i, err = 0; + + mutex_lock(&i2c_dev->i2c_lock); + + /* Enable i2c unit */ + HDMI_WRITE(HDMI_ICRH, 0x00008760); + + /* Enable irq */ + hdmi_i2c_irq_enable(hdmi_dev); + for (i = 0; i < num; i++) { + if (pmsg->len && pmsg->buf) { + if (pmsg->flags & I2C_M_RD) + err = xfer_read(adap, pmsg); + else + err = xfer_write(adap, pmsg); + } + pmsg++; /* next message */ + } + + /* Disable irq */ + hdmi_i2c_irq_disable(hdmi_dev); + + mutex_unlock(&i2c_dev->i2c_lock); + + return i; +} + +static u32 mrst_hdmi_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR; +} + +static const struct i2c_algorithm mrst_hdmi_i2c_algorithm = { + .master_xfer = mrst_hdmi_i2c_access, + .functionality = mrst_hdmi_i2c_func, +}; + +static struct i2c_adapter mrst_hdmi_i2c_adapter = { + .name = "mrst_hdmi_i2c", + .nr = 3, + .owner = THIS_MODULE, + .class = I2C_CLASS_DDC, + .algo = &mrst_hdmi_i2c_algorithm, +}; + +static void hdmi_i2c_read(struct mrst_hdmi_dev *hdmi_dev) +{ + struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; + struct i2c_msg *msg = i2c_dev->msg; + u8 *buf = msg->buf; + u32 temp; + int i, offset; + + offset = i2c_dev->buf_offset; + for (i = 0; i < 0x10; i++) { + temp = HDMI_READ(HDMI_HI2CRDB0 + (i * 4)); + memcpy(buf + (offset + i * 4), &temp, 4); + } + i2c_dev->buf_offset += (0x10 * 4); + + /* clearing read buffer full intr */ + temp = HDMI_READ(HDMI_HISR); + HDMI_WRITE(HDMI_HISR, temp | HDMI_INTR_I2C_FULL); + HDMI_READ(HDMI_HISR); + + /* continue read transaction */ + temp = HDMI_READ(HDMI_HI2CHCR); + HDMI_WRITE(HDMI_HI2CHCR, temp | HI2C_READ_CONTINUE); + HDMI_READ(HDMI_HI2CHCR); + + i2c_dev->status = I2C_READ_DONE; + return; +} + +static void hdmi_i2c_transaction_done(struct mrst_hdmi_dev *hdmi_dev) +{ + struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; + u32 temp; + + /* clear transaction done intr */ + temp = HDMI_READ(HDMI_HISR); + HDMI_WRITE(HDMI_HISR, temp | HDMI_INTR_I2C_DONE); + HDMI_READ(HDMI_HISR); + + + temp = HDMI_READ(HDMI_HI2CHCR); + HDMI_WRITE(HDMI_HI2CHCR, temp & ~HI2C_ENABLE_TRANSACTION); + HDMI_READ(HDMI_HI2CHCR); + + i2c_dev->status = I2C_TRANSACTION_DONE; + return; +} + +static irqreturn_t mrst_hdmi_i2c_handler(int this_irq, void *dev) +{ + struct mrst_hdmi_dev *hdmi_dev = dev; + struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; + u32 stat; + + stat = HDMI_READ(HDMI_HISR); + + if (stat & HDMI_INTR_HPD) { + HDMI_WRITE(HDMI_HISR, stat | HDMI_INTR_HPD); + HDMI_READ(HDMI_HISR); + } + + if (stat & HDMI_INTR_I2C_FULL) + hdmi_i2c_read(hdmi_dev); + + if (stat & HDMI_INTR_I2C_DONE) + hdmi_i2c_transaction_done(hdmi_dev); + + complete(&i2c_dev->complete); + + return IRQ_HANDLED; +} + +/* + * choose alternate function 2 of GPIO pin 52, 53, + * which is used by HDMI I2C logic + */ +static void mrst_hdmi_i2c_gpio_fix(void) +{ + void *base; + unsigned int gpio_base = 0xff12c000; + int gpio_len = 0x1000; + u32 temp; + + base = ioremap((resource_size_t)gpio_base, gpio_len); + if (base == NULL) { + DRM_ERROR("gpio ioremap fail\n"); + return; + } + + temp = readl(base + 0x44); + DRM_DEBUG_DRIVER("old gpio val %x\n", temp); + writel((temp | 0x00000a00), (base + 0x44)); + temp = readl(base + 0x44); + DRM_DEBUG_DRIVER("new gpio val %x\n", temp); + + iounmap(base); +} + +int mrst_hdmi_i2c_init(struct pci_dev *dev) +{ + struct mrst_hdmi_dev *hdmi_dev; + struct hdmi_i2c_dev *i2c_dev; + int ret; + + hdmi_dev = pci_get_drvdata(dev); + + i2c_dev = kzalloc(sizeof(struct hdmi_i2c_dev), GFP_KERNEL); + if (i2c_dev == NULL) { + DRM_ERROR("Can't allocate interface\n"); + ret = -ENOMEM; + goto exit; + } + + i2c_dev->adap = &mrst_hdmi_i2c_adapter; + i2c_dev->status = I2C_STAT_INIT; + init_completion(&i2c_dev->complete); + mutex_init(&i2c_dev->i2c_lock); + i2c_set_adapdata(&mrst_hdmi_i2c_adapter, hdmi_dev); + hdmi_dev->i2c_dev = i2c_dev; + + /* Enable HDMI I2C function on gpio */ + mrst_hdmi_i2c_gpio_fix(); + + /* request irq */ + ret = request_irq(dev->irq, mrst_hdmi_i2c_handler, IRQF_SHARED, + mrst_hdmi_i2c_adapter.name, hdmi_dev); + if (ret) { + DRM_ERROR("Failed to request IRQ for I2C controller\n"); + goto err; + } + + /* Adapter registration */ + ret = i2c_add_numbered_adapter(&mrst_hdmi_i2c_adapter); + return ret; + +err: + kfree(i2c_dev); +exit: + return ret; +} + +void mrst_hdmi_i2c_exit(struct pci_dev *dev) +{ + struct mrst_hdmi_dev *hdmi_dev; + struct hdmi_i2c_dev *i2c_dev; + + hdmi_dev = pci_get_drvdata(dev); + if (i2c_del_adapter(&mrst_hdmi_i2c_adapter)) + DRM_DEBUG_DRIVER("Failed to delete hdmi-i2c adapter\n"); + + i2c_dev = hdmi_dev->i2c_dev; + kfree(i2c_dev); + free_irq(dev->irq, hdmi_dev); +} diff --git a/trunk/drivers/staging/gma500/mrst_lvds.c b/trunk/drivers/staging/gma500/mrst_lvds.c new file mode 100644 index 000000000000..e7999a2a3796 --- /dev/null +++ b/trunk/drivers/staging/gma500/mrst_lvds.c @@ -0,0 +1,407 @@ +/* + * Copyright © 2006-2009 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * 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. + * + * Authors: + * Eric Anholt + * Dave Airlie + * Jesse Barnes + */ + +#include +#include +#include + +#include "intel_bios.h" +#include "psb_drv.h" +#include "psb_intel_drv.h" +#include "psb_intel_reg.h" +#include "power.h" +#include + +/* The max/min PWM frequency in BPCR[31:17] - */ +/* The smallest number is 1 (not 0) that can fit in the + * 15-bit field of the and then*/ +/* shifts to the left by one bit to get the actual 16-bit + * value that the 15-bits correspond to.*/ +#define MRST_BLC_MAX_PWM_REG_FREQ 0xFFFF +#define BRIGHTNESS_MAX_LEVEL 100 + +/** + * Sets the power state for the panel. + */ +static void mrst_lvds_set_power(struct drm_device *dev, + struct psb_intel_output *output, bool on) +{ + u32 pp_status; + struct drm_psb_private *dev_priv = dev->dev_private; + + if (!gma_power_begin(dev, true)) + return; + + if (on) { + REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) | + POWER_TARGET_ON); + do { + pp_status = REG_READ(PP_STATUS); + } while ((pp_status & (PP_ON | PP_READY)) == PP_READY); + dev_priv->is_lvds_on = true; + if (dev_priv->ops->lvds_bl_power) + dev_priv->ops->lvds_bl_power(dev, true); + } else { + if (dev_priv->ops->lvds_bl_power) + dev_priv->ops->lvds_bl_power(dev, false); + REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) & + ~POWER_TARGET_ON); + do { + pp_status = REG_READ(PP_STATUS); + } while (pp_status & PP_ON); + dev_priv->is_lvds_on = false; + pm_request_idle(&dev->pdev->dev); + } + gma_power_end(dev); +} + +static void mrst_lvds_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + + if (mode == DRM_MODE_DPMS_ON) + mrst_lvds_set_power(dev, output, true); + else + mrst_lvds_set_power(dev, output, false); + + /* XXX: We never power down the LVDS pairs. */ +} + +static void mrst_lvds_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct psb_intel_mode_device *mode_dev = + enc_to_psb_intel_output(encoder)->mode_dev; + struct drm_device *dev = encoder->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + u32 lvds_port; + uint64_t v = DRM_MODE_SCALE_FULLSCREEN; + + if (!gma_power_begin(dev, true)) + return; + + /* + * The LVDS pin pair will already have been turned on in the + * psb_intel_crtc_mode_set since it has a large impact on the DPLL + * settings. + */ + lvds_port = (REG_READ(LVDS) & + (~LVDS_PIPEB_SELECT)) | + LVDS_PORT_EN | + LVDS_BORDER_EN; + + /* If the firmware says dither on Moorestown, or the BIOS does + on Oaktrail then enable dithering */ + if (mode_dev->panel_wants_dither || dev_priv->lvds_dither) + lvds_port |= MRST_PANEL_8TO6_DITHER_ENABLE; + + REG_WRITE(LVDS, lvds_port); + + drm_connector_property_get_value( + &enc_to_psb_intel_output(encoder)->base, + dev->mode_config.scaling_mode_property, + &v); + + if (v == DRM_MODE_SCALE_NO_SCALE) + REG_WRITE(PFIT_CONTROL, 0); + else if (v == DRM_MODE_SCALE_ASPECT) { + if ((mode->vdisplay != adjusted_mode->crtc_vdisplay) || + (mode->hdisplay != adjusted_mode->crtc_hdisplay)) { + if ((adjusted_mode->crtc_hdisplay * mode->vdisplay) == + (mode->hdisplay * adjusted_mode->crtc_vdisplay)) + REG_WRITE(PFIT_CONTROL, PFIT_ENABLE); + else if ((adjusted_mode->crtc_hdisplay * + mode->vdisplay) > (mode->hdisplay * + adjusted_mode->crtc_vdisplay)) + REG_WRITE(PFIT_CONTROL, PFIT_ENABLE | + PFIT_SCALING_MODE_PILLARBOX); + else + REG_WRITE(PFIT_CONTROL, PFIT_ENABLE | + PFIT_SCALING_MODE_LETTERBOX); + } else + REG_WRITE(PFIT_CONTROL, PFIT_ENABLE); + } else /*(v == DRM_MODE_SCALE_FULLSCREEN)*/ + REG_WRITE(PFIT_CONTROL, PFIT_ENABLE); + + gma_power_end(dev); +} + +static void mrst_lvds_prepare(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + struct psb_intel_mode_device *mode_dev = output->mode_dev; + + if (!gma_power_begin(dev, true)) + return; + + mode_dev->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL); + mode_dev->backlight_duty_cycle = (mode_dev->saveBLC_PWM_CTL & + BACKLIGHT_DUTY_CYCLE_MASK); + mrst_lvds_set_power(dev, output, false); + gma_power_end(dev); +} + +static u32 mrst_lvds_get_max_backlight(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + u32 ret; + + if (gma_power_begin(dev, false)) { + ret = ((REG_READ(BLC_PWM_CTL) & + BACKLIGHT_MODULATION_FREQ_MASK) >> + BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; + + gma_power_end(dev); + } else + ret = ((dev_priv->saveBLC_PWM_CTL & + BACKLIGHT_MODULATION_FREQ_MASK) >> + BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; + + return ret; +} + +static void mrst_lvds_commit(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + struct psb_intel_mode_device *mode_dev = output->mode_dev; + + if (mode_dev->backlight_duty_cycle == 0) + mode_dev->backlight_duty_cycle = + mrst_lvds_get_max_backlight(dev); + mrst_lvds_set_power(dev, output, true); +} + +static const struct drm_encoder_helper_funcs mrst_lvds_helper_funcs = { + .dpms = mrst_lvds_dpms, + .mode_fixup = psb_intel_lvds_mode_fixup, + .prepare = mrst_lvds_prepare, + .mode_set = mrst_lvds_mode_set, + .commit = mrst_lvds_commit, +}; + +static struct drm_display_mode lvds_configuration_modes[] = { + /* hard coded fixed mode for TPO LTPS LPJ040K001A */ + { DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER, 33264, 800, 836, + 846, 1056, 0, 480, 489, 491, 525, 0, 0) }, + /* hard coded fixed mode for LVDS 800x480 */ + { DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER, 30994, 800, 801, + 802, 1024, 0, 480, 481, 482, 525, 0, 0) }, + /* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */ + { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 53990, 1024, 1072, + 1104, 1184, 0, 600, 603, 604, 608, 0, 0) }, + /* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */ + { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 53990, 1024, 1104, + 1136, 1184, 0, 600, 603, 604, 608, 0, 0) }, + /* hard coded fixed mode for Sharp wsvga LVDS 1024x600 */ + { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 48885, 1024, 1124, + 1204, 1312, 0, 600, 607, 610, 621, 0, 0) }, + /* hard coded fixed mode for LVDS 1024x768 */ + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, + 1184, 1344, 0, 768, 771, 777, 806, 0, 0) }, + /* hard coded fixed mode for LVDS 1366x768 */ + { DRM_MODE("1366x768", DRM_MODE_TYPE_DRIVER, 77500, 1366, 1430, + 1558, 1664, 0, 768, 769, 770, 776, 0, 0) }, +}; + +/* Returns the panel fixed mode from configuration. */ + +static struct drm_display_mode * +mrst_lvds_get_configuration_mode(struct drm_device *dev) +{ + struct drm_display_mode *mode = NULL; + struct drm_psb_private *dev_priv = dev->dev_private; + struct mrst_timing_info *ti = &dev_priv->gct_data.DTD; + + if (dev_priv->vbt_data.size != 0x00) { /*if non-zero, then use vbt*/ + mode = kzalloc(sizeof(*mode), GFP_KERNEL); + if (!mode) + return NULL; + + mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo; + mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo; + mode->hsync_start = mode->hdisplay + \ + ((ti->hsync_offset_hi << 8) | \ + ti->hsync_offset_lo); + mode->hsync_end = mode->hsync_start + \ + ((ti->hsync_pulse_width_hi << 8) | \ + ti->hsync_pulse_width_lo); + mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) | \ + ti->hblank_lo); + mode->vsync_start = \ + mode->vdisplay + ((ti->vsync_offset_hi << 4) | \ + ti->vsync_offset_lo); + mode->vsync_end = \ + mode->vsync_start + ((ti->vsync_pulse_width_hi << 4) | \ + ti->vsync_pulse_width_lo); + mode->vtotal = mode->vdisplay + \ + ((ti->vblank_hi << 8) | ti->vblank_lo); + mode->clock = ti->pixel_clock * 10; +#if 0 + printk(KERN_INFO "hdisplay is %d\n", mode->hdisplay); + printk(KERN_INFO "vdisplay is %d\n", mode->vdisplay); + printk(KERN_INFO "HSS is %d\n", mode->hsync_start); + printk(KERN_INFO "HSE is %d\n", mode->hsync_end); + printk(KERN_INFO "htotal is %d\n", mode->htotal); + printk(KERN_INFO "VSS is %d\n", mode->vsync_start); + printk(KERN_INFO "VSE is %d\n", mode->vsync_end); + printk(KERN_INFO "vtotal is %d\n", mode->vtotal); + printk(KERN_INFO "clock is %d\n", mode->clock); +#endif + } else + mode = drm_mode_duplicate(dev, &lvds_configuration_modes[2]); + + drm_mode_set_name(mode); + drm_mode_set_crtcinfo(mode, 0); + + return mode; +} + +/** + * mrst_lvds_init - setup LVDS connectors on this device + * @dev: drm device + * + * Create the connector, register the LVDS DDC bus, and try to figure out what + * modes we can display on the LVDS panel (if present). + */ +void mrst_lvds_init(struct drm_device *dev, + struct psb_intel_mode_device *mode_dev) +{ + struct psb_intel_output *psb_intel_output; + struct drm_connector *connector; + struct drm_encoder *encoder; + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + struct edid *edid; + int ret = 0; + struct i2c_adapter *i2c_adap; + struct drm_display_mode *scan; /* *modes, *bios_mode; */ + + psb_intel_output = kzalloc(sizeof(struct psb_intel_output), GFP_KERNEL); + if (!psb_intel_output) + return; + + psb_intel_output->mode_dev = mode_dev; + connector = &psb_intel_output->base; + encoder = &psb_intel_output->enc; + dev_priv->is_lvds_on = true; + drm_connector_init(dev, &psb_intel_output->base, + &psb_intel_lvds_connector_funcs, + DRM_MODE_CONNECTOR_LVDS); + + drm_encoder_init(dev, &psb_intel_output->enc, &psb_intel_lvds_enc_funcs, + DRM_MODE_ENCODER_LVDS); + + drm_mode_connector_attach_encoder(&psb_intel_output->base, + &psb_intel_output->enc); + psb_intel_output->type = INTEL_OUTPUT_LVDS; + + drm_encoder_helper_add(encoder, &mrst_lvds_helper_funcs); + drm_connector_helper_add(connector, + &psb_intel_lvds_connector_helper_funcs); + connector->display_info.subpixel_order = SubPixelHorizontalRGB; + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + + drm_connector_attach_property(connector, + dev->mode_config.scaling_mode_property, + DRM_MODE_SCALE_FULLSCREEN); + drm_connector_attach_property(connector, + dev_priv->backlight_property, + BRIGHTNESS_MAX_LEVEL); + + mode_dev->panel_wants_dither = false; + if (dev_priv->vbt_data.size != 0x00) + mode_dev->panel_wants_dither = (dev_priv->gct_data. + Panel_Port_Control & MRST_PANEL_8TO6_DITHER_ENABLE); + + /* + * LVDS discovery: + * 1) check for EDID on DDC + * 2) check for VBT data + * 3) check to see if LVDS is already on + * if none of the above, no panel + * 4) make sure lid is open + * if closed, act like it's not there for now + */ + + i2c_adap = i2c_get_adapter(dev_priv->ops->i2c_bus); + + if (i2c_adap == NULL) + dev_err(dev->dev, "No ddc adapter available!\n"); + /* + * Attempt to get the fixed panel mode from DDC. Assume that the + * preferred mode is the right one. + */ + if (i2c_adap) { + edid = drm_get_edid(connector, i2c_adap); + if (edid) { + drm_mode_connector_update_edid_property(connector, + edid); + ret = drm_add_edid_modes(connector, edid); + kfree(edid); + } + + list_for_each_entry(scan, &connector->probed_modes, head) { + if (scan->type & DRM_MODE_TYPE_PREFERRED) { + mode_dev->panel_fixed_mode = + drm_mode_duplicate(dev, scan); + goto out; /* FIXME: check for quirks */ + } + } + } + /* + * If we didn't get EDID, try geting panel timing + * from configuration data + */ + mode_dev->panel_fixed_mode = mrst_lvds_get_configuration_mode(dev); + + if (mode_dev->panel_fixed_mode) { + mode_dev->panel_fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; + goto out; /* FIXME: check for quirks */ + } + + /* If we still don't have a mode after all that, give up. */ + if (!mode_dev->panel_fixed_mode) { + dev_err(dev->dev, "Found no modes on the lvds, ignoring the LVDS\n"); + goto failed_find; + } + +out: + drm_sysfs_connector_add(connector); + return; + +failed_find: + dev_dbg(dev->dev, "No LVDS modes found, disabling.\n"); + if (psb_intel_output->ddc_bus) + psb_intel_i2c_destroy(psb_intel_output->ddc_bus); + +/* failed_ddc: */ + + drm_encoder_cleanup(encoder); + drm_connector_cleanup(connector); + kfree(connector); +} + diff --git a/trunk/drivers/staging/gma500/power.c b/trunk/drivers/staging/gma500/power.c new file mode 100644 index 000000000000..408257038335 --- /dev/null +++ b/trunk/drivers/staging/gma500/power.c @@ -0,0 +1,318 @@ +/************************************************************************** + * Copyright (c) 2009-2011, Intel Corporation. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE 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: + * Benjamin Defnet + * Rajesh Poornachandran + * Massively reworked + * Alan Cox + */ + +#include "power.h" +#include "psb_drv.h" +#include "psb_reg.h" +#include "psb_intel_reg.h" +#include +#include + +static struct mutex power_mutex; /* Serialize power ops */ +static spinlock_t power_ctrl_lock; /* Serialize power claim */ + +/** + * gma_power_init - initialise power manager + * @dev: our device + * + * Set up for power management tracking of our hardware. + */ +void gma_power_init(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + + /* FIXME: Move APM/OSPM base into relevant device code */ + dev_priv->apm_base = dev_priv->apm_reg & 0xffff; + dev_priv->ospm_base &= 0xffff; + + dev_priv->display_power = true; /* We start active */ + dev_priv->display_count = 0; /* Currently no users */ + dev_priv->suspended = false; /* And not suspended */ + spin_lock_init(&power_ctrl_lock); + mutex_init(&power_mutex); + + dev_priv->ops->init_pm(dev); +} + +/** + * gma_power_uninit - end power manager + * @dev: device to end for + * + * Undo the effects of gma_power_init + */ +void gma_power_uninit(struct drm_device *dev) +{ + pm_runtime_disable(&dev->pdev->dev); + pm_runtime_set_suspended(&dev->pdev->dev); +} + +/** + * gma_suspend_display - suspend the display logic + * @dev: our DRM device + * + * Suspend the display logic of the graphics interface + */ +static void gma_suspend_display(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + + if (!dev_priv->display_power) + return; + dev_priv->ops->save_regs(dev); + dev_priv->ops->power_down(dev); + dev_priv->display_power = false; +} + +/** + * gma_resume_display - resume display side logic + * + * Resume the display hardware restoring state and enabling + * as necessary. + */ +static void gma_resume_display(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + struct drm_psb_private *dev_priv = dev->dev_private; + + if (dev_priv->display_power) + return; + + /* turn on the display power island */ + dev_priv->ops->power_up(dev); + dev_priv->suspended = false; + dev_priv->display_power = true; + + PSB_WVDC32(dev_priv->pge_ctl | _PSB_PGETBL_ENABLED, PSB_PGETBL_CTL); + pci_write_config_word(pdev, PSB_GMCH_CTRL, + dev_priv->gmch_ctrl | _PSB_GMCH_ENABLED); + dev_priv->ops->restore_regs(dev); +} + +/** + * gma_suspend_pci - suspend PCI side + * @pdev: PCI device + * + * Perform the suspend processing on our PCI device state + */ +static void gma_suspend_pci(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + struct drm_psb_private *dev_priv = dev->dev_private; + int bsm, vbt; + + if (dev_priv->suspended) + return; + + pci_save_state(pdev); + pci_read_config_dword(pdev, 0x5C, &bsm); + dev_priv->saveBSM = bsm; + pci_read_config_dword(pdev, 0xFC, &vbt); + dev_priv->saveVBT = vbt; + pci_read_config_dword(pdev, PSB_PCIx_MSI_ADDR_LOC, &dev_priv->msi_addr); + pci_read_config_dword(pdev, PSB_PCIx_MSI_DATA_LOC, &dev_priv->msi_data); + + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + + dev_priv->suspended = true; +} + +/** + * gma_resume_pci - resume helper + * @dev: our PCI device + * + * Perform the resume processing on our PCI device state - rewrite + * register state and re-enable the PCI device + */ +static bool gma_resume_pci(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + struct drm_psb_private *dev_priv = dev->dev_private; + int ret; + + if (!dev_priv->suspended) + return true; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + pci_write_config_dword(pdev, 0x5c, dev_priv->saveBSM); + pci_write_config_dword(pdev, 0xFC, dev_priv->saveVBT); + /* restoring MSI address and data in PCIx space */ + pci_write_config_dword(pdev, PSB_PCIx_MSI_ADDR_LOC, dev_priv->msi_addr); + pci_write_config_dword(pdev, PSB_PCIx_MSI_DATA_LOC, dev_priv->msi_data); + ret = pci_enable_device(pdev); + + if (ret != 0) + dev_err(&pdev->dev, "pci_enable failed: %d\n", ret); + else + dev_priv->suspended = false; + return !dev_priv->suspended; +} + +/** + * gma_power_suspend - bus callback for suspend + * @pdev: our PCI device + * @state: suspend type + * + * Called back by the PCI layer during a suspend of the system. We + * perform the necessary shut down steps and save enough state that + * we can undo this when resume is called. + */ +int gma_power_suspend(struct device *_dev) +{ + struct pci_dev *pdev = container_of(_dev, struct pci_dev, dev); + struct drm_device *dev = pci_get_drvdata(pdev); + struct drm_psb_private *dev_priv = dev->dev_private; + + mutex_lock(&power_mutex); + if (!dev_priv->suspended) { + if (dev_priv->display_count) { + mutex_unlock(&power_mutex); + return -EBUSY; + } + psb_irq_uninstall(dev); + gma_suspend_display(dev); + gma_suspend_pci(pdev); + } + mutex_unlock(&power_mutex); + return 0; +} + +/** + * gma_power_resume - resume power + * @pdev: PCI device + * + * Resume the PCI side of the graphics and then the displays + */ +int gma_power_resume(struct device *_dev) +{ + struct pci_dev *pdev = container_of(_dev, struct pci_dev, dev); + struct drm_device *dev = pci_get_drvdata(pdev); + + mutex_lock(&power_mutex); + gma_resume_pci(pdev); + gma_resume_display(pdev); + psb_irq_preinstall(dev); + psb_irq_postinstall(dev); + mutex_unlock(&power_mutex); + return 0; +} + +/** + * gma_power_is_on - returne true if power is on + * @dev: our DRM device + * + * Returns true if the display island power is on at this moment + */ +bool gma_power_is_on(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + return dev_priv->display_power; +} + +/** + * gma_power_begin - begin requiring power + * @dev: our DRM device + * @force_on: true to force power on + * + * Begin an action that requires the display power island is enabled. + * We refcount the islands. + */ +bool gma_power_begin(struct drm_device *dev, bool force_on) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + int ret; + unsigned long flags; + + spin_lock_irqsave(&power_ctrl_lock, flags); + /* Power already on ? */ + if (dev_priv->display_power) { + dev_priv->display_count++; + pm_runtime_get(&dev->pdev->dev); + spin_unlock_irqrestore(&power_ctrl_lock, flags); + return true; + } + if (force_on == false) + goto out_false; + + /* Ok power up needed */ + ret = gma_resume_pci(dev->pdev); + if (ret == 0) { + /* FIXME: we want to defer this for Medfield/Oaktrail */ + gma_resume_display(dev->pdev); + psb_irq_preinstall(dev); + psb_irq_postinstall(dev); + pm_runtime_get(&dev->pdev->dev); + dev_priv->display_count++; + spin_unlock_irqrestore(&power_ctrl_lock, flags); + return true; + } +out_false: + spin_unlock_irqrestore(&power_ctrl_lock, flags); + return false; +} + +/** + * gma_power_end - end use of power + * @dev: Our DRM device + * + * Indicate that one of our gma_power_begin() requested periods when + * the diplay island power is needed has completed. + */ +void gma_power_end(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + unsigned long flags; + spin_lock_irqsave(&power_ctrl_lock, flags); + dev_priv->display_count--; + WARN_ON(dev_priv->display_count < 0); + spin_unlock_irqrestore(&power_ctrl_lock, flags); + pm_runtime_put(&dev->pdev->dev); +} + +int psb_runtime_suspend(struct device *dev) +{ + return gma_power_suspend(dev); +} + +int psb_runtime_resume(struct device *dev) +{ + return gma_power_resume(dev);; +} + +int psb_runtime_idle(struct device *dev) +{ + struct drm_device *drmdev = pci_get_drvdata(to_pci_dev(dev)); + struct drm_psb_private *dev_priv = drmdev->dev_private; + if (dev_priv->display_count) + return 0; + else + return 1; +} diff --git a/trunk/drivers/staging/gma500/power.h b/trunk/drivers/staging/gma500/power.h new file mode 100644 index 000000000000..1969d2ecb328 --- /dev/null +++ b/trunk/drivers/staging/gma500/power.h @@ -0,0 +1,67 @@ +/************************************************************************** + * Copyright (c) 2009-2011, Intel Corporation. + * All Rights Reserved. + + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE 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: + * Benjamin Defnet + * Rajesh Poornachandran + * Massively reworked + * Alan Cox + */ +#ifndef _PSB_POWERMGMT_H_ +#define _PSB_POWERMGMT_H_ + +#include +#include + +void gma_power_init(struct drm_device *dev); +void gma_power_uninit(struct drm_device *dev); + +/* + * The kernel bus power management will call these functions + */ +int gma_power_suspend(struct device *dev); +int gma_power_resume(struct device *dev); + +/* + * These are the functions the driver should use to wrap all hw access + * (i.e. register reads and writes) + */ +bool gma_power_begin(struct drm_device *dev, bool force); +void gma_power_end(struct drm_device *dev); + +/* + * Use this function to do an instantaneous check for if the hw is on. + * Only use this in cases where you know the mutex is already held such + * as in irq install/uninstall and you need to + * prevent a deadlock situation. Otherwise use gma_power_begin(). + */ +bool gma_power_is_on(struct drm_device *dev); + +/* + * GFX-Runtime PM callbacks + */ +int psb_runtime_suspend(struct device *dev); +int psb_runtime_resume(struct device *dev); +int psb_runtime_idle(struct device *dev); + +#endif /*_PSB_POWERMGMT_H_*/ diff --git a/trunk/drivers/staging/gma500/psb_device.c b/trunk/drivers/staging/gma500/psb_device.c new file mode 100644 index 000000000000..b97aa78519f2 --- /dev/null +++ b/trunk/drivers/staging/gma500/psb_device.c @@ -0,0 +1,321 @@ +/************************************************************************** + * 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 +#include +#include +#include "psb_drm.h" +#include "psb_drv.h" +#include "psb_reg.h" +#include "psb_intel_reg.h" +#include "intel_bios.h" + + +static int psb_output_init(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + psb_intel_lvds_init(dev, &dev_priv->mode_dev); + psb_intel_sdvo_init(dev, SDVOB); + return 0; +} + +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + +/* + * Poulsbo Backlight Interfaces + */ + +#define BLC_PWM_PRECISION_FACTOR 100 /* 10000000 */ +#define BLC_PWM_FREQ_CALC_CONSTANT 32 +#define MHz 1000000 + +#define PSB_BLC_PWM_PRECISION_FACTOR 10 +#define PSB_BLC_MAX_PWM_REG_FREQ 0xFFFE +#define PSB_BLC_MIN_PWM_REG_FREQ 0x2 + +#define PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE) +#define PSB_BACKLIGHT_PWM_CTL_SHIFT (16) + +static int psb_brightness; +static struct backlight_device *psb_backlight_device; + +static int psb_get_brightness(struct backlight_device *bd) +{ + /* return locally cached var instead of HW read (due to DPST etc.) */ + /* FIXME: ideally return actual value in case firmware fiddled with + it */ + return psb_brightness; +} + + +static int psb_backlight_setup(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + unsigned long core_clock; + /* u32 bl_max_freq; */ + /* unsigned long value; */ + u16 bl_max_freq; + uint32_t value; + uint32_t blc_pwm_precision_factor; + + /* get bl_max_freq and pol from dev_priv*/ + if (!dev_priv->lvds_bl) { + dev_err(dev->dev, "Has no valid LVDS backlight info\n"); + return -ENOENT; + } + bl_max_freq = dev_priv->lvds_bl->freq; + blc_pwm_precision_factor = PSB_BLC_PWM_PRECISION_FACTOR; + + core_clock = dev_priv->core_freq; + + value = (core_clock * MHz) / BLC_PWM_FREQ_CALC_CONSTANT; + value *= blc_pwm_precision_factor; + value /= bl_max_freq; + value /= blc_pwm_precision_factor; + + if (value > (unsigned long long)PSB_BLC_MAX_PWM_REG_FREQ || + value < (unsigned long long)PSB_BLC_MIN_PWM_REG_FREQ) + return -ERANGE; + else { + value &= PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR; + REG_WRITE(BLC_PWM_CTL, + (value << PSB_BACKLIGHT_PWM_CTL_SHIFT) | (value)); + } + return 0; +} + +static int psb_set_brightness(struct backlight_device *bd) +{ + struct drm_device *dev = bl_get_data(psb_backlight_device); + int level = bd->props.brightness; + + /* Percentage 1-100% being valid */ + if (level < 1) + level = 1; + + psb_intel_lvds_set_brightness(dev, level); + psb_brightness = level; + return 0; +} + +static const struct backlight_ops psb_ops = { + .get_brightness = psb_get_brightness, + .update_status = psb_set_brightness, +}; + +static int psb_backlight_init(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + int ret; + struct backlight_properties props; + + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = 100; + props.type = BACKLIGHT_PLATFORM; + + psb_backlight_device = backlight_device_register("psb-bl", + NULL, (void *)dev, &psb_ops, &props); + if (IS_ERR(psb_backlight_device)) + return PTR_ERR(psb_backlight_device); + + ret = psb_backlight_setup(dev); + if (ret < 0) { + backlight_device_unregister(psb_backlight_device); + psb_backlight_device = NULL; + return ret; + } + psb_backlight_device->props.brightness = 100; + psb_backlight_device->props.max_brightness = 100; + backlight_update_status(psb_backlight_device); + dev_priv->backlight_device = psb_backlight_device; + return 0; +} + +#endif + +/* + * Provide the Poulsbo specific chip logic and low level methods + * for power management + */ + +static void psb_init_pm(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + + u32 gating = PSB_RSGX32(PSB_CR_CLKGATECTL); + gating &= ~3; /* Disable 2D clock gating */ + gating |= 1; + PSB_WSGX32(gating, PSB_CR_CLKGATECTL); + PSB_RSGX32(PSB_CR_CLKGATECTL); +} + +/** + * psb_save_display_registers - save registers lost on suspend + * @dev: our DRM device + * + * Save the state we need in order to be able to restore the interface + * upon resume from suspend + */ +static int psb_save_display_registers(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc; + struct drm_connector *connector; + + /* Display arbitration control + watermarks */ + dev_priv->saveDSPARB = PSB_RVDC32(DSPARB); + dev_priv->saveDSPFW1 = PSB_RVDC32(DSPFW1); + dev_priv->saveDSPFW2 = PSB_RVDC32(DSPFW2); + dev_priv->saveDSPFW3 = PSB_RVDC32(DSPFW3); + dev_priv->saveDSPFW4 = PSB_RVDC32(DSPFW4); + dev_priv->saveDSPFW5 = PSB_RVDC32(DSPFW5); + dev_priv->saveDSPFW6 = PSB_RVDC32(DSPFW6); + dev_priv->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT); + + /* Save crtc and output state */ + mutex_lock(&dev->mode_config.mutex); + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (drm_helper_crtc_in_use(crtc)) + crtc->funcs->save(crtc); + } + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) + connector->funcs->save(connector); + + mutex_unlock(&dev->mode_config.mutex); + return 0; +} + +/** + * psb_restore_display_registers - restore lost register state + * @dev: our DRM device + * + * Restore register state that was lost during suspend and resume. + */ +static int psb_restore_display_registers(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc; + struct drm_connector *connector; + + /* Display arbitration + watermarks */ + PSB_WVDC32(dev_priv->saveDSPARB, DSPARB); + PSB_WVDC32(dev_priv->saveDSPFW1, DSPFW1); + PSB_WVDC32(dev_priv->saveDSPFW2, DSPFW2); + PSB_WVDC32(dev_priv->saveDSPFW3, DSPFW3); + PSB_WVDC32(dev_priv->saveDSPFW4, DSPFW4); + PSB_WVDC32(dev_priv->saveDSPFW5, DSPFW5); + PSB_WVDC32(dev_priv->saveDSPFW6, DSPFW6); + PSB_WVDC32(dev_priv->saveCHICKENBIT, DSPCHICKENBIT); + + /*make sure VGA plane is off. it initializes to on after reset!*/ + PSB_WVDC32(0x80000000, VGACNTRL); + + mutex_lock(&dev->mode_config.mutex); + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + if (drm_helper_crtc_in_use(crtc)) + crtc->funcs->restore(crtc); + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) + connector->funcs->restore(connector); + + mutex_unlock(&dev->mode_config.mutex); + return 0; +} + +static int psb_power_down(struct drm_device *dev) +{ + return 0; +} + +static int psb_power_up(struct drm_device *dev) +{ + return 0; +} + +static void psb_get_core_freq(struct drm_device *dev) +{ + uint32_t clock; + struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0); + struct drm_psb_private *dev_priv = dev->dev_private; + + /*pci_write_config_dword(pci_root, 0xD4, 0x00C32004);*/ + /*pci_write_config_dword(pci_root, 0xD0, 0xE0033000);*/ + + pci_write_config_dword(pci_root, 0xD0, 0xD0050300); + pci_read_config_dword(pci_root, 0xD4, &clock); + pci_dev_put(pci_root); + + switch (clock & 0x07) { + case 0: + dev_priv->core_freq = 100; + break; + case 1: + dev_priv->core_freq = 133; + break; + case 2: + dev_priv->core_freq = 150; + break; + case 3: + dev_priv->core_freq = 178; + break; + case 4: + dev_priv->core_freq = 200; + break; + case 5: + case 6: + case 7: + dev_priv->core_freq = 266; + default: + dev_priv->core_freq = 0; + } +} + +static int psb_chip_setup(struct drm_device *dev) +{ + psb_get_core_freq(dev); + gma_intel_opregion_init(dev); + psb_intel_init_bios(dev); + return 0; +} + +const struct psb_ops psb_chip_ops = { + .name = "Poulsbo", + .accel_2d = 1, + .pipes = 2, + .crtcs = 2, + .sgx_offset = PSB_SGX_OFFSET, + .chip_setup = psb_chip_setup, + + .crtc_helper = &psb_intel_helper_funcs, + .crtc_funcs = &psb_intel_crtc_funcs, + + .output_init = psb_output_init, + +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + .backlight_init = psb_backlight_init, +#endif + + .init_pm = psb_init_pm, + .save_regs = psb_save_display_registers, + .restore_regs = psb_restore_display_registers, + .power_down = psb_power_down, + .power_up = psb_power_up, +}; + diff --git a/trunk/drivers/staging/gma500/psb_drm.h b/trunk/drivers/staging/gma500/psb_drm.h new file mode 100644 index 000000000000..0da846835688 --- /dev/null +++ b/trunk/drivers/staging/gma500/psb_drm.h @@ -0,0 +1,219 @@ +/************************************************************************** + * Copyright (c) 2007-2011, Intel Corporation. + * All Rights Reserved. + * Copyright (c) 2008, Tungsten Graphics Inc. Cedar Park, TX., USA. + * 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. + * + **************************************************************************/ + +#ifndef _PSB_DRM_H_ +#define _PSB_DRM_H_ + +#define PSB_NUM_PIPE 3 + +#define PSB_GPU_ACCESS_READ (1ULL << 32) +#define PSB_GPU_ACCESS_WRITE (1ULL << 33) +#define PSB_GPU_ACCESS_MASK (PSB_GPU_ACCESS_READ | PSB_GPU_ACCESS_WRITE) + +#define PSB_BO_FLAG_COMMAND (1ULL << 52) + +/* + * Feedback components: + */ + +struct drm_psb_sizes_arg { + u32 ta_mem_size; + u32 mmu_size; + u32 pds_size; + u32 rastgeom_size; + u32 tt_size; + u32 vram_size; +}; + +struct drm_psb_dpst_lut_arg { + uint8_t lut[256]; + int output_id; +}; + +#define PSB_DC_CRTC_SAVE 0x01 +#define PSB_DC_CRTC_RESTORE 0x02 +#define PSB_DC_OUTPUT_SAVE 0x04 +#define PSB_DC_OUTPUT_RESTORE 0x08 +#define PSB_DC_CRTC_MASK 0x03 +#define PSB_DC_OUTPUT_MASK 0x0C + +struct drm_psb_dc_state_arg { + u32 flags; + u32 obj_id; +}; + +struct drm_psb_mode_operation_arg { + u32 obj_id; + u16 operation; + struct drm_mode_modeinfo mode; + void *data; +}; + +struct drm_psb_stolen_memory_arg { + u32 base; + u32 size; +}; + +/*Display Register Bits*/ +#define REGRWBITS_PFIT_CONTROLS (1 << 0) +#define REGRWBITS_PFIT_AUTOSCALE_RATIOS (1 << 1) +#define REGRWBITS_PFIT_PROGRAMMED_SCALE_RATIOS (1 << 2) +#define REGRWBITS_PIPEASRC (1 << 3) +#define REGRWBITS_PIPEBSRC (1 << 4) +#define REGRWBITS_VTOTAL_A (1 << 5) +#define REGRWBITS_VTOTAL_B (1 << 6) +#define REGRWBITS_DSPACNTR (1 << 8) +#define REGRWBITS_DSPBCNTR (1 << 9) +#define REGRWBITS_DSPCCNTR (1 << 10) + +/*Overlay Register Bits*/ +#define OV_REGRWBITS_OVADD (1 << 0) +#define OV_REGRWBITS_OGAM_ALL (1 << 1) + +#define OVC_REGRWBITS_OVADD (1 << 2) +#define OVC_REGRWBITS_OGAM_ALL (1 << 3) + +struct drm_psb_register_rw_arg { + u32 b_force_hw_on; + + u32 display_read_mask; + u32 display_write_mask; + + struct { + u32 pfit_controls; + u32 pfit_autoscale_ratios; + u32 pfit_programmed_scale_ratios; + u32 pipeasrc; + u32 pipebsrc; + u32 vtotal_a; + u32 vtotal_b; + } display; + + u32 overlay_read_mask; + u32 overlay_write_mask; + + struct { + u32 OVADD; + u32 OGAMC0; + u32 OGAMC1; + u32 OGAMC2; + u32 OGAMC3; + u32 OGAMC4; + u32 OGAMC5; + u32 IEP_ENABLED; + u32 IEP_BLE_MINMAX; + u32 IEP_BSSCC_CONTROL; + u32 b_wait_vblank; + } overlay; + + u32 sprite_enable_mask; + u32 sprite_disable_mask; + + struct { + u32 dspa_control; + u32 dspa_key_value; + u32 dspa_key_mask; + u32 dspc_control; + u32 dspc_stride; + u32 dspc_position; + u32 dspc_linear_offset; + u32 dspc_size; + u32 dspc_surface; + } sprite; + + u32 subpicture_enable_mask; + u32 subpicture_disable_mask; +}; + +/* Controlling the kernel modesetting buffers */ + +#define DRM_PSB_SIZES 0x07 +#define DRM_PSB_FUSE_REG 0x08 +#define DRM_PSB_DC_STATE 0x0A +#define DRM_PSB_ADB 0x0B +#define DRM_PSB_MODE_OPERATION 0x0C +#define DRM_PSB_STOLEN_MEMORY 0x0D +#define DRM_PSB_REGISTER_RW 0x0E + +/* + * NOTE: Add new commands here, but increment + * the values below and increment their + * corresponding defines where they're + * defined elsewhere. + */ + +#define DRM_PSB_GEM_CREATE 0x10 +#define DRM_PSB_2D_OP 0x11 +#define DRM_PSB_GEM_MMAP 0x12 +#define DRM_PSB_DPST 0x1B +#define DRM_PSB_GAMMA 0x1C +#define DRM_PSB_DPST_BL 0x1D +#define DRM_PSB_GET_PIPE_FROM_CRTC_ID 0x1F + +#define PSB_MODE_OPERATION_MODE_VALID 0x01 +#define PSB_MODE_OPERATION_SET_DC_BASE 0x02 + +struct drm_psb_get_pipe_from_crtc_id_arg { + /** ID of CRTC being requested **/ + u32 crtc_id; + + /** pipe of requested CRTC **/ + u32 pipe; +}; + +/* FIXME: move this into a medfield header once we are sure it isn't needed for an + ioctl */ +struct psb_drm_dpu_rect { + int x, y; + int width, height; +}; + +struct drm_psb_gem_create { + __u64 size; + __u32 handle; + __u32 flags; +#define PSB_GEM_CREATE_STOLEN 1 /* Stolen memory can be used */ +}; + +#define PSB_2D_OP_BUFLEN 16 + +struct drm_psb_2d_op { + __u32 src; /* Handles, only src supported right now */ + __u32 dst; + __u32 mask; + __u32 pat; + __u32 size; /* In dwords of command */ + __u32 spare; /* And bumps array to u64 align */ + __u32 cmd[PSB_2D_OP_BUFLEN]; +}; + +struct drm_psb_gem_mmap { + __u32 handle; + __u32 pad; + /** + * Fake offset to use for subsequent mmap call + * + * This is a fixed-size type for 32/64 compatibility. + */ + __u64 offset; +}; + +#endif diff --git a/trunk/drivers/staging/gma500/psb_drv.c b/trunk/drivers/staging/gma500/psb_drv.c new file mode 100644 index 000000000000..95816808f867 --- /dev/null +++ b/trunk/drivers/staging/gma500/psb_drv.c @@ -0,0 +1,1230 @@ +/************************************************************************** + * Copyright (c) 2007-2011, Intel Corporation. + * All Rights Reserved. + * Copyright (c) 2008, Tungsten Graphics, Inc. Cedar Park, TX., USA. + * 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 +#include +#include "psb_drm.h" +#include "psb_drv.h" +#include "framebuffer.h" +#include "psb_reg.h" +#include "psb_intel_reg.h" +#include "intel_bios.h" +#include "mid_bios.h" +#include "mdfld_dsi_dbi.h" +#include +#include "power.h" +#include +#include +#include +#include +#include +#include + +static int drm_psb_trap_pagefaults; + +int drm_psb_no_fb; + +static int psb_probe(struct pci_dev *pdev, const struct pci_device_id *ent); + +MODULE_PARM_DESC(no_fb, "Disable FBdev"); +MODULE_PARM_DESC(trap_pagefaults, "Error and reset on MMU pagefaults"); +module_param_named(no_fb, drm_psb_no_fb, int, 0600); +module_param_named(trap_pagefaults, drm_psb_trap_pagefaults, int, 0600); + + +static DEFINE_PCI_DEVICE_TABLE(pciidlist) = { + { 0x8086, 0x8108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &psb_chip_ops }, + { 0x8086, 0x8109, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &psb_chip_ops }, +#if defined(CONFIG_DRM_PSB_MRST) + { 0x8086, 0x4100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mrst_chip_ops}, + { 0x8086, 0x4101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mrst_chip_ops}, + { 0x8086, 0x4102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mrst_chip_ops}, + { 0x8086, 0x4103, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mrst_chip_ops}, + { 0x8086, 0x4104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mrst_chip_ops}, + { 0x8086, 0x4105, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mrst_chip_ops}, + { 0x8086, 0x4106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mrst_chip_ops}, + { 0x8086, 0x4107, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mrst_chip_ops}, +#endif +#if defined(CONFIG_DRM_PSB_MFLD) + { 0x8086, 0x0130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, + { 0x8086, 0x0131, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, + { 0x8086, 0x0132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, + { 0x8086, 0x0133, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, + { 0x8086, 0x0134, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, + { 0x8086, 0x0135, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, + { 0x8086, 0x0136, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, + { 0x8086, 0x0137, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, +#endif +#if defined(CONFIG_DRM_PSB_CDV) + { 0x8086, 0x0be0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, + { 0x8086, 0x0be1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, + { 0x8086, 0x0be2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, + { 0x8086, 0x0be3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, + { 0x8086, 0x0be4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, + { 0x8086, 0x0be5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, + { 0x8086, 0x0be6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, + { 0x8086, 0x0be7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, +#endif + { 0, 0, 0} +}; +MODULE_DEVICE_TABLE(pci, pciidlist); + +/* + * Standard IOCTLs. + */ + +#define DRM_IOCTL_PSB_SIZES \ + DRM_IOR(DRM_PSB_SIZES + DRM_COMMAND_BASE, \ + struct drm_psb_sizes_arg) +#define DRM_IOCTL_PSB_FUSE_REG \ + DRM_IOWR(DRM_PSB_FUSE_REG + DRM_COMMAND_BASE, uint32_t) +#define DRM_IOCTL_PSB_DC_STATE \ + DRM_IOW(DRM_PSB_DC_STATE + DRM_COMMAND_BASE, \ + struct drm_psb_dc_state_arg) +#define DRM_IOCTL_PSB_ADB \ + DRM_IOWR(DRM_PSB_ADB + DRM_COMMAND_BASE, uint32_t) +#define DRM_IOCTL_PSB_MODE_OPERATION \ + DRM_IOWR(DRM_PSB_MODE_OPERATION + DRM_COMMAND_BASE, \ + struct drm_psb_mode_operation_arg) +#define DRM_IOCTL_PSB_STOLEN_MEMORY \ + DRM_IOWR(DRM_PSB_STOLEN_MEMORY + DRM_COMMAND_BASE, \ + struct drm_psb_stolen_memory_arg) +#define DRM_IOCTL_PSB_REGISTER_RW \ + DRM_IOWR(DRM_PSB_REGISTER_RW + DRM_COMMAND_BASE, \ + struct drm_psb_register_rw_arg) +#define DRM_IOCTL_PSB_DPST \ + DRM_IOWR(DRM_PSB_DPST + DRM_COMMAND_BASE, \ + uint32_t) +#define DRM_IOCTL_PSB_GAMMA \ + DRM_IOWR(DRM_PSB_GAMMA + DRM_COMMAND_BASE, \ + struct drm_psb_dpst_lut_arg) +#define DRM_IOCTL_PSB_DPST_BL \ + DRM_IOWR(DRM_PSB_DPST_BL + DRM_COMMAND_BASE, \ + uint32_t) +#define DRM_IOCTL_PSB_GET_PIPE_FROM_CRTC_ID \ + DRM_IOWR(DRM_PSB_GET_PIPE_FROM_CRTC_ID + DRM_COMMAND_BASE, \ + struct drm_psb_get_pipe_from_crtc_id_arg) +#define DRM_IOCTL_PSB_GEM_CREATE \ + DRM_IOWR(DRM_PSB_GEM_CREATE + DRM_COMMAND_BASE, \ + struct drm_psb_gem_create) +#define DRM_IOCTL_PSB_2D_OP \ + DRM_IOW(DRM_PSB_2D_OP + DRM_COMMAND_BASE, \ + struct drm_psb_2d_op) +#define DRM_IOCTL_PSB_GEM_MMAP \ + DRM_IOWR(DRM_PSB_GEM_MMAP + DRM_COMMAND_BASE, \ + struct drm_psb_gem_mmap) + +static int psb_sizes_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +static int psb_dc_state_ioctl(struct drm_device *dev, void * data, + struct drm_file *file_priv); +static int psb_adb_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +static int psb_mode_operation_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +static int psb_stolen_memory_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +static int psb_register_rw_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +static int psb_dpst_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +static int psb_gamma_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +static int psb_dpst_bl_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +#define PSB_IOCTL_DEF(ioctl, func, flags) \ + [DRM_IOCTL_NR(ioctl) - DRM_COMMAND_BASE] = {ioctl, flags, func} + +static struct drm_ioctl_desc psb_ioctls[] = { + PSB_IOCTL_DEF(DRM_IOCTL_PSB_SIZES, psb_sizes_ioctl, DRM_AUTH), + PSB_IOCTL_DEF(DRM_IOCTL_PSB_DC_STATE, psb_dc_state_ioctl, DRM_AUTH), + PSB_IOCTL_DEF(DRM_IOCTL_PSB_ADB, psb_adb_ioctl, DRM_AUTH), + PSB_IOCTL_DEF(DRM_IOCTL_PSB_MODE_OPERATION, psb_mode_operation_ioctl, + DRM_AUTH), + PSB_IOCTL_DEF(DRM_IOCTL_PSB_STOLEN_MEMORY, psb_stolen_memory_ioctl, + DRM_AUTH), + PSB_IOCTL_DEF(DRM_IOCTL_PSB_REGISTER_RW, psb_register_rw_ioctl, + DRM_AUTH), + PSB_IOCTL_DEF(DRM_IOCTL_PSB_DPST, psb_dpst_ioctl, DRM_AUTH), + PSB_IOCTL_DEF(DRM_IOCTL_PSB_GAMMA, psb_gamma_ioctl, DRM_AUTH), + PSB_IOCTL_DEF(DRM_IOCTL_PSB_DPST_BL, psb_dpst_bl_ioctl, DRM_AUTH), + PSB_IOCTL_DEF(DRM_IOCTL_PSB_GET_PIPE_FROM_CRTC_ID, + psb_intel_get_pipe_from_crtc_id, 0), + PSB_IOCTL_DEF(DRM_IOCTL_PSB_GEM_CREATE, psb_gem_create_ioctl, + DRM_UNLOCKED | DRM_AUTH), + PSB_IOCTL_DEF(DRM_IOCTL_PSB_2D_OP, psb_accel_ioctl, + DRM_UNLOCKED| DRM_AUTH), + PSB_IOCTL_DEF(DRM_IOCTL_PSB_GEM_MMAP, psb_gem_mmap_ioctl, + DRM_UNLOCKED | DRM_AUTH), +}; + +static void psb_lastclose(struct drm_device *dev) +{ + return; +} + +static void psb_do_takedown(struct drm_device *dev) +{ +} + +static int psb_do_init(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_gtt *pg = &dev_priv->gtt; + + uint32_t stolen_gtt; + + int ret = -ENOMEM; + + if (pg->mmu_gatt_start & 0x0FFFFFFF) { + dev_err(dev->dev, "Gatt must be 256M aligned. This is a bug.\n"); + ret = -EINVAL; + goto out_err; + } + + + stolen_gtt = (pg->stolen_size >> PAGE_SHIFT) * 4; + stolen_gtt = (stolen_gtt + PAGE_SIZE - 1) >> PAGE_SHIFT; + stolen_gtt = + (stolen_gtt < pg->gtt_pages) ? stolen_gtt : pg->gtt_pages; + + dev_priv->gatt_free_offset = pg->mmu_gatt_start + + (stolen_gtt << PAGE_SHIFT) * 1024; + + if (1 || drm_debug) { + uint32_t core_id = PSB_RSGX32(PSB_CR_CORE_ID); + uint32_t core_rev = PSB_RSGX32(PSB_CR_CORE_REVISION); + DRM_INFO("SGX core id = 0x%08x\n", core_id); + DRM_INFO("SGX core rev major = 0x%02x, minor = 0x%02x\n", + (core_rev & _PSB_CC_REVISION_MAJOR_MASK) >> + _PSB_CC_REVISION_MAJOR_SHIFT, + (core_rev & _PSB_CC_REVISION_MINOR_MASK) >> + _PSB_CC_REVISION_MINOR_SHIFT); + DRM_INFO + ("SGX core rev maintenance = 0x%02x, designer = 0x%02x\n", + (core_rev & _PSB_CC_REVISION_MAINTENANCE_MASK) >> + _PSB_CC_REVISION_MAINTENANCE_SHIFT, + (core_rev & _PSB_CC_REVISION_DESIGNER_MASK) >> + _PSB_CC_REVISION_DESIGNER_SHIFT); + } + + + spin_lock_init(&dev_priv->irqmask_lock); + spin_lock_init(&dev_priv->lock_2d); + + PSB_WSGX32(0x00000000, PSB_CR_BIF_BANK0); + PSB_WSGX32(0x00000000, PSB_CR_BIF_BANK1); + PSB_RSGX32(PSB_CR_BIF_BANK1); + PSB_WSGX32(PSB_RSGX32(PSB_CR_BIF_CTRL) | _PSB_MMU_ER_MASK, + PSB_CR_BIF_CTRL); + psb_spank(dev_priv); + + /* mmu_gatt ?? */ + PSB_WSGX32(pg->gatt_start, PSB_CR_BIF_TWOD_REQ_BASE); + return 0; +out_err: + psb_do_takedown(dev); + return ret; +} + +static int psb_driver_unload(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + + /* Kill vblank etc here */ + + gma_backlight_exit(dev); + + if (drm_psb_no_fb == 0) + psb_modeset_cleanup(dev); + + if (dev_priv) { + psb_lid_timer_takedown(dev_priv); + gma_intel_opregion_exit(dev); + + if (dev_priv->ops->chip_teardown) + dev_priv->ops->chip_teardown(dev); + psb_do_takedown(dev); + + + if (dev_priv->pf_pd) { + psb_mmu_free_pagedir(dev_priv->pf_pd); + dev_priv->pf_pd = NULL; + } + if (dev_priv->mmu) { + struct psb_gtt *pg = &dev_priv->gtt; + + down_read(&pg->sem); + psb_mmu_remove_pfn_sequence( + psb_mmu_get_default_pd + (dev_priv->mmu), + pg->mmu_gatt_start, + dev_priv->vram_stolen_size >> PAGE_SHIFT); + up_read(&pg->sem); + psb_mmu_driver_takedown(dev_priv->mmu); + dev_priv->mmu = NULL; + } + psb_gtt_takedown(dev); + if (dev_priv->scratch_page) { + __free_page(dev_priv->scratch_page); + dev_priv->scratch_page = NULL; + } + if (dev_priv->vdc_reg) { + iounmap(dev_priv->vdc_reg); + dev_priv->vdc_reg = NULL; + } + if (dev_priv->sgx_reg) { + iounmap(dev_priv->sgx_reg); + dev_priv->sgx_reg = NULL; + } + + kfree(dev_priv); + dev->dev_private = NULL; + + /*destroy VBT data*/ + psb_intel_destroy_bios(dev); + } + + gma_power_uninit(dev); + + return 0; +} + + +static int psb_driver_load(struct drm_device *dev, unsigned long chipset) +{ + struct drm_psb_private *dev_priv; + unsigned long resource_start; + struct psb_gtt *pg; + unsigned long irqflags; + int ret = -ENOMEM; + uint32_t tt_pages; + struct drm_connector *connector; + struct psb_intel_output *psb_intel_output; + + dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL); + if (dev_priv == NULL) + return -ENOMEM; + + dev_priv->ops = (struct psb_ops *)chipset; + dev_priv->dev = dev; + dev->dev_private = (void *) dev_priv; + + if (!IS_PSB(dev)) { + if (pci_enable_msi(dev->pdev)) + dev_warn(dev->dev, "Enabling MSI failed!\n"); + } + + dev_priv->num_pipe = dev_priv->ops->pipes; + + resource_start = pci_resource_start(dev->pdev, PSB_MMIO_RESOURCE); + + dev_priv->vdc_reg = + ioremap(resource_start + PSB_VDC_OFFSET, PSB_VDC_SIZE); + if (!dev_priv->vdc_reg) + goto out_err; + + dev_priv->sgx_reg = ioremap(resource_start + dev_priv->ops->sgx_offset, + PSB_SGX_SIZE); + if (!dev_priv->sgx_reg) + goto out_err; + + ret = dev_priv->ops->chip_setup(dev); + if (ret) + goto out_err; + + /* Init OSPM support */ + gma_power_init(dev); + + ret = -ENOMEM; + + dev_priv->scratch_page = alloc_page(GFP_DMA32 | __GFP_ZERO); + if (!dev_priv->scratch_page) + goto out_err; + + set_pages_uc(dev_priv->scratch_page, 1); + + ret = psb_gtt_init(dev, 0); + if (ret) + goto out_err; + + dev_priv->mmu = psb_mmu_driver_init((void *)0, + drm_psb_trap_pagefaults, 0, + dev_priv); + if (!dev_priv->mmu) + goto out_err; + + pg = &dev_priv->gtt; + + tt_pages = (pg->gatt_pages < PSB_TT_PRIV0_PLIMIT) ? + (pg->gatt_pages) : PSB_TT_PRIV0_PLIMIT; + + + dev_priv->pf_pd = psb_mmu_alloc_pd(dev_priv->mmu, 1, 0); + if (!dev_priv->pf_pd) + goto out_err; + + psb_mmu_set_pd_context(psb_mmu_get_default_pd(dev_priv->mmu), 0); + psb_mmu_set_pd_context(dev_priv->pf_pd, 1); + + ret = psb_do_init(dev); + if (ret) + return ret; + + PSB_WSGX32(0x20000000, PSB_CR_PDS_EXEC_BASE); + PSB_WSGX32(0x30000000, PSB_CR_BIF_3D_REQ_BASE); + +/* igd_opregion_init(&dev_priv->opregion_dev); */ + acpi_video_register(); + if (dev_priv->lid_state) + psb_lid_timer_init(dev_priv); + + ret = drm_vblank_init(dev, dev_priv->num_pipe); + if (ret) + goto out_err; + + /* + * Install interrupt handlers prior to powering off SGX or else we will + * crash. + */ + dev_priv->vdc_irq_mask = 0; + dev_priv->pipestat[0] = 0; + dev_priv->pipestat[1] = 0; + dev_priv->pipestat[2] = 0; + spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); + PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM); + PSB_WVDC32(0x00000000, PSB_INT_ENABLE_R); + PSB_WVDC32(0xFFFFFFFF, PSB_INT_MASK_R); + spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); + if (IS_PSB(dev) && drm_core_check_feature(dev, DRIVER_MODESET)) + drm_irq_install(dev); + + dev->vblank_disable_allowed = 1; + + dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ + + dev->driver->get_vblank_counter = psb_get_vblank_counter; + +#if defined(CONFIG_DRM_PSB_MFLD) + /* FIXME: this is not the right place for this stuff ! */ + mdfld_output_setup(dev); +#endif + if (drm_psb_no_fb == 0) { + psb_modeset_init(dev); + psb_fbdev_init(dev); + drm_kms_helper_poll_init(dev); + } + + /* Only add backlight support if we have LVDS output */ + list_for_each_entry(connector, &dev->mode_config.connector_list, + head) { + psb_intel_output = to_psb_intel_output(connector); + + switch (psb_intel_output->type) { + case INTEL_OUTPUT_LVDS: + case INTEL_OUTPUT_MIPI: + ret = gma_backlight_init(dev); + break; + } + } + + if (ret) + return ret; + + /* Enable runtime pm at last */ + pm_runtime_set_active(&dev->pdev->dev); + return 0; +out_err: + psb_driver_unload(dev); + return ret; +} + +int psb_driver_device_is_agp(struct drm_device *dev) +{ + return 0; +} + + +static int psb_sizes_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_psb_private *dev_priv = psb_priv(dev); + struct drm_psb_sizes_arg *arg = data; + + *arg = dev_priv->sizes; + return 0; +} + +static int psb_dc_state_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + uint32_t flags; + uint32_t obj_id; + struct drm_mode_object *obj; + struct drm_connector *connector; + struct drm_crtc *crtc; + struct drm_psb_dc_state_arg *arg = data; + + + /* Double check MRST case */ + if (IS_MRST(dev) || IS_MFLD(dev)) + return -EOPNOTSUPP; + + flags = arg->flags; + obj_id = arg->obj_id; + + if (flags & PSB_DC_CRTC_MASK) { + obj = drm_mode_object_find(dev, obj_id, + DRM_MODE_OBJECT_CRTC); + if (!obj) { + dev_dbg(dev->dev, "Invalid CRTC object.\n"); + return -EINVAL; + } + + crtc = obj_to_crtc(obj); + + mutex_lock(&dev->mode_config.mutex); + if (drm_helper_crtc_in_use(crtc)) { + if (flags & PSB_DC_CRTC_SAVE) + crtc->funcs->save(crtc); + else + crtc->funcs->restore(crtc); + } + mutex_unlock(&dev->mode_config.mutex); + + return 0; + } else if (flags & PSB_DC_OUTPUT_MASK) { + obj = drm_mode_object_find(dev, obj_id, + DRM_MODE_OBJECT_CONNECTOR); + if (!obj) { + dev_dbg(dev->dev, "Invalid connector id.\n"); + return -EINVAL; + } + + connector = obj_to_connector(obj); + if (flags & PSB_DC_OUTPUT_SAVE) + connector->funcs->save(connector); + else + connector->funcs->restore(connector); + + return 0; + } + return -EINVAL; +} + +static inline void get_brightness(struct backlight_device *bd) +{ +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + if (bd) { + bd->props.brightness = bd->ops->get_brightness(bd); + backlight_update_status(bd); + } +#endif +} + +static int psb_dpst_bl_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_psb_private *dev_priv = psb_priv(dev); + uint32_t *arg = data; + + dev_priv->blc_adj2 = *arg; + get_brightness(dev_priv->backlight_device); + return 0; +} + +static int psb_adb_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_psb_private *dev_priv = psb_priv(dev); + uint32_t *arg = data; + + dev_priv->blc_adj1 = *arg; + get_brightness(dev_priv->backlight_device); + return 0; +} + +/* return the current mode to the dpst module */ +static int psb_dpst_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_psb_private *dev_priv = psb_priv(dev); + uint32_t *arg = data; + uint32_t x; + uint32_t y; + uint32_t reg; + + if (!gma_power_begin(dev, 0)) + return -EIO; + + reg = PSB_RVDC32(PIPEASRC); + + gma_power_end(dev); + + /* horizontal is the left 16 bits */ + x = reg >> 16; + /* vertical is the right 16 bits */ + y = reg & 0x0000ffff; + + /* the values are the image size minus one */ + x++; + y++; + + *arg = (x << 16) | y; + + return 0; +} +static int psb_gamma_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_psb_dpst_lut_arg *lut_arg = data; + struct drm_mode_object *obj; + struct drm_crtc *crtc; + struct drm_connector *connector; + struct psb_intel_crtc *psb_intel_crtc; + int i = 0; + int32_t obj_id; + + obj_id = lut_arg->output_id; + obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_CONNECTOR); + if (!obj) { + dev_dbg(dev->dev, "Invalid Connector object.\n"); + return -EINVAL; + } + + connector = obj_to_connector(obj); + crtc = connector->encoder->crtc; + psb_intel_crtc = to_psb_intel_crtc(crtc); + + for (i = 0; i < 256; i++) + psb_intel_crtc->lut_adj[i] = lut_arg->lut[i]; + + psb_intel_crtc_load_lut(crtc); + + return 0; +} + +static int psb_mode_operation_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + uint32_t obj_id; + uint16_t op; + struct drm_mode_modeinfo *umode; + struct drm_display_mode *mode = NULL; + struct drm_psb_mode_operation_arg *arg; + struct drm_mode_object *obj; + struct drm_connector *connector; + struct drm_framebuffer *drm_fb; + struct psb_framebuffer *psb_fb; + struct drm_connector_helper_funcs *connector_funcs; + int ret = 0; + int resp = MODE_OK; + struct drm_psb_private *dev_priv = psb_priv(dev); + + arg = (struct drm_psb_mode_operation_arg *)data; + obj_id = arg->obj_id; + op = arg->operation; + + switch (op) { + case PSB_MODE_OPERATION_SET_DC_BASE: + obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_FB); + if (!obj) { + dev_dbg(dev->dev, "Invalid FB id %d\n", obj_id); + return -EINVAL; + } + + drm_fb = obj_to_fb(obj); + psb_fb = to_psb_fb(drm_fb); + + if (gma_power_begin(dev, 0)) { + REG_WRITE(DSPASURF, psb_fb->gtt->offset); + REG_READ(DSPASURF); + gma_power_end(dev); + } else { + dev_priv->saveDSPASURF = psb_fb->gtt->offset; + } + + return 0; + case PSB_MODE_OPERATION_MODE_VALID: + umode = &arg->mode; + + mutex_lock(&dev->mode_config.mutex); + + obj = drm_mode_object_find(dev, obj_id, + DRM_MODE_OBJECT_CONNECTOR); + if (!obj) { + ret = -EINVAL; + goto mode_op_out; + } + + connector = obj_to_connector(obj); + + mode = drm_mode_create(dev); + if (!mode) { + ret = -ENOMEM; + goto mode_op_out; + } + + /* drm_crtc_convert_umode(mode, umode); */ + { + mode->clock = umode->clock; + mode->hdisplay = umode->hdisplay; + mode->hsync_start = umode->hsync_start; + mode->hsync_end = umode->hsync_end; + mode->htotal = umode->htotal; + mode->hskew = umode->hskew; + mode->vdisplay = umode->vdisplay; + mode->vsync_start = umode->vsync_start; + mode->vsync_end = umode->vsync_end; + mode->vtotal = umode->vtotal; + mode->vscan = umode->vscan; + mode->vrefresh = umode->vrefresh; + mode->flags = umode->flags; + mode->type = umode->type; + strncpy(mode->name, umode->name, DRM_DISPLAY_MODE_LEN); + mode->name[DRM_DISPLAY_MODE_LEN-1] = 0; + } + + connector_funcs = (struct drm_connector_helper_funcs *) + connector->helper_private; + + if (connector_funcs->mode_valid) { + resp = connector_funcs->mode_valid(connector, mode); + arg->data = (void *)resp; + } + + /*do some clean up work*/ + if (mode) + drm_mode_destroy(dev, mode); +mode_op_out: + mutex_unlock(&dev->mode_config.mutex); + return ret; + + default: + dev_dbg(dev->dev, "Unsupported psb mode operation\n"); + return -EOPNOTSUPP; + } + + return 0; +} + +static int psb_stolen_memory_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_psb_private *dev_priv = psb_priv(dev); + struct drm_psb_stolen_memory_arg *arg = data; + + arg->base = dev_priv->stolen_base; + arg->size = dev_priv->vram_stolen_size; + + return 0; +} + +/* FIXME: needs Medfield changes */ +static int psb_register_rw_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_psb_private *dev_priv = psb_priv(dev); + struct drm_psb_register_rw_arg *arg = data; + bool usage = arg->b_force_hw_on ? true : false; + + if (arg->display_write_mask != 0) { + if (gma_power_begin(dev, usage)) { + if (arg->display_write_mask & REGRWBITS_PFIT_CONTROLS) + PSB_WVDC32(arg->display.pfit_controls, + PFIT_CONTROL); + if (arg->display_write_mask & + REGRWBITS_PFIT_AUTOSCALE_RATIOS) + PSB_WVDC32(arg->display.pfit_autoscale_ratios, + PFIT_AUTO_RATIOS); + if (arg->display_write_mask & + REGRWBITS_PFIT_PROGRAMMED_SCALE_RATIOS) + PSB_WVDC32( + arg->display.pfit_programmed_scale_ratios, + PFIT_PGM_RATIOS); + if (arg->display_write_mask & REGRWBITS_PIPEASRC) + PSB_WVDC32(arg->display.pipeasrc, + PIPEASRC); + if (arg->display_write_mask & REGRWBITS_PIPEBSRC) + PSB_WVDC32(arg->display.pipebsrc, + PIPEBSRC); + if (arg->display_write_mask & REGRWBITS_VTOTAL_A) + PSB_WVDC32(arg->display.vtotal_a, + VTOTAL_A); + if (arg->display_write_mask & REGRWBITS_VTOTAL_B) + PSB_WVDC32(arg->display.vtotal_b, + VTOTAL_B); + gma_power_end(dev); + } else { + if (arg->display_write_mask & REGRWBITS_PFIT_CONTROLS) + dev_priv->savePFIT_CONTROL = + arg->display.pfit_controls; + if (arg->display_write_mask & + REGRWBITS_PFIT_AUTOSCALE_RATIOS) + dev_priv->savePFIT_AUTO_RATIOS = + arg->display.pfit_autoscale_ratios; + if (arg->display_write_mask & + REGRWBITS_PFIT_PROGRAMMED_SCALE_RATIOS) + dev_priv->savePFIT_PGM_RATIOS = + arg->display.pfit_programmed_scale_ratios; + if (arg->display_write_mask & REGRWBITS_PIPEASRC) + dev_priv->savePIPEASRC = arg->display.pipeasrc; + if (arg->display_write_mask & REGRWBITS_PIPEBSRC) + dev_priv->savePIPEBSRC = arg->display.pipebsrc; + if (arg->display_write_mask & REGRWBITS_VTOTAL_A) + dev_priv->saveVTOTAL_A = arg->display.vtotal_a; + if (arg->display_write_mask & REGRWBITS_VTOTAL_B) + dev_priv->saveVTOTAL_B = arg->display.vtotal_b; + } + } + + if (arg->display_read_mask != 0) { + if (gma_power_begin(dev, usage)) { + if (arg->display_read_mask & + REGRWBITS_PFIT_CONTROLS) + arg->display.pfit_controls = + PSB_RVDC32(PFIT_CONTROL); + if (arg->display_read_mask & + REGRWBITS_PFIT_AUTOSCALE_RATIOS) + arg->display.pfit_autoscale_ratios = + PSB_RVDC32(PFIT_AUTO_RATIOS); + if (arg->display_read_mask & + REGRWBITS_PFIT_PROGRAMMED_SCALE_RATIOS) + arg->display.pfit_programmed_scale_ratios = + PSB_RVDC32(PFIT_PGM_RATIOS); + if (arg->display_read_mask & REGRWBITS_PIPEASRC) + arg->display.pipeasrc = PSB_RVDC32(PIPEASRC); + if (arg->display_read_mask & REGRWBITS_PIPEBSRC) + arg->display.pipebsrc = PSB_RVDC32(PIPEBSRC); + if (arg->display_read_mask & REGRWBITS_VTOTAL_A) + arg->display.vtotal_a = PSB_RVDC32(VTOTAL_A); + if (arg->display_read_mask & REGRWBITS_VTOTAL_B) + arg->display.vtotal_b = PSB_RVDC32(VTOTAL_B); + gma_power_end(dev); + } else { + if (arg->display_read_mask & + REGRWBITS_PFIT_CONTROLS) + arg->display.pfit_controls = + dev_priv->savePFIT_CONTROL; + if (arg->display_read_mask & + REGRWBITS_PFIT_AUTOSCALE_RATIOS) + arg->display.pfit_autoscale_ratios = + dev_priv->savePFIT_AUTO_RATIOS; + if (arg->display_read_mask & + REGRWBITS_PFIT_PROGRAMMED_SCALE_RATIOS) + arg->display.pfit_programmed_scale_ratios = + dev_priv->savePFIT_PGM_RATIOS; + if (arg->display_read_mask & REGRWBITS_PIPEASRC) + arg->display.pipeasrc = dev_priv->savePIPEASRC; + if (arg->display_read_mask & REGRWBITS_PIPEBSRC) + arg->display.pipebsrc = dev_priv->savePIPEBSRC; + if (arg->display_read_mask & REGRWBITS_VTOTAL_A) + arg->display.vtotal_a = dev_priv->saveVTOTAL_A; + if (arg->display_read_mask & REGRWBITS_VTOTAL_B) + arg->display.vtotal_b = dev_priv->saveVTOTAL_B; + } + } + + if (arg->overlay_write_mask != 0) { + if (gma_power_begin(dev, usage)) { + if (arg->overlay_write_mask & OV_REGRWBITS_OGAM_ALL) { + PSB_WVDC32(arg->overlay.OGAMC5, OV_OGAMC5); + PSB_WVDC32(arg->overlay.OGAMC4, OV_OGAMC4); + PSB_WVDC32(arg->overlay.OGAMC3, OV_OGAMC3); + PSB_WVDC32(arg->overlay.OGAMC2, OV_OGAMC2); + PSB_WVDC32(arg->overlay.OGAMC1, OV_OGAMC1); + PSB_WVDC32(arg->overlay.OGAMC0, OV_OGAMC0); + } + if (arg->overlay_write_mask & OVC_REGRWBITS_OGAM_ALL) { + PSB_WVDC32(arg->overlay.OGAMC5, OVC_OGAMC5); + PSB_WVDC32(arg->overlay.OGAMC4, OVC_OGAMC4); + PSB_WVDC32(arg->overlay.OGAMC3, OVC_OGAMC3); + PSB_WVDC32(arg->overlay.OGAMC2, OVC_OGAMC2); + PSB_WVDC32(arg->overlay.OGAMC1, OVC_OGAMC1); + PSB_WVDC32(arg->overlay.OGAMC0, OVC_OGAMC0); + } + + if (arg->overlay_write_mask & OV_REGRWBITS_OVADD) { + PSB_WVDC32(arg->overlay.OVADD, OV_OVADD); + + if (arg->overlay.b_wait_vblank) { + /* Wait for 20ms.*/ + unsigned long vblank_timeout = jiffies + + HZ/50; + uint32_t temp; + while (time_before_eq(jiffies, + vblank_timeout)) { + temp = PSB_RVDC32(OV_DOVASTA); + if ((temp & (0x1 << 31)) != 0) + break; + cpu_relax(); + } + } + } + if (arg->overlay_write_mask & OVC_REGRWBITS_OVADD) { + PSB_WVDC32(arg->overlay.OVADD, OVC_OVADD); + if (arg->overlay.b_wait_vblank) { + /* Wait for 20ms.*/ + unsigned long vblank_timeout = + jiffies + HZ/50; + uint32_t temp; + while (time_before_eq(jiffies, + vblank_timeout)) { + temp = PSB_RVDC32(OVC_DOVCSTA); + if ((temp & (0x1 << 31)) != 0) + break; + cpu_relax(); + } + } + } + gma_power_end(dev); + } else { + if (arg->overlay_write_mask & OV_REGRWBITS_OGAM_ALL) { + dev_priv->saveOV_OGAMC5 = arg->overlay.OGAMC5; + dev_priv->saveOV_OGAMC4 = arg->overlay.OGAMC4; + dev_priv->saveOV_OGAMC3 = arg->overlay.OGAMC3; + dev_priv->saveOV_OGAMC2 = arg->overlay.OGAMC2; + dev_priv->saveOV_OGAMC1 = arg->overlay.OGAMC1; + dev_priv->saveOV_OGAMC0 = arg->overlay.OGAMC0; + } + if (arg->overlay_write_mask & OVC_REGRWBITS_OGAM_ALL) { + dev_priv->saveOVC_OGAMC5 = arg->overlay.OGAMC5; + dev_priv->saveOVC_OGAMC4 = arg->overlay.OGAMC4; + dev_priv->saveOVC_OGAMC3 = arg->overlay.OGAMC3; + dev_priv->saveOVC_OGAMC2 = arg->overlay.OGAMC2; + dev_priv->saveOVC_OGAMC1 = arg->overlay.OGAMC1; + dev_priv->saveOVC_OGAMC0 = arg->overlay.OGAMC0; + } + if (arg->overlay_write_mask & OV_REGRWBITS_OVADD) + dev_priv->saveOV_OVADD = arg->overlay.OVADD; + if (arg->overlay_write_mask & OVC_REGRWBITS_OVADD) + dev_priv->saveOVC_OVADD = arg->overlay.OVADD; + } + } + + if (arg->overlay_read_mask != 0) { + if (gma_power_begin(dev, usage)) { + if (arg->overlay_read_mask & OV_REGRWBITS_OGAM_ALL) { + arg->overlay.OGAMC5 = PSB_RVDC32(OV_OGAMC5); + arg->overlay.OGAMC4 = PSB_RVDC32(OV_OGAMC4); + arg->overlay.OGAMC3 = PSB_RVDC32(OV_OGAMC3); + arg->overlay.OGAMC2 = PSB_RVDC32(OV_OGAMC2); + arg->overlay.OGAMC1 = PSB_RVDC32(OV_OGAMC1); + arg->overlay.OGAMC0 = PSB_RVDC32(OV_OGAMC0); + } + if (arg->overlay_read_mask & OVC_REGRWBITS_OGAM_ALL) { + arg->overlay.OGAMC5 = PSB_RVDC32(OVC_OGAMC5); + arg->overlay.OGAMC4 = PSB_RVDC32(OVC_OGAMC4); + arg->overlay.OGAMC3 = PSB_RVDC32(OVC_OGAMC3); + arg->overlay.OGAMC2 = PSB_RVDC32(OVC_OGAMC2); + arg->overlay.OGAMC1 = PSB_RVDC32(OVC_OGAMC1); + arg->overlay.OGAMC0 = PSB_RVDC32(OVC_OGAMC0); + } + if (arg->overlay_read_mask & OV_REGRWBITS_OVADD) + arg->overlay.OVADD = PSB_RVDC32(OV_OVADD); + if (arg->overlay_read_mask & OVC_REGRWBITS_OVADD) + arg->overlay.OVADD = PSB_RVDC32(OVC_OVADD); + gma_power_end(dev); + } else { + if (arg->overlay_read_mask & OV_REGRWBITS_OGAM_ALL) { + arg->overlay.OGAMC5 = dev_priv->saveOV_OGAMC5; + arg->overlay.OGAMC4 = dev_priv->saveOV_OGAMC4; + arg->overlay.OGAMC3 = dev_priv->saveOV_OGAMC3; + arg->overlay.OGAMC2 = dev_priv->saveOV_OGAMC2; + arg->overlay.OGAMC1 = dev_priv->saveOV_OGAMC1; + arg->overlay.OGAMC0 = dev_priv->saveOV_OGAMC0; + } + if (arg->overlay_read_mask & OVC_REGRWBITS_OGAM_ALL) { + arg->overlay.OGAMC5 = dev_priv->saveOVC_OGAMC5; + arg->overlay.OGAMC4 = dev_priv->saveOVC_OGAMC4; + arg->overlay.OGAMC3 = dev_priv->saveOVC_OGAMC3; + arg->overlay.OGAMC2 = dev_priv->saveOVC_OGAMC2; + arg->overlay.OGAMC1 = dev_priv->saveOVC_OGAMC1; + arg->overlay.OGAMC0 = dev_priv->saveOVC_OGAMC0; + } + if (arg->overlay_read_mask & OV_REGRWBITS_OVADD) + arg->overlay.OVADD = dev_priv->saveOV_OVADD; + if (arg->overlay_read_mask & OVC_REGRWBITS_OVADD) + arg->overlay.OVADD = dev_priv->saveOVC_OVADD; + } + } + + if (arg->sprite_enable_mask != 0) { + if (gma_power_begin(dev, usage)) { + PSB_WVDC32(0x1F3E, DSPARB); + PSB_WVDC32(arg->sprite.dspa_control + | PSB_RVDC32(DSPACNTR), DSPACNTR); + PSB_WVDC32(arg->sprite.dspa_key_value, DSPAKEYVAL); + PSB_WVDC32(arg->sprite.dspa_key_mask, DSPAKEYMASK); + PSB_WVDC32(PSB_RVDC32(DSPASURF), DSPASURF); + PSB_RVDC32(DSPASURF); + PSB_WVDC32(arg->sprite.dspc_control, DSPCCNTR); + PSB_WVDC32(arg->sprite.dspc_stride, DSPCSTRIDE); + PSB_WVDC32(arg->sprite.dspc_position, DSPCPOS); + PSB_WVDC32(arg->sprite.dspc_linear_offset, DSPCLINOFF); + PSB_WVDC32(arg->sprite.dspc_size, DSPCSIZE); + PSB_WVDC32(arg->sprite.dspc_surface, DSPCSURF); + PSB_RVDC32(DSPCSURF); + gma_power_end(dev); + } + } + + if (arg->sprite_disable_mask != 0) { + if (gma_power_begin(dev, usage)) { + PSB_WVDC32(0x3F3E, DSPARB); + PSB_WVDC32(0x0, DSPCCNTR); + PSB_WVDC32(arg->sprite.dspc_surface, DSPCSURF); + PSB_RVDC32(DSPCSURF); + gma_power_end(dev); + } + } + + if (arg->subpicture_enable_mask != 0) { + if (gma_power_begin(dev, usage)) { + uint32_t temp; + if (arg->subpicture_enable_mask & REGRWBITS_DSPACNTR) { + temp = PSB_RVDC32(DSPACNTR); + temp &= ~DISPPLANE_PIXFORMAT_MASK; + temp &= ~DISPPLANE_BOTTOM; + temp |= DISPPLANE_32BPP; + PSB_WVDC32(temp, DSPACNTR); + + temp = PSB_RVDC32(DSPABASE); + PSB_WVDC32(temp, DSPABASE); + PSB_RVDC32(DSPABASE); + temp = PSB_RVDC32(DSPASURF); + PSB_WVDC32(temp, DSPASURF); + PSB_RVDC32(DSPASURF); + } + if (arg->subpicture_enable_mask & REGRWBITS_DSPBCNTR) { + temp = PSB_RVDC32(DSPBCNTR); + temp &= ~DISPPLANE_PIXFORMAT_MASK; + temp &= ~DISPPLANE_BOTTOM; + temp |= DISPPLANE_32BPP; + PSB_WVDC32(temp, DSPBCNTR); + + temp = PSB_RVDC32(DSPBBASE); + PSB_WVDC32(temp, DSPBBASE); + PSB_RVDC32(DSPBBASE); + temp = PSB_RVDC32(DSPBSURF); + PSB_WVDC32(temp, DSPBSURF); + PSB_RVDC32(DSPBSURF); + } + if (arg->subpicture_enable_mask & REGRWBITS_DSPCCNTR) { + temp = PSB_RVDC32(DSPCCNTR); + temp &= ~DISPPLANE_PIXFORMAT_MASK; + temp &= ~DISPPLANE_BOTTOM; + temp |= DISPPLANE_32BPP; + PSB_WVDC32(temp, DSPCCNTR); + + temp = PSB_RVDC32(DSPCBASE); + PSB_WVDC32(temp, DSPCBASE); + PSB_RVDC32(DSPCBASE); + temp = PSB_RVDC32(DSPCSURF); + PSB_WVDC32(temp, DSPCSURF); + PSB_RVDC32(DSPCSURF); + } + gma_power_end(dev); + } + } + + if (arg->subpicture_disable_mask != 0) { + if (gma_power_begin(dev, usage)) { + uint32_t temp; + if (arg->subpicture_disable_mask & REGRWBITS_DSPACNTR) { + temp = PSB_RVDC32(DSPACNTR); + temp &= ~DISPPLANE_PIXFORMAT_MASK; + temp |= DISPPLANE_32BPP_NO_ALPHA; + PSB_WVDC32(temp, DSPACNTR); + + temp = PSB_RVDC32(DSPABASE); + PSB_WVDC32(temp, DSPABASE); + PSB_RVDC32(DSPABASE); + temp = PSB_RVDC32(DSPASURF); + PSB_WVDC32(temp, DSPASURF); + PSB_RVDC32(DSPASURF); + } + if (arg->subpicture_disable_mask & REGRWBITS_DSPBCNTR) { + temp = PSB_RVDC32(DSPBCNTR); + temp &= ~DISPPLANE_PIXFORMAT_MASK; + temp |= DISPPLANE_32BPP_NO_ALPHA; + PSB_WVDC32(temp, DSPBCNTR); + + temp = PSB_RVDC32(DSPBBASE); + PSB_WVDC32(temp, DSPBBASE); + PSB_RVDC32(DSPBBASE); + temp = PSB_RVDC32(DSPBSURF); + PSB_WVDC32(temp, DSPBSURF); + PSB_RVDC32(DSPBSURF); + } + if (arg->subpicture_disable_mask & REGRWBITS_DSPCCNTR) { + temp = PSB_RVDC32(DSPCCNTR); + temp &= ~DISPPLANE_PIXFORMAT_MASK; + temp |= DISPPLANE_32BPP_NO_ALPHA; + PSB_WVDC32(temp, DSPCCNTR); + + temp = PSB_RVDC32(DSPCBASE); + PSB_WVDC32(temp, DSPCBASE); + PSB_RVDC32(DSPCBASE); + temp = PSB_RVDC32(DSPCSURF); + PSB_WVDC32(temp, DSPCSURF); + PSB_RVDC32(DSPCSURF); + } + gma_power_end(dev); + } + } + + return 0; +} + +static int psb_driver_open(struct drm_device *dev, struct drm_file *priv) +{ + return 0; +} + +static void psb_driver_close(struct drm_device *dev, struct drm_file *priv) +{ +} + +static long psb_unlocked_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct drm_file *file_priv = filp->private_data; + struct drm_device *dev = file_priv->minor->dev; + int ret; + + pm_runtime_forbid(dev->dev); + ret = drm_ioctl(filp, cmd, arg); + pm_runtime_allow(dev->dev); + return ret; + /* FIXME: do we need to wrap the other side of this */ +} + + +/* When a client dies: + * - Check for and clean up flipped page state + */ +void psb_driver_preclose(struct drm_device *dev, struct drm_file *priv) +{ +} + +static void psb_remove(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + drm_put_dev(dev); +} + +static const struct dev_pm_ops psb_pm_ops = { + .suspend = gma_power_suspend, + .resume = gma_power_resume, + .freeze = gma_power_suspend, + .thaw = gma_power_resume, + .poweroff = gma_power_suspend, + .restore = gma_power_resume, + .runtime_suspend = psb_runtime_suspend, + .runtime_resume = psb_runtime_resume, + .runtime_idle = psb_runtime_idle, +}; + +static struct vm_operations_struct psb_gem_vm_ops = { + .fault = psb_gem_fault, + .open = drm_gem_vm_open, + .close = drm_gem_vm_close, +}; + +static const struct file_operations gma500_driver_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = psb_unlocked_ioctl, + .mmap = drm_gem_mmap, + .poll = drm_poll, + .fasync = drm_fasync, + .read = drm_read, +}; + +static struct drm_driver driver = { + .driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | \ + DRIVER_IRQ_VBL | DRIVER_MODESET | DRIVER_GEM , + .load = psb_driver_load, + .unload = psb_driver_unload, + + .ioctls = psb_ioctls, + .num_ioctls = DRM_ARRAY_SIZE(psb_ioctls), + .device_is_agp = psb_driver_device_is_agp, + .irq_preinstall = psb_irq_preinstall, + .irq_postinstall = psb_irq_postinstall, + .irq_uninstall = psb_irq_uninstall, + .irq_handler = psb_irq_handler, + .enable_vblank = psb_enable_vblank, + .disable_vblank = psb_disable_vblank, + .get_vblank_counter = psb_get_vblank_counter, + .lastclose = psb_lastclose, + .open = psb_driver_open, + .preclose = psb_driver_preclose, + .postclose = psb_driver_close, + .reclaim_buffers = drm_core_reclaim_buffers, + + .gem_init_object = psb_gem_init_object, + .gem_free_object = psb_gem_free_object, + .gem_vm_ops = &psb_gem_vm_ops, + .dumb_create = psb_gem_dumb_create, + .dumb_map_offset = psb_gem_dumb_map_gtt, + .dumb_destroy = psb_gem_dumb_destroy, + .fops = &gma500_driver_fops, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = PSB_DRM_DRIVER_DATE, + .major = PSB_DRM_DRIVER_MAJOR, + .minor = PSB_DRM_DRIVER_MINOR, + .patchlevel = PSB_DRM_DRIVER_PATCHLEVEL +}; + +static struct pci_driver psb_pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, + .probe = psb_probe, + .remove = psb_remove, + .driver.pm = &psb_pm_ops, +}; + +static int psb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + return drm_get_pci_dev(pdev, ent, &driver); +} + +static int __init psb_init(void) +{ + return drm_pci_init(&driver, &psb_pci_driver); +} + +static void __exit psb_exit(void) +{ + drm_pci_exit(&driver, &psb_pci_driver); +} + +late_initcall(psb_init); +module_exit(psb_exit); + +MODULE_AUTHOR("Alan Cox and others"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/staging/gma500/psb_drv.h b/trunk/drivers/staging/gma500/psb_drv.h new file mode 100644 index 000000000000..11d963a055be --- /dev/null +++ b/trunk/drivers/staging/gma500/psb_drv.h @@ -0,0 +1,952 @@ +/************************************************************************** + * Copyright (c) 2007-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. + * + **************************************************************************/ + +#ifndef _PSB_DRV_H_ +#define _PSB_DRV_H_ + +#include + +#include +#include "drm_global.h" +#include "gem_glue.h" +#include "psb_drm.h" +#include "psb_reg.h" +#include "psb_intel_drv.h" +#include "gtt.h" +#include "power.h" +#include "mrst.h" +#include "medfield.h" + +/* Append new drm mode definition here, align with libdrm definition */ +#define DRM_MODE_SCALE_NO_SCALE 2 + +enum { + CHIP_PSB_8108 = 0, /* Poulsbo */ + CHIP_PSB_8109 = 1, /* Poulsbo */ + CHIP_MRST_4100 = 2, /* Moorestown/Oaktrail */ + CHIP_MFLD_0130 = 3, /* Medfield */ +}; + +#define IS_PSB(dev) (((dev)->pci_device & 0xfffe) == 0x8108) +#define IS_MRST(dev) (((dev)->pci_device & 0xfffc) == 0x4100) +#define IS_MFLD(dev) (((dev)->pci_device & 0xfff8) == 0x0130) + +/* + * Driver definitions + */ + +#define DRIVER_NAME "gma500" +#define DRIVER_DESC "DRM driver for the Intel GMA500" + +#define PSB_DRM_DRIVER_DATE "2011-06-06" +#define PSB_DRM_DRIVER_MAJOR 1 +#define PSB_DRM_DRIVER_MINOR 0 +#define PSB_DRM_DRIVER_PATCHLEVEL 0 + +/* + * Hardware offsets + */ +#define PSB_VDC_OFFSET 0x00000000 +#define PSB_VDC_SIZE 0x000080000 +#define MRST_MMIO_SIZE 0x0000C0000 +#define MDFLD_MMIO_SIZE 0x000100000 +#define PSB_SGX_SIZE 0x8000 +#define PSB_SGX_OFFSET 0x00040000 +#define MRST_SGX_OFFSET 0x00080000 +/* + * PCI resource identifiers + */ +#define PSB_MMIO_RESOURCE 0 +#define PSB_GATT_RESOURCE 2 +#define PSB_GTT_RESOURCE 3 +/* + * PCI configuration + */ +#define PSB_GMCH_CTRL 0x52 +#define PSB_BSM 0x5C +#define _PSB_GMCH_ENABLED 0x4 +#define PSB_PGETBL_CTL 0x2020 +#define _PSB_PGETBL_ENABLED 0x00000001 +#define PSB_SGX_2D_SLAVE_PORT 0x4000 + +/* To get rid of */ +#define PSB_TT_PRIV0_LIMIT (256*1024*1024) +#define PSB_TT_PRIV0_PLIMIT (PSB_TT_PRIV0_LIMIT >> PAGE_SHIFT) + +/* + * SGX side MMU definitions (these can probably go) + */ + +/* + * Flags for external memory type field. + */ +#define PSB_MMU_CACHED_MEMORY 0x0001 /* Bind to MMU only */ +#define PSB_MMU_RO_MEMORY 0x0002 /* MMU RO memory */ +#define PSB_MMU_WO_MEMORY 0x0004 /* MMU WO memory */ +/* + * PTE's and PDE's + */ +#define PSB_PDE_MASK 0x003FFFFF +#define PSB_PDE_SHIFT 22 +#define PSB_PTE_SHIFT 12 +/* + * Cache control + */ +#define PSB_PTE_VALID 0x0001 /* PTE / PDE valid */ +#define PSB_PTE_WO 0x0002 /* Write only */ +#define PSB_PTE_RO 0x0004 /* Read only */ +#define PSB_PTE_CACHED 0x0008 /* CPU cache coherent */ + +/* + * VDC registers and bits + */ +#define PSB_MSVDX_CLOCKGATING 0x2064 +#define PSB_TOPAZ_CLOCKGATING 0x2068 +#define PSB_HWSTAM 0x2098 +#define PSB_INSTPM 0x20C0 +#define PSB_INT_IDENTITY_R 0x20A4 +#define _MDFLD_PIPEC_EVENT_FLAG (1<<2) +#define _MDFLD_PIPEC_VBLANK_FLAG (1<<3) +#define _PSB_DPST_PIPEB_FLAG (1<<4) +#define _MDFLD_PIPEB_EVENT_FLAG (1<<4) +#define _PSB_VSYNC_PIPEB_FLAG (1<<5) +#define _PSB_DPST_PIPEA_FLAG (1<<6) +#define _PSB_PIPEA_EVENT_FLAG (1<<6) +#define _PSB_VSYNC_PIPEA_FLAG (1<<7) +#define _MDFLD_MIPIA_FLAG (1<<16) +#define _MDFLD_MIPIC_FLAG (1<<17) +#define _PSB_IRQ_SGX_FLAG (1<<18) +#define _PSB_IRQ_MSVDX_FLAG (1<<19) +#define _LNC_IRQ_TOPAZ_FLAG (1<<20) + +#define _PSB_PIPE_EVENT_FLAG (_PSB_VSYNC_PIPEA_FLAG | \ + _PSB_VSYNC_PIPEB_FLAG) + +/* This flag includes all the display IRQ bits excepts the vblank irqs. */ +#define _MDFLD_DISP_ALL_IRQ_FLAG (_MDFLD_PIPEC_EVENT_FLAG | \ + _MDFLD_PIPEB_EVENT_FLAG | \ + _PSB_PIPEA_EVENT_FLAG | \ + _PSB_VSYNC_PIPEA_FLAG | \ + _MDFLD_MIPIA_FLAG | \ + _MDFLD_MIPIC_FLAG) +#define PSB_INT_IDENTITY_R 0x20A4 +#define PSB_INT_MASK_R 0x20A8 +#define PSB_INT_ENABLE_R 0x20A0 + +#define _PSB_MMU_ER_MASK 0x0001FF00 +#define _PSB_MMU_ER_HOST (1 << 16) +#define GPIOA 0x5010 +#define GPIOB 0x5014 +#define GPIOC 0x5018 +#define GPIOD 0x501c +#define GPIOE 0x5020 +#define GPIOF 0x5024 +#define GPIOG 0x5028 +#define GPIOH 0x502c +#define GPIO_CLOCK_DIR_MASK (1 << 0) +#define GPIO_CLOCK_DIR_IN (0 << 1) +#define GPIO_CLOCK_DIR_OUT (1 << 1) +#define GPIO_CLOCK_VAL_MASK (1 << 2) +#define GPIO_CLOCK_VAL_OUT (1 << 3) +#define GPIO_CLOCK_VAL_IN (1 << 4) +#define GPIO_CLOCK_PULLUP_DISABLE (1 << 5) +#define GPIO_DATA_DIR_MASK (1 << 8) +#define GPIO_DATA_DIR_IN (0 << 9) +#define GPIO_DATA_DIR_OUT (1 << 9) +#define GPIO_DATA_VAL_MASK (1 << 10) +#define GPIO_DATA_VAL_OUT (1 << 11) +#define GPIO_DATA_VAL_IN (1 << 12) +#define GPIO_DATA_PULLUP_DISABLE (1 << 13) + +#define VCLK_DIVISOR_VGA0 0x6000 +#define VCLK_DIVISOR_VGA1 0x6004 +#define VCLK_POST_DIV 0x6010 + +#define PSB_COMM_2D (PSB_ENGINE_2D << 4) +#define PSB_COMM_3D (PSB_ENGINE_3D << 4) +#define PSB_COMM_TA (PSB_ENGINE_TA << 4) +#define PSB_COMM_HP (PSB_ENGINE_HP << 4) +#define PSB_COMM_USER_IRQ (1024 >> 2) +#define PSB_COMM_USER_IRQ_LOST (PSB_COMM_USER_IRQ + 1) +#define PSB_COMM_FW (2048 >> 2) + +#define PSB_UIRQ_VISTEST 1 +#define PSB_UIRQ_OOM_REPLY 2 +#define PSB_UIRQ_FIRE_TA_REPLY 3 +#define PSB_UIRQ_FIRE_RASTER_REPLY 4 + +#define PSB_2D_SIZE (256*1024*1024) +#define PSB_MAX_RELOC_PAGES 1024 + +#define PSB_LOW_REG_OFFS 0x0204 +#define PSB_HIGH_REG_OFFS 0x0600 + +#define PSB_NUM_VBLANKS 2 + + +#define PSB_2D_SIZE (256*1024*1024) +#define PSB_MAX_RELOC_PAGES 1024 + +#define PSB_LOW_REG_OFFS 0x0204 +#define PSB_HIGH_REG_OFFS 0x0600 + +#define PSB_NUM_VBLANKS 2 +#define PSB_WATCHDOG_DELAY (DRM_HZ * 2) +#define PSB_LID_DELAY (DRM_HZ / 10) + +#define MDFLD_PNW_B0 0x04 +#define MDFLD_PNW_C0 0x08 + +#define MDFLD_DSR_2D_3D_0 (1 << 0) +#define MDFLD_DSR_2D_3D_2 (1 << 1) +#define MDFLD_DSR_CURSOR_0 (1 << 2) +#define MDFLD_DSR_CURSOR_2 (1 << 3) +#define MDFLD_DSR_OVERLAY_0 (1 << 4) +#define MDFLD_DSR_OVERLAY_2 (1 << 5) +#define MDFLD_DSR_MIPI_CONTROL (1 << 6) +#define MDFLD_DSR_DAMAGE_MASK_0 ((1 << 0) | (1 << 2) | (1 << 4)) +#define MDFLD_DSR_DAMAGE_MASK_2 ((1 << 1) | (1 << 3) | (1 << 5)) +#define MDFLD_DSR_2D_3D (MDFLD_DSR_2D_3D_0 | MDFLD_DSR_2D_3D_2) + +#define MDFLD_DSR_RR 45 +#define MDFLD_DPU_ENABLE (1 << 31) +#define MDFLD_DSR_FULLSCREEN (1 << 30) +#define MDFLD_DSR_DELAY (DRM_HZ / MDFLD_DSR_RR) + +#define PSB_PWR_STATE_ON 1 +#define PSB_PWR_STATE_OFF 2 + +#define PSB_PMPOLICY_NOPM 0 +#define PSB_PMPOLICY_CLOCKGATING 1 +#define PSB_PMPOLICY_POWERDOWN 2 + +#define PSB_PMSTATE_POWERUP 0 +#define PSB_PMSTATE_CLOCKGATED 1 +#define PSB_PMSTATE_POWERDOWN 2 +#define PSB_PCIx_MSI_ADDR_LOC 0x94 +#define PSB_PCIx_MSI_DATA_LOC 0x98 + +/* Medfield crystal settings */ +#define KSEL_CRYSTAL_19 1 +#define KSEL_BYPASS_19 5 +#define KSEL_BYPASS_25 6 +#define KSEL_BYPASS_83_100 7 + +struct opregion_header; +struct opregion_acpi; +struct opregion_swsci; +struct opregion_asle; + +struct psb_intel_opregion { + struct opregion_header *header; + struct opregion_acpi *acpi; + struct opregion_swsci *swsci; + struct opregion_asle *asle; + int enabled; +}; + +struct psb_ops; + +struct drm_psb_private { + struct drm_device *dev; + const struct psb_ops *ops; + + struct psb_gtt gtt; + + /* GTT Memory manager */ + struct psb_gtt_mm *gtt_mm; + struct page *scratch_page; + u32 *gtt_map; + uint32_t stolen_base; + void *vram_addr; + unsigned long vram_stolen_size; + int gtt_initialized; + u16 gmch_ctrl; /* Saved GTT setup */ + u32 pge_ctl; + + struct mutex gtt_mutex; + struct resource *gtt_mem; /* Our PCI resource */ + + struct psb_mmu_driver *mmu; + struct psb_mmu_pd *pf_pd; + + /* + * Register base + */ + + uint8_t *sgx_reg; + uint8_t *vdc_reg; + uint32_t gatt_free_offset; + + /* + * Fencing / irq. + */ + + uint32_t vdc_irq_mask; + uint32_t pipestat[PSB_NUM_PIPE]; + + spinlock_t irqmask_lock; + + /* + * Power + */ + + bool suspended; + bool display_power; + int display_count; + + /* + * Modesetting + */ + struct psb_intel_mode_device mode_dev; + + struct drm_crtc *plane_to_crtc_mapping[PSB_NUM_PIPE]; + struct drm_crtc *pipe_to_crtc_mapping[PSB_NUM_PIPE]; + uint32_t num_pipe; + + /* + * OSPM info (Power management base) (can go ?) + */ + uint32_t ospm_base; + + /* + * Sizes info + */ + + struct drm_psb_sizes_arg sizes; + + u32 fuse_reg_value; + u32 video_device_fuse; + + /* PCI revision ID for B0:D2:F0 */ + uint8_t platform_rev_id; + + /* + * LVDS info + */ + int backlight_duty_cycle; /* restore backlight to this value */ + bool panel_wants_dither; + struct drm_display_mode *panel_fixed_mode; + struct drm_display_mode *lfp_lvds_vbt_mode; + struct drm_display_mode *sdvo_lvds_vbt_mode; + + struct bdb_lvds_backlight *lvds_bl; /* LVDS backlight info from VBT */ + struct psb_intel_i2c_chan *lvds_i2c_bus; + + /* Feature bits from the VBIOS */ + unsigned int int_tv_support:1; + unsigned int lvds_dither:1; + unsigned int lvds_vbt:1; + unsigned int int_crt_support:1; + unsigned int lvds_use_ssc:1; + int lvds_ssc_freq; + bool is_lvds_on; + bool is_mipi_on; + u32 mipi_ctrl_display; + + unsigned int core_freq; + uint32_t iLVDS_enable; + + /* Runtime PM state */ + int rpm_enabled; + + /* MID specific */ + struct mrst_vbt vbt_data; + struct mrst_gct_data gct_data; + + /* MIPI Panel type etc */ + int panel_id; + bool dual_mipi; /* dual display - DPI & DBI */ + bool dpi_panel_on; /* The DPI panel power is on */ + bool dpi_panel_on2; /* The DPI panel power is on */ + bool dbi_panel_on; /* The DBI panel power is on */ + bool dbi_panel_on2; /* The DBI panel power is on */ + u32 dsr_fb_update; /* DSR FB update counter */ + + /* Moorestown HDMI state */ + struct mrst_hdmi_dev *hdmi_priv; + + /* Moorestown pipe config register value cache */ + uint32_t pipeconf; + uint32_t pipeconf1; + uint32_t pipeconf2; + + /* Moorestown plane control register value cache */ + uint32_t dspcntr; + uint32_t dspcntr1; + uint32_t dspcntr2; + + /* Moorestown MM backlight cache */ + uint8_t saveBKLTCNT; + uint8_t saveBKLTREQ; + uint8_t saveBKLTBRTL; + + /* + * Register state + */ + uint32_t saveDSPACNTR; + uint32_t saveDSPBCNTR; + uint32_t savePIPEACONF; + uint32_t savePIPEBCONF; + uint32_t savePIPEASRC; + uint32_t savePIPEBSRC; + uint32_t saveFPA0; + uint32_t saveFPA1; + uint32_t saveDPLL_A; + uint32_t saveDPLL_A_MD; + uint32_t saveHTOTAL_A; + uint32_t saveHBLANK_A; + uint32_t saveHSYNC_A; + uint32_t saveVTOTAL_A; + uint32_t saveVBLANK_A; + uint32_t saveVSYNC_A; + uint32_t saveDSPASTRIDE; + uint32_t saveDSPASIZE; + uint32_t saveDSPAPOS; + uint32_t saveDSPABASE; + uint32_t saveDSPASURF; + uint32_t saveDSPASTATUS; + uint32_t saveFPB0; + uint32_t saveFPB1; + uint32_t saveDPLL_B; + uint32_t saveDPLL_B_MD; + uint32_t saveHTOTAL_B; + uint32_t saveHBLANK_B; + uint32_t saveHSYNC_B; + uint32_t saveVTOTAL_B; + uint32_t saveVBLANK_B; + uint32_t saveVSYNC_B; + uint32_t saveDSPBSTRIDE; + uint32_t saveDSPBSIZE; + uint32_t saveDSPBPOS; + uint32_t saveDSPBBASE; + uint32_t saveDSPBSURF; + uint32_t saveDSPBSTATUS; + uint32_t saveVCLK_DIVISOR_VGA0; + uint32_t saveVCLK_DIVISOR_VGA1; + uint32_t saveVCLK_POST_DIV; + uint32_t saveVGACNTRL; + uint32_t saveADPA; + uint32_t saveLVDS; + uint32_t saveDVOA; + uint32_t saveDVOB; + uint32_t saveDVOC; + uint32_t savePP_ON; + uint32_t savePP_OFF; + uint32_t savePP_CONTROL; + uint32_t savePP_CYCLE; + uint32_t savePFIT_CONTROL; + uint32_t savePaletteA[256]; + uint32_t savePaletteB[256]; + uint32_t saveBLC_PWM_CTL2; + uint32_t saveBLC_PWM_CTL; + uint32_t saveCLOCKGATING; + uint32_t saveDSPARB; + uint32_t saveDSPATILEOFF; + uint32_t saveDSPBTILEOFF; + uint32_t saveDSPAADDR; + uint32_t saveDSPBADDR; + uint32_t savePFIT_AUTO_RATIOS; + uint32_t savePFIT_PGM_RATIOS; + uint32_t savePP_ON_DELAYS; + uint32_t savePP_OFF_DELAYS; + uint32_t savePP_DIVISOR; + uint32_t saveBSM; + uint32_t saveVBT; + uint32_t saveBCLRPAT_A; + uint32_t saveBCLRPAT_B; + uint32_t saveDSPALINOFF; + uint32_t saveDSPBLINOFF; + uint32_t savePERF_MODE; + uint32_t saveDSPFW1; + uint32_t saveDSPFW2; + uint32_t saveDSPFW3; + uint32_t saveDSPFW4; + uint32_t saveDSPFW5; + uint32_t saveDSPFW6; + uint32_t saveCHICKENBIT; + uint32_t saveDSPACURSOR_CTRL; + uint32_t saveDSPBCURSOR_CTRL; + uint32_t saveDSPACURSOR_BASE; + uint32_t saveDSPBCURSOR_BASE; + uint32_t saveDSPACURSOR_POS; + uint32_t saveDSPBCURSOR_POS; + uint32_t save_palette_a[256]; + uint32_t save_palette_b[256]; + uint32_t saveOV_OVADD; + uint32_t saveOV_OGAMC0; + uint32_t saveOV_OGAMC1; + uint32_t saveOV_OGAMC2; + uint32_t saveOV_OGAMC3; + uint32_t saveOV_OGAMC4; + uint32_t saveOV_OGAMC5; + uint32_t saveOVC_OVADD; + uint32_t saveOVC_OGAMC0; + uint32_t saveOVC_OGAMC1; + uint32_t saveOVC_OGAMC2; + uint32_t saveOVC_OGAMC3; + uint32_t saveOVC_OGAMC4; + uint32_t saveOVC_OGAMC5; + + /* MSI reg save */ + uint32_t msi_addr; + uint32_t msi_data; + + /* Medfield specific register save state */ + uint32_t saveHDMIPHYMISCCTL; + uint32_t saveHDMIB_CONTROL; + uint32_t saveDSPCCNTR; + uint32_t savePIPECCONF; + uint32_t savePIPECSRC; + uint32_t saveHTOTAL_C; + uint32_t saveHBLANK_C; + uint32_t saveHSYNC_C; + uint32_t saveVTOTAL_C; + uint32_t saveVBLANK_C; + uint32_t saveVSYNC_C; + uint32_t saveDSPCSTRIDE; + uint32_t saveDSPCSIZE; + uint32_t saveDSPCPOS; + uint32_t saveDSPCSURF; + uint32_t saveDSPCSTATUS; + uint32_t saveDSPCLINOFF; + uint32_t saveDSPCTILEOFF; + uint32_t saveDSPCCURSOR_CTRL; + uint32_t saveDSPCCURSOR_BASE; + uint32_t saveDSPCCURSOR_POS; + uint32_t save_palette_c[256]; + uint32_t saveOV_OVADD_C; + uint32_t saveOV_OGAMC0_C; + uint32_t saveOV_OGAMC1_C; + uint32_t saveOV_OGAMC2_C; + uint32_t saveOV_OGAMC3_C; + uint32_t saveOV_OGAMC4_C; + uint32_t saveOV_OGAMC5_C; + + /* DSI register save */ + uint32_t saveDEVICE_READY_REG; + uint32_t saveINTR_EN_REG; + uint32_t saveDSI_FUNC_PRG_REG; + uint32_t saveHS_TX_TIMEOUT_REG; + uint32_t saveLP_RX_TIMEOUT_REG; + uint32_t saveTURN_AROUND_TIMEOUT_REG; + uint32_t saveDEVICE_RESET_REG; + uint32_t saveDPI_RESOLUTION_REG; + uint32_t saveHORIZ_SYNC_PAD_COUNT_REG; + uint32_t saveHORIZ_BACK_PORCH_COUNT_REG; + uint32_t saveHORIZ_FRONT_PORCH_COUNT_REG; + uint32_t saveHORIZ_ACTIVE_AREA_COUNT_REG; + uint32_t saveVERT_SYNC_PAD_COUNT_REG; + uint32_t saveVERT_BACK_PORCH_COUNT_REG; + uint32_t saveVERT_FRONT_PORCH_COUNT_REG; + uint32_t saveHIGH_LOW_SWITCH_COUNT_REG; + uint32_t saveINIT_COUNT_REG; + uint32_t saveMAX_RET_PAK_REG; + uint32_t saveVIDEO_FMT_REG; + uint32_t saveEOT_DISABLE_REG; + uint32_t saveLP_BYTECLK_REG; + uint32_t saveHS_LS_DBI_ENABLE_REG; + uint32_t saveTXCLKESC_REG; + uint32_t saveDPHY_PARAM_REG; + uint32_t saveMIPI_CONTROL_REG; + uint32_t saveMIPI; + uint32_t saveMIPI_C; + + /* DPST register save */ + uint32_t saveHISTOGRAM_INT_CONTROL_REG; + uint32_t saveHISTOGRAM_LOGIC_CONTROL_REG; + uint32_t savePWM_CONTROL_LOGIC; + + /* + * DSI info. + */ + void * dbi_dsr_info; + void * dbi_dpu_info; + void * dsi_configs[2]; + /* + * LID-Switch + */ + spinlock_t lid_lock; + struct timer_list lid_timer; + struct psb_intel_opregion opregion; + u32 *lid_state; + u32 lid_last_state; + + /* + * Watchdog + */ + + uint32_t apm_reg; + uint16_t apm_base; + + /* + * Used for modifying backlight from + * xrandr -- consider removing and using HAL instead + */ + struct backlight_device *backlight_device; + struct drm_property *backlight_property; + uint32_t blc_adj1; + uint32_t blc_adj2; + + void *fbdev; + /* DPST state */ + uint32_t dsr_idle_count; + bool is_in_idle; + bool dsr_enable; + void (*exit_idle)(struct drm_device *dev, u32 update_src); + + /* 2D acceleration */ + spinlock_t lock_2d; + + /* FIXME: Arrays anyone ? */ + struct mdfld_dsi_encoder *encoder0; + struct mdfld_dsi_encoder *encoder2; + struct mdfld_dsi_dbi_output * dbi_output; + struct mdfld_dsi_dbi_output * dbi_output2; + u32 bpp; + u32 bpp2; + + bool dispstatus; +}; + + +/* + * Operations for each board type + */ + +struct psb_ops { + const char *name; + unsigned int accel_2d:1; + int pipes; /* Number of output pipes */ + int crtcs; /* Number of CRTCs */ + int sgx_offset; /* Base offset of SGX device */ + + /* Sub functions */ + struct drm_crtc_helper_funcs const *crtc_helper; + struct drm_crtc_funcs const *crtc_funcs; + + /* Setup hooks */ + int (*chip_setup)(struct drm_device *dev); + void (*chip_teardown)(struct drm_device *dev); + + /* Display management hooks */ + int (*output_init)(struct drm_device *dev); + /* Power management hooks */ + void (*init_pm)(struct drm_device *dev); + int (*save_regs)(struct drm_device *dev); + int (*restore_regs)(struct drm_device *dev); + int (*power_up)(struct drm_device *dev); + int (*power_down)(struct drm_device *dev); + + void (*lvds_bl_power)(struct drm_device *dev, bool on); +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + /* Backlight */ + int (*backlight_init)(struct drm_device *dev); +#endif + int i2c_bus; /* I2C bus identifier for Moorestown */ +}; + + + +struct psb_mmu_driver; + +extern int drm_crtc_probe_output_modes(struct drm_device *dev, int, int); +extern int drm_pick_crtcs(struct drm_device *dev); + +static inline struct drm_psb_private *psb_priv(struct drm_device *dev) +{ + return (struct drm_psb_private *) dev->dev_private; +} + +/* + * MMU stuff. + */ + +extern struct psb_mmu_driver *psb_mmu_driver_init(uint8_t __iomem * registers, + int trap_pagefaults, + int invalid_type, + struct drm_psb_private *dev_priv); +extern void psb_mmu_driver_takedown(struct psb_mmu_driver *driver); +extern struct psb_mmu_pd *psb_mmu_get_default_pd(struct psb_mmu_driver + *driver); +extern void psb_mmu_mirror_gtt(struct psb_mmu_pd *pd, uint32_t mmu_offset, + uint32_t gtt_start, uint32_t gtt_pages); +extern struct psb_mmu_pd *psb_mmu_alloc_pd(struct psb_mmu_driver *driver, + int trap_pagefaults, + int invalid_type); +extern void psb_mmu_free_pagedir(struct psb_mmu_pd *pd); +extern void psb_mmu_flush(struct psb_mmu_driver *driver, int rc_prot); +extern void psb_mmu_remove_pfn_sequence(struct psb_mmu_pd *pd, + unsigned long address, + uint32_t num_pages); +extern int psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd, + uint32_t start_pfn, + unsigned long address, + uint32_t num_pages, int type); +extern int psb_mmu_virtual_to_pfn(struct psb_mmu_pd *pd, uint32_t virtual, + unsigned long *pfn); + +/* + * Enable / disable MMU for different requestors. + */ + + +extern void psb_mmu_set_pd_context(struct psb_mmu_pd *pd, int hw_context); +extern int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages, + unsigned long address, uint32_t num_pages, + uint32_t desired_tile_stride, + uint32_t hw_tile_stride, int type); +extern void psb_mmu_remove_pages(struct psb_mmu_pd *pd, + unsigned long address, uint32_t num_pages, + uint32_t desired_tile_stride, + uint32_t hw_tile_stride); +/* + *psb_irq.c + */ + +extern irqreturn_t psb_irq_handler(DRM_IRQ_ARGS); +extern int psb_irq_enable_dpst(struct drm_device *dev); +extern int psb_irq_disable_dpst(struct drm_device *dev); +extern void psb_irq_preinstall(struct drm_device *dev); +extern int psb_irq_postinstall(struct drm_device *dev); +extern void psb_irq_uninstall(struct drm_device *dev); +extern void psb_irq_turn_on_dpst(struct drm_device *dev); +extern void psb_irq_turn_off_dpst(struct drm_device *dev); + +extern void psb_irq_uninstall_islands(struct drm_device *dev, int hw_islands); +extern int psb_vblank_wait2(struct drm_device *dev, unsigned int *sequence); +extern int psb_vblank_wait(struct drm_device *dev, unsigned int *sequence); +extern int psb_enable_vblank(struct drm_device *dev, int crtc); +extern void psb_disable_vblank(struct drm_device *dev, int crtc); +void +psb_enable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask); + +void +psb_disable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask); + +extern u32 psb_get_vblank_counter(struct drm_device *dev, int crtc); + +extern int mdfld_enable_te(struct drm_device *dev, int pipe); +extern void mdfld_disable_te(struct drm_device *dev, int pipe); + +/* + * intel_opregion.c + */ +extern int gma_intel_opregion_init(struct drm_device *dev); +extern int gma_intel_opregion_exit(struct drm_device *dev); + +/* + * framebuffer.c + */ +extern int psbfb_probed(struct drm_device *dev); +extern int psbfb_remove(struct drm_device *dev, + struct drm_framebuffer *fb); +/* + * accel_2d.c + */ +extern void psbfb_copyarea(struct fb_info *info, + const struct fb_copyarea *region); +extern int psbfb_sync(struct fb_info *info); +extern void psb_spank(struct drm_psb_private *dev_priv); +extern int psb_accel_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); + +/* + * psb_reset.c + */ + +extern void psb_lid_timer_init(struct drm_psb_private *dev_priv); +extern void psb_lid_timer_takedown(struct drm_psb_private *dev_priv); +extern void psb_print_pagefault(struct drm_psb_private *dev_priv); + +/* modesetting */ +extern void psb_modeset_init(struct drm_device *dev); +extern void psb_modeset_cleanup(struct drm_device *dev); +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); + +/* mrst_crtc.c */ +extern const struct drm_crtc_helper_funcs mrst_helper_funcs; + +/* mrst_lvds.c */ +extern void mrst_lvds_init(struct drm_device *dev, + struct psb_intel_mode_device *mode_dev); + +/* psb_intel_display.c */ +extern const struct drm_crtc_helper_funcs psb_intel_helper_funcs; +extern const struct drm_crtc_funcs psb_intel_crtc_funcs; + +/* psb_intel_lvds.c */ +extern const struct drm_connector_helper_funcs + psb_intel_lvds_connector_helper_funcs; +extern const struct drm_connector_funcs psb_intel_lvds_connector_funcs; + +/* gem.c */ +extern int psb_gem_init_object(struct drm_gem_object *obj); +extern void psb_gem_free_object(struct drm_gem_object *obj); +extern int psb_gem_get_aperture(struct drm_device *dev, void *data, + struct drm_file *file); +extern int psb_gem_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args); +extern int psb_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev, + uint32_t handle); +extern int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev, + uint32_t handle, uint64_t *offset); +extern int psb_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); +extern int psb_gem_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); +extern int psb_gem_mmap_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); + +/* psb_device.c */ +extern const struct psb_ops psb_chip_ops; + +/* mrst_device.c */ +extern const struct psb_ops mrst_chip_ops; + +/* mdfld_device.c */ +extern const struct psb_ops mdfld_chip_ops; + +/* cdv_device.c */ +extern const struct psb_ops cdv_chip_ops; + +/* + * Debug print bits setting + */ +#define PSB_D_GENERAL (1 << 0) +#define PSB_D_INIT (1 << 1) +#define PSB_D_IRQ (1 << 2) +#define PSB_D_ENTRY (1 << 3) +/* debug the get H/V BP/FP count */ +#define PSB_D_HV (1 << 4) +#define PSB_D_DBI_BF (1 << 5) +#define PSB_D_PM (1 << 6) +#define PSB_D_RENDER (1 << 7) +#define PSB_D_REG (1 << 8) +#define PSB_D_MSVDX (1 << 9) +#define PSB_D_TOPAZ (1 << 10) + +extern int drm_psb_no_fb; +extern int drm_idle_check_interval; + +/* + * Utilities + */ + +static inline u32 MRST_MSG_READ32(uint port, uint offset) +{ + int mcr = (0xD0<<24) | (port << 16) | (offset << 8); + uint32_t ret_val = 0; + struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0); + pci_write_config_dword(pci_root, 0xD0, mcr); + pci_read_config_dword(pci_root, 0xD4, &ret_val); + pci_dev_put(pci_root); + return ret_val; +} +static inline void MRST_MSG_WRITE32(uint port, uint offset, u32 value) +{ + int mcr = (0xE0<<24) | (port << 16) | (offset << 8) | 0xF0; + struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0); + pci_write_config_dword(pci_root, 0xD4, value); + pci_write_config_dword(pci_root, 0xD0, mcr); + pci_dev_put(pci_root); +} +static inline u32 MDFLD_MSG_READ32(uint port, uint offset) +{ + int mcr = (0x10<<24) | (port << 16) | (offset << 8); + uint32_t ret_val = 0; + struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0); + pci_write_config_dword(pci_root, 0xD0, mcr); + pci_read_config_dword(pci_root, 0xD4, &ret_val); + pci_dev_put(pci_root); + return ret_val; +} +static inline void MDFLD_MSG_WRITE32(uint port, uint offset, u32 value) +{ + int mcr = (0x11<<24) | (port << 16) | (offset << 8) | 0xF0; + struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0); + pci_write_config_dword(pci_root, 0xD4, value); + pci_write_config_dword(pci_root, 0xD0, mcr); + pci_dev_put(pci_root); +} + +static inline uint32_t REGISTER_READ(struct drm_device *dev, uint32_t reg) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + return ioread32(dev_priv->vdc_reg + reg); +} + +#define REG_READ(reg) REGISTER_READ(dev, (reg)) + +static inline void REGISTER_WRITE(struct drm_device *dev, uint32_t reg, + uint32_t val) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + iowrite32((val), dev_priv->vdc_reg + (reg)); +} + +#define REG_WRITE(reg, val) REGISTER_WRITE(dev, (reg), (val)) + +static inline void REGISTER_WRITE16(struct drm_device *dev, + uint32_t reg, uint32_t val) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + iowrite16((val), dev_priv->vdc_reg + (reg)); +} + +#define REG_WRITE16(reg, val) REGISTER_WRITE16(dev, (reg), (val)) + +static inline void REGISTER_WRITE8(struct drm_device *dev, + uint32_t reg, uint32_t val) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + iowrite8((val), dev_priv->vdc_reg + (reg)); +} + +#define REG_WRITE8(reg, val) REGISTER_WRITE8(dev, (reg), (val)) + +#define PSB_WVDC32(_val, _offs) iowrite32(_val, dev_priv->vdc_reg + (_offs)) +#define PSB_RVDC32(_offs) ioread32(dev_priv->vdc_reg + (_offs)) + +/* #define TRAP_SGX_PM_FAULT 1 */ +#ifdef TRAP_SGX_PM_FAULT +#define PSB_RSGX32(_offs) \ +({ \ + if (inl(dev_priv->apm_base + PSB_APM_STS) & 0x3) { \ + printk(KERN_ERR \ + "access sgx when it's off!! (READ) %s, %d\n", \ + __FILE__, __LINE__); \ + melay(1000); \ + } \ + ioread32(dev_priv->sgx_reg + (_offs)); \ +}) +#else +#define PSB_RSGX32(_offs) ioread32(dev_priv->sgx_reg + (_offs)) +#endif +#define PSB_WSGX32(_val, _offs) iowrite32(_val, dev_priv->sgx_reg + (_offs)) + +#define MSVDX_REG_DUMP 0 + +#define PSB_WMSVDX32(_val, _offs) iowrite32(_val, dev_priv->msvdx_reg + (_offs)) +#define PSB_RMSVDX32(_offs) ioread32(dev_priv->msvdx_reg + (_offs)) + +#endif diff --git a/trunk/drivers/staging/gma500/psb_intel_display.c b/trunk/drivers/staging/gma500/psb_intel_display.c new file mode 100644 index 000000000000..85659613ae62 --- /dev/null +++ b/trunk/drivers/staging/gma500/psb_intel_display.c @@ -0,0 +1,1429 @@ +/* + * Copyright © 2006-2011 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * 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. + * + * Authors: + * Eric Anholt + */ + +#include +#include + +#include +#include "framebuffer.h" +#include "psb_drv.h" +#include "psb_intel_drv.h" +#include "psb_intel_reg.h" +#include "psb_intel_display.h" +#include "power.h" + +#include "mdfld_output.h" + +struct psb_intel_clock_t { + /* given values */ + int n; + int m1, m2; + int p1, p2; + /* derived values */ + int dot; + int vco; + int m; + int p; +}; + +struct psb_intel_range_t { + int min, max; +}; + +struct psb_intel_p2_t { + int dot_limit; + int p2_slow, p2_fast; +}; + +#define INTEL_P2_NUM 2 + +struct psb_intel_limit_t { + struct psb_intel_range_t dot, vco, n, m, m1, m2, p, p1; + struct psb_intel_p2_t p2; +}; + +#define I8XX_DOT_MIN 25000 +#define I8XX_DOT_MAX 350000 +#define I8XX_VCO_MIN 930000 +#define I8XX_VCO_MAX 1400000 +#define I8XX_N_MIN 3 +#define I8XX_N_MAX 16 +#define I8XX_M_MIN 96 +#define I8XX_M_MAX 140 +#define I8XX_M1_MIN 18 +#define I8XX_M1_MAX 26 +#define I8XX_M2_MIN 6 +#define I8XX_M2_MAX 16 +#define I8XX_P_MIN 4 +#define I8XX_P_MAX 128 +#define I8XX_P1_MIN 2 +#define I8XX_P1_MAX 33 +#define I8XX_P1_LVDS_MIN 1 +#define I8XX_P1_LVDS_MAX 6 +#define I8XX_P2_SLOW 4 +#define I8XX_P2_FAST 2 +#define I8XX_P2_LVDS_SLOW 14 +#define I8XX_P2_LVDS_FAST 14 /* No fast option */ +#define I8XX_P2_SLOW_LIMIT 165000 + +#define I9XX_DOT_MIN 20000 +#define I9XX_DOT_MAX 400000 +#define I9XX_VCO_MIN 1400000 +#define I9XX_VCO_MAX 2800000 +#define I9XX_N_MIN 3 +#define I9XX_N_MAX 8 +#define I9XX_M_MIN 70 +#define I9XX_M_MAX 120 +#define I9XX_M1_MIN 10 +#define I9XX_M1_MAX 20 +#define I9XX_M2_MIN 5 +#define I9XX_M2_MAX 9 +#define I9XX_P_SDVO_DAC_MIN 5 +#define I9XX_P_SDVO_DAC_MAX 80 +#define I9XX_P_LVDS_MIN 7 +#define I9XX_P_LVDS_MAX 98 +#define I9XX_P1_MIN 1 +#define I9XX_P1_MAX 8 +#define I9XX_P2_SDVO_DAC_SLOW 10 +#define I9XX_P2_SDVO_DAC_FAST 5 +#define I9XX_P2_SDVO_DAC_SLOW_LIMIT 200000 +#define I9XX_P2_LVDS_SLOW 14 +#define I9XX_P2_LVDS_FAST 7 +#define I9XX_P2_LVDS_SLOW_LIMIT 112000 + +#define INTEL_LIMIT_I8XX_DVO_DAC 0 +#define INTEL_LIMIT_I8XX_LVDS 1 +#define INTEL_LIMIT_I9XX_SDVO_DAC 2 +#define INTEL_LIMIT_I9XX_LVDS 3 + +static const struct psb_intel_limit_t psb_intel_limits[] = { + { /* INTEL_LIMIT_I8XX_DVO_DAC */ + .dot = {.min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX}, + .vco = {.min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX}, + .n = {.min = I8XX_N_MIN, .max = I8XX_N_MAX}, + .m = {.min = I8XX_M_MIN, .max = I8XX_M_MAX}, + .m1 = {.min = I8XX_M1_MIN, .max = I8XX_M1_MAX}, + .m2 = {.min = I8XX_M2_MIN, .max = I8XX_M2_MAX}, + .p = {.min = I8XX_P_MIN, .max = I8XX_P_MAX}, + .p1 = {.min = I8XX_P1_MIN, .max = I8XX_P1_MAX}, + .p2 = {.dot_limit = I8XX_P2_SLOW_LIMIT, + .p2_slow = I8XX_P2_SLOW, .p2_fast = I8XX_P2_FAST}, + }, + { /* INTEL_LIMIT_I8XX_LVDS */ + .dot = {.min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX}, + .vco = {.min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX}, + .n = {.min = I8XX_N_MIN, .max = I8XX_N_MAX}, + .m = {.min = I8XX_M_MIN, .max = I8XX_M_MAX}, + .m1 = {.min = I8XX_M1_MIN, .max = I8XX_M1_MAX}, + .m2 = {.min = I8XX_M2_MIN, .max = I8XX_M2_MAX}, + .p = {.min = I8XX_P_MIN, .max = I8XX_P_MAX}, + .p1 = {.min = I8XX_P1_LVDS_MIN, .max = I8XX_P1_LVDS_MAX}, + .p2 = {.dot_limit = I8XX_P2_SLOW_LIMIT, + .p2_slow = I8XX_P2_LVDS_SLOW, .p2_fast = I8XX_P2_LVDS_FAST}, + }, + { /* INTEL_LIMIT_I9XX_SDVO_DAC */ + .dot = {.min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX}, + .vco = {.min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX}, + .n = {.min = I9XX_N_MIN, .max = I9XX_N_MAX}, + .m = {.min = I9XX_M_MIN, .max = I9XX_M_MAX}, + .m1 = {.min = I9XX_M1_MIN, .max = I9XX_M1_MAX}, + .m2 = {.min = I9XX_M2_MIN, .max = I9XX_M2_MAX}, + .p = {.min = I9XX_P_SDVO_DAC_MIN, .max = I9XX_P_SDVO_DAC_MAX}, + .p1 = {.min = I9XX_P1_MIN, .max = I9XX_P1_MAX}, + .p2 = {.dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT, + .p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = + I9XX_P2_SDVO_DAC_FAST}, + }, + { /* INTEL_LIMIT_I9XX_LVDS */ + .dot = {.min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX}, + .vco = {.min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX}, + .n = {.min = I9XX_N_MIN, .max = I9XX_N_MAX}, + .m = {.min = I9XX_M_MIN, .max = I9XX_M_MAX}, + .m1 = {.min = I9XX_M1_MIN, .max = I9XX_M1_MAX}, + .m2 = {.min = I9XX_M2_MIN, .max = I9XX_M2_MAX}, + .p = {.min = I9XX_P_LVDS_MIN, .max = I9XX_P_LVDS_MAX}, + .p1 = {.min = I9XX_P1_MIN, .max = I9XX_P1_MAX}, + /* The single-channel range is 25-112Mhz, and dual-channel + * is 80-224Mhz. Prefer single channel as much as possible. + */ + .p2 = {.dot_limit = I9XX_P2_LVDS_SLOW_LIMIT, + .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_FAST}, + }, +}; + +static const struct psb_intel_limit_t *psb_intel_limit(struct drm_crtc *crtc) +{ + const struct psb_intel_limit_t *limit; + + if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) + limit = &psb_intel_limits[INTEL_LIMIT_I9XX_LVDS]; + else + limit = &psb_intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC]; + return limit; +} + +/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */ + +static void i8xx_clock(int refclk, struct psb_intel_clock_t *clock) +{ + clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); + clock->p = clock->p1 * clock->p2; + clock->vco = refclk * clock->m / (clock->n + 2); + clock->dot = clock->vco / clock->p; +} + +/** Derive the pixel clock for the given refclk and divisors for 9xx chips. */ + +static void i9xx_clock(int refclk, struct psb_intel_clock_t *clock) +{ + clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); + clock->p = clock->p1 * clock->p2; + clock->vco = refclk * clock->m / (clock->n + 2); + clock->dot = clock->vco / clock->p; +} + +static void psb_intel_clock(struct drm_device *dev, int refclk, + struct psb_intel_clock_t *clock) +{ + return i9xx_clock(refclk, clock); +} + +/** + * Returns whether any output on the specified pipe is of the specified type + */ +bool psb_intel_pipe_has_type(struct drm_crtc *crtc, int type) +{ + struct drm_device *dev = crtc->dev; + struct drm_mode_config *mode_config = &dev->mode_config; + struct drm_connector *l_entry; + + list_for_each_entry(l_entry, &mode_config->connector_list, head) { + if (l_entry->encoder && l_entry->encoder->crtc == crtc) { + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(l_entry); + if (psb_intel_output->type == type) + return true; + } + } + return false; +} + +#define INTELPllInvalid(s) { /* ErrorF (s) */; return false; } +/** + * Returns whether the given set of divisors are valid for a given refclk with + * the given connectors. + */ + +static bool psb_intel_PLL_is_valid(struct drm_crtc *crtc, + struct psb_intel_clock_t *clock) +{ + const struct psb_intel_limit_t *limit = psb_intel_limit(crtc); + + if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1) + INTELPllInvalid("p1 out of range\n"); + if (clock->p < limit->p.min || limit->p.max < clock->p) + INTELPllInvalid("p out of range\n"); + if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2) + INTELPllInvalid("m2 out of range\n"); + if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1) + INTELPllInvalid("m1 out of range\n"); + if (clock->m1 <= clock->m2) + INTELPllInvalid("m1 <= m2\n"); + if (clock->m < limit->m.min || limit->m.max < clock->m) + INTELPllInvalid("m out of range\n"); + if (clock->n < limit->n.min || limit->n.max < clock->n) + INTELPllInvalid("n out of range\n"); + if (clock->vco < limit->vco.min || limit->vco.max < clock->vco) + INTELPllInvalid("vco out of range\n"); + /* XXX: We may need to be checking "Dot clock" + * depending on the multiplier, connector, etc., + * rather than just a single range. + */ + if (clock->dot < limit->dot.min || limit->dot.max < clock->dot) + INTELPllInvalid("dot out of range\n"); + + return true; +} + +/** + * Returns a set of divisors for the desired target clock with the given + * refclk, or FALSE. The returned values represent the clock equation: + * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. + */ +static bool psb_intel_find_best_PLL(struct drm_crtc *crtc, int target, + int refclk, + struct psb_intel_clock_t *best_clock) +{ + struct drm_device *dev = crtc->dev; + struct psb_intel_clock_t clock; + const struct psb_intel_limit_t *limit = psb_intel_limit(crtc); + int err = target; + + if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && + (REG_READ(LVDS) & LVDS_PORT_EN) != 0) { + /* + * For LVDS, if the panel is on, just rely on its current + * settings for dual-channel. We haven't figured out how to + * reliably set up different single/dual channel state, if we + * even can. + */ + if ((REG_READ(LVDS) & LVDS_CLKB_POWER_MASK) == + LVDS_CLKB_POWER_UP) + clock.p2 = limit->p2.p2_fast; + else + clock.p2 = limit->p2.p2_slow; + } else { + if (target < limit->p2.dot_limit) + clock.p2 = limit->p2.p2_slow; + else + clock.p2 = limit->p2.p2_fast; + } + + memset(best_clock, 0, sizeof(*best_clock)); + + for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; + clock.m1++) { + for (clock.m2 = limit->m2.min; + clock.m2 < clock.m1 && clock.m2 <= limit->m2.max; + clock.m2++) { + for (clock.n = limit->n.min; + clock.n <= limit->n.max; clock.n++) { + for (clock.p1 = limit->p1.min; + clock.p1 <= limit->p1.max; + clock.p1++) { + int this_err; + + psb_intel_clock(dev, refclk, &clock); + + if (!psb_intel_PLL_is_valid + (crtc, &clock)) + continue; + + this_err = abs(clock.dot - target); + if (this_err < err) { + *best_clock = clock; + err = this_err; + } + } + } + } + } + + return err != target; +} + +void psb_intel_wait_for_vblank(struct drm_device *dev) +{ + /* Wait for 20ms, i.e. one cycle at 50hz. */ + mdelay(20); +} + +int psb_intel_pipe_set_base(struct drm_crtc *crtc, + int x, int y, struct drm_framebuffer *old_fb) +{ + struct drm_device *dev = crtc->dev; + /* struct drm_i915_master_private *master_priv; */ + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb); + int pipe = psb_intel_crtc->pipe; + unsigned long start, offset; + int dspbase = (pipe == 0 ? DSPABASE : DSPBBASE); + int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF); + int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE; + int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; + u32 dspcntr; + int ret = 0; + + if (!gma_power_begin(dev, true)) + return 0; + + /* no fb bound */ + if (!crtc->fb) { + dev_dbg(dev->dev, "No FB bound\n"); + goto psb_intel_pipe_cleaner; + } + + /* We are displaying this buffer, make sure it is actually loaded + into the GTT */ + ret = psb_gtt_pin(psbfb->gtt); + if (ret < 0) + goto psb_intel_pipe_set_base_exit; + start = psbfb->gtt->offset; + + offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8); + + REG_WRITE(dspstride, crtc->fb->pitches[0]); + + dspcntr = REG_READ(dspcntr_reg); + dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; + + switch (crtc->fb->bits_per_pixel) { + case 8: + dspcntr |= DISPPLANE_8BPP; + break; + case 16: + if (crtc->fb->depth == 15) + dspcntr |= DISPPLANE_15_16BPP; + else + dspcntr |= DISPPLANE_16BPP; + break; + case 24: + case 32: + dspcntr |= DISPPLANE_32BPP_NO_ALPHA; + break; + default: + dev_err(dev->dev, "Unknown color depth\n"); + ret = -EINVAL; + psb_gtt_unpin(psbfb->gtt); + goto psb_intel_pipe_set_base_exit; + } + REG_WRITE(dspcntr_reg, dspcntr); + + + if (0 /* FIXMEAC - check what PSB needs */) { + REG_WRITE(dspbase, offset); + REG_READ(dspbase); + REG_WRITE(dspsurf, start); + REG_READ(dspsurf); + } else { + REG_WRITE(dspbase, start + offset); + REG_READ(dspbase); + } + +psb_intel_pipe_cleaner: + /* If there was a previous display we can now unpin it */ + if (old_fb) + psb_gtt_unpin(to_psb_fb(old_fb)->gtt); + +psb_intel_pipe_set_base_exit: + gma_power_end(dev); + return ret; +} + +/** + * Sets the power management mode of the pipe and plane. + * + * This code should probably grow support for turning the cursor off and back + * on appropriately at the same time as we're turning the pipe off/on. + */ +static void psb_intel_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct drm_device *dev = crtc->dev; + /* struct drm_i915_master_private *master_priv; */ + /* struct drm_i915_private *dev_priv = dev->dev_private; */ + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + int pipe = psb_intel_crtc->pipe; + int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; + int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; + int dspbase_reg = (pipe == 0) ? DSPABASE : DSPBBASE; + int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; + u32 temp; + bool enabled; + + /* 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: + /* Enable the DPLL */ + temp = REG_READ(dpll_reg); + if ((temp & DPLL_VCO_ENABLE) == 0) { + REG_WRITE(dpll_reg, temp); + REG_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + udelay(150); + REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); + REG_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + udelay(150); + REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); + REG_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + udelay(150); + } + + /* Enable the pipe */ + temp = REG_READ(pipeconf_reg); + if ((temp & PIPEACONF_ENABLE) == 0) + REG_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE); + + /* Enable the plane */ + temp = REG_READ(dspcntr_reg); + if ((temp & DISPLAY_PLANE_ENABLE) == 0) { + REG_WRITE(dspcntr_reg, + temp | DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + } + + psb_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 */ + break; + case DRM_MODE_DPMS_OFF: + /* Give the overlay scaler a chance to disable + * if it's on this pipe */ + /* psb_intel_crtc_dpms_video(crtc, FALSE); TODO */ + + /* Disable the VGA plane that we never use */ + REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); + + /* Disable display plane */ + temp = REG_READ(dspcntr_reg); + if ((temp & DISPLAY_PLANE_ENABLE) != 0) { + REG_WRITE(dspcntr_reg, + temp & ~DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + REG_READ(dspbase_reg); + } + + /* Next, disable display pipes */ + temp = REG_READ(pipeconf_reg); + if ((temp & PIPEACONF_ENABLE) != 0) { + REG_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE); + REG_READ(pipeconf_reg); + } + + /* Wait for vblank for the disable to take effect. */ + psb_intel_wait_for_vblank(dev); + + temp = REG_READ(dpll_reg); + if ((temp & DPLL_VCO_ENABLE) != 0) { + REG_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE); + REG_READ(dpll_reg); + } + + /* Wait for the clocks to turn off. */ + udelay(150); + break; + } + + enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF; + + /*Set FIFO Watermarks*/ + REG_WRITE(DSPARB, 0x3F3E); +} + +static void psb_intel_crtc_prepare(struct drm_crtc *crtc) +{ + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); +} + +static void psb_intel_crtc_commit(struct drm_crtc *crtc) +{ + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); +} + +void psb_intel_encoder_prepare(struct drm_encoder *encoder) +{ + struct drm_encoder_helper_funcs *encoder_funcs = + encoder->helper_private; + /* lvds has its own version of prepare see psb_intel_lvds_prepare */ + encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF); +} + +void psb_intel_encoder_commit(struct drm_encoder *encoder) +{ + struct drm_encoder_helper_funcs *encoder_funcs = + encoder->helper_private; + /* lvds has its own version of commit see psb_intel_lvds_commit */ + encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); +} + +static bool psb_intel_crtc_mode_fixup(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + + +/** + * Return the pipe currently connected to the panel fitter, + * or -1 if the panel fitter is not present or not in use + */ +static int psb_intel_panel_fitter_pipe(struct drm_device *dev) +{ + u32 pfit_control; + + pfit_control = REG_READ(PFIT_CONTROL); + + /* See if the panel fitter is in use */ + if ((pfit_control & PFIT_ENABLE) == 0) + return -1; + /* Must be on PIPE 1 for PSB */ + return 1; +} + +static int psb_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_device *dev = crtc->dev; + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + int pipe = psb_intel_crtc->pipe; + int fp_reg = (pipe == 0) ? FPA0 : FPB0; + int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; + int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; + int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; + int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; + int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; + int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; + int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B; + int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B; + int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B; + int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE; + int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS; + int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; + int refclk; + struct psb_intel_clock_t clock; + u32 dpll = 0, fp = 0, dspcntr, pipeconf; + bool ok, is_sdvo = false, is_dvo = false; + bool is_crt = false, is_lvds = false, is_tv = false; + struct drm_mode_config *mode_config = &dev->mode_config; + struct drm_connector *connector; + + /* No scan out no play */ + if (crtc->fb == NULL) { + crtc_funcs->mode_set_base(crtc, x, y, old_fb); + return 0; + } + + list_for_each_entry(connector, &mode_config->connector_list, head) { + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + + if (!connector->encoder + || connector->encoder->crtc != crtc) + continue; + + switch (psb_intel_output->type) { + case INTEL_OUTPUT_LVDS: + is_lvds = true; + break; + case INTEL_OUTPUT_SDVO: + is_sdvo = true; + break; + case INTEL_OUTPUT_DVO: + is_dvo = true; + break; + case INTEL_OUTPUT_TVOUT: + is_tv = true; + break; + case INTEL_OUTPUT_ANALOG: + is_crt = true; + break; + } + } + + refclk = 96000; + + ok = psb_intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, + &clock); + if (!ok) { + dev_err(dev->dev, "Couldn't find PLL settings for mode!\n"); + return 0; + } + + fp = clock.n << 16 | clock.m1 << 8 | clock.m2; + + dpll = DPLL_VGA_MODE_DIS; + if (is_lvds) { + dpll |= DPLLB_MODE_LVDS; + dpll |= DPLL_DVO_HIGH_SPEED; + } else + dpll |= DPLLB_MODE_DAC_SERIAL; + if (is_sdvo) { + int sdvo_pixel_multiply = + adjusted_mode->clock / mode->clock; + dpll |= DPLL_DVO_HIGH_SPEED; + dpll |= + (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES; + } + + /* compute bitmask from p1 value */ + dpll |= (1 << (clock.p1 - 1)) << 16; + switch (clock.p2) { + case 5: + dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5; + break; + case 7: + dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7; + break; + case 10: + dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10; + break; + case 14: + dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14; + break; + } + + if (is_tv) { + /* XXX: just matching BIOS for now */ +/* dpll |= PLL_REF_INPUT_TVCLKINBC; */ + dpll |= 3; + } + dpll |= PLL_REF_INPUT_DREFCLK; + + /* setup pipeconf */ + pipeconf = REG_READ(pipeconf_reg); + + /* Set up the display plane register */ + dspcntr = DISPPLANE_GAMMA_ENABLE; + + if (pipe == 0) + dspcntr |= DISPPLANE_SEL_PIPE_A; + else + dspcntr |= DISPPLANE_SEL_PIPE_B; + + dspcntr |= DISPLAY_PLANE_ENABLE; + pipeconf |= PIPEACONF_ENABLE; + dpll |= DPLL_VCO_ENABLE; + + + /* Disable the panel fitter if it was on our pipe */ + if (psb_intel_panel_fitter_pipe(dev) == pipe) + REG_WRITE(PFIT_CONTROL, 0); + + drm_mode_debug_printmodeline(mode); + + if (dpll & DPLL_VCO_ENABLE) { + REG_WRITE(fp_reg, fp); + REG_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE); + REG_READ(dpll_reg); + udelay(150); + } + + /* The LVDS pin pair needs to be on before the DPLLs are enabled. + * This is an exception to the general rule that mode_set doesn't turn + * things on. + */ + if (is_lvds) { + u32 lvds = REG_READ(LVDS); + + lvds &= ~LVDS_PIPEB_SELECT; + if (pipe == 1) + lvds |= LVDS_PIPEB_SELECT; + + lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; + /* Set the B0-B3 data pairs corresponding to + * whether we're going to + * set the DPLLs for dual-channel mode or not. + */ + lvds &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); + if (clock.p2 == 7) + lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; + + /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) + * appropriately here, but we need to look more + * thoroughly into how panels behave in the two modes. + */ + + REG_WRITE(LVDS, lvds); + REG_READ(LVDS); + } + + REG_WRITE(fp_reg, fp); + REG_WRITE(dpll_reg, dpll); + REG_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + udelay(150); + + /* write it again -- the BIOS does, after all */ + REG_WRITE(dpll_reg, dpll); + + REG_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + udelay(150); + + REG_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) | + ((adjusted_mode->crtc_htotal - 1) << 16)); + REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | + ((adjusted_mode->crtc_hblank_end - 1) << 16)); + REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | + ((adjusted_mode->crtc_hsync_end - 1) << 16)); + REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | + ((adjusted_mode->crtc_vtotal - 1) << 16)); + REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | + ((adjusted_mode->crtc_vblank_end - 1) << 16)); + REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | + ((adjusted_mode->crtc_vsync_end - 1) << 16)); + /* pipesrc and dspsize control the size that is scaled from, + * which should always be the user's requested size. + */ + REG_WRITE(dspsize_reg, + ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); + REG_WRITE(dsppos_reg, 0); + REG_WRITE(pipesrc_reg, + ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); + REG_WRITE(pipeconf_reg, pipeconf); + REG_READ(pipeconf_reg); + + psb_intel_wait_for_vblank(dev); + + REG_WRITE(dspcntr_reg, dspcntr); + + /* Flush the plane changes */ + crtc_funcs->mode_set_base(crtc, x, y, old_fb); + + psb_intel_wait_for_vblank(dev); + + return 0; +} + +/** Loads the palette/gamma unit for the CRTC with the prepared values */ +void psb_intel_crtc_load_lut(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = + (struct drm_psb_private *)dev->dev_private; + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + int palreg = PALETTE_A; + int i; + + /* The clocks have to be on to load the palette. */ + if (!crtc->enabled) + return; + + switch (psb_intel_crtc->pipe) { + case 0: + break; + case 1: + palreg = PALETTE_B; + break; + case 2: + palreg = PALETTE_C; + break; + default: + dev_err(dev->dev, "Illegal Pipe Number.\n"); + return; + } + + if (gma_power_begin(dev, false)) { + for (i = 0; i < 256; i++) { + REG_WRITE(palreg + 4 * i, + ((psb_intel_crtc->lut_r[i] + + psb_intel_crtc->lut_adj[i]) << 16) | + ((psb_intel_crtc->lut_g[i] + + psb_intel_crtc->lut_adj[i]) << 8) | + (psb_intel_crtc->lut_b[i] + + psb_intel_crtc->lut_adj[i])); + } + gma_power_end(dev); + } else { + for (i = 0; i < 256; i++) { + dev_priv->save_palette_a[i] = + ((psb_intel_crtc->lut_r[i] + + psb_intel_crtc->lut_adj[i]) << 16) | + ((psb_intel_crtc->lut_g[i] + + psb_intel_crtc->lut_adj[i]) << 8) | + (psb_intel_crtc->lut_b[i] + + psb_intel_crtc->lut_adj[i]); + } + + } +} + +/** + * Save HW states of giving crtc + */ +static void psb_intel_crtc_save(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + /* struct drm_psb_private *dev_priv = + (struct drm_psb_private *)dev->dev_private; */ + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + struct psb_intel_crtc_state *crtc_state = psb_intel_crtc->crtc_state; + int pipeA = (psb_intel_crtc->pipe == 0); + uint32_t paletteReg; + int i; + + if (!crtc_state) { + dev_err(dev->dev, "No CRTC state found\n"); + return; + } + + crtc_state->saveDSPCNTR = REG_READ(pipeA ? DSPACNTR : DSPBCNTR); + crtc_state->savePIPECONF = REG_READ(pipeA ? PIPEACONF : PIPEBCONF); + crtc_state->savePIPESRC = REG_READ(pipeA ? PIPEASRC : PIPEBSRC); + crtc_state->saveFP0 = REG_READ(pipeA ? FPA0 : FPB0); + crtc_state->saveFP1 = REG_READ(pipeA ? FPA1 : FPB1); + crtc_state->saveDPLL = REG_READ(pipeA ? DPLL_A : DPLL_B); + crtc_state->saveHTOTAL = REG_READ(pipeA ? HTOTAL_A : HTOTAL_B); + crtc_state->saveHBLANK = REG_READ(pipeA ? HBLANK_A : HBLANK_B); + crtc_state->saveHSYNC = REG_READ(pipeA ? HSYNC_A : HSYNC_B); + crtc_state->saveVTOTAL = REG_READ(pipeA ? VTOTAL_A : VTOTAL_B); + crtc_state->saveVBLANK = REG_READ(pipeA ? VBLANK_A : VBLANK_B); + crtc_state->saveVSYNC = REG_READ(pipeA ? VSYNC_A : VSYNC_B); + crtc_state->saveDSPSTRIDE = REG_READ(pipeA ? DSPASTRIDE : DSPBSTRIDE); + + /*NOTE: DSPSIZE DSPPOS only for psb*/ + crtc_state->saveDSPSIZE = REG_READ(pipeA ? DSPASIZE : DSPBSIZE); + crtc_state->saveDSPPOS = REG_READ(pipeA ? DSPAPOS : DSPBPOS); + + crtc_state->saveDSPBASE = REG_READ(pipeA ? DSPABASE : DSPBBASE); + + paletteReg = pipeA ? PALETTE_A : PALETTE_B; + for (i = 0; i < 256; ++i) + crtc_state->savePalette[i] = REG_READ(paletteReg + (i << 2)); +} + +/** + * Restore HW states of giving crtc + */ +static void psb_intel_crtc_restore(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + /* struct drm_psb_private * dev_priv = + (struct drm_psb_private *)dev->dev_private; */ + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + struct psb_intel_crtc_state *crtc_state = psb_intel_crtc->crtc_state; + /* struct drm_crtc_helper_funcs * crtc_funcs = crtc->helper_private; */ + int pipeA = (psb_intel_crtc->pipe == 0); + uint32_t paletteReg; + int i; + + if (!crtc_state) { + dev_err(dev->dev, "No crtc state\n"); + return; + } + + if (crtc_state->saveDPLL & DPLL_VCO_ENABLE) { + REG_WRITE(pipeA ? DPLL_A : DPLL_B, + crtc_state->saveDPLL & ~DPLL_VCO_ENABLE); + REG_READ(pipeA ? DPLL_A : DPLL_B); + udelay(150); + } + + REG_WRITE(pipeA ? FPA0 : FPB0, crtc_state->saveFP0); + REG_READ(pipeA ? FPA0 : FPB0); + + REG_WRITE(pipeA ? FPA1 : FPB1, crtc_state->saveFP1); + REG_READ(pipeA ? FPA1 : FPB1); + + REG_WRITE(pipeA ? DPLL_A : DPLL_B, crtc_state->saveDPLL); + REG_READ(pipeA ? DPLL_A : DPLL_B); + udelay(150); + + REG_WRITE(pipeA ? HTOTAL_A : HTOTAL_B, crtc_state->saveHTOTAL); + REG_WRITE(pipeA ? HBLANK_A : HBLANK_B, crtc_state->saveHBLANK); + REG_WRITE(pipeA ? HSYNC_A : HSYNC_B, crtc_state->saveHSYNC); + REG_WRITE(pipeA ? VTOTAL_A : VTOTAL_B, crtc_state->saveVTOTAL); + REG_WRITE(pipeA ? VBLANK_A : VBLANK_B, crtc_state->saveVBLANK); + REG_WRITE(pipeA ? VSYNC_A : VSYNC_B, crtc_state->saveVSYNC); + REG_WRITE(pipeA ? DSPASTRIDE : DSPBSTRIDE, crtc_state->saveDSPSTRIDE); + + REG_WRITE(pipeA ? DSPASIZE : DSPBSIZE, crtc_state->saveDSPSIZE); + REG_WRITE(pipeA ? DSPAPOS : DSPBPOS, crtc_state->saveDSPPOS); + + REG_WRITE(pipeA ? PIPEASRC : PIPEBSRC, crtc_state->savePIPESRC); + REG_WRITE(pipeA ? DSPABASE : DSPBBASE, crtc_state->saveDSPBASE); + REG_WRITE(pipeA ? PIPEACONF : PIPEBCONF, crtc_state->savePIPECONF); + + psb_intel_wait_for_vblank(dev); + + REG_WRITE(pipeA ? DSPACNTR : DSPBCNTR, crtc_state->saveDSPCNTR); + REG_WRITE(pipeA ? DSPABASE : DSPBBASE, crtc_state->saveDSPBASE); + + psb_intel_wait_for_vblank(dev); + + paletteReg = pipeA ? PALETTE_A : PALETTE_B; + for (i = 0; i < 256; ++i) + REG_WRITE(paletteReg + (i << 2), crtc_state->savePalette[i]); +} + +static int psb_intel_crtc_cursor_set(struct drm_crtc *crtc, + struct drm_file *file_priv, + uint32_t handle, + uint32_t width, uint32_t height) +{ + struct drm_device *dev = crtc->dev; + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + int pipe = psb_intel_crtc->pipe; + uint32_t control = (pipe == 0) ? CURACNTR : CURBCNTR; + uint32_t base = (pipe == 0) ? CURABASE : CURBBASE; + uint32_t temp; + size_t addr = 0; + struct gtt_range *gt; + struct drm_gem_object *obj; + int ret; + + /* if we want to turn of the cursor ignore width and height */ + if (!handle) { + /* turn off the cursor */ + temp = CURSOR_MODE_DISABLE; + + if (gma_power_begin(dev, false)) { + REG_WRITE(control, temp); + REG_WRITE(base, 0); + gma_power_end(dev); + } + + /* Unpin the old GEM object */ + if (psb_intel_crtc->cursor_obj) { + gt = container_of(psb_intel_crtc->cursor_obj, + struct gtt_range, gem); + psb_gtt_unpin(gt); + drm_gem_object_unreference(psb_intel_crtc->cursor_obj); + psb_intel_crtc->cursor_obj = NULL; + } + + return 0; + } + + /* Currently we only support 64x64 cursors */ + if (width != 64 || height != 64) { + dev_dbg(dev->dev, "we currently only support 64x64 cursors\n"); + return -EINVAL; + } + + obj = drm_gem_object_lookup(dev, file_priv, handle); + if (!obj) + return -ENOENT; + + if (obj->size < width * height * 4) { + dev_dbg(dev->dev, "buffer is to small\n"); + return -ENOMEM; + } + + gt = container_of(obj, struct gtt_range, gem); + + /* Pin the memory into the GTT */ + ret = psb_gtt_pin(gt); + if (ret) { + dev_err(dev->dev, "Can not pin down handle 0x%x\n", handle); + return ret; + } + + + addr = gt->offset; /* Or resource.start ??? */ + + psb_intel_crtc->cursor_addr = addr; + + temp = 0; + /* set the pipe for the cursor */ + temp |= (pipe << 28); + temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE; + + if (gma_power_begin(dev, false)) { + REG_WRITE(control, temp); + REG_WRITE(base, addr); + gma_power_end(dev); + } + + /* unpin the old bo */ + if (psb_intel_crtc->cursor_obj) { + gt = container_of(psb_intel_crtc->cursor_obj, + struct gtt_range, gem); + psb_gtt_unpin(gt); + drm_gem_object_unreference(psb_intel_crtc->cursor_obj); + psb_intel_crtc->cursor_obj = obj; + } + return 0; +} + +static int psb_intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) +{ + struct drm_device *dev = crtc->dev; + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + int pipe = psb_intel_crtc->pipe; + uint32_t temp = 0; + uint32_t addr; + + + if (x < 0) { + temp |= (CURSOR_POS_SIGN << CURSOR_X_SHIFT); + x = -x; + } + if (y < 0) { + temp |= (CURSOR_POS_SIGN << CURSOR_Y_SHIFT); + y = -y; + } + + temp |= ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT); + temp |= ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT); + + addr = psb_intel_crtc->cursor_addr; + + if (gma_power_begin(dev, false)) { + REG_WRITE((pipe == 0) ? CURAPOS : CURBPOS, temp); + REG_WRITE((pipe == 0) ? CURABASE : CURBBASE, addr); + gma_power_end(dev); + } + return 0; +} + +void psb_intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, + u16 *green, u16 *blue, uint32_t type, uint32_t size) +{ + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + int i; + + if (size != 256) + return; + + for (i = 0; i < 256; i++) { + psb_intel_crtc->lut_r[i] = red[i] >> 8; + psb_intel_crtc->lut_g[i] = green[i] >> 8; + psb_intel_crtc->lut_b[i] = blue[i] >> 8; + } + + psb_intel_crtc_load_lut(crtc); +} + +static int psb_crtc_set_config(struct drm_mode_set *set) +{ + int ret; + struct drm_device *dev = set->crtc->dev; + + pm_runtime_forbid(&dev->pdev->dev); + ret = drm_crtc_helper_set_config(set); + pm_runtime_allow(&dev->pdev->dev); + return ret; +} + +/* Returns the clock of the currently programmed mode of the given pipe. */ +static int psb_intel_crtc_clock_get(struct drm_device *dev, + struct drm_crtc *crtc) +{ + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + int pipe = psb_intel_crtc->pipe; + u32 dpll; + u32 fp; + struct psb_intel_clock_t clock; + bool is_lvds; + struct drm_psb_private *dev_priv = dev->dev_private; + + if (gma_power_begin(dev, false)) { + dpll = REG_READ((pipe == 0) ? DPLL_A : DPLL_B); + if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) + fp = REG_READ((pipe == 0) ? FPA0 : FPB0); + else + fp = REG_READ((pipe == 0) ? FPA1 : FPB1); + is_lvds = (pipe == 1) && (REG_READ(LVDS) & LVDS_PORT_EN); + gma_power_end(dev); + } else { + dpll = (pipe == 0) ? + dev_priv->saveDPLL_A : dev_priv->saveDPLL_B; + + if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) + fp = (pipe == 0) ? + dev_priv->saveFPA0 : + dev_priv->saveFPB0; + else + fp = (pipe == 0) ? + dev_priv->saveFPA1 : + dev_priv->saveFPB1; + + is_lvds = (pipe == 1) && (dev_priv->saveLVDS & LVDS_PORT_EN); + } + + clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT; + clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT; + clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT; + + if (is_lvds) { + clock.p1 = + ffs((dpll & + DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >> + DPLL_FPA01_P1_POST_DIV_SHIFT); + clock.p2 = 14; + + if ((dpll & PLL_REF_INPUT_MASK) == + PLLB_REF_INPUT_SPREADSPECTRUMIN) { + /* XXX: might not be 66MHz */ + i8xx_clock(66000, &clock); + } else + i8xx_clock(48000, &clock); + } else { + if (dpll & PLL_P1_DIVIDE_BY_TWO) + clock.p1 = 2; + else { + clock.p1 = + ((dpll & + DPLL_FPA01_P1_POST_DIV_MASK_I830) >> + DPLL_FPA01_P1_POST_DIV_SHIFT) + 2; + } + if (dpll & PLL_P2_DIVIDE_BY_4) + clock.p2 = 4; + else + clock.p2 = 2; + + i8xx_clock(48000, &clock); + } + + /* XXX: It would be nice to validate the clocks, but we can't reuse + * i830PllIsValid() because it relies on the xf86_config connector + * configuration being accurate, which it isn't necessarily. + */ + + return clock.dot; +} + +/** Returns the currently programmed mode of the given pipe. */ +struct drm_display_mode *psb_intel_crtc_mode_get(struct drm_device *dev, + struct drm_crtc *crtc) +{ + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + int pipe = psb_intel_crtc->pipe; + struct drm_display_mode *mode; + int htot; + int hsync; + int vtot; + int vsync; + struct drm_psb_private *dev_priv = dev->dev_private; + + if (gma_power_begin(dev, false)) { + htot = REG_READ((pipe == 0) ? HTOTAL_A : HTOTAL_B); + hsync = REG_READ((pipe == 0) ? HSYNC_A : HSYNC_B); + vtot = REG_READ((pipe == 0) ? VTOTAL_A : VTOTAL_B); + vsync = REG_READ((pipe == 0) ? VSYNC_A : VSYNC_B); + gma_power_end(dev); + } else { + htot = (pipe == 0) ? + dev_priv->saveHTOTAL_A : dev_priv->saveHTOTAL_B; + hsync = (pipe == 0) ? + dev_priv->saveHSYNC_A : dev_priv->saveHSYNC_B; + vtot = (pipe == 0) ? + dev_priv->saveVTOTAL_A : dev_priv->saveVTOTAL_B; + vsync = (pipe == 0) ? + dev_priv->saveVSYNC_A : dev_priv->saveVSYNC_B; + } + + mode = kzalloc(sizeof(*mode), GFP_KERNEL); + if (!mode) + return NULL; + + mode->clock = psb_intel_crtc_clock_get(dev, crtc); + mode->hdisplay = (htot & 0xffff) + 1; + mode->htotal = ((htot & 0xffff0000) >> 16) + 1; + mode->hsync_start = (hsync & 0xffff) + 1; + mode->hsync_end = ((hsync & 0xffff0000) >> 16) + 1; + mode->vdisplay = (vtot & 0xffff) + 1; + mode->vtotal = ((vtot & 0xffff0000) >> 16) + 1; + mode->vsync_start = (vsync & 0xffff) + 1; + mode->vsync_end = ((vsync & 0xffff0000) >> 16) + 1; + + drm_mode_set_name(mode); + drm_mode_set_crtcinfo(mode, 0); + + return mode; +} + +void psb_intel_crtc_destroy(struct drm_crtc *crtc) +{ + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + struct gtt_range *gt; + + /* Unpin the old GEM object */ + if (psb_intel_crtc->cursor_obj) { + gt = container_of(psb_intel_crtc->cursor_obj, + struct gtt_range, gem); + psb_gtt_unpin(gt); + drm_gem_object_unreference(psb_intel_crtc->cursor_obj); + psb_intel_crtc->cursor_obj = NULL; + } + kfree(psb_intel_crtc->crtc_state); + drm_crtc_cleanup(crtc); + kfree(psb_intel_crtc); +} + +const struct drm_crtc_helper_funcs psb_intel_helper_funcs = { + .dpms = psb_intel_crtc_dpms, + .mode_fixup = psb_intel_crtc_mode_fixup, + .mode_set = psb_intel_crtc_mode_set, + .mode_set_base = psb_intel_pipe_set_base, + .prepare = psb_intel_crtc_prepare, + .commit = psb_intel_crtc_commit, +}; + +const struct drm_crtc_funcs psb_intel_crtc_funcs = { + .save = psb_intel_crtc_save, + .restore = psb_intel_crtc_restore, + .cursor_set = psb_intel_crtc_cursor_set, + .cursor_move = psb_intel_crtc_cursor_move, + .gamma_set = psb_intel_crtc_gamma_set, + .set_config = psb_crtc_set_config, + .destroy = psb_intel_crtc_destroy, +}; + +/* + * Set the default value of cursor control and base register + * to zero. This is a workaround for h/w defect on Oaktrail + */ +static void psb_intel_cursor_init(struct drm_device *dev, int pipe) +{ + u32 control[3] = { CURACNTR, CURBCNTR, CURCCNTR }; + u32 base[3] = { CURABASE, CURBBASE, CURCBASE }; + + REG_WRITE(control[pipe], 0); + REG_WRITE(base[pipe], 0); +} + +void psb_intel_crtc_init(struct drm_device *dev, int pipe, + struct psb_intel_mode_device *mode_dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_intel_crtc *psb_intel_crtc; + int i; + uint16_t *r_base, *g_base, *b_base; + + /* We allocate a extra array of drm_connector pointers + * for fbdev after the crtc */ + psb_intel_crtc = + kzalloc(sizeof(struct psb_intel_crtc) + + (INTELFB_CONN_LIMIT * sizeof(struct drm_connector *)), + GFP_KERNEL); + if (psb_intel_crtc == NULL) + return; + + psb_intel_crtc->crtc_state = + kzalloc(sizeof(struct psb_intel_crtc_state), GFP_KERNEL); + if (!psb_intel_crtc->crtc_state) { + dev_err(dev->dev, "Crtc state error: No memory\n"); + kfree(psb_intel_crtc); + return; + } + + /* Set the CRTC operations from the chip specific data */ + drm_crtc_init(dev, &psb_intel_crtc->base, dev_priv->ops->crtc_funcs); + + drm_mode_crtc_set_gamma_size(&psb_intel_crtc->base, 256); + psb_intel_crtc->pipe = pipe; + psb_intel_crtc->plane = pipe; + + r_base = psb_intel_crtc->base.gamma_store; + g_base = r_base + 256; + b_base = g_base + 256; + for (i = 0; i < 256; i++) { + psb_intel_crtc->lut_r[i] = i; + psb_intel_crtc->lut_g[i] = i; + psb_intel_crtc->lut_b[i] = i; + r_base[i] = i << 8; + g_base[i] = i << 8; + b_base[i] = i << 8; + + psb_intel_crtc->lut_adj[i] = 0; + } + + psb_intel_crtc->mode_dev = mode_dev; + psb_intel_crtc->cursor_addr = 0; + + drm_crtc_helper_add(&psb_intel_crtc->base, + dev_priv->ops->crtc_helper); + + /* Setup the array of drm_connector pointer array */ + psb_intel_crtc->mode_set.crtc = &psb_intel_crtc->base; + BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) || + dev_priv->plane_to_crtc_mapping[psb_intel_crtc->plane] != NULL); + dev_priv->plane_to_crtc_mapping[psb_intel_crtc->plane] = + &psb_intel_crtc->base; + dev_priv->pipe_to_crtc_mapping[psb_intel_crtc->pipe] = + &psb_intel_crtc->base; + psb_intel_crtc->mode_set.connectors = + (struct drm_connector **) (psb_intel_crtc + 1); + psb_intel_crtc->mode_set.num_connectors = 0; + psb_intel_cursor_init(dev, pipe); +} + +int psb_intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct drm_psb_get_pipe_from_crtc_id_arg *pipe_from_crtc_id = data; + struct drm_mode_object *drmmode_obj; + struct psb_intel_crtc *crtc; + + if (!dev_priv) { + dev_err(dev->dev, "called with no initialization\n"); + return -EINVAL; + } + + drmmode_obj = drm_mode_object_find(dev, pipe_from_crtc_id->crtc_id, + DRM_MODE_OBJECT_CRTC); + + if (!drmmode_obj) { + dev_err(dev->dev, "no such CRTC id\n"); + return -EINVAL; + } + + crtc = to_psb_intel_crtc(obj_to_crtc(drmmode_obj)); + pipe_from_crtc_id->pipe = crtc->pipe; + + return 0; +} + +struct drm_crtc *psb_intel_get_crtc_from_pipe(struct drm_device *dev, int pipe) +{ + struct drm_crtc *crtc = NULL; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + if (psb_intel_crtc->pipe == pipe) + break; + } + return crtc; +} + +int psb_intel_connector_clones(struct drm_device *dev, int type_mask) +{ + int index_mask = 0; + struct drm_connector *connector; + int entry = 0; + + list_for_each_entry(connector, &dev->mode_config.connector_list, + head) { + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + if (type_mask & (1 << psb_intel_output->type)) + index_mask |= (1 << entry); + entry++; + } + return index_mask; +} + + +void psb_intel_modeset_cleanup(struct drm_device *dev) +{ + drm_mode_config_cleanup(dev); +} + + +/* current intel driver doesn't take advantage of encoders + always give back the encoder for the connector +*/ +struct drm_encoder *psb_intel_best_encoder(struct drm_connector *connector) +{ + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + + return &psb_intel_output->enc; +} + diff --git a/trunk/drivers/staging/gma500/psb_intel_display.h b/trunk/drivers/staging/gma500/psb_intel_display.h new file mode 100644 index 000000000000..535b49a5e409 --- /dev/null +++ b/trunk/drivers/staging/gma500/psb_intel_display.h @@ -0,0 +1,28 @@ +/* copyright (c) 2008, Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * 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. + * + * Authors: + * Eric Anholt + */ + +#ifndef _INTEL_DISPLAY_H_ +#define _INTEL_DISPLAY_H_ + +bool psb_intel_pipe_has_type(struct drm_crtc *crtc, int type); +void psb_intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, + u16 *green, u16 *blue, uint32_t type, uint32_t size); +void psb_intel_crtc_destroy(struct drm_crtc *crtc); + +#endif diff --git a/trunk/drivers/staging/gma500/psb_intel_drv.h b/trunk/drivers/staging/gma500/psb_intel_drv.h new file mode 100644 index 000000000000..36b554b5c335 --- /dev/null +++ b/trunk/drivers/staging/gma500/psb_intel_drv.h @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2009-2011, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef __INTEL_DRV_H__ +#define __INTEL_DRV_H__ + +#include +#include +#include +#include +#include + +/* + * 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 + +#define INTEL_I2C_BUS_DVO 1 +#define INTEL_I2C_BUS_SDVO 2 + +/* these are outputs from the chip - integrated only + * external chips are via DVO or SDVO output */ +#define INTEL_OUTPUT_UNUSED 0 +#define INTEL_OUTPUT_ANALOG 1 +#define INTEL_OUTPUT_DVO 2 +#define INTEL_OUTPUT_SDVO 3 +#define INTEL_OUTPUT_LVDS 4 +#define INTEL_OUTPUT_TVOUT 5 +#define INTEL_OUTPUT_HDMI 6 +#define INTEL_OUTPUT_MIPI 7 +#define INTEL_OUTPUT_MIPI2 8 + +#define INTEL_DVO_CHIP_NONE 0 +#define INTEL_DVO_CHIP_LVDS 1 +#define INTEL_DVO_CHIP_TMDS 2 +#define INTEL_DVO_CHIP_TVOUT 4 + +/* + * Hold information useally put on the device driver privates here, + * since it needs to be shared across multiple of devices drivers privates. + */ +struct psb_intel_mode_device { + + /* + * Abstracted memory manager operations + */ + size_t(*bo_offset) (struct drm_device *dev, void *bo); + + /* + * Cursor (Can go ?) + */ + int cursor_needs_physical; + + /* + * LVDS info + */ + int backlight_duty_cycle; /* restore backlight to this value */ + bool panel_wants_dither; + struct drm_display_mode *panel_fixed_mode; + struct drm_display_mode *panel_fixed_mode2; + struct drm_display_mode *vbt_mode; /* if any */ + + uint32_t saveBLC_PWM_CTL; +}; + +struct psb_intel_i2c_chan { + /* for getting at dev. private (mmio etc.) */ + struct drm_device *drm_dev; + u32 reg; /* GPIO reg */ + struct i2c_adapter adapter; + struct i2c_algo_bit_data algo; + u8 slave_addr; +}; + +struct psb_intel_output { + struct drm_connector base; + + struct drm_encoder enc; + int type; + + struct psb_intel_i2c_chan *i2c_bus; /* for control functions */ + struct psb_intel_i2c_chan *ddc_bus; /* for DDC only stuff */ + bool load_detect_temp; + void *dev_priv; + + struct psb_intel_mode_device *mode_dev; + struct i2c_adapter *hdmi_i2c_adapter; /* for control functions */ +}; + +struct psb_intel_crtc_state { + uint32_t saveDSPCNTR; + uint32_t savePIPECONF; + uint32_t savePIPESRC; + uint32_t saveDPLL; + uint32_t saveFP0; + uint32_t saveFP1; + uint32_t saveHTOTAL; + uint32_t saveHBLANK; + uint32_t saveHSYNC; + uint32_t saveVTOTAL; + uint32_t saveVBLANK; + uint32_t saveVSYNC; + uint32_t saveDSPSTRIDE; + uint32_t saveDSPSIZE; + uint32_t saveDSPPOS; + uint32_t saveDSPBASE; + uint32_t savePalette[256]; +}; + +struct psb_intel_crtc { + struct drm_crtc base; + int pipe; + int plane; + uint32_t cursor_addr; + u8 lut_r[256], lut_g[256], lut_b[256]; + u8 lut_adj[256]; + struct psb_intel_framebuffer *fbdev_fb; + /* a mode_set for fbdev users on this crtc */ + struct drm_mode_set mode_set; + + /* GEM object that holds our cursor */ + struct drm_gem_object *cursor_obj; + + struct drm_display_mode saved_mode; + struct drm_display_mode saved_adjusted_mode; + + struct psb_intel_mode_device *mode_dev; + + /*crtc mode setting flags*/ + u32 mode_flags; + + /* Saved Crtc HW states */ + struct psb_intel_crtc_state *crtc_state; +}; + +#define to_psb_intel_crtc(x) \ + container_of(x, struct psb_intel_crtc, base) +#define to_psb_intel_output(x) \ + container_of(x, struct psb_intel_output, base) +#define enc_to_psb_intel_output(x) \ + container_of(x, struct psb_intel_output, enc) +#define to_psb_intel_framebuffer(x) \ + container_of(x, struct psb_intel_framebuffer, base) + +struct psb_intel_i2c_chan *psb_intel_i2c_create(struct drm_device *dev, + const u32 reg, const char *name); +void psb_intel_i2c_destroy(struct psb_intel_i2c_chan *chan); +int psb_intel_ddc_get_modes(struct psb_intel_output *psb_intel_output); +extern bool psb_intel_ddc_probe(struct psb_intel_output *psb_intel_output); + +extern void psb_intel_crtc_init(struct drm_device *dev, int pipe, + struct psb_intel_mode_device *mode_dev); +extern void psb_intel_crt_init(struct drm_device *dev); +extern void psb_intel_sdvo_init(struct drm_device *dev, int output_device); +extern void psb_intel_dvo_init(struct drm_device *dev); +extern void psb_intel_tv_init(struct drm_device *dev); +extern void psb_intel_lvds_init(struct drm_device *dev, + struct psb_intel_mode_device *mode_dev); +extern void psb_intel_lvds_set_brightness(struct drm_device *dev, int level); +extern void mrst_lvds_init(struct drm_device *dev, + struct psb_intel_mode_device *mode_dev); +extern void mrst_wait_for_INTR_PKT_SENT(struct drm_device *dev); +extern void mrst_dsi_init(struct drm_device *dev, + struct psb_intel_mode_device *mode_dev); +extern void mid_dsi_init(struct drm_device *dev, + struct psb_intel_mode_device *mode_dev, int dsi_num); + +extern void psb_intel_crtc_load_lut(struct drm_crtc *crtc); +extern void psb_intel_encoder_prepare(struct drm_encoder *encoder); +extern void psb_intel_encoder_commit(struct drm_encoder *encoder); + +extern struct drm_encoder *psb_intel_best_encoder(struct drm_connector + *connector); + +extern struct drm_display_mode *psb_intel_crtc_mode_get(struct drm_device *dev, + struct drm_crtc *crtc); +extern void psb_intel_wait_for_vblank(struct drm_device *dev); +extern int psb_intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern struct drm_crtc *psb_intel_get_crtc_from_pipe(struct drm_device *dev, + int pipe); +extern struct drm_connector *psb_intel_sdvo_find(struct drm_device *dev, + int sdvoB); +extern int psb_intel_sdvo_supports_hotplug(struct drm_connector *connector); +extern void psb_intel_sdvo_set_hotplug(struct drm_connector *connector, + int enable); +extern int intelfb_probe(struct drm_device *dev); +extern int intelfb_remove(struct drm_device *dev, + struct drm_framebuffer *fb); +extern struct drm_framebuffer *psb_intel_framebuffer_create(struct drm_device + *dev, struct + drm_mode_fb_cmd + *mode_cmd, + void *mm_private); +extern bool psb_intel_lvds_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +extern int psb_intel_lvds_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode); +extern int psb_intel_lvds_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t value); +extern void psb_intel_lvds_destroy(struct drm_connector *connector); +extern const struct drm_encoder_funcs psb_intel_lvds_enc_funcs; + +extern void mdfldWaitForPipeDisable(struct drm_device *dev, int pipe); +extern void mdfldWaitForPipeEnable(struct drm_device *dev, int pipe); + +#endif /* __INTEL_DRV_H__ */ diff --git a/trunk/drivers/staging/gma500/psb_intel_lvds.c b/trunk/drivers/staging/gma500/psb_intel_lvds.c new file mode 100644 index 000000000000..21022e1a977a --- /dev/null +++ b/trunk/drivers/staging/gma500/psb_intel_lvds.c @@ -0,0 +1,854 @@ +/* + * Copyright © 2006-2007 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * 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. + * + * Authors: + * Eric Anholt + * Dave Airlie + * Jesse Barnes + */ + +#include +#include + +#include "intel_bios.h" +#include "psb_drv.h" +#include "psb_intel_drv.h" +#include "psb_intel_reg.h" +#include "power.h" +#include + +/* + * LVDS I2C backlight control macros + */ +#define BRIGHTNESS_MAX_LEVEL 100 +#define BRIGHTNESS_MASK 0xFF +#define BLC_I2C_TYPE 0x01 +#define BLC_PWM_TYPT 0x02 + +#define BLC_POLARITY_NORMAL 0 +#define BLC_POLARITY_INVERSE 1 + +#define PSB_BLC_MAX_PWM_REG_FREQ (0xFFFE) +#define PSB_BLC_MIN_PWM_REG_FREQ (0x2) +#define PSB_BLC_PWM_PRECISION_FACTOR (10) +#define PSB_BACKLIGHT_PWM_CTL_SHIFT (16) +#define PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE) + +struct psb_intel_lvds_priv { + /* + * Saved LVDO output states + */ + uint32_t savePP_ON; + uint32_t savePP_OFF; + uint32_t saveLVDS; + uint32_t savePP_CONTROL; + uint32_t savePP_CYCLE; + uint32_t savePFIT_CONTROL; + uint32_t savePFIT_PGM_RATIOS; + uint32_t saveBLC_PWM_CTL; +}; + + +/* + * Returns the maximum level of the backlight duty cycle field. + */ +static u32 psb_intel_lvds_get_max_backlight(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + u32 ret; + + if (gma_power_begin(dev, false)) { + ret = REG_READ(BLC_PWM_CTL); + gma_power_end(dev); + } else /* Powered off, use the saved value */ + ret = dev_priv->saveBLC_PWM_CTL; + + /* Top 15bits hold the frequency mask */ + ret = (ret & BACKLIGHT_MODULATION_FREQ_MASK) >> + BACKLIGHT_MODULATION_FREQ_SHIFT; + + ret *= 2; /* Return a 16bit range as needed for setting */ + if (ret == 0) + dev_err(dev->dev, "BL bug: Reg %08x save %08X\n", + REG_READ(BLC_PWM_CTL), dev_priv->saveBLC_PWM_CTL); + return ret; +} + +/* + * Set LVDS backlight level by I2C command + * + * FIXME: at some point we need to both track this for PM and also + * disable runtime pm on MRST if the brightness is nil (ie blanked) + */ +static int psb_lvds_i2c_set_brightness(struct drm_device *dev, + unsigned int level) +{ + struct drm_psb_private *dev_priv = + (struct drm_psb_private *)dev->dev_private; + + struct psb_intel_i2c_chan *lvds_i2c_bus = dev_priv->lvds_i2c_bus; + u8 out_buf[2]; + unsigned int blc_i2c_brightness; + + struct i2c_msg msgs[] = { + { + .addr = lvds_i2c_bus->slave_addr, + .flags = 0, + .len = 2, + .buf = out_buf, + } + }; + + blc_i2c_brightness = BRIGHTNESS_MASK & ((unsigned int)level * + BRIGHTNESS_MASK / + BRIGHTNESS_MAX_LEVEL); + + if (dev_priv->lvds_bl->pol == BLC_POLARITY_INVERSE) + blc_i2c_brightness = BRIGHTNESS_MASK - blc_i2c_brightness; + + out_buf[0] = dev_priv->lvds_bl->brightnesscmd; + out_buf[1] = (u8)blc_i2c_brightness; + + if (i2c_transfer(&lvds_i2c_bus->adapter, msgs, 1) == 1) { + dev_dbg(dev->dev, "I2C set brightness.(command, value) (%d, %d)\n", + dev_priv->lvds_bl->brightnesscmd, + blc_i2c_brightness); + return 0; + } + + dev_err(dev->dev, "I2C transfer error\n"); + return -1; +} + + +static int psb_lvds_pwm_set_brightness(struct drm_device *dev, int level) +{ + struct drm_psb_private *dev_priv = + (struct drm_psb_private *)dev->dev_private; + + u32 max_pwm_blc; + u32 blc_pwm_duty_cycle; + + max_pwm_blc = psb_intel_lvds_get_max_backlight(dev); + + /*BLC_PWM_CTL Should be initiated while backlight device init*/ + BUG_ON(max_pwm_blc == 0); + + blc_pwm_duty_cycle = level * max_pwm_blc / BRIGHTNESS_MAX_LEVEL; + + if (dev_priv->lvds_bl->pol == BLC_POLARITY_INVERSE) + blc_pwm_duty_cycle = max_pwm_blc - blc_pwm_duty_cycle; + + blc_pwm_duty_cycle &= PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR; + REG_WRITE(BLC_PWM_CTL, + (max_pwm_blc << PSB_BACKLIGHT_PWM_CTL_SHIFT) | + (blc_pwm_duty_cycle)); + + dev_info(dev->dev, "Backlight lvds set brightness %08x\n", + (max_pwm_blc << PSB_BACKLIGHT_PWM_CTL_SHIFT) | + (blc_pwm_duty_cycle)); + + return 0; +} + +/* + * Set LVDS backlight level either by I2C or PWM + */ +void psb_intel_lvds_set_brightness(struct drm_device *dev, int level) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + + dev_dbg(dev->dev, "backlight level is %d\n", level); + + if (!dev_priv->lvds_bl) { + dev_err(dev->dev, "NO LVDS backlight info\n"); + return; + } + + if (dev_priv->lvds_bl->type == BLC_I2C_TYPE) + psb_lvds_i2c_set_brightness(dev, level); + else + psb_lvds_pwm_set_brightness(dev, level); +} + +/* + * Sets the backlight level. + * + * level: backlight level, from 0 to psb_intel_lvds_get_max_backlight(). + */ +static void psb_intel_lvds_set_backlight(struct drm_device *dev, int level) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + u32 blc_pwm_ctl; + + if (gma_power_begin(dev, false)) { + blc_pwm_ctl = REG_READ(BLC_PWM_CTL); + blc_pwm_ctl &= ~BACKLIGHT_DUTY_CYCLE_MASK; + REG_WRITE(BLC_PWM_CTL, + (blc_pwm_ctl | + (level << BACKLIGHT_DUTY_CYCLE_SHIFT))); + dev_priv->saveBLC_PWM_CTL = (blc_pwm_ctl | + (level << BACKLIGHT_DUTY_CYCLE_SHIFT)); + gma_power_end(dev); + } else { + blc_pwm_ctl = dev_priv->saveBLC_PWM_CTL & + ~BACKLIGHT_DUTY_CYCLE_MASK; + dev_priv->saveBLC_PWM_CTL = (blc_pwm_ctl | + (level << BACKLIGHT_DUTY_CYCLE_SHIFT)); + } +} + +/* + * Sets the power state for the panel. + */ +static void psb_intel_lvds_set_power(struct drm_device *dev, + struct psb_intel_output *output, bool on) +{ + u32 pp_status; + + if (!gma_power_begin(dev, true)) { + dev_err(dev->dev, "set power, chip off!\n"); + return; + } + + if (on) { + REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) | + POWER_TARGET_ON); + do { + pp_status = REG_READ(PP_STATUS); + } while ((pp_status & PP_ON) == 0); + + psb_intel_lvds_set_backlight(dev, + output-> + mode_dev->backlight_duty_cycle); + } else { + psb_intel_lvds_set_backlight(dev, 0); + + REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) & + ~POWER_TARGET_ON); + do { + pp_status = REG_READ(PP_STATUS); + } while (pp_status & PP_ON); + } + + gma_power_end(dev); +} + +static void psb_intel_lvds_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + + if (mode == DRM_MODE_DPMS_ON) + psb_intel_lvds_set_power(dev, output, true); + else + psb_intel_lvds_set_power(dev, output, false); + + /* XXX: We never power down the LVDS pairs. */ +} + +static void psb_intel_lvds_save(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_psb_private *dev_priv = + (struct drm_psb_private *)dev->dev_private; + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + struct psb_intel_lvds_priv *lvds_priv = + (struct psb_intel_lvds_priv *)psb_intel_output->dev_priv; + + lvds_priv->savePP_ON = REG_READ(LVDSPP_ON); + lvds_priv->savePP_OFF = REG_READ(LVDSPP_OFF); + lvds_priv->saveLVDS = REG_READ(LVDS); + lvds_priv->savePP_CONTROL = REG_READ(PP_CONTROL); + lvds_priv->savePP_CYCLE = REG_READ(PP_CYCLE); + /*lvds_priv->savePP_DIVISOR = REG_READ(PP_DIVISOR);*/ + lvds_priv->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL); + lvds_priv->savePFIT_CONTROL = REG_READ(PFIT_CONTROL); + lvds_priv->savePFIT_PGM_RATIOS = REG_READ(PFIT_PGM_RATIOS); + + /*TODO: move backlight_duty_cycle to psb_intel_lvds_priv*/ + dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL & + BACKLIGHT_DUTY_CYCLE_MASK); + + /* + * If the light is off at server startup, + * just make it full brightness + */ + if (dev_priv->backlight_duty_cycle == 0) + dev_priv->backlight_duty_cycle = + psb_intel_lvds_get_max_backlight(dev); + + dev_dbg(dev->dev, "(0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n", + lvds_priv->savePP_ON, + lvds_priv->savePP_OFF, + lvds_priv->saveLVDS, + lvds_priv->savePP_CONTROL, + lvds_priv->savePP_CYCLE, + lvds_priv->saveBLC_PWM_CTL); +} + +static void psb_intel_lvds_restore(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + u32 pp_status; + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + struct psb_intel_lvds_priv *lvds_priv = + (struct psb_intel_lvds_priv *)psb_intel_output->dev_priv; + + dev_dbg(dev->dev, "(0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n", + lvds_priv->savePP_ON, + lvds_priv->savePP_OFF, + lvds_priv->saveLVDS, + lvds_priv->savePP_CONTROL, + lvds_priv->savePP_CYCLE, + lvds_priv->saveBLC_PWM_CTL); + + REG_WRITE(BLC_PWM_CTL, lvds_priv->saveBLC_PWM_CTL); + REG_WRITE(PFIT_CONTROL, lvds_priv->savePFIT_CONTROL); + REG_WRITE(PFIT_PGM_RATIOS, lvds_priv->savePFIT_PGM_RATIOS); + REG_WRITE(LVDSPP_ON, lvds_priv->savePP_ON); + REG_WRITE(LVDSPP_OFF, lvds_priv->savePP_OFF); + /*REG_WRITE(PP_DIVISOR, lvds_priv->savePP_DIVISOR);*/ + REG_WRITE(PP_CYCLE, lvds_priv->savePP_CYCLE); + REG_WRITE(PP_CONTROL, lvds_priv->savePP_CONTROL); + REG_WRITE(LVDS, lvds_priv->saveLVDS); + + if (lvds_priv->savePP_CONTROL & POWER_TARGET_ON) { + REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) | + POWER_TARGET_ON); + do { + pp_status = REG_READ(PP_STATUS); + } while ((pp_status & PP_ON) == 0); + } else { + REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) & + ~POWER_TARGET_ON); + do { + pp_status = REG_READ(PP_STATUS); + } while (pp_status & PP_ON); + } +} + +int psb_intel_lvds_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + struct drm_display_mode *fixed_mode = + psb_intel_output->mode_dev->panel_fixed_mode; + + if (psb_intel_output->type == INTEL_OUTPUT_MIPI2) + fixed_mode = psb_intel_output->mode_dev->panel_fixed_mode2; + + /* just in case */ + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + + /* just in case */ + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + return MODE_NO_INTERLACE; + + if (fixed_mode) { + if (mode->hdisplay > fixed_mode->hdisplay) + return MODE_PANEL; + if (mode->vdisplay > fixed_mode->vdisplay) + return MODE_PANEL; + } + return MODE_OK; +} + +bool psb_intel_lvds_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct psb_intel_mode_device *mode_dev = + enc_to_psb_intel_output(encoder)->mode_dev; + struct drm_device *dev = encoder->dev; + struct psb_intel_crtc *psb_intel_crtc = + to_psb_intel_crtc(encoder->crtc); + struct drm_encoder *tmp_encoder; + struct drm_display_mode *panel_fixed_mode = mode_dev->panel_fixed_mode; + struct psb_intel_output *psb_intel_output = + enc_to_psb_intel_output(encoder); + + if (psb_intel_output->type == INTEL_OUTPUT_MIPI2) + panel_fixed_mode = mode_dev->panel_fixed_mode2; + + /* PSB requires the LVDS is on pipe B, MRST has only one pipe anyway */ + if (!IS_MRST(dev) && psb_intel_crtc->pipe == 0) { + printk(KERN_ERR "Can't support LVDS on pipe A\n"); + return false; + } + if (IS_MRST(dev) && psb_intel_crtc->pipe != 0) { + printk(KERN_ERR "Must use PIPE A\n"); + return false; + } + /* Should never happen!! */ + list_for_each_entry(tmp_encoder, &dev->mode_config.encoder_list, + head) { + if (tmp_encoder != encoder + && tmp_encoder->crtc == encoder->crtc) { + printk(KERN_ERR "Can't enable LVDS and another " + "encoder on the same pipe\n"); + return false; + } + } + + /* + * If we have timings from the BIOS for the panel, put them in + * to the adjusted mode. The CRTC will be set up for this mode, + * with the panel scaling set up to source from the H/VDisplay + * of the original mode. + */ + if (panel_fixed_mode != NULL) { + adjusted_mode->hdisplay = panel_fixed_mode->hdisplay; + adjusted_mode->hsync_start = panel_fixed_mode->hsync_start; + adjusted_mode->hsync_end = panel_fixed_mode->hsync_end; + adjusted_mode->htotal = panel_fixed_mode->htotal; + adjusted_mode->vdisplay = panel_fixed_mode->vdisplay; + adjusted_mode->vsync_start = panel_fixed_mode->vsync_start; + adjusted_mode->vsync_end = panel_fixed_mode->vsync_end; + adjusted_mode->vtotal = panel_fixed_mode->vtotal; + adjusted_mode->clock = panel_fixed_mode->clock; + drm_mode_set_crtcinfo(adjusted_mode, + CRTC_INTERLACE_HALVE_V); + } + + /* + * XXX: It would be nice to support lower refresh rates on the + * panels to reduce power consumption, and perhaps match the + * user's requested refresh rate. + */ + + return true; +} + +static void psb_intel_lvds_prepare(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + struct psb_intel_mode_device *mode_dev = output->mode_dev; + + if (!gma_power_begin(dev, true)) + return; + + mode_dev->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL); + mode_dev->backlight_duty_cycle = (mode_dev->saveBLC_PWM_CTL & + BACKLIGHT_DUTY_CYCLE_MASK); + + psb_intel_lvds_set_power(dev, output, false); + + gma_power_end(dev); +} + +static void psb_intel_lvds_commit(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + struct psb_intel_mode_device *mode_dev = output->mode_dev; + + if (mode_dev->backlight_duty_cycle == 0) + mode_dev->backlight_duty_cycle = + psb_intel_lvds_get_max_backlight(dev); + + psb_intel_lvds_set_power(dev, output, true); +} + +static void psb_intel_lvds_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + u32 pfit_control; + + /* + * The LVDS pin pair will already have been turned on in the + * psb_intel_crtc_mode_set since it has a large impact on the DPLL + * settings. + */ + + /* + * Enable automatic panel scaling so that non-native modes fill the + * screen. Should be enabled before the pipe is enabled, according to + * register description and PRM. + */ + if (mode->hdisplay != adjusted_mode->hdisplay || + mode->vdisplay != adjusted_mode->vdisplay) + pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE | + HORIZ_AUTO_SCALE | VERT_INTERP_BILINEAR | + HORIZ_INTERP_BILINEAR); + else + pfit_control = 0; + + if (dev_priv->lvds_dither) + pfit_control |= PANEL_8TO6_DITHER_ENABLE; + + REG_WRITE(PFIT_CONTROL, pfit_control); +} + +/* + * Detect the LVDS connection. + * + * This always returns CONNECTOR_STATUS_CONNECTED. + * This connector should only have + * been set up if the LVDS was actually connected anyway. + */ +static enum drm_connector_status psb_intel_lvds_detect(struct drm_connector + *connector, bool force) +{ + return connector_status_connected; +} + +/* + * Return the list of DDC modes if available, or the BIOS fixed mode otherwise. + */ +static int psb_intel_lvds_get_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + struct psb_intel_mode_device *mode_dev = + psb_intel_output->mode_dev; + int ret = 0; + + if (!IS_MRST(dev)) + ret = psb_intel_ddc_get_modes(psb_intel_output); + + if (ret) + return ret; + + /* Didn't get an EDID, so + * Set wide sync ranges so we get all modes + * handed to valid_mode for checking + */ + connector->display_info.min_vfreq = 0; + connector->display_info.max_vfreq = 200; + connector->display_info.min_hfreq = 0; + connector->display_info.max_hfreq = 200; + + if (mode_dev->panel_fixed_mode != NULL) { + struct drm_display_mode *mode = + drm_mode_duplicate(dev, mode_dev->panel_fixed_mode); + drm_mode_probed_add(connector, mode); + return 1; + } + + return 0; +} + +/** + * psb_intel_lvds_destroy - unregister and free LVDS structures + * @connector: connector to free + * + * Unregister the DDC bus for this connector then free the driver private + * structure. + */ +void psb_intel_lvds_destroy(struct drm_connector *connector) +{ + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + + if (psb_intel_output->ddc_bus) + psb_intel_i2c_destroy(psb_intel_output->ddc_bus); + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + kfree(connector); +} + +int psb_intel_lvds_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t value) +{ + struct drm_encoder *encoder = connector->encoder; + + if (!encoder) + return -1; + + if (!strcmp(property->name, "scaling mode")) { + struct psb_intel_crtc *crtc = + to_psb_intel_crtc(encoder->crtc); + uint64_t curval; + + if (!crtc) + goto set_prop_error; + + switch (value) { + case DRM_MODE_SCALE_FULLSCREEN: + break; + case DRM_MODE_SCALE_NO_SCALE: + break; + case DRM_MODE_SCALE_ASPECT: + break; + default: + goto set_prop_error; + } + + if (drm_connector_property_get_value(connector, + property, + &curval)) + goto set_prop_error; + + if (curval == value) + goto set_prop_done; + + if (drm_connector_property_set_value(connector, + property, + value)) + goto set_prop_error; + + if (crtc->saved_mode.hdisplay != 0 && + crtc->saved_mode.vdisplay != 0) { + if (!drm_crtc_helper_set_mode(encoder->crtc, + &crtc->saved_mode, + encoder->crtc->x, + encoder->crtc->y, + encoder->crtc->fb)) + goto set_prop_error; + } + } else if (!strcmp(property->name, "backlight")) { + if (drm_connector_property_set_value(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 if (!strcmp(property->name, "DPMS")) { + struct drm_encoder_helper_funcs *hfuncs + = encoder->helper_private; + hfuncs->dpms(encoder, value); + } + +set_prop_done: + return 0; +set_prop_error: + return -1; +} + +static const struct drm_encoder_helper_funcs psb_intel_lvds_helper_funcs = { + .dpms = psb_intel_lvds_encoder_dpms, + .mode_fixup = psb_intel_lvds_mode_fixup, + .prepare = psb_intel_lvds_prepare, + .mode_set = psb_intel_lvds_mode_set, + .commit = psb_intel_lvds_commit, +}; + +const struct drm_connector_helper_funcs + psb_intel_lvds_connector_helper_funcs = { + .get_modes = psb_intel_lvds_get_modes, + .mode_valid = psb_intel_lvds_mode_valid, + .best_encoder = psb_intel_best_encoder, +}; + +const struct drm_connector_funcs psb_intel_lvds_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .save = psb_intel_lvds_save, + .restore = psb_intel_lvds_restore, + .detect = psb_intel_lvds_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = psb_intel_lvds_set_property, + .destroy = psb_intel_lvds_destroy, +}; + + +static void psb_intel_lvds_enc_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +const struct drm_encoder_funcs psb_intel_lvds_enc_funcs = { + .destroy = psb_intel_lvds_enc_destroy, +}; + + + +/** + * psb_intel_lvds_init - setup LVDS connectors on this device + * @dev: drm device + * + * Create the connector, register the LVDS DDC bus, and try to figure out what + * modes we can display on the LVDS panel (if present). + */ +void psb_intel_lvds_init(struct drm_device *dev, + struct psb_intel_mode_device *mode_dev) +{ + struct psb_intel_output *psb_intel_output; + struct psb_intel_lvds_priv *lvds_priv; + struct drm_connector *connector; + struct drm_encoder *encoder; + struct drm_display_mode *scan; /* *modes, *bios_mode; */ + struct drm_crtc *crtc; + struct drm_psb_private *dev_priv = dev->dev_private; + u32 lvds; + int pipe; + + psb_intel_output = kzalloc(sizeof(struct psb_intel_output), GFP_KERNEL); + if (!psb_intel_output) + return; + + lvds_priv = kzalloc(sizeof(struct psb_intel_lvds_priv), GFP_KERNEL); + if (!lvds_priv) { + kfree(psb_intel_output); + dev_err(dev->dev, "LVDS private allocation error\n"); + return; + } + + psb_intel_output->dev_priv = lvds_priv; + psb_intel_output->mode_dev = mode_dev; + + connector = &psb_intel_output->base; + encoder = &psb_intel_output->enc; + drm_connector_init(dev, &psb_intel_output->base, + &psb_intel_lvds_connector_funcs, + DRM_MODE_CONNECTOR_LVDS); + + drm_encoder_init(dev, &psb_intel_output->enc, + &psb_intel_lvds_enc_funcs, + DRM_MODE_ENCODER_LVDS); + + drm_mode_connector_attach_encoder(&psb_intel_output->base, + &psb_intel_output->enc); + psb_intel_output->type = INTEL_OUTPUT_LVDS; + + drm_encoder_helper_add(encoder, &psb_intel_lvds_helper_funcs); + drm_connector_helper_add(connector, + &psb_intel_lvds_connector_helper_funcs); + connector->display_info.subpixel_order = SubPixelHorizontalRGB; + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + + /*Attach connector properties*/ + drm_connector_attach_property(connector, + dev->mode_config.scaling_mode_property, + DRM_MODE_SCALE_FULLSCREEN); + drm_connector_attach_property(connector, + dev_priv->backlight_property, + BRIGHTNESS_MAX_LEVEL); + + /* + * Set up I2C bus + * FIXME: distroy i2c_bus when exit + */ + psb_intel_output->i2c_bus = psb_intel_i2c_create(dev, + GPIOB, + "LVDSBLC_B"); + if (!psb_intel_output->i2c_bus) { + dev_printk(KERN_ERR, + &dev->pdev->dev, "I2C bus registration failed.\n"); + goto failed_blc_i2c; + } + psb_intel_output->i2c_bus->slave_addr = 0x2C; + dev_priv->lvds_i2c_bus = psb_intel_output->i2c_bus; + + /* + * LVDS discovery: + * 1) check for EDID on DDC + * 2) check for VBT data + * 3) check to see if LVDS is already on + * if none of the above, no panel + * 4) make sure lid is open + * if closed, act like it's not there for now + */ + + /* Set up the DDC bus. */ + psb_intel_output->ddc_bus = psb_intel_i2c_create(dev, + GPIOC, + "LVDSDDC_C"); + if (!psb_intel_output->ddc_bus) { + dev_printk(KERN_ERR, &dev->pdev->dev, + "DDC bus registration " "failed.\n"); + goto failed_ddc; + } + + /* + * Attempt to get the fixed panel mode from DDC. Assume that the + * preferred mode is the right one. + */ + psb_intel_ddc_get_modes(psb_intel_output); + list_for_each_entry(scan, &connector->probed_modes, head) { + if (scan->type & DRM_MODE_TYPE_PREFERRED) { + mode_dev->panel_fixed_mode = + drm_mode_duplicate(dev, scan); + goto out; /* FIXME: check for quirks */ + } + } + + /* Failed to get EDID, what about VBT? do we need this? */ + if (mode_dev->vbt_mode) + mode_dev->panel_fixed_mode = + drm_mode_duplicate(dev, mode_dev->vbt_mode); + + if (!mode_dev->panel_fixed_mode) + if (dev_priv->lfp_lvds_vbt_mode) + mode_dev->panel_fixed_mode = + drm_mode_duplicate(dev, + dev_priv->lfp_lvds_vbt_mode); + + /* + * If we didn't get EDID, try checking if the panel is already turned + * on. If so, assume that whatever is currently programmed is the + * correct mode. + */ + lvds = REG_READ(LVDS); + pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0; + crtc = psb_intel_get_crtc_from_pipe(dev, pipe); + + if (crtc && (lvds & LVDS_PORT_EN)) { + mode_dev->panel_fixed_mode = + psb_intel_crtc_mode_get(dev, crtc); + if (mode_dev->panel_fixed_mode) { + mode_dev->panel_fixed_mode->type |= + DRM_MODE_TYPE_PREFERRED; + goto out; /* FIXME: check for quirks */ + } + } + + /* If we still don't have a mode after all that, give up. */ + if (!mode_dev->panel_fixed_mode) { + dev_err(dev->dev, "Found no modes on the lvds, ignoring the LVDS\n"); + goto failed_find; + } + + /* + * Blacklist machines with BIOSes that list an LVDS panel without + * actually having one. + */ +out: + drm_sysfs_connector_add(connector); + return; + +failed_find: + if (psb_intel_output->ddc_bus) + psb_intel_i2c_destroy(psb_intel_output->ddc_bus); +failed_ddc: + if (psb_intel_output->i2c_bus) + psb_intel_i2c_destroy(psb_intel_output->i2c_bus); +failed_blc_i2c: + drm_encoder_cleanup(encoder); + drm_connector_cleanup(connector); + kfree(connector); +} + diff --git a/trunk/drivers/staging/gma500/psb_intel_modes.c b/trunk/drivers/staging/gma500/psb_intel_modes.c new file mode 100644 index 000000000000..bde1aff96190 --- /dev/null +++ b/trunk/drivers/staging/gma500/psb_intel_modes.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2007 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * 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. + * + * Authers: Jesse Barnes + */ + +#include +#include +#include +#include "psb_intel_drv.h" + +/** + * psb_intel_ddc_probe + * + */ +bool psb_intel_ddc_probe(struct psb_intel_output *psb_intel_output) +{ + u8 out_buf[] = { 0x0, 0x0 }; + u8 buf[2]; + int ret; + struct i2c_msg msgs[] = { + { + .addr = 0x50, + .flags = 0, + .len = 1, + .buf = out_buf, + }, + { + .addr = 0x50, + .flags = I2C_M_RD, + .len = 1, + .buf = buf, + } + }; + + ret = i2c_transfer(&psb_intel_output->ddc_bus->adapter, msgs, 2); + if (ret == 2) + return true; + + return false; +} + +/** + * psb_intel_ddc_get_modes - get modelist from monitor + * @connector: DRM connector device to use + * + * Fetch the EDID information from @connector using the DDC bus. + */ +int psb_intel_ddc_get_modes(struct psb_intel_output *psb_intel_output) +{ + struct edid *edid; + int ret = 0; + + edid = + drm_get_edid(&psb_intel_output->base, + &psb_intel_output->ddc_bus->adapter); + if (edid) { + drm_mode_connector_update_edid_property(&psb_intel_output-> + base, edid); + ret = drm_add_edid_modes(&psb_intel_output->base, edid); + kfree(edid); + } + return ret; +} diff --git a/trunk/drivers/staging/gma500/psb_intel_reg.h b/trunk/drivers/staging/gma500/psb_intel_reg.h new file mode 100644 index 000000000000..1ac16aa791c9 --- /dev/null +++ b/trunk/drivers/staging/gma500/psb_intel_reg.h @@ -0,0 +1,1235 @@ +/* + * Copyright (c) 2009, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef __PSB_INTEL_REG_H__ +#define __PSB_INTEL_REG_H__ + +#define BLC_PWM_CTL 0x61254 +#define BLC_PWM_CTL2 0x61250 +#define BLC_PWM_CTL_C 0x62254 +#define BLC_PWM_CTL2_C 0x62250 +#define BACKLIGHT_MODULATION_FREQ_SHIFT (17) +/* + * This is the most significant 15 bits of the number of backlight cycles in a + * complete cycle of the modulated backlight control. + * + * The actual value is this field multiplied by two. + */ +#define BACKLIGHT_MODULATION_FREQ_MASK (0x7fff << 17) +#define BLM_LEGACY_MODE (1 << 16) +/* + * This is the number of cycles out of the backlight modulation cycle for which + * the backlight is on. + * + * This field must be no greater than the number of cycles in the complete + * backlight modulation cycle. + */ +#define BACKLIGHT_DUTY_CYCLE_SHIFT (0) +#define BACKLIGHT_DUTY_CYCLE_MASK (0xffff) + +#define I915_GCFGC 0xf0 +#define I915_LOW_FREQUENCY_ENABLE (1 << 7) +#define I915_DISPLAY_CLOCK_190_200_MHZ (0 << 4) +#define I915_DISPLAY_CLOCK_333_MHZ (4 << 4) +#define I915_DISPLAY_CLOCK_MASK (7 << 4) + +#define I855_HPLLCC 0xc0 +#define I855_CLOCK_CONTROL_MASK (3 << 0) +#define I855_CLOCK_133_200 (0 << 0) +#define I855_CLOCK_100_200 (1 << 0) +#define I855_CLOCK_100_133 (2 << 0) +#define I855_CLOCK_166_250 (3 << 0) + +/* I830 CRTC registers */ +#define HTOTAL_A 0x60000 +#define HBLANK_A 0x60004 +#define HSYNC_A 0x60008 +#define VTOTAL_A 0x6000c +#define VBLANK_A 0x60010 +#define VSYNC_A 0x60014 +#define PIPEASRC 0x6001c +#define BCLRPAT_A 0x60020 +#define VSYNCSHIFT_A 0x60028 + +#define HTOTAL_B 0x61000 +#define HBLANK_B 0x61004 +#define HSYNC_B 0x61008 +#define VTOTAL_B 0x6100c +#define VBLANK_B 0x61010 +#define VSYNC_B 0x61014 +#define PIPEBSRC 0x6101c +#define BCLRPAT_B 0x61020 +#define VSYNCSHIFT_B 0x61028 + +#define HTOTAL_C 0x62000 +#define HBLANK_C 0x62004 +#define HSYNC_C 0x62008 +#define VTOTAL_C 0x6200c +#define VBLANK_C 0x62010 +#define VSYNC_C 0x62014 +#define PIPECSRC 0x6201c +#define BCLRPAT_C 0x62020 +#define VSYNCSHIFT_C 0x62028 + +#define PP_STATUS 0x61200 +# define PP_ON (1 << 31) +/* + * Indicates that all dependencies of the panel are on: + * + * - PLL enabled + * - pipe enabled + * - LVDS/DVOB/DVOC on + */ +#define PP_READY (1 << 30) +#define PP_SEQUENCE_NONE (0 << 28) +#define PP_SEQUENCE_ON (1 << 28) +#define PP_SEQUENCE_OFF (2 << 28) +#define PP_SEQUENCE_MASK 0x30000000 +#define PP_CONTROL 0x61204 +#define POWER_TARGET_ON (1 << 0) + +#define LVDSPP_ON 0x61208 +#define LVDSPP_OFF 0x6120c +#define PP_CYCLE 0x61210 + +#define PFIT_CONTROL 0x61230 +#define PFIT_ENABLE (1 << 31) +#define PFIT_PIPE_MASK (3 << 29) +#define PFIT_PIPE_SHIFT 29 +#define PFIT_SCALING_MODE_PILLARBOX (1 << 27) +#define PFIT_SCALING_MODE_LETTERBOX (3 << 26) +#define VERT_INTERP_DISABLE (0 << 10) +#define VERT_INTERP_BILINEAR (1 << 10) +#define VERT_INTERP_MASK (3 << 10) +#define VERT_AUTO_SCALE (1 << 9) +#define HORIZ_INTERP_DISABLE (0 << 6) +#define HORIZ_INTERP_BILINEAR (1 << 6) +#define HORIZ_INTERP_MASK (3 << 6) +#define HORIZ_AUTO_SCALE (1 << 5) +#define PANEL_8TO6_DITHER_ENABLE (1 << 3) + +#define PFIT_PGM_RATIOS 0x61234 +#define PFIT_VERT_SCALE_MASK 0xfff00000 +#define PFIT_HORIZ_SCALE_MASK 0x0000fff0 + +#define PFIT_AUTO_RATIOS 0x61238 + +#define DPLL_A 0x06014 +#define DPLL_B 0x06018 +#define DPLL_VCO_ENABLE (1 << 31) +#define DPLL_DVO_HIGH_SPEED (1 << 30) +#define DPLL_SYNCLOCK_ENABLE (1 << 29) +#define DPLL_VGA_MODE_DIS (1 << 28) +#define DPLLB_MODE_DAC_SERIAL (1 << 26) /* i915 */ +#define DPLLB_MODE_LVDS (2 << 26) /* i915 */ +#define DPLL_MODE_MASK (3 << 26) +#define DPLL_DAC_SERIAL_P2_CLOCK_DIV_10 (0 << 24) /* i915 */ +#define DPLL_DAC_SERIAL_P2_CLOCK_DIV_5 (1 << 24) /* i915 */ +#define DPLLB_LVDS_P2_CLOCK_DIV_14 (0 << 24) /* i915 */ +#define DPLLB_LVDS_P2_CLOCK_DIV_7 (1 << 24) /* i915 */ +#define DPLL_P2_CLOCK_DIV_MASK 0x03000000 /* i915 */ +#define DPLL_FPA01_P1_POST_DIV_MASK 0x00ff0000 /* i915 */ +#define DPLL_LOCK (1 << 15) /* CDV */ + +/* + * The i830 generation, in DAC/serial mode, defines p1 as two plus this + * bitfield, or just 2 if PLL_P1_DIVIDE_BY_TWO is set. + */ +# define DPLL_FPA01_P1_POST_DIV_MASK_I830 0x001f0000 +/* + * The i830 generation, in LVDS mode, defines P1 as the bit number set within + * this field (only one bit may be set). + */ +#define DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS 0x003f0000 +#define DPLL_FPA01_P1_POST_DIV_SHIFT 16 +#define PLL_P2_DIVIDE_BY_4 (1 << 23) /* i830, required + * in DVO non-gang */ +# define PLL_P1_DIVIDE_BY_TWO (1 << 21) /* i830 */ +#define PLL_REF_INPUT_DREFCLK (0 << 13) +#define PLL_REF_INPUT_TVCLKINA (1 << 13) /* i830 */ +#define PLL_REF_INPUT_TVCLKINBC (2 << 13) /* SDVO + * TVCLKIN */ +#define PLLB_REF_INPUT_SPREADSPECTRUMIN (3 << 13) +#define PLL_REF_INPUT_MASK (3 << 13) +#define PLL_LOAD_PULSE_PHASE_SHIFT 9 +/* + * Parallel to Serial Load Pulse phase selection. + * Selects the phase for the 10X DPLL clock for the PCIe + * digital display port. The range is 4 to 13; 10 or more + * is just a flip delay. The default is 6 + */ +#define PLL_LOAD_PULSE_PHASE_MASK (0xf << PLL_LOAD_PULSE_PHASE_SHIFT) +#define DISPLAY_RATE_SELECT_FPA1 (1 << 8) + +/* + * SDVO multiplier for 945G/GM. Not used on 965. + * + * DPLL_MD_UDI_MULTIPLIER_MASK + */ +#define SDVO_MULTIPLIER_MASK 0x000000ff +#define SDVO_MULTIPLIER_SHIFT_HIRES 4 +#define SDVO_MULTIPLIER_SHIFT_VGA 0 + +/* + * PLL_MD + */ +/* Pipe A SDVO/UDI clock multiplier/divider register for G965. */ +#define DPLL_A_MD 0x0601c +/* Pipe B SDVO/UDI clock multiplier/divider register for G965. */ +#define DPLL_B_MD 0x06020 +/* + * UDI pixel divider, controlling how many pixels are stuffed into a packet. + * + * Value is pixels minus 1. Must be set to 1 pixel for SDVO. + */ +#define DPLL_MD_UDI_DIVIDER_MASK 0x3f000000 +#define DPLL_MD_UDI_DIVIDER_SHIFT 24 +/* UDI pixel divider for VGA, same as DPLL_MD_UDI_DIVIDER_MASK. */ +#define DPLL_MD_VGA_UDI_DIVIDER_MASK 0x003f0000 +#define DPLL_MD_VGA_UDI_DIVIDER_SHIFT 16 +/* + * SDVO/UDI pixel multiplier. + * + * SDVO requires that the bus clock rate be between 1 and 2 Ghz, and the bus + * clock rate is 10 times the DPLL clock. At low resolution/refresh rate + * modes, the bus rate would be below the limits, so SDVO allows for stuffing + * dummy bytes in the datastream at an increased clock rate, with both sides of + * the link knowing how many bytes are fill. + * + * So, for a mode with a dotclock of 65Mhz, we would want to double the clock + * rate to 130Mhz to get a bus rate of 1.30Ghz. The DPLL clock rate would be + * set to 130Mhz, and the SDVO multiplier set to 2x in this register and + * through an SDVO command. + * + * This register field has values of multiplication factor minus 1, with + * a maximum multiplier of 5 for SDVO. + */ +#define DPLL_MD_UDI_MULTIPLIER_MASK 0x00003f00 +#define DPLL_MD_UDI_MULTIPLIER_SHIFT 8 +/* + * SDVO/UDI pixel multiplier for VGA, same as DPLL_MD_UDI_MULTIPLIER_MASK. + * This best be set to the default value (3) or the CRT won't work. No, + * I don't entirely understand what this does... + */ +#define DPLL_MD_VGA_UDI_MULTIPLIER_MASK 0x0000003f +#define DPLL_MD_VGA_UDI_MULTIPLIER_SHIFT 0 + +#define DPLL_TEST 0x606c +#define DPLLB_TEST_SDVO_DIV_1 (0 << 22) +#define DPLLB_TEST_SDVO_DIV_2 (1 << 22) +#define DPLLB_TEST_SDVO_DIV_4 (2 << 22) +#define DPLLB_TEST_SDVO_DIV_MASK (3 << 22) +#define DPLLB_TEST_N_BYPASS (1 << 19) +#define DPLLB_TEST_M_BYPASS (1 << 18) +#define DPLLB_INPUT_BUFFER_ENABLE (1 << 16) +#define DPLLA_TEST_N_BYPASS (1 << 3) +#define DPLLA_TEST_M_BYPASS (1 << 2) +#define DPLLA_INPUT_BUFFER_ENABLE (1 << 0) + +#define ADPA 0x61100 +#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_USE_VGA_HVPOLARITY (1 << 15) +#define ADPA_SETS_HVPOLARITY 0 +#define ADPA_VSYNC_CNTL_DISABLE (1 << 11) +#define ADPA_VSYNC_CNTL_ENABLE 0 +#define ADPA_HSYNC_CNTL_DISABLE (1 << 10) +#define ADPA_HSYNC_CNTL_ENABLE 0 +#define ADPA_VSYNC_ACTIVE_HIGH (1 << 4) +#define ADPA_VSYNC_ACTIVE_LOW 0 +#define ADPA_HSYNC_ACTIVE_HIGH (1 << 3) +#define ADPA_HSYNC_ACTIVE_LOW 0 + +#define FPA0 0x06040 +#define FPA1 0x06044 +#define FPB0 0x06048 +#define FPB1 0x0604c +#define FP_N_DIV_MASK 0x003f0000 +#define FP_N_DIV_SHIFT 16 +#define FP_M1_DIV_MASK 0x00003f00 +#define FP_M1_DIV_SHIFT 8 +#define FP_M2_DIV_MASK 0x0000003f +#define FP_M2_DIV_SHIFT 0 + +#define PORT_HOTPLUG_EN 0x61110 +#define SDVOB_HOTPLUG_INT_EN (1 << 26) +#define SDVOC_HOTPLUG_INT_EN (1 << 25) +#define TV_HOTPLUG_INT_EN (1 << 18) +#define CRT_HOTPLUG_INT_EN (1 << 9) +#define CRT_HOTPLUG_FORCE_DETECT (1 << 3) +/* CDV.. */ +#define CRT_HOTPLUG_ACTIVATION_PERIOD_64 (1 << 8) +#define CRT_HOTPLUG_DAC_ON_TIME_2M (0 << 7) +#define CRT_HOTPLUG_DAC_ON_TIME_4M (1 << 7) +#define CRT_HOTPLUG_VOLTAGE_COMPARE_40 (0 << 5) +#define CRT_HOTPLUG_VOLTAGE_COMPARE_50 (1 << 5) +#define CRT_HOTPLUG_VOLTAGE_COMPARE_60 (2 << 5) +#define CRT_HOTPLUG_VOLTAGE_COMPARE_70 (3 << 5) +#define CRT_HOTPLUG_VOLTAGE_COMPARE_MASK (3 << 5) +#define CRT_HOTPLUG_DETECT_DELAY_1G (0 << 4) +#define CRT_HOTPLUG_DETECT_DELAY_2G (1 << 4) +#define CRT_HOTPLUG_DETECT_VOLTAGE_325MV (0 << 2) +#define CRT_HOTPLUG_DETECT_VOLTAGE_475MV (1 << 2) +#define CRT_HOTPLUG_DETECT_MASK 0x000000F8 + +#define PORT_HOTPLUG_STAT 0x61114 +#define CRT_HOTPLUG_INT_STATUS (1 << 11) +#define TV_HOTPLUG_INT_STATUS (1 << 10) +#define CRT_HOTPLUG_MONITOR_MASK (3 << 8) +#define CRT_HOTPLUG_MONITOR_COLOR (3 << 8) +#define CRT_HOTPLUG_MONITOR_MONO (2 << 8) +#define CRT_HOTPLUG_MONITOR_NONE (0 << 8) +#define SDVOC_HOTPLUG_INT_STATUS (1 << 7) +#define SDVOB_HOTPLUG_INT_STATUS (1 << 6) + +#define SDVOB 0x61140 +#define SDVOC 0x61160 +#define SDVO_ENABLE (1 << 31) +#define SDVO_PIPE_B_SELECT (1 << 30) +#define SDVO_STALL_SELECT (1 << 29) +#define SDVO_INTERRUPT_ENABLE (1 << 26) + +/** + * 915G/GM SDVO pixel multiplier. + * + * Programmed value is multiplier - 1, up to 5x. + * + * DPLL_MD_UDI_MULTIPLIER_MASK + */ +#define SDVO_PORT_MULTIPLY_MASK (7 << 23) +#define SDVO_PORT_MULTIPLY_SHIFT 23 +#define SDVO_PHASE_SELECT_MASK (15 << 19) +#define SDVO_PHASE_SELECT_DEFAULT (6 << 19) +#define SDVO_CLOCK_OUTPUT_INVERT (1 << 18) +#define SDVOC_GANG_MODE (1 << 16) +#define SDVO_BORDER_ENABLE (1 << 7) +#define SDVOB_PCIE_CONCURRENCY (1 << 3) +#define SDVO_DETECTED (1 << 2) +/* Bits to be preserved when writing */ +#define SDVOB_PRESERVE_MASK ((1 << 17) | (1 << 16) | (1 << 14)) +#define SDVOC_PRESERVE_MASK (1 << 17) + +/* + * This register controls the LVDS output enable, pipe selection, and data + * format selection. + * + * All of the clock/data pairs are force powered down by power sequencing. + */ +#define LVDS 0x61180 +/* + * Enables the LVDS port. This bit must be set before DPLLs are enabled, as + * the DPLL semantics change when the LVDS is assigned to that pipe. + */ +#define LVDS_PORT_EN (1 << 31) +/* Selects pipe B for LVDS data. Must be set on pre-965. */ +#define LVDS_PIPEB_SELECT (1 << 30) + +/* Turns on border drawing to allow centered display. */ +#define LVDS_BORDER_EN (1 << 15) + +/* + * Enables the A0-A2 data pairs and CLKA, containing 18 bits of color data per + * pixel. + */ +#define LVDS_A0A2_CLKA_POWER_MASK (3 << 8) +#define LVDS_A0A2_CLKA_POWER_DOWN (0 << 8) +#define LVDS_A0A2_CLKA_POWER_UP (3 << 8) +/* + * Controls the A3 data pair, which contains the additional LSBs for 24 bit + * mode. Only enabled if LVDS_A0A2_CLKA_POWER_UP also indicates it should be + * on. + */ +#define LVDS_A3_POWER_MASK (3 << 6) +#define LVDS_A3_POWER_DOWN (0 << 6) +#define LVDS_A3_POWER_UP (3 << 6) +/* + * Controls the CLKB pair. This should only be set when LVDS_B0B3_POWER_UP + * is set. + */ +#define LVDS_CLKB_POWER_MASK (3 << 4) +#define LVDS_CLKB_POWER_DOWN (0 << 4) +#define LVDS_CLKB_POWER_UP (3 << 4) +/* + * Controls the B0-B3 data pairs. This must be set to match the DPLL p2 + * setting for whether we are in dual-channel mode. The B3 pair will + * additionally only be powered up when LVDS_A3_POWER_UP is set. + */ +#define LVDS_B0B3_POWER_MASK (3 << 2) +#define LVDS_B0B3_POWER_DOWN (0 << 2) +#define LVDS_B0B3_POWER_UP (3 << 2) + +#define PIPEACONF 0x70008 +#define PIPEACONF_ENABLE (1 << 31) +#define PIPEACONF_DISABLE 0 +#define PIPEACONF_DOUBLE_WIDE (1 << 30) +#define PIPECONF_ACTIVE (1 << 30) +#define I965_PIPECONF_ACTIVE (1 << 30) +#define PIPECONF_DSIPLL_LOCK (1 << 29) +#define PIPEACONF_SINGLE_WIDE 0 +#define PIPEACONF_PIPE_UNLOCKED 0 +#define PIPEACONF_DSR (1 << 26) +#define PIPEACONF_PIPE_LOCKED (1 << 25) +#define PIPEACONF_PALETTE 0 +#define PIPECONF_FORCE_BORDER (1 << 25) +#define PIPEACONF_GAMMA (1 << 24) +#define PIPECONF_PROGRESSIVE (0 << 21) +#define PIPECONF_INTERLACE_W_FIELD_INDICATION (6 << 21) +#define PIPECONF_INTERLACE_FIELD_0_ONLY (7 << 21) +#define PIPECONF_PLANE_OFF (1 << 19) +#define PIPECONF_CURSOR_OFF (1 << 18) + +#define PIPEBCONF 0x71008 +#define PIPEBCONF_ENABLE (1 << 31) +#define PIPEBCONF_DISABLE 0 +#define PIPEBCONF_DOUBLE_WIDE (1 << 30) +#define PIPEBCONF_DISABLE 0 +#define PIPEBCONF_GAMMA (1 << 24) +#define PIPEBCONF_PALETTE 0 + +#define PIPECCONF 0x72008 + +#define PIPEBGCMAXRED 0x71010 +#define PIPEBGCMAXGREEN 0x71014 +#define PIPEBGCMAXBLUE 0x71018 + +#define PIPEASTAT 0x70024 +#define PIPEBSTAT 0x71024 +#define PIPECSTAT 0x72024 +#define PIPE_VBLANK_INTERRUPT_STATUS (1UL << 1) +#define PIPE_START_VBLANK_INTERRUPT_STATUS (1UL << 2) +#define PIPE_VBLANK_CLEAR (1 << 1) +#define PIPE_VBLANK_STATUS (1 << 1) +#define PIPE_TE_STATUS (1UL << 6) +#define PIPE_DPST_EVENT_STATUS (1UL << 7) +#define PIPE_VSYNC_CLEAR (1UL << 9) +#define PIPE_VSYNC_STATUS (1UL << 9) +#define PIPE_HDMI_AUDIO_UNDERRUN_STATUS (1UL << 10) +#define PIPE_HDMI_AUDIO_BUFFER_DONE_STATUS (1UL << 11) +#define PIPE_VBLANK_INTERRUPT_ENABLE (1UL << 17) +#define PIPE_START_VBLANK_INTERRUPT_ENABLE (1UL << 18) +#define PIPE_TE_ENABLE (1UL << 22) +#define PIPE_DPST_EVENT_ENABLE (1UL << 23) +#define PIPE_VSYNC_ENABL (1UL << 25) +#define PIPE_HDMI_AUDIO_UNDERRUN (1UL << 26) +#define PIPE_HDMI_AUDIO_BUFFER_DONE (1UL << 27) +#define PIPE_HDMI_AUDIO_INT_MASK (PIPE_HDMI_AUDIO_UNDERRUN | \ + PIPE_HDMI_AUDIO_BUFFER_DONE) +#define PIPE_EVENT_MASK ((1 << 29)|(1 << 28)|(1 << 27)|(1 << 26)|(1 << 24)|(1 << 23)|(1 << 22)|(1 << 21)|(1 << 20)|(1 << 16)) +#define PIPE_VBLANK_MASK ((1 << 25)|(1 << 24)|(1 << 18)|(1 << 17)) +#define HISTOGRAM_INT_CONTROL 0x61268 +#define HISTOGRAM_BIN_DATA 0X61264 +#define HISTOGRAM_LOGIC_CONTROL 0x61260 +#define PWM_CONTROL_LOGIC 0x61250 +#define PIPE_HOTPLUG_INTERRUPT_STATUS (1UL << 10) +#define HISTOGRAM_INTERRUPT_ENABLE (1UL << 31) +#define HISTOGRAM_LOGIC_ENABLE (1UL << 31) +#define PWM_LOGIC_ENABLE (1UL << 31) +#define PWM_PHASEIN_ENABLE (1UL << 25) +#define PWM_PHASEIN_INT_ENABLE (1UL << 24) +#define PWM_PHASEIN_VB_COUNT 0x00001f00 +#define PWM_PHASEIN_INC 0x0000001f +#define HISTOGRAM_INT_CTRL_CLEAR (1UL << 30) +#define DPST_YUV_LUMA_MODE 0 + +struct dpst_ie_histogram_control { + union { + uint32_t data; + struct { + uint32_t bin_reg_index:7; + uint32_t reserved:4; + uint32_t bin_reg_func_select:1; + uint32_t sync_to_phase_in:1; + uint32_t alt_enhancement_mode:2; + uint32_t reserved1:1; + uint32_t sync_to_phase_in_count:8; + uint32_t histogram_mode_select:1; + uint32_t reserved2:4; + uint32_t ie_pipe_assignment:1; + uint32_t ie_mode_table_enabled:1; + uint32_t ie_histogram_enable:1; + }; + }; +}; + +struct dpst_guardband { + union { + uint32_t data; + struct { + uint32_t guardband:22; + uint32_t guardband_interrupt_delay:8; + uint32_t interrupt_status:1; + uint32_t interrupt_enable:1; + }; + }; +}; + +#define PIPEAFRAMEHIGH 0x70040 +#define PIPEAFRAMEPIXEL 0x70044 +#define PIPEBFRAMEHIGH 0x71040 +#define PIPEBFRAMEPIXEL 0x71044 +#define PIPECFRAMEHIGH 0x72040 +#define PIPECFRAMEPIXEL 0x72044 +#define PIPE_FRAME_HIGH_MASK 0x0000ffff +#define PIPE_FRAME_HIGH_SHIFT 0 +#define PIPE_FRAME_LOW_MASK 0xff000000 +#define PIPE_FRAME_LOW_SHIFT 24 +#define PIPE_PIXEL_MASK 0x00ffffff +#define PIPE_PIXEL_SHIFT 0 + +#define DSPARB 0x70030 +#define DSPFW1 0x70034 +#define DSPFW2 0x70038 +#define DSPFW3 0x7003c +#define DSPFW4 0x70050 +#define DSPFW5 0x70054 +#define DSPFW6 0x70058 +#define DSPCHICKENBIT 0x70400 +#define DSPACNTR 0x70180 +#define DSPBCNTR 0x71180 +#define DSPCCNTR 0x72180 +#define DISPLAY_PLANE_ENABLE (1 << 31) +#define DISPLAY_PLANE_DISABLE 0 +#define DISPPLANE_GAMMA_ENABLE (1 << 30) +#define DISPPLANE_GAMMA_DISABLE 0 +#define DISPPLANE_PIXFORMAT_MASK (0xf << 26) +#define DISPPLANE_8BPP (0x2 << 26) +#define DISPPLANE_15_16BPP (0x4 << 26) +#define DISPPLANE_16BPP (0x5 << 26) +#define DISPPLANE_32BPP_NO_ALPHA (0x6 << 26) +#define DISPPLANE_32BPP (0x7 << 26) +#define DISPPLANE_STEREO_ENABLE (1 << 25) +#define DISPPLANE_STEREO_DISABLE 0 +#define DISPPLANE_SEL_PIPE_MASK (1 << 24) +#define DISPPLANE_SEL_PIPE_POS 24 +#define DISPPLANE_SEL_PIPE_A 0 +#define DISPPLANE_SEL_PIPE_B (1 << 24) +#define DISPPLANE_SRC_KEY_ENABLE (1 << 22) +#define DISPPLANE_SRC_KEY_DISABLE 0 +#define DISPPLANE_LINE_DOUBLE (1 << 20) +#define DISPPLANE_NO_LINE_DOUBLE 0 +#define DISPPLANE_STEREO_POLARITY_FIRST 0 +#define DISPPLANE_STEREO_POLARITY_SECOND (1 << 18) +/* plane B only */ +#define DISPPLANE_ALPHA_TRANS_ENABLE (1 << 15) +#define DISPPLANE_ALPHA_TRANS_DISABLE 0 +#define DISPPLANE_SPRITE_ABOVE_DISPLAYA 0 +#define DISPPLANE_SPRITE_ABOVE_OVERLAY (1) +#define DISPPLANE_BOTTOM (4) + +#define DSPABASE 0x70184 +#define DSPALINOFF 0x70184 +#define DSPASTRIDE 0x70188 + +#define DSPBBASE 0x71184 +#define DSPBLINOFF 0X71184 +#define DSPBADDR DSPBBASE +#define DSPBSTRIDE 0x71188 + +#define DSPCBASE 0x72184 +#define DSPCLINOFF 0x72184 +#define DSPCSTRIDE 0x72188 + +#define DSPAKEYVAL 0x70194 +#define DSPAKEYMASK 0x70198 + +#define DSPAPOS 0x7018C /* reserved */ +#define DSPASIZE 0x70190 +#define DSPBPOS 0x7118C +#define DSPBSIZE 0x71190 +#define DSPCPOS 0x7218C +#define DSPCSIZE 0x72190 + +#define DSPASURF 0x7019C +#define DSPATILEOFF 0x701A4 + +#define DSPBSURF 0x7119C +#define DSPBTILEOFF 0x711A4 + +#define DSPCSURF 0x7219C +#define DSPCTILEOFF 0x721A4 +#define DSPCKEYMAXVAL 0x721A0 +#define DSPCKEYMINVAL 0x72194 +#define DSPCKEYMSK 0x72198 + +#define VGACNTRL 0x71400 +#define VGA_DISP_DISABLE (1 << 31) +#define VGA_2X_MODE (1 << 30) +#define VGA_PIPE_B_SELECT (1 << 29) + +/* + * Overlay registers + */ +#define OV_C_OFFSET 0x08000 +#define OV_OVADD 0x30000 +#define OV_DOVASTA 0x30008 +# define OV_PIPE_SELECT ((1 << 6)|(1 << 7)) +# define OV_PIPE_SELECT_POS 6 +# define OV_PIPE_A 0 +# define OV_PIPE_C 1 +#define OV_OGAMC5 0x30010 +#define OV_OGAMC4 0x30014 +#define OV_OGAMC3 0x30018 +#define OV_OGAMC2 0x3001C +#define OV_OGAMC1 0x30020 +#define OV_OGAMC0 0x30024 +#define OVC_OVADD 0x38000 +#define OVC_DOVCSTA 0x38008 +#define OVC_OGAMC5 0x38010 +#define OVC_OGAMC4 0x38014 +#define OVC_OGAMC3 0x38018 +#define OVC_OGAMC2 0x3801C +#define OVC_OGAMC1 0x38020 +#define OVC_OGAMC0 0x38024 + +/* + * Some BIOS scratch area registers. The 845 (and 830?) store the amount + * of video memory available to the BIOS in SWF1. + */ +#define SWF0 0x71410 +#define SWF1 0x71414 +#define SWF2 0x71418 +#define SWF3 0x7141c +#define SWF4 0x71420 +#define SWF5 0x71424 +#define SWF6 0x71428 + +/* + * 855 scratch registers. + */ +#define SWF00 0x70410 +#define SWF01 0x70414 +#define SWF02 0x70418 +#define SWF03 0x7041c +#define SWF04 0x70420 +#define SWF05 0x70424 +#define SWF06 0x70428 + +#define SWF10 SWF0 +#define SWF11 SWF1 +#define SWF12 SWF2 +#define SWF13 SWF3 +#define SWF14 SWF4 +#define SWF15 SWF5 +#define SWF16 SWF6 + +#define SWF30 0x72414 +#define SWF31 0x72418 +#define SWF32 0x7241c + + +/* + * Palette registers + */ +#define PALETTE_A 0x0a000 +#define PALETTE_B 0x0a800 +#define PALETTE_C 0x0ac00 + +/* Cursor A & B regs */ +#define CURACNTR 0x70080 +#define CURSOR_MODE_DISABLE 0x00 +#define CURSOR_MODE_64_32B_AX 0x07 +#define CURSOR_MODE_64_ARGB_AX ((1 << 5) | CURSOR_MODE_64_32B_AX) +#define MCURSOR_GAMMA_ENABLE (1 << 26) +#define CURABASE 0x70084 +#define CURAPOS 0x70088 +#define CURSOR_POS_MASK 0x007FF +#define CURSOR_POS_SIGN 0x8000 +#define CURSOR_X_SHIFT 0 +#define CURSOR_Y_SHIFT 16 +#define CURBCNTR 0x700c0 +#define CURBBASE 0x700c4 +#define CURBPOS 0x700c8 +#define CURCCNTR 0x700e0 +#define CURCBASE 0x700e4 +#define CURCPOS 0x700e8 + +/* + * Interrupt Registers + */ +#define IER 0x020a0 +#define IIR 0x020a4 +#define IMR 0x020a8 +#define ISR 0x020ac + +/* + * MOORESTOWN delta registers + */ +#define MRST_DPLL_A 0x0f014 +#define MDFLD_DPLL_B 0x0f018 +#define MDFLD_INPUT_REF_SEL (1 << 14) +#define MDFLD_VCO_SEL (1 << 16) +#define DPLLA_MODE_LVDS (2 << 26) /* mrst */ +#define MDFLD_PLL_LATCHEN (1 << 28) +#define MDFLD_PWR_GATE_EN (1 << 30) +#define MDFLD_P1_MASK (0x1FF << 17) +#define MRST_FPA0 0x0f040 +#define MRST_FPA1 0x0f044 +#define MDFLD_DPLL_DIV0 0x0f048 +#define MDFLD_DPLL_DIV1 0x0f04c +#define MRST_PERF_MODE 0x020f4 + +/* + * MEDFIELD HDMI registers + */ +#define HDMIPHYMISCCTL 0x61134 +#define HDMI_PHY_POWER_DOWN 0x7f +#define HDMIB_CONTROL 0x61140 +#define HDMIB_PORT_EN (1 << 31) +#define HDMIB_PIPE_B_SELECT (1 << 30) +#define HDMIB_NULL_PACKET (1 << 9) +#define HDMIB_HDCP_PORT (1 << 5) + +/* #define LVDS 0x61180 */ +#define MRST_PANEL_8TO6_DITHER_ENABLE (1 << 25) +#define MRST_PANEL_24_DOT_1_FORMAT (1 << 24) +#define LVDS_A3_POWER_UP_0_OUTPUT (1 << 6) + +#define MIPI 0x61190 +#define MIPI_C 0x62190 +#define MIPI_PORT_EN (1 << 31) +/* Turns on border drawing to allow centered display. */ +#define SEL_FLOPPED_HSTX (1 << 23) +#define PASS_FROM_SPHY_TO_AFE (1 << 16) +#define MIPI_BORDER_EN (1 << 15) +#define MIPIA_3LANE_MIPIC_1LANE 0x1 +#define MIPIA_2LANE_MIPIC_2LANE 0x2 +#define TE_TRIGGER_DSI_PROTOCOL (1 << 2) +#define TE_TRIGGER_GPIO_PIN (1 << 3) +#define MIPI_TE_COUNT 0x61194 + +/* #define PP_CONTROL 0x61204 */ +#define POWER_DOWN_ON_RESET (1 << 1) + +/* #define PFIT_CONTROL 0x61230 */ +#define PFIT_PIPE_SELECT (3 << 29) +#define PFIT_PIPE_SELECT_SHIFT (29) + +/* #define BLC_PWM_CTL 0x61254 */ +#define MRST_BACKLIGHT_MODULATION_FREQ_SHIFT (16) +#define MRST_BACKLIGHT_MODULATION_FREQ_MASK (0xffff << 16) + +/* #define PIPEACONF 0x70008 */ +#define PIPEACONF_PIPE_STATE (1 << 30) +/* #define DSPACNTR 0x70180 */ + +#define MRST_DSPABASE 0x7019c +#define MRST_DSPBBASE 0x7119c +#define MDFLD_DSPCBASE 0x7219c + +/* + * Moorestown registers. + */ + +/* + * MIPI IP registers + */ +#define MIPIC_REG_OFFSET 0x800 + +#define DEVICE_READY_REG 0xb000 +#define LP_OUTPUT_HOLD (1 << 16) +#define EXIT_ULPS_DEV_READY 0x3 +#define LP_OUTPUT_HOLD_RELEASE 0x810000 +# define ENTERING_ULPS (2 << 1) +# define EXITING_ULPS (1 << 1) +# define ULPS_MASK (3 << 1) +# define BUS_POSSESSION (1 << 3) +#define INTR_STAT_REG 0xb004 +#define RX_SOT_ERROR (1 << 0) +#define RX_SOT_SYNC_ERROR (1 << 1) +#define RX_ESCAPE_MODE_ENTRY_ERROR (1 << 3) +#define RX_LP_TX_SYNC_ERROR (1 << 4) +#define RX_HS_RECEIVE_TIMEOUT_ERROR (1 << 5) +#define RX_FALSE_CONTROL_ERROR (1 << 6) +#define RX_ECC_SINGLE_BIT_ERROR (1 << 7) +#define RX_ECC_MULTI_BIT_ERROR (1 << 8) +#define RX_CHECKSUM_ERROR (1 << 9) +#define RX_DSI_DATA_TYPE_NOT_RECOGNIZED (1 << 10) +#define RX_DSI_VC_ID_INVALID (1 << 11) +#define TX_FALSE_CONTROL_ERROR (1 << 12) +#define TX_ECC_SINGLE_BIT_ERROR (1 << 13) +#define TX_ECC_MULTI_BIT_ERROR (1 << 14) +#define TX_CHECKSUM_ERROR (1 << 15) +#define TX_DSI_DATA_TYPE_NOT_RECOGNIZED (1 << 16) +#define TX_DSI_VC_ID_INVALID (1 << 17) +#define HIGH_CONTENTION (1 << 18) +#define LOW_CONTENTION (1 << 19) +#define DPI_FIFO_UNDER_RUN (1 << 20) +#define HS_TX_TIMEOUT (1 << 21) +#define LP_RX_TIMEOUT (1 << 22) +#define TURN_AROUND_ACK_TIMEOUT (1 << 23) +#define ACK_WITH_NO_ERROR (1 << 24) +#define HS_GENERIC_WR_FIFO_FULL (1 << 27) +#define LP_GENERIC_WR_FIFO_FULL (1 << 28) +#define SPL_PKT_SENT (1 << 30) +#define INTR_EN_REG 0xb008 +#define DSI_FUNC_PRG_REG 0xb00c +#define DPI_CHANNEL_NUMBER_POS 0x03 +#define DBI_CHANNEL_NUMBER_POS 0x05 +#define FMT_DPI_POS 0x07 +#define FMT_DBI_POS 0x0A +#define DBI_DATA_WIDTH_POS 0x0D + +/* DPI PIXEL FORMATS */ +#define RGB_565_FMT 0x01 /* RGB 565 FORMAT */ +#define RGB_666_FMT 0x02 /* RGB 666 FORMAT */ +#define LRGB_666_FMT 0x03 /* RGB LOOSELY PACKED + * 666 FORMAT + */ +#define RGB_888_FMT 0x04 /* RGB 888 FORMAT */ +#define VIRTUAL_CHANNEL_NUMBER_0 0x00 /* Virtual channel 0 */ +#define VIRTUAL_CHANNEL_NUMBER_1 0x01 /* Virtual channel 1 */ +#define VIRTUAL_CHANNEL_NUMBER_2 0x02 /* Virtual channel 2 */ +#define VIRTUAL_CHANNEL_NUMBER_3 0x03 /* Virtual channel 3 */ + +#define DBI_NOT_SUPPORTED 0x00 /* command mode + * is not supported + */ +#define DBI_DATA_WIDTH_16BIT 0x01 /* 16 bit data */ +#define DBI_DATA_WIDTH_9BIT 0x02 /* 9 bit data */ +#define DBI_DATA_WIDTH_8BIT 0x03 /* 8 bit data */ +#define DBI_DATA_WIDTH_OPT1 0x04 /* option 1 */ +#define DBI_DATA_WIDTH_OPT2 0x05 /* option 2 */ + +#define HS_TX_TIMEOUT_REG 0xb010 +#define LP_RX_TIMEOUT_REG 0xb014 +#define TURN_AROUND_TIMEOUT_REG 0xb018 +#define DEVICE_RESET_REG 0xb01C +#define DPI_RESOLUTION_REG 0xb020 +#define RES_V_POS 0x10 +#define DBI_RESOLUTION_REG 0xb024 /* Reserved for MDFLD */ +#define HORIZ_SYNC_PAD_COUNT_REG 0xb028 +#define HORIZ_BACK_PORCH_COUNT_REG 0xb02C +#define HORIZ_FRONT_PORCH_COUNT_REG 0xb030 +#define HORIZ_ACTIVE_AREA_COUNT_REG 0xb034 +#define VERT_SYNC_PAD_COUNT_REG 0xb038 +#define VERT_BACK_PORCH_COUNT_REG 0xb03c +#define VERT_FRONT_PORCH_COUNT_REG 0xb040 +#define HIGH_LOW_SWITCH_COUNT_REG 0xb044 +#define DPI_CONTROL_REG 0xb048 +#define DPI_SHUT_DOWN (1 << 0) +#define DPI_TURN_ON (1 << 1) +#define DPI_COLOR_MODE_ON (1 << 2) +#define DPI_COLOR_MODE_OFF (1 << 3) +#define DPI_BACK_LIGHT_ON (1 << 4) +#define DPI_BACK_LIGHT_OFF (1 << 5) +#define DPI_LP (1 << 6) +#define DPI_DATA_REG 0xb04c +#define DPI_BACK_LIGHT_ON_DATA 0x07 +#define DPI_BACK_LIGHT_OFF_DATA 0x17 +#define INIT_COUNT_REG 0xb050 +#define MAX_RET_PAK_REG 0xb054 +#define VIDEO_FMT_REG 0xb058 +#define COMPLETE_LAST_PCKT (1 << 2) +#define EOT_DISABLE_REG 0xb05c +#define ENABLE_CLOCK_STOPPING (1 << 1) +#define LP_BYTECLK_REG 0xb060 +#define LP_GEN_DATA_REG 0xb064 +#define HS_GEN_DATA_REG 0xb068 +#define LP_GEN_CTRL_REG 0xb06C +#define HS_GEN_CTRL_REG 0xb070 +#define DCS_CHANNEL_NUMBER_POS 0x6 +#define MCS_COMMANDS_POS 0x8 +#define WORD_COUNTS_POS 0x8 +#define MCS_PARAMETER_POS 0x10 +#define GEN_FIFO_STAT_REG 0xb074 +#define HS_DATA_FIFO_FULL (1 << 0) +#define HS_DATA_FIFO_HALF_EMPTY (1 << 1) +#define HS_DATA_FIFO_EMPTY (1 << 2) +#define LP_DATA_FIFO_FULL (1 << 8) +#define LP_DATA_FIFO_HALF_EMPTY (1 << 9) +#define LP_DATA_FIFO_EMPTY (1 << 10) +#define HS_CTRL_FIFO_FULL (1 << 16) +#define HS_CTRL_FIFO_HALF_EMPTY (1 << 17) +#define HS_CTRL_FIFO_EMPTY (1 << 18) +#define LP_CTRL_FIFO_FULL (1 << 24) +#define LP_CTRL_FIFO_HALF_EMPTY (1 << 25) +#define LP_CTRL_FIFO_EMPTY (1 << 26) +#define DBI_FIFO_EMPTY (1 << 27) +#define DPI_FIFO_EMPTY (1 << 28) +#define HS_LS_DBI_ENABLE_REG 0xb078 +#define TXCLKESC_REG 0xb07c +#define DPHY_PARAM_REG 0xb080 +#define DBI_BW_CTRL_REG 0xb084 +#define CLK_LANE_SWT_REG 0xb088 + +/* + * MIPI Adapter registers + */ +#define MIPI_CONTROL_REG 0xb104 +#define MIPI_2X_CLOCK_BITS ((1 << 0) | (1 << 1)) +#define MIPI_DATA_ADDRESS_REG 0xb108 +#define MIPI_DATA_LENGTH_REG 0xb10C +#define MIPI_COMMAND_ADDRESS_REG 0xb110 +#define MIPI_COMMAND_LENGTH_REG 0xb114 +#define MIPI_READ_DATA_RETURN_REG0 0xb118 +#define MIPI_READ_DATA_RETURN_REG1 0xb11C +#define MIPI_READ_DATA_RETURN_REG2 0xb120 +#define MIPI_READ_DATA_RETURN_REG3 0xb124 +#define MIPI_READ_DATA_RETURN_REG4 0xb128 +#define MIPI_READ_DATA_RETURN_REG5 0xb12C +#define MIPI_READ_DATA_RETURN_REG6 0xb130 +#define MIPI_READ_DATA_RETURN_REG7 0xb134 +#define MIPI_READ_DATA_VALID_REG 0xb138 + +/* DBI COMMANDS */ +#define soft_reset 0x01 +/* + * The display module performs a software reset. + * Registers are written with their SW Reset default values. + */ +#define get_power_mode 0x0a +/* + * The display module returns the current power mode + */ +#define get_address_mode 0x0b +/* + * The display module returns the current status. + */ +#define get_pixel_format 0x0c +/* + * This command gets the pixel format for the RGB image data + * used by the interface. + */ +#define get_display_mode 0x0d +/* + * The display module returns the Display Image Mode status. + */ +#define get_signal_mode 0x0e +/* + * The display module returns the Display Signal Mode. + */ +#define get_diagnostic_result 0x0f +/* + * The display module returns the self-diagnostic results following + * a Sleep Out command. + */ +#define enter_sleep_mode 0x10 +/* + * This command causes the display module to enter the Sleep mode. + * In this mode, all unnecessary blocks inside the display module are + * disabled except interface communication. This is the lowest power + * mode the display module supports. + */ +#define exit_sleep_mode 0x11 +/* + * This command causes the display module to exit Sleep mode. + * All blocks inside the display module are enabled. + */ +#define enter_partial_mode 0x12 +/* + * This command causes the display module to enter the Partial Display + * Mode. The Partial Display Mode window is described by the + * set_partial_area command. + */ +#define enter_normal_mode 0x13 +/* + * This command causes the display module to enter the Normal mode. + * Normal Mode is defined as Partial Display mode and Scroll mode are off + */ +#define exit_invert_mode 0x20 +/* + * This command causes the display module to stop inverting the image + * data on the display device. The frame memory contents remain unchanged. + * No status bits are changed. + */ +#define enter_invert_mode 0x21 +/* + * This command causes the display module to invert the image data only on + * the display device. The frame memory contents remain unchanged. + * No status bits are changed. + */ +#define set_gamma_curve 0x26 +/* + * This command selects the desired gamma curve for the display device. + * Four fixed gamma curves are defined in section DCS spec. + */ +#define set_display_off 0x28 +/* ************************************************************************* *\ +This command causes the display module to stop displaying the image data +on the display device. The frame memory contents remain unchanged. +No status bits are changed. +\* ************************************************************************* */ +#define set_display_on 0x29 +/* ************************************************************************* *\ +This command causes the display module to start displaying the image data +on the display device. The frame memory contents remain unchanged. +No status bits are changed. +\* ************************************************************************* */ +#define set_column_address 0x2a +/* + * This command defines the column extent of the frame memory accessed by + * the hostprocessor with the read_memory_continue and + * write_memory_continue commands. + * No status bits are changed. + */ +#define set_page_addr 0x2b +/* + * This command defines the page extent of the frame memory accessed by + * the host processor with the write_memory_continue and + * read_memory_continue command. + * No status bits are changed. + */ +#define write_mem_start 0x2c +/* + * This command transfers image data from the host processor to the + * display modules frame memory starting at the pixel location specified + * by preceding set_column_address and set_page_address commands. + */ +#define set_partial_area 0x30 +/* + * This command defines the Partial Display mode s display area. + * There are two parameters associated with this command, the first + * defines the Start Row (SR) and the second the End Row (ER). SR and ER + * refer to the Frame Memory Line Pointer. + */ +#define set_scroll_area 0x33 +/* + * This command defines the display modules Vertical Scrolling Area. + */ +#define set_tear_off 0x34 +/* + * This command turns off the display modules Tearing Effect output + * signal on the TE signal line. + */ +#define set_tear_on 0x35 +/* + * This command turns on the display modules Tearing Effect output signal + * on the TE signal line. + */ +#define set_address_mode 0x36 +/* + * This command sets the data order for transfers from the host processor + * to display modules frame memory,bits B[7:5] and B3, and from the + * display modules frame memory to the display device, bits B[2:0] and B4. + */ +#define set_scroll_start 0x37 +/* + * This command sets the start of the vertical scrolling area in the frame + * memory. The vertical scrolling area is fully defined when this command + * is used with the set_scroll_area command The set_scroll_start command + * has one parameter, the Vertical Scroll Pointer. The VSP defines the + * line in the frame memory that is written to the display device as the + * first line of the vertical scroll area. + */ +#define exit_idle_mode 0x38 +/* + * This command causes the display module to exit Idle mode. + */ +#define enter_idle_mode 0x39 +/* + * This command causes the display module to enter Idle Mode. + * In Idle Mode, color expression is reduced. Colors are shown on the + * display device using the MSB of each of the R, G and B color + * components in the frame memory + */ +#define set_pixel_format 0x3a +/* + * This command sets the pixel format for the RGB image data used by the + * interface. + * Bits D[6:4] DPI Pixel Format Definition + * Bits D[2:0] DBI Pixel Format Definition + * Bits D7 and D3 are not used. + */ +#define DCS_PIXEL_FORMAT_3bpp 0x1 +#define DCS_PIXEL_FORMAT_8bpp 0x2 +#define DCS_PIXEL_FORMAT_12bpp 0x3 +#define DCS_PIXEL_FORMAT_16bpp 0x5 +#define DCS_PIXEL_FORMAT_18bpp 0x6 +#define DCS_PIXEL_FORMAT_24bpp 0x7 + +#define write_mem_cont 0x3c + +/* + * This command transfers image data from the host processor to the + * display module's frame memory continuing from the pixel location + * following the previous write_memory_continue or write_memory_start + * command. + */ +#define set_tear_scanline 0x44 +/* + * This command turns on the display modules Tearing Effect output signal + * on the TE signal line when the display module reaches line N. + */ +#define get_scanline 0x45 +/* + * The display module returns the current scanline, N, used to update the + * display device. The total number of scanlines on a display device is + * defined as VSYNC + VBP + VACT + VFP.The first scanline is defined as + * the first line of V Sync and is denoted as Line 0. + * When in Sleep Mode, the value returned by get_scanline is undefined. + */ + +/* MCS or Generic COMMANDS */ +/* MCS/generic data type */ +#define GEN_SHORT_WRITE_0 0x03 /* generic short write, no parameters */ +#define GEN_SHORT_WRITE_1 0x13 /* generic short write, 1 parameters */ +#define GEN_SHORT_WRITE_2 0x23 /* generic short write, 2 parameters */ +#define GEN_READ_0 0x04 /* generic read, no parameters */ +#define GEN_READ_1 0x14 /* generic read, 1 parameters */ +#define GEN_READ_2 0x24 /* generic read, 2 parameters */ +#define GEN_LONG_WRITE 0x29 /* generic long write */ +#define MCS_SHORT_WRITE_0 0x05 /* MCS short write, no parameters */ +#define MCS_SHORT_WRITE_1 0x15 /* MCS short write, 1 parameters */ +#define MCS_READ 0x06 /* MCS read, no parameters */ +#define MCS_LONG_WRITE 0x39 /* MCS long write */ +/* MCS/generic commands */ +/* TPO MCS */ +#define write_display_profile 0x50 +#define write_display_brightness 0x51 +#define write_ctrl_display 0x53 +#define write_ctrl_cabc 0x55 + #define UI_IMAGE 0x01 + #define STILL_IMAGE 0x02 + #define MOVING_IMAGE 0x03 +#define write_hysteresis 0x57 +#define write_gamma_setting 0x58 +#define write_cabc_min_bright 0x5e +#define write_kbbc_profile 0x60 +/* TMD MCS */ +#define tmd_write_display_brightness 0x8c + +/* + * This command is used to control ambient light, panel backlight + * brightness and gamma settings. + */ +#define BRIGHT_CNTL_BLOCK_ON (1 << 5) +#define AMBIENT_LIGHT_SENSE_ON (1 << 4) +#define DISPLAY_DIMMING_ON (1 << 3) +#define BACKLIGHT_ON (1 << 2) +#define DISPLAY_BRIGHTNESS_AUTO (1 << 1) +#define GAMMA_AUTO (1 << 0) + +/* DCS Interface Pixel Formats */ +#define DCS_PIXEL_FORMAT_3BPP 0x1 +#define DCS_PIXEL_FORMAT_8BPP 0x2 +#define DCS_PIXEL_FORMAT_12BPP 0x3 +#define DCS_PIXEL_FORMAT_16BPP 0x5 +#define DCS_PIXEL_FORMAT_18BPP 0x6 +#define DCS_PIXEL_FORMAT_24BPP 0x7 +/* ONE PARAMETER READ DATA */ +#define addr_mode_data 0xfc +#define diag_res_data 0x00 +#define disp_mode_data 0x23 +#define pxl_fmt_data 0x77 +#define pwr_mode_data 0x74 +#define sig_mode_data 0x00 +/* TWO PARAMETERS READ DATA */ +#define scanline_data1 0xff +#define scanline_data2 0xff +#define NON_BURST_MODE_SYNC_PULSE 0x01 /* Non Burst Mode + * with Sync Pulse + */ +#define NON_BURST_MODE_SYNC_EVENTS 0x02 /* Non Burst Mode + * with Sync events + */ +#define BURST_MODE 0x03 /* Burst Mode */ +#define DBI_COMMAND_BUFFER_SIZE 0x240 /* 0x32 */ /* 0x120 */ + /* Allocate at least + * 0x100 Byte with 32 + * byte alignment + */ +#define DBI_DATA_BUFFER_SIZE 0x120 /* Allocate at least + * 0x100 Byte with 32 + * byte alignment + */ +#define DBI_CB_TIME_OUT 0xFFFF + +#define GEN_FB_TIME_OUT 2000 + +#define SKU_83 0x01 +#define SKU_100 0x02 +#define SKU_100L 0x04 +#define SKU_BYPASS 0x08 + +/* Some handy macros for playing with bitfields. */ +#define PSB_MASK(high, low) (((1<<((high)-(low)+1))-1)<<(low)) +#define SET_FIELD(value, field) (((value) << field ## _SHIFT) & field ## _MASK) +#define GET_FIELD(word, field) (((word) & field ## _MASK) >> field ## _SHIFT) + +#define _PIPE(pipe, a, b) ((a) + (pipe)*((b)-(a))) + +/* PCI config space */ + +#define SB_PCKT 0x02100 /* cedarview */ +# define SB_OPCODE_MASK PSB_MASK(31, 16) +# define SB_OPCODE_SHIFT 16 +# define SB_OPCODE_READ 0 +# define SB_OPCODE_WRITE 1 +# define SB_DEST_MASK PSB_MASK(15, 8) +# define SB_DEST_SHIFT 8 +# define SB_DEST_DPLL 0x88 +# define SB_BYTE_ENABLE_MASK PSB_MASK(7, 4) +# define SB_BYTE_ENABLE_SHIFT 4 +# define SB_BUSY (1 << 0) + + +/* 32-bit value read/written from the DPIO reg. */ +#define SB_DATA 0x02104 /* cedarview */ +/* 32-bit address of the DPIO reg to be read/written. */ +#define SB_ADDR 0x02108 /* cedarview */ +#define DPIO_CFG 0x02110 /* cedarview */ +# define DPIO_MODE_SELECT_1 (1 << 3) +# define DPIO_MODE_SELECT_0 (1 << 2) +# define DPIO_SFR_BYPASS (1 << 1) +/* reset is active low */ +# define DPIO_CMN_RESET_N (1 << 0) + +/* Cedarview sideband registers */ +#define _SB_M_A 0x8008 +#define _SB_M_B 0x8028 +#define SB_M(pipe) _PIPE(pipe, _SB_M_A, _SB_M_B) +# define SB_M_DIVIDER_MASK (0xFF << 24) +# define SB_M_DIVIDER_SHIFT 24 + +#define _SB_N_VCO_A 0x8014 +#define _SB_N_VCO_B 0x8034 +#define SB_N_VCO(pipe) _PIPE(pipe, _SB_N_VCO_A, _SB_N_VCO_B) +#define SB_N_VCO_SEL_MASK PSB_MASK(31, 30) +#define SB_N_VCO_SEL_SHIFT 30 +#define SB_N_DIVIDER_MASK PSB_MASK(29, 26) +#define SB_N_DIVIDER_SHIFT 26 +#define SB_N_CB_TUNE_MASK PSB_MASK(25, 24) +#define SB_N_CB_TUNE_SHIFT 24 + +#define _SB_REF_A 0x8018 +#define _SB_REF_B 0x8038 +#define SB_REF_SFR(pipe) _PIPE(pipe, _SB_REF_A, _SB_REF_B) + +#define _SB_P_A 0x801c +#define _SB_P_B 0x803c +#define SB_P(pipe) _PIPE(pipe, _SB_P_A, _SB_P_B) +#define SB_P2_DIVIDER_MASK PSB_MASK(31, 30) +#define SB_P2_DIVIDER_SHIFT 30 +#define SB_P2_10 0 /* HDMI, DP, DAC */ +#define SB_P2_5 1 /* DAC */ +#define SB_P2_14 2 /* LVDS single */ +#define SB_P2_7 3 /* LVDS double */ +#define SB_P1_DIVIDER_MASK PSB_MASK(15, 12) +#define SB_P1_DIVIDER_SHIFT 12 + +#define PSB_LANE0 0x120 +#define PSB_LANE1 0x220 +#define PSB_LANE2 0x2320 +#define PSB_LANE3 0x2420 + +#define LANE_PLL_MASK (0x7 << 20) +#define LANE_PLL_ENABLE (0x3 << 20) + + +#endif diff --git a/trunk/drivers/staging/gma500/psb_intel_sdvo.c b/trunk/drivers/staging/gma500/psb_intel_sdvo.c new file mode 100644 index 000000000000..a4bad1af4b7c --- /dev/null +++ b/trunk/drivers/staging/gma500/psb_intel_sdvo.c @@ -0,0 +1,1293 @@ +/* + * Copyright (c) 2006-2007 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * 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. + * + * Authors: + * Eric Anholt + */ + +#include +#include +/* #include */ +#include +#include "psb_drv.h" +#include "psb_intel_drv.h" +#include "psb_intel_reg.h" +#include "psb_intel_sdvo_regs.h" + +struct psb_intel_sdvo_priv { + struct psb_intel_i2c_chan *i2c_bus; + int slaveaddr; + int output_device; + + u16 active_outputs; + + struct psb_intel_sdvo_caps caps; + int pixel_clock_min, pixel_clock_max; + + int save_sdvo_mult; + u16 save_active_outputs; + struct psb_intel_sdvo_dtd save_input_dtd_1, save_input_dtd_2; + struct psb_intel_sdvo_dtd save_output_dtd[16]; + u32 save_SDVOX; + u8 in_out_map[4]; + + u8 by_input_wiring; + u32 active_device; +}; + +/** + * Writes the SDVOB or SDVOC with the given value, but always writes both + * SDVOB and SDVOC to work around apparent hardware issues (according to + * comments in the BIOS). + */ +void psb_intel_sdvo_write_sdvox(struct psb_intel_output *psb_intel_output, + u32 val) +{ + struct drm_device *dev = psb_intel_output->base.dev; + struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv; + u32 bval = val, cval = val; + int i; + + if (sdvo_priv->output_device == SDVOB) + cval = REG_READ(SDVOC); + else + bval = REG_READ(SDVOB); + /* + * Write the registers twice for luck. Sometimes, + * writing them only once doesn't appear to 'stick'. + * The BIOS does this too. Yay, magic + */ + for (i = 0; i < 2; i++) { + REG_WRITE(SDVOB, bval); + REG_READ(SDVOB); + REG_WRITE(SDVOC, cval); + REG_READ(SDVOC); + } +} + +static bool psb_intel_sdvo_read_byte( + struct psb_intel_output *psb_intel_output, + u8 addr, u8 *ch) +{ + struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv; + u8 out_buf[2]; + u8 buf[2]; + int ret; + + struct i2c_msg msgs[] = { + { + .addr = sdvo_priv->i2c_bus->slave_addr, + .flags = 0, + .len = 1, + .buf = out_buf, + }, + { + .addr = sdvo_priv->i2c_bus->slave_addr, + .flags = I2C_M_RD, + .len = 1, + .buf = buf, + } + }; + + out_buf[0] = addr; + out_buf[1] = 0; + + ret = i2c_transfer(&sdvo_priv->i2c_bus->adapter, msgs, 2); + if (ret == 2) { + *ch = buf[0]; + return true; + } + + return false; +} + +static bool psb_intel_sdvo_write_byte( + struct psb_intel_output *psb_intel_output, + int addr, u8 ch) +{ + u8 out_buf[2]; + struct i2c_msg msgs[] = { + { + .addr = psb_intel_output->i2c_bus->slave_addr, + .flags = 0, + .len = 2, + .buf = out_buf, + } + }; + + out_buf[0] = addr; + out_buf[1] = ch; + + if (i2c_transfer(&psb_intel_output->i2c_bus->adapter, msgs, 1) == 1) + return true; + return false; +} + +#define SDVO_CMD_NAME_ENTRY(cmd) {cmd, #cmd} +/** Mapping of command numbers to names, for debug output */ +static const struct _sdvo_cmd_name { + u8 cmd; + char *name; +} sdvo_cmd_names[] = { +SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FIRMWARE_REV), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TRAINED_INPUTS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_OUTPUTS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_OUTPUTS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_IN_OUT_MAP), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_IN_OUT_MAP), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ATTACHED_DISPLAYS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HOT_PLUG_SUPPORT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_HOT_PLUG), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_HOT_PLUG), + SDVO_CMD_NAME_ENTRY + (SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_INPUT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_OUTPUT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART2), + SDVO_CMD_NAME_ENTRY + (SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING), + SDVO_CMD_NAME_ENTRY + (SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1), + SDVO_CMD_NAME_ENTRY + (SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2), + SDVO_CMD_NAME_ENTRY + (SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE), + SDVO_CMD_NAME_ENTRY + (SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE), + SDVO_CMD_NAME_ENTRY + (SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CLOCK_RATE_MULT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CLOCK_RATE_MULT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT), + SDVO_CMD_NAME_ENTRY + (SDVO_CMD_SET_TV_RESOLUTION_SUPPORT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH),}; + +#define SDVO_NAME(dev_priv) \ + ((dev_priv)->output_device == SDVOB ? "SDVOB" : "SDVOC") +#define SDVO_PRIV(output) ((struct psb_intel_sdvo_priv *) (output)->dev_priv) + +static void psb_intel_sdvo_write_cmd(struct psb_intel_output *psb_intel_output, + u8 cmd, + void *args, + int args_len) +{ + struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv; + int i; + + if (0) { + printk(KERN_DEBUG "%s: W: %02X ", SDVO_NAME(sdvo_priv), cmd); + for (i = 0; i < args_len; i++) + printk(KERN_CONT "%02X ", ((u8 *) args)[i]); + for (; i < 8; i++) + printk(KERN_CONT " "); + for (i = 0; + i < + sizeof(sdvo_cmd_names) / sizeof(sdvo_cmd_names[0]); + i++) { + if (cmd == sdvo_cmd_names[i].cmd) { + printk(KERN_CONT + "(%s)", sdvo_cmd_names[i].name); + break; + } + } + if (i == + sizeof(sdvo_cmd_names) / sizeof(sdvo_cmd_names[0])) + printk(KERN_CONT "(%02X)", cmd); + printk(KERN_CONT "\n"); + } + + for (i = 0; i < args_len; i++) { + psb_intel_sdvo_write_byte(psb_intel_output, + SDVO_I2C_ARG_0 - i, + ((u8 *) args)[i]); + } + + psb_intel_sdvo_write_byte(psb_intel_output, SDVO_I2C_OPCODE, cmd); +} + +static const char *const cmd_status_names[] = { + "Power on", + "Success", + "Not supported", + "Invalid arg", + "Pending", + "Target not specified", + "Scaling not supported" +}; + +static u8 psb_intel_sdvo_read_response( + struct psb_intel_output *psb_intel_output, + void *response, int response_len) +{ + struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv; + int i; + u8 status; + u8 retry = 50; + + while (retry--) { + /* Read the command response */ + for (i = 0; i < response_len; i++) { + psb_intel_sdvo_read_byte(psb_intel_output, + SDVO_I2C_RETURN_0 + i, + &((u8 *) response)[i]); + } + + /* read the return status */ + psb_intel_sdvo_read_byte(psb_intel_output, + SDVO_I2C_CMD_STATUS, + &status); + + if (0) { + pr_debug("%s: R: ", SDVO_NAME(sdvo_priv)); + for (i = 0; i < response_len; i++) + printk(KERN_CONT "%02X ", ((u8 *) response)[i]); + for (; i < 8; i++) + printk(" "); + if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP) + printk(KERN_CONT "(%s)", + cmd_status_names[status]); + else + printk(KERN_CONT "(??? %d)", status); + printk(KERN_CONT "\n"); + } + + if (status != SDVO_CMD_STATUS_PENDING) + return status; + + mdelay(50); + } + + return status; +} + +int psb_intel_sdvo_get_pixel_multiplier(struct drm_display_mode *mode) +{ + if (mode->clock >= 100000) + return 1; + else if (mode->clock >= 50000) + return 2; + else + return 4; +} + +/** + * Don't check status code from this as it switches the bus back to the + * SDVO chips which defeats the purpose of doing a bus switch in the first + * place. + */ +void psb_intel_sdvo_set_control_bus_switch( + struct psb_intel_output *psb_intel_output, + u8 target) +{ + psb_intel_sdvo_write_cmd(psb_intel_output, + SDVO_CMD_SET_CONTROL_BUS_SWITCH, + &target, + 1); +} + +static bool psb_intel_sdvo_set_target_input( + struct psb_intel_output *psb_intel_output, + bool target_0, bool target_1) +{ + struct psb_intel_sdvo_set_target_input_args targets = { 0 }; + u8 status; + + if (target_0 && target_1) + return SDVO_CMD_STATUS_NOTSUPP; + + if (target_1) + targets.target_1 = 1; + + psb_intel_sdvo_write_cmd(psb_intel_output, SDVO_CMD_SET_TARGET_INPUT, + &targets, sizeof(targets)); + + status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0); + + return status == SDVO_CMD_STATUS_SUCCESS; +} + +/** + * Return whether each input is trained. + * + * This function is making an assumption about the layout of the response, + * which should be checked against the docs. + */ +static bool psb_intel_sdvo_get_trained_inputs(struct psb_intel_output + *psb_intel_output, bool *input_1, + bool *input_2) +{ + struct psb_intel_sdvo_get_trained_inputs_response response; + u8 status; + + psb_intel_sdvo_write_cmd(psb_intel_output, SDVO_CMD_GET_TRAINED_INPUTS, + NULL, 0); + status = + psb_intel_sdvo_read_response(psb_intel_output, &response, + sizeof(response)); + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + + *input_1 = response.input0_trained; + *input_2 = response.input1_trained; + return true; +} + +static bool psb_intel_sdvo_get_active_outputs(struct psb_intel_output + *psb_intel_output, u16 *outputs) +{ + u8 status; + + psb_intel_sdvo_write_cmd(psb_intel_output, SDVO_CMD_GET_ACTIVE_OUTPUTS, + NULL, 0); + status = + psb_intel_sdvo_read_response(psb_intel_output, outputs, + sizeof(*outputs)); + + return status == SDVO_CMD_STATUS_SUCCESS; +} + +static bool psb_intel_sdvo_set_active_outputs(struct psb_intel_output + *psb_intel_output, u16 outputs) +{ + u8 status; + + psb_intel_sdvo_write_cmd(psb_intel_output, SDVO_CMD_SET_ACTIVE_OUTPUTS, + &outputs, sizeof(outputs)); + status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0); + return status == SDVO_CMD_STATUS_SUCCESS; +} + +static bool psb_intel_sdvo_set_encoder_power_state(struct psb_intel_output + *psb_intel_output, int mode) +{ + u8 status, state = SDVO_ENCODER_STATE_ON; + + switch (mode) { + case DRM_MODE_DPMS_ON: + state = SDVO_ENCODER_STATE_ON; + break; + case DRM_MODE_DPMS_STANDBY: + state = SDVO_ENCODER_STATE_STANDBY; + break; + case DRM_MODE_DPMS_SUSPEND: + state = SDVO_ENCODER_STATE_SUSPEND; + break; + case DRM_MODE_DPMS_OFF: + state = SDVO_ENCODER_STATE_OFF; + break; + } + + psb_intel_sdvo_write_cmd(psb_intel_output, + SDVO_CMD_SET_ENCODER_POWER_STATE, &state, + sizeof(state)); + status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0); + + return status == SDVO_CMD_STATUS_SUCCESS; +} + +static bool psb_intel_sdvo_get_input_pixel_clock_range(struct psb_intel_output + *psb_intel_output, + int *clock_min, + int *clock_max) +{ + struct psb_intel_sdvo_pixel_clock_range clocks; + u8 status; + + psb_intel_sdvo_write_cmd(psb_intel_output, + SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE, NULL, + 0); + + status = + psb_intel_sdvo_read_response(psb_intel_output, &clocks, + sizeof(clocks)); + + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + + /* Convert the values from units of 10 kHz to kHz. */ + *clock_min = clocks.min * 10; + *clock_max = clocks.max * 10; + + return true; +} + +static bool psb_intel_sdvo_set_target_output( + struct psb_intel_output *psb_intel_output, + u16 outputs) +{ + u8 status; + + psb_intel_sdvo_write_cmd(psb_intel_output, SDVO_CMD_SET_TARGET_OUTPUT, + &outputs, sizeof(outputs)); + + status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0); + return status == SDVO_CMD_STATUS_SUCCESS; +} + +static bool psb_intel_sdvo_get_timing(struct psb_intel_output *psb_intel_output, + u8 cmd, struct psb_intel_sdvo_dtd *dtd) +{ + u8 status; + + psb_intel_sdvo_write_cmd(psb_intel_output, cmd, NULL, 0); + status = psb_intel_sdvo_read_response(psb_intel_output, &dtd->part1, + sizeof(dtd->part1)); + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + + psb_intel_sdvo_write_cmd(psb_intel_output, cmd + 1, NULL, 0); + status = psb_intel_sdvo_read_response(psb_intel_output, &dtd->part2, + sizeof(dtd->part2)); + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + + return true; +} + +static bool psb_intel_sdvo_get_input_timing( + struct psb_intel_output *psb_intel_output, + struct psb_intel_sdvo_dtd *dtd) +{ + return psb_intel_sdvo_get_timing(psb_intel_output, + SDVO_CMD_GET_INPUT_TIMINGS_PART1, + dtd); +} + +static bool psb_intel_sdvo_set_timing( + struct psb_intel_output *psb_intel_output, + u8 cmd, + struct psb_intel_sdvo_dtd *dtd) +{ + u8 status; + + psb_intel_sdvo_write_cmd(psb_intel_output, cmd, &dtd->part1, + sizeof(dtd->part1)); + status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0); + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + + psb_intel_sdvo_write_cmd(psb_intel_output, cmd + 1, &dtd->part2, + sizeof(dtd->part2)); + status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0); + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + + return true; +} + +static bool psb_intel_sdvo_set_input_timing( + struct psb_intel_output *psb_intel_output, + struct psb_intel_sdvo_dtd *dtd) +{ + return psb_intel_sdvo_set_timing(psb_intel_output, + SDVO_CMD_SET_INPUT_TIMINGS_PART1, + dtd); +} + +static bool psb_intel_sdvo_set_output_timing( + struct psb_intel_output *psb_intel_output, + struct psb_intel_sdvo_dtd *dtd) +{ + return psb_intel_sdvo_set_timing(psb_intel_output, + SDVO_CMD_SET_OUTPUT_TIMINGS_PART1, + dtd); +} + +static int psb_intel_sdvo_get_clock_rate_mult(struct psb_intel_output + *psb_intel_output) +{ + u8 response, status; + + psb_intel_sdvo_write_cmd(psb_intel_output, + SDVO_CMD_GET_CLOCK_RATE_MULT, + NULL, + 0); + + status = psb_intel_sdvo_read_response(psb_intel_output, &response, 1); + + if (status != SDVO_CMD_STATUS_SUCCESS) { + DRM_DEBUG("Couldn't get SDVO clock rate multiplier\n"); + return SDVO_CLOCK_RATE_MULT_1X; + } else { + DRM_DEBUG("Current clock rate multiplier: %d\n", response); + } + + return response; +} + +static bool psb_intel_sdvo_set_clock_rate_mult(struct psb_intel_output + *psb_intel_output, u8 val) +{ + u8 status; + + psb_intel_sdvo_write_cmd(psb_intel_output, + SDVO_CMD_SET_CLOCK_RATE_MULT, + &val, + 1); + + status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0); + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + + return true; +} + +static bool psb_sdvo_set_current_inoutmap(struct psb_intel_output *output, + u32 in0outputmask, + u32 in1outputmask) +{ + u8 byArgs[4]; + u8 status; + int i; + struct psb_intel_sdvo_priv *sdvo_priv = output->dev_priv; + + /* Make all fields of the args/ret to zero */ + memset(byArgs, 0, sizeof(byArgs)); + + /* Fill up the argument values; */ + byArgs[0] = (u8) (in0outputmask & 0xFF); + byArgs[1] = (u8) ((in0outputmask >> 8) & 0xFF); + byArgs[2] = (u8) (in1outputmask & 0xFF); + byArgs[3] = (u8) ((in1outputmask >> 8) & 0xFF); + + + /*save inoutmap arg here*/ + for (i = 0; i < 4; i++) + sdvo_priv->in_out_map[i] = byArgs[0]; + + psb_intel_sdvo_write_cmd(output, SDVO_CMD_SET_IN_OUT_MAP, byArgs, 4); + status = psb_intel_sdvo_read_response(output, NULL, 0); + + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + return true; +} + + +static void psb_intel_sdvo_set_iomap(struct psb_intel_output *output) +{ + u32 dwCurrentSDVOIn0 = 0; + u32 dwCurrentSDVOIn1 = 0; + u32 dwDevMask = 0; + + + struct psb_intel_sdvo_priv *sdvo_priv = output->dev_priv; + + /* Please DO NOT change the following code. */ + /* SDVOB_IN0 or SDVOB_IN1 ==> sdvo_in0 */ + /* SDVOC_IN0 or SDVOC_IN1 ==> sdvo_in1 */ + if (sdvo_priv->by_input_wiring & (SDVOB_IN0 | SDVOC_IN0)) { + switch (sdvo_priv->active_device) { + case SDVO_DEVICE_LVDS: + dwDevMask = SDVO_OUTPUT_LVDS0 | SDVO_OUTPUT_LVDS1; + break; + case SDVO_DEVICE_TMDS: + dwDevMask = SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1; + break; + case SDVO_DEVICE_TV: + dwDevMask = + SDVO_OUTPUT_YPRPB0 | SDVO_OUTPUT_SVID0 | + SDVO_OUTPUT_CVBS0 | SDVO_OUTPUT_YPRPB1 | + SDVO_OUTPUT_SVID1 | SDVO_OUTPUT_CVBS1 | + SDVO_OUTPUT_SCART0 | SDVO_OUTPUT_SCART1; + break; + case SDVO_DEVICE_CRT: + dwDevMask = SDVO_OUTPUT_RGB0 | SDVO_OUTPUT_RGB1; + break; + } + dwCurrentSDVOIn0 = (sdvo_priv->active_outputs & dwDevMask); + } else if (sdvo_priv->by_input_wiring & (SDVOB_IN1 | SDVOC_IN1)) { + switch (sdvo_priv->active_device) { + case SDVO_DEVICE_LVDS: + dwDevMask = SDVO_OUTPUT_LVDS0 | SDVO_OUTPUT_LVDS1; + break; + case SDVO_DEVICE_TMDS: + dwDevMask = SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1; + break; + case SDVO_DEVICE_TV: + dwDevMask = + SDVO_OUTPUT_YPRPB0 | SDVO_OUTPUT_SVID0 | + SDVO_OUTPUT_CVBS0 | SDVO_OUTPUT_YPRPB1 | + SDVO_OUTPUT_SVID1 | SDVO_OUTPUT_CVBS1 | + SDVO_OUTPUT_SCART0 | SDVO_OUTPUT_SCART1; + break; + case SDVO_DEVICE_CRT: + dwDevMask = SDVO_OUTPUT_RGB0 | SDVO_OUTPUT_RGB1; + break; + } + dwCurrentSDVOIn1 = (sdvo_priv->active_outputs & dwDevMask); + } + + psb_sdvo_set_current_inoutmap(output, dwCurrentSDVOIn0, + dwCurrentSDVOIn1); +} + + +static bool psb_intel_sdvo_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + /* Make the CRTC code factor in the SDVO pixel multiplier. The SDVO + * device will be told of the multiplier during mode_set. + */ + adjusted_mode->clock *= psb_intel_sdvo_get_pixel_multiplier(mode); + return true; +} + +static void psb_intel_sdvo_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_crtc *crtc = encoder->crtc; + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + struct psb_intel_output *psb_intel_output = + enc_to_psb_intel_output(encoder); + struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv; + u16 width, height; + u16 h_blank_len, h_sync_len, v_blank_len, v_sync_len; + u16 h_sync_offset, v_sync_offset; + u32 sdvox; + struct psb_intel_sdvo_dtd output_dtd; + int sdvo_pixel_multiply; + + if (!mode) + return; + + psb_intel_sdvo_set_target_output(psb_intel_output, 0); + + width = mode->crtc_hdisplay; + height = mode->crtc_vdisplay; + + /* do some mode translations */ + h_blank_len = mode->crtc_hblank_end - mode->crtc_hblank_start; + h_sync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; + + v_blank_len = mode->crtc_vblank_end - mode->crtc_vblank_start; + v_sync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; + + h_sync_offset = mode->crtc_hsync_start - mode->crtc_hblank_start; + v_sync_offset = mode->crtc_vsync_start - mode->crtc_vblank_start; + + output_dtd.part1.clock = mode->clock / 10; + output_dtd.part1.h_active = width & 0xff; + output_dtd.part1.h_blank = h_blank_len & 0xff; + output_dtd.part1.h_high = (((width >> 8) & 0xf) << 4) | + ((h_blank_len >> 8) & 0xf); + output_dtd.part1.v_active = height & 0xff; + output_dtd.part1.v_blank = v_blank_len & 0xff; + output_dtd.part1.v_high = (((height >> 8) & 0xf) << 4) | + ((v_blank_len >> 8) & 0xf); + + output_dtd.part2.h_sync_off = h_sync_offset; + output_dtd.part2.h_sync_width = h_sync_len & 0xff; + output_dtd.part2.v_sync_off_width = (v_sync_offset & 0xf) << 4 | + (v_sync_len & 0xf); + output_dtd.part2.sync_off_width_high = + ((h_sync_offset & 0x300) >> 2) | ((h_sync_len & 0x300) >> 4) | + ((v_sync_offset & 0x30) >> 2) | ((v_sync_len & 0x30) >> 4); + + output_dtd.part2.dtd_flags = 0x18; + if (mode->flags & DRM_MODE_FLAG_PHSYNC) + output_dtd.part2.dtd_flags |= 0x2; + if (mode->flags & DRM_MODE_FLAG_PVSYNC) + output_dtd.part2.dtd_flags |= 0x4; + + output_dtd.part2.sdvo_flags = 0; + output_dtd.part2.v_sync_off_high = v_sync_offset & 0xc0; + output_dtd.part2.reserved = 0; + + /* Set the output timing to the screen */ + psb_intel_sdvo_set_target_output(psb_intel_output, + sdvo_priv->active_outputs); + + /* Set the input timing to the screen. Assume always input 0. */ + psb_intel_sdvo_set_target_input(psb_intel_output, true, false); + + psb_intel_sdvo_set_output_timing(psb_intel_output, &output_dtd); + + /* We would like to use i830_sdvo_create_preferred_input_timing() to + * provide the device with a timing it can support, if it supports that + * feature. However, presumably we would need to adjust the CRTC to + * output the preferred timing, and we don't support that currently. + */ + psb_intel_sdvo_set_input_timing(psb_intel_output, &output_dtd); + + switch (psb_intel_sdvo_get_pixel_multiplier(mode)) { + case 1: + psb_intel_sdvo_set_clock_rate_mult(psb_intel_output, + SDVO_CLOCK_RATE_MULT_1X); + break; + case 2: + psb_intel_sdvo_set_clock_rate_mult(psb_intel_output, + SDVO_CLOCK_RATE_MULT_2X); + break; + case 4: + psb_intel_sdvo_set_clock_rate_mult(psb_intel_output, + SDVO_CLOCK_RATE_MULT_4X); + break; + } + + /* Set the SDVO control regs. */ + sdvox = REG_READ(sdvo_priv->output_device); + switch (sdvo_priv->output_device) { + case SDVOB: + sdvox &= SDVOB_PRESERVE_MASK; + break; + case SDVOC: + sdvox &= SDVOC_PRESERVE_MASK; + break; + } + sdvox |= (9 << 19) | SDVO_BORDER_ENABLE; + if (psb_intel_crtc->pipe == 1) + sdvox |= SDVO_PIPE_B_SELECT; + + sdvo_pixel_multiply = psb_intel_sdvo_get_pixel_multiplier(mode); + + psb_intel_sdvo_write_sdvox(psb_intel_output, sdvox); + + psb_intel_sdvo_set_iomap(psb_intel_output); +} + +static void psb_intel_sdvo_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct psb_intel_output *psb_intel_output = + enc_to_psb_intel_output(encoder); + struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv; + u32 temp; + + if (mode != DRM_MODE_DPMS_ON) { + psb_intel_sdvo_set_active_outputs(psb_intel_output, 0); + if (0) + psb_intel_sdvo_set_encoder_power_state( + psb_intel_output, + mode); + + if (mode == DRM_MODE_DPMS_OFF) { + temp = REG_READ(sdvo_priv->output_device); + if ((temp & SDVO_ENABLE) != 0) { + psb_intel_sdvo_write_sdvox(psb_intel_output, + temp & + ~SDVO_ENABLE); + } + } + } else { + bool input1, input2; + int i; + u8 status; + + temp = REG_READ(sdvo_priv->output_device); + if ((temp & SDVO_ENABLE) == 0) + psb_intel_sdvo_write_sdvox(psb_intel_output, + temp | SDVO_ENABLE); + for (i = 0; i < 2; i++) + psb_intel_wait_for_vblank(dev); + + status = + psb_intel_sdvo_get_trained_inputs(psb_intel_output, + &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 + ("First %s output reported failure to sync\n", + SDVO_NAME(sdvo_priv)); + } + + if (0) + psb_intel_sdvo_set_encoder_power_state( + psb_intel_output, + mode); + psb_intel_sdvo_set_active_outputs(psb_intel_output, + sdvo_priv->active_outputs); + } + return; +} + +static void psb_intel_sdvo_save(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv; + /*int o;*/ + + sdvo_priv->save_sdvo_mult = + psb_intel_sdvo_get_clock_rate_mult(psb_intel_output); + psb_intel_sdvo_get_active_outputs(psb_intel_output, + &sdvo_priv->save_active_outputs); + + if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) { + psb_intel_sdvo_set_target_input(psb_intel_output, + true, + false); + psb_intel_sdvo_get_input_timing(psb_intel_output, + &sdvo_priv->save_input_dtd_1); + } + + if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) { + psb_intel_sdvo_set_target_input(psb_intel_output, + false, + true); + psb_intel_sdvo_get_input_timing(psb_intel_output, + &sdvo_priv->save_input_dtd_2); + } + sdvo_priv->save_SDVOX = REG_READ(sdvo_priv->output_device); + + /*TODO: save the in_out_map state*/ +} + +static void psb_intel_sdvo_restore(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv; + /*int o;*/ + int i; + bool input1, input2; + u8 status; + + psb_intel_sdvo_set_active_outputs(psb_intel_output, 0); + + if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) { + psb_intel_sdvo_set_target_input(psb_intel_output, true, false); + psb_intel_sdvo_set_input_timing(psb_intel_output, + &sdvo_priv->save_input_dtd_1); + } + + if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) { + psb_intel_sdvo_set_target_input(psb_intel_output, false, true); + psb_intel_sdvo_set_input_timing(psb_intel_output, + &sdvo_priv->save_input_dtd_2); + } + + psb_intel_sdvo_set_clock_rate_mult(psb_intel_output, + sdvo_priv->save_sdvo_mult); + + REG_WRITE(sdvo_priv->output_device, sdvo_priv->save_SDVOX); + + if (sdvo_priv->save_SDVOX & SDVO_ENABLE) { + for (i = 0; i < 2; i++) + psb_intel_wait_for_vblank(dev); + status = + psb_intel_sdvo_get_trained_inputs(psb_intel_output, + &input1, + &input2); + if (status == SDVO_CMD_STATUS_SUCCESS && !input1) + DRM_DEBUG + ("First %s output reported failure to sync\n", + SDVO_NAME(sdvo_priv)); + } + + psb_intel_sdvo_set_active_outputs(psb_intel_output, + sdvo_priv->save_active_outputs); + + /*TODO: restore in_out_map*/ + psb_intel_sdvo_write_cmd(psb_intel_output, + SDVO_CMD_SET_IN_OUT_MAP, + sdvo_priv->in_out_map, + 4); + + psb_intel_sdvo_read_response(psb_intel_output, NULL, 0); +} + +static int psb_intel_sdvo_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv; + + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + + if (sdvo_priv->pixel_clock_min > mode->clock) + return MODE_CLOCK_LOW; + + if (sdvo_priv->pixel_clock_max < mode->clock) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + +static bool psb_intel_sdvo_get_capabilities( + struct psb_intel_output *psb_intel_output, + struct psb_intel_sdvo_caps *caps) +{ + u8 status; + + psb_intel_sdvo_write_cmd(psb_intel_output, + SDVO_CMD_GET_DEVICE_CAPS, + NULL, + 0); + status = psb_intel_sdvo_read_response(psb_intel_output, + caps, + sizeof(*caps)); + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + + return true; +} + +struct drm_connector *psb_intel_sdvo_find(struct drm_device *dev, int sdvoB) +{ + struct drm_connector *connector = NULL; + struct psb_intel_output *iout = NULL; + struct psb_intel_sdvo_priv *sdvo; + + /* find the sdvo connector */ + list_for_each_entry(connector, &dev->mode_config.connector_list, + head) { + iout = to_psb_intel_output(connector); + + if (iout->type != INTEL_OUTPUT_SDVO) + continue; + + sdvo = iout->dev_priv; + + if (sdvo->output_device == SDVOB && sdvoB) + return connector; + + if (sdvo->output_device == SDVOC && !sdvoB) + return connector; + + } + + return NULL; +} + +int psb_intel_sdvo_supports_hotplug(struct drm_connector *connector) +{ + u8 response[2]; + u8 status; + struct psb_intel_output *psb_intel_output; + + if (!connector) + return 0; + + psb_intel_output = to_psb_intel_output(connector); + + psb_intel_sdvo_write_cmd(psb_intel_output, + SDVO_CMD_GET_HOT_PLUG_SUPPORT, + NULL, + 0); + status = psb_intel_sdvo_read_response(psb_intel_output, + &response, + 2); + + if (response[0] != 0) + return 1; + + return 0; +} + +void psb_intel_sdvo_set_hotplug(struct drm_connector *connector, int on) +{ + u8 response[2]; + u8 status; + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + + psb_intel_sdvo_write_cmd(psb_intel_output, + SDVO_CMD_GET_ACTIVE_HOT_PLUG, + NULL, + 0); + psb_intel_sdvo_read_response(psb_intel_output, &response, 2); + + if (on) { + psb_intel_sdvo_write_cmd(psb_intel_output, + SDVO_CMD_GET_HOT_PLUG_SUPPORT, NULL, + 0); + status = psb_intel_sdvo_read_response(psb_intel_output, + &response, + 2); + + psb_intel_sdvo_write_cmd(psb_intel_output, + SDVO_CMD_SET_ACTIVE_HOT_PLUG, + &response, 2); + } else { + response[0] = 0; + response[1] = 0; + psb_intel_sdvo_write_cmd(psb_intel_output, + SDVO_CMD_SET_ACTIVE_HOT_PLUG, + &response, 2); + } + + psb_intel_sdvo_write_cmd(psb_intel_output, + SDVO_CMD_GET_ACTIVE_HOT_PLUG, + NULL, + 0); + psb_intel_sdvo_read_response(psb_intel_output, &response, 2); +} + +static enum drm_connector_status psb_intel_sdvo_detect(struct drm_connector + *connector, bool force) +{ + u8 response[2]; + u8 status; + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + + psb_intel_sdvo_write_cmd(psb_intel_output, + SDVO_CMD_GET_ATTACHED_DISPLAYS, + NULL, + 0); + status = psb_intel_sdvo_read_response(psb_intel_output, &response, 2); + + DRM_DEBUG("SDVO response %d %d\n", response[0], response[1]); + if ((response[0] != 0) || (response[1] != 0)) + return connector_status_connected; + else + return connector_status_disconnected; +} + +static int psb_intel_sdvo_get_modes(struct drm_connector *connector) +{ + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + + /* set the bus switch and get the modes */ + psb_intel_sdvo_set_control_bus_switch(psb_intel_output, + SDVO_CONTROL_BUS_DDC2); + psb_intel_ddc_get_modes(psb_intel_output); + + if (list_empty(&connector->probed_modes)) + return 0; + return 1; +} + +static void psb_intel_sdvo_destroy(struct drm_connector *connector) +{ + struct psb_intel_output *psb_intel_output = + to_psb_intel_output(connector); + + if (psb_intel_output->i2c_bus) + psb_intel_i2c_destroy(psb_intel_output->i2c_bus); + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + kfree(psb_intel_output); +} + +static const struct drm_encoder_helper_funcs psb_intel_sdvo_helper_funcs = { + .dpms = psb_intel_sdvo_dpms, + .mode_fixup = psb_intel_sdvo_mode_fixup, + .prepare = psb_intel_encoder_prepare, + .mode_set = psb_intel_sdvo_mode_set, + .commit = psb_intel_encoder_commit, +}; + +static const struct drm_connector_funcs psb_intel_sdvo_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .save = psb_intel_sdvo_save, + .restore = psb_intel_sdvo_restore, + .detect = psb_intel_sdvo_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = psb_intel_sdvo_destroy, +}; + +static const struct drm_connector_helper_funcs + psb_intel_sdvo_connector_helper_funcs = { + .get_modes = psb_intel_sdvo_get_modes, + .mode_valid = psb_intel_sdvo_mode_valid, + .best_encoder = psb_intel_best_encoder, +}; + +void psb_intel_sdvo_enc_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs psb_intel_sdvo_enc_funcs = { + .destroy = psb_intel_sdvo_enc_destroy, +}; + + +void psb_intel_sdvo_init(struct drm_device *dev, int output_device) +{ + struct drm_connector *connector; + struct psb_intel_output *psb_intel_output; + struct psb_intel_sdvo_priv *sdvo_priv; + struct psb_intel_i2c_chan *i2cbus = NULL; + int connector_type; + u8 ch[0x40]; + int i; + int encoder_type, output_id; + + psb_intel_output = + kcalloc(sizeof(struct psb_intel_output) + + sizeof(struct psb_intel_sdvo_priv), 1, GFP_KERNEL); + if (!psb_intel_output) + return; + + connector = &psb_intel_output->base; + + drm_connector_init(dev, connector, &psb_intel_sdvo_connector_funcs, + DRM_MODE_CONNECTOR_Unknown); + drm_connector_helper_add(connector, + &psb_intel_sdvo_connector_helper_funcs); + sdvo_priv = (struct psb_intel_sdvo_priv *) (psb_intel_output + 1); + psb_intel_output->type = INTEL_OUTPUT_SDVO; + + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + + /* setup the DDC bus. */ + if (output_device == SDVOB) + i2cbus = + psb_intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOB"); + else + i2cbus = + psb_intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOC"); + + if (!i2cbus) + goto err_connector; + + sdvo_priv->i2c_bus = i2cbus; + + if (output_device == SDVOB) { + output_id = 1; + sdvo_priv->by_input_wiring = SDVOB_IN0; + sdvo_priv->i2c_bus->slave_addr = 0x38; + } else { + output_id = 2; + sdvo_priv->i2c_bus->slave_addr = 0x39; + } + + sdvo_priv->output_device = output_device; + psb_intel_output->i2c_bus = i2cbus; + psb_intel_output->dev_priv = sdvo_priv; + + + /* Read the regs to test if we can talk to the device */ + for (i = 0; i < 0x40; i++) { + if (!psb_intel_sdvo_read_byte(psb_intel_output, i, &ch[i])) { + dev_dbg(dev->dev, "No SDVO device found on SDVO%c\n", + output_device == SDVOB ? 'B' : 'C'); + goto err_i2c; + } + } + + psb_intel_sdvo_get_capabilities(psb_intel_output, &sdvo_priv->caps); + + memset(&sdvo_priv->active_outputs, 0, + sizeof(sdvo_priv->active_outputs)); + + /* TODO, CVBS, SVID, YPRPB & SCART outputs. */ + if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB0) { + sdvo_priv->active_outputs = SDVO_OUTPUT_RGB0; + sdvo_priv->active_device = SDVO_DEVICE_CRT; + connector->display_info.subpixel_order = + SubPixelHorizontalRGB; + encoder_type = DRM_MODE_ENCODER_DAC; + connector_type = DRM_MODE_CONNECTOR_VGA; + } else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB1) { + sdvo_priv->active_outputs = SDVO_OUTPUT_RGB1; + sdvo_priv->active_outputs = SDVO_DEVICE_CRT; + connector->display_info.subpixel_order = + SubPixelHorizontalRGB; + encoder_type = DRM_MODE_ENCODER_DAC; + connector_type = DRM_MODE_CONNECTOR_VGA; + } else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0) { + sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS0; + sdvo_priv->active_device = SDVO_DEVICE_TMDS; + connector->display_info.subpixel_order = + SubPixelHorizontalRGB; + encoder_type = DRM_MODE_ENCODER_TMDS; + connector_type = DRM_MODE_CONNECTOR_DVID; + } else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS1) { + sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS1; + sdvo_priv->active_device = SDVO_DEVICE_TMDS; + connector->display_info.subpixel_order = + SubPixelHorizontalRGB; + encoder_type = DRM_MODE_ENCODER_TMDS; + connector_type = DRM_MODE_CONNECTOR_DVID; + } else { + unsigned char bytes[2]; + + memcpy(bytes, &sdvo_priv->caps.output_flags, 2); + dev_dbg(dev->dev, "%s: No active RGB or TMDS outputs (0x%02x%02x)\n", + SDVO_NAME(sdvo_priv), bytes[0], bytes[1]); + goto err_i2c; + } + + drm_encoder_init(dev, &psb_intel_output->enc, &psb_intel_sdvo_enc_funcs, + encoder_type); + drm_encoder_helper_add(&psb_intel_output->enc, + &psb_intel_sdvo_helper_funcs); + connector->connector_type = connector_type; + + drm_mode_connector_attach_encoder(&psb_intel_output->base, + &psb_intel_output->enc); + drm_sysfs_connector_add(connector); + + /* Set the input timing to the screen. Assume always input 0. */ + psb_intel_sdvo_set_target_input(psb_intel_output, true, false); + + psb_intel_sdvo_get_input_pixel_clock_range(psb_intel_output, + &sdvo_priv->pixel_clock_min, + &sdvo_priv-> + pixel_clock_max); + + + dev_dbg(dev->dev, "%s device VID/DID: %02X:%02X.%02X, " + "clock range %dMHz - %dMHz, " + "input 1: %c, input 2: %c, " + "output 1: %c, output 2: %c\n", + SDVO_NAME(sdvo_priv), + sdvo_priv->caps.vendor_id, sdvo_priv->caps.device_id, + sdvo_priv->caps.device_rev_id, + sdvo_priv->pixel_clock_min / 1000, + sdvo_priv->pixel_clock_max / 1000, + (sdvo_priv->caps.sdvo_inputs_mask & 0x1) ? 'Y' : 'N', + (sdvo_priv->caps.sdvo_inputs_mask & 0x2) ? 'Y' : 'N', + /* check currently supported outputs */ + sdvo_priv->caps.output_flags & + (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_RGB0) ? 'Y' : 'N', + sdvo_priv->caps.output_flags & + (SDVO_OUTPUT_TMDS1 | SDVO_OUTPUT_RGB1) ? 'Y' : 'N'); + + psb_intel_output->ddc_bus = i2cbus; + + return; + +err_i2c: + psb_intel_i2c_destroy(psb_intel_output->i2c_bus); +err_connector: + drm_connector_cleanup(connector); + kfree(psb_intel_output); + + return; +} diff --git a/trunk/drivers/staging/gma500/psb_intel_sdvo_regs.h b/trunk/drivers/staging/gma500/psb_intel_sdvo_regs.h new file mode 100644 index 000000000000..96862ea65aba --- /dev/null +++ b/trunk/drivers/staging/gma500/psb_intel_sdvo_regs.h @@ -0,0 +1,338 @@ +/* + * SDVO command definitions and structures. + * + * Copyright (c) 2008, Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * 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. + * + * Authors: + * Eric Anholt + */ + +#define SDVO_OUTPUT_FIRST (0) +#define SDVO_OUTPUT_TMDS0 (1 << 0) +#define SDVO_OUTPUT_RGB0 (1 << 1) +#define SDVO_OUTPUT_CVBS0 (1 << 2) +#define SDVO_OUTPUT_SVID0 (1 << 3) +#define SDVO_OUTPUT_YPRPB0 (1 << 4) +#define SDVO_OUTPUT_SCART0 (1 << 5) +#define SDVO_OUTPUT_LVDS0 (1 << 6) +#define SDVO_OUTPUT_TMDS1 (1 << 8) +#define SDVO_OUTPUT_RGB1 (1 << 9) +#define SDVO_OUTPUT_CVBS1 (1 << 10) +#define SDVO_OUTPUT_SVID1 (1 << 11) +#define SDVO_OUTPUT_YPRPB1 (1 << 12) +#define SDVO_OUTPUT_SCART1 (1 << 13) +#define SDVO_OUTPUT_LVDS1 (1 << 14) +#define SDVO_OUTPUT_LAST (14) + +struct psb_intel_sdvo_caps { + u8 vendor_id; + u8 device_id; + u8 device_rev_id; + u8 sdvo_version_major; + u8 sdvo_version_minor; + unsigned int sdvo_inputs_mask:2; + unsigned int smooth_scaling:1; + unsigned int sharp_scaling:1; + unsigned int up_scaling:1; + unsigned int down_scaling:1; + unsigned int stall_support:1; + unsigned int pad:1; + u16 output_flags; +} __packed; + +/** This matches the EDID DTD structure, more or less */ +struct psb_intel_sdvo_dtd { + struct { + u16 clock; /**< pixel clock, in 10kHz units */ + u8 h_active; /**< lower 8 bits (pixels) */ + u8 h_blank; /**< lower 8 bits (pixels) */ + u8 h_high; /**< upper 4 bits each h_active, h_blank */ + u8 v_active; /**< lower 8 bits (lines) */ + u8 v_blank; /**< lower 8 bits (lines) */ + u8 v_high; /**< upper 4 bits each v_active, v_blank */ + } part1; + + struct { + u8 h_sync_off; + /**< lower 8 bits, from hblank start */ + u8 h_sync_width;/**< lower 8 bits (pixels) */ + /** lower 4 bits each vsync offset, vsync width */ + u8 v_sync_off_width; + /** + * 2 high bits of hsync offset, 2 high bits of hsync width, + * bits 4-5 of vsync offset, and 2 high bits of vsync width. + */ + u8 sync_off_width_high; + u8 dtd_flags; + u8 sdvo_flags; + /** bits 6-7 of vsync offset at bits 6-7 */ + u8 v_sync_off_high; + u8 reserved; + } part2; +} __packed; + +struct psb_intel_sdvo_pixel_clock_range { + u16 min; /**< pixel clock, in 10kHz units */ + u16 max; /**< pixel clock, in 10kHz units */ +} __packed; + +struct psb_intel_sdvo_preferred_input_timing_args { + u16 clock; + u16 width; + u16 height; +} __packed; + +/* I2C registers for SDVO */ +#define SDVO_I2C_ARG_0 0x07 +#define SDVO_I2C_ARG_1 0x06 +#define SDVO_I2C_ARG_2 0x05 +#define SDVO_I2C_ARG_3 0x04 +#define SDVO_I2C_ARG_4 0x03 +#define SDVO_I2C_ARG_5 0x02 +#define SDVO_I2C_ARG_6 0x01 +#define SDVO_I2C_ARG_7 0x00 +#define SDVO_I2C_OPCODE 0x08 +#define SDVO_I2C_CMD_STATUS 0x09 +#define SDVO_I2C_RETURN_0 0x0a +#define SDVO_I2C_RETURN_1 0x0b +#define SDVO_I2C_RETURN_2 0x0c +#define SDVO_I2C_RETURN_3 0x0d +#define SDVO_I2C_RETURN_4 0x0e +#define SDVO_I2C_RETURN_5 0x0f +#define SDVO_I2C_RETURN_6 0x10 +#define SDVO_I2C_RETURN_7 0x11 +#define SDVO_I2C_VENDOR_BEGIN 0x20 + +/* Status results */ +#define SDVO_CMD_STATUS_POWER_ON 0x0 +#define SDVO_CMD_STATUS_SUCCESS 0x1 +#define SDVO_CMD_STATUS_NOTSUPP 0x2 +#define SDVO_CMD_STATUS_INVALID_ARG 0x3 +#define SDVO_CMD_STATUS_PENDING 0x4 +#define SDVO_CMD_STATUS_TARGET_NOT_SPECIFIED 0x5 +#define SDVO_CMD_STATUS_SCALING_NOT_SUPP 0x6 + +/* SDVO commands, argument/result registers */ + +#define SDVO_CMD_RESET 0x01 + +/** Returns a struct psb_intel_sdvo_caps */ +#define SDVO_CMD_GET_DEVICE_CAPS 0x02 + +#define SDVO_CMD_GET_FIRMWARE_REV 0x86 +# define SDVO_DEVICE_FIRMWARE_MINOR SDVO_I2C_RETURN_0 +# define SDVO_DEVICE_FIRMWARE_MAJOR SDVO_I2C_RETURN_1 +# define SDVO_DEVICE_FIRMWARE_PATCH SDVO_I2C_RETURN_2 + +/** + * Reports which inputs are trained (managed to sync). + * + * Devices must have trained within 2 vsyncs of a mode change. + */ +#define SDVO_CMD_GET_TRAINED_INPUTS 0x03 +struct psb_intel_sdvo_get_trained_inputs_response { + unsigned int input0_trained:1; + unsigned int input1_trained:1; + unsigned int pad:6; +} __packed; + +/** Returns a struct psb_intel_sdvo_output_flags of active outputs. */ +#define SDVO_CMD_GET_ACTIVE_OUTPUTS 0x04 + +/** + * Sets the current set of active outputs. + * + * Takes a struct psb_intel_sdvo_output_flags. + * Must be preceded by a SET_IN_OUT_MAP + * on multi-output devices. + */ +#define SDVO_CMD_SET_ACTIVE_OUTPUTS 0x05 + +/** + * Returns the current mapping of SDVO inputs to outputs on the device. + * + * Returns two struct psb_intel_sdvo_output_flags structures. + */ +#define SDVO_CMD_GET_IN_OUT_MAP 0x06 + +/** + * Sets the current mapping of SDVO inputs to outputs on the device. + * + * Takes two struct i380_sdvo_output_flags structures. + */ +#define SDVO_CMD_SET_IN_OUT_MAP 0x07 + +/** + * Returns a struct psb_intel_sdvo_output_flags of attached displays. + */ +#define SDVO_CMD_GET_ATTACHED_DISPLAYS 0x0b + +/** + * Returns a struct psb_intel_sdvo_ouptut_flags of displays supporting hot plugging. + */ +#define SDVO_CMD_GET_HOT_PLUG_SUPPORT 0x0c + +/** + * Takes a struct psb_intel_sdvo_output_flags. + */ +#define SDVO_CMD_SET_ACTIVE_HOT_PLUG 0x0d + +/** + * Returns a struct psb_intel_sdvo_output_flags of displays with hot plug + * interrupts enabled. + */ +#define SDVO_CMD_GET_ACTIVE_HOT_PLUG 0x0e + +#define SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE 0x0f +struct psb_intel_sdvo_get_interrupt_event_source_response { + u16 interrupt_status; + unsigned int ambient_light_interrupt:1; + unsigned int pad:7; +} __packed; + +/** + * Selects which input is affected by future input commands. + * + * Commands affected include SET_INPUT_TIMINGS_PART[12], + * GET_INPUT_TIMINGS_PART[12], GET_PREFERRED_INPUT_TIMINGS_PART[12], + * GET_INPUT_PIXEL_CLOCK_RANGE, and CREATE_PREFERRED_INPUT_TIMINGS. + */ +#define SDVO_CMD_SET_TARGET_INPUT 0x10 +struct psb_intel_sdvo_set_target_input_args { + unsigned int target_1:1; + unsigned int pad:7; +} __packed; + +/** + * Takes a struct psb_intel_sdvo_output_flags of which outputs are targeted by + * future output commands. + * + * Affected commands inclue SET_OUTPUT_TIMINGS_PART[12], + * GET_OUTPUT_TIMINGS_PART[12], and GET_OUTPUT_PIXEL_CLOCK_RANGE. + */ +#define SDVO_CMD_SET_TARGET_OUTPUT 0x11 + +#define SDVO_CMD_GET_INPUT_TIMINGS_PART1 0x12 +#define SDVO_CMD_GET_INPUT_TIMINGS_PART2 0x13 +#define SDVO_CMD_SET_INPUT_TIMINGS_PART1 0x14 +#define SDVO_CMD_SET_INPUT_TIMINGS_PART2 0x15 +#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART1 0x16 +#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART2 0x17 +#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART1 0x18 +#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART2 0x19 +/* Part 1 */ +# define SDVO_DTD_CLOCK_LOW SDVO_I2C_ARG_0 +# define SDVO_DTD_CLOCK_HIGH SDVO_I2C_ARG_1 +# define SDVO_DTD_H_ACTIVE SDVO_I2C_ARG_2 +# define SDVO_DTD_H_BLANK SDVO_I2C_ARG_3 +# define SDVO_DTD_H_HIGH SDVO_I2C_ARG_4 +# define SDVO_DTD_V_ACTIVE SDVO_I2C_ARG_5 +# define SDVO_DTD_V_BLANK SDVO_I2C_ARG_6 +# define SDVO_DTD_V_HIGH SDVO_I2C_ARG_7 +/* Part 2 */ +# define SDVO_DTD_HSYNC_OFF SDVO_I2C_ARG_0 +# define SDVO_DTD_HSYNC_WIDTH SDVO_I2C_ARG_1 +# define SDVO_DTD_VSYNC_OFF_WIDTH SDVO_I2C_ARG_2 +# define SDVO_DTD_SYNC_OFF_WIDTH_HIGH SDVO_I2C_ARG_3 +# define SDVO_DTD_DTD_FLAGS SDVO_I2C_ARG_4 +# define SDVO_DTD_DTD_FLAG_INTERLACED (1 << 7) +# define SDVO_DTD_DTD_FLAG_STEREO_MASK (3 << 5) +# define SDVO_DTD_DTD_FLAG_INPUT_MASK (3 << 3) +# define SDVO_DTD_DTD_FLAG_SYNC_MASK (3 << 1) +# define SDVO_DTD_SDVO_FLAS SDVO_I2C_ARG_5 +# define SDVO_DTD_SDVO_FLAG_STALL (1 << 7) +# define SDVO_DTD_SDVO_FLAG_CENTERED (0 << 6) +# define SDVO_DTD_SDVO_FLAG_UPPER_LEFT (1 << 6) +# define SDVO_DTD_SDVO_FLAG_SCALING_MASK (3 << 4) +# define SDVO_DTD_SDVO_FLAG_SCALING_NONE (0 << 4) +# define SDVO_DTD_SDVO_FLAG_SCALING_SHARP (1 << 4) +# define SDVO_DTD_SDVO_FLAG_SCALING_SMOOTH (2 << 4) +# define SDVO_DTD_VSYNC_OFF_HIGH SDVO_I2C_ARG_6 + +/** + * Generates a DTD based on the given width, height, and flags. + * + * This will be supported by any device supporting scaling or interlaced + * modes. + */ +#define SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING 0x1a +# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_LOW SDVO_I2C_ARG_0 +# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_HIGH SDVO_I2C_ARG_1 +# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_LOW SDVO_I2C_ARG_2 +# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_HIGH SDVO_I2C_ARG_3 +# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_LOW SDVO_I2C_ARG_4 +# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_HIGH SDVO_I2C_ARG_5 +# define SDVO_PREFERRED_INPUT_TIMING_FLAGS SDVO_I2C_ARG_6 +# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_INTERLACED (1 << 0) +# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_SCALED (1 << 1) + +#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1 0x1b +#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2 0x1c + +/** Returns a struct psb_intel_sdvo_pixel_clock_range */ +#define SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE 0x1d +/** Returns a struct psb_intel_sdvo_pixel_clock_range */ +#define SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE 0x1e + +/** Returns a byte bitfield containing SDVO_CLOCK_RATE_MULT_* flags */ +#define SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS 0x1f + +/** Returns a byte containing a SDVO_CLOCK_RATE_MULT_* flag */ +#define SDVO_CMD_GET_CLOCK_RATE_MULT 0x20 +/** Takes a byte containing a SDVO_CLOCK_RATE_MULT_* flag */ +#define SDVO_CMD_SET_CLOCK_RATE_MULT 0x21 +# define SDVO_CLOCK_RATE_MULT_1X (1 << 0) +# define SDVO_CLOCK_RATE_MULT_2X (1 << 1) +# define SDVO_CLOCK_RATE_MULT_4X (1 << 3) + +#define SDVO_CMD_GET_SUPPORTED_TV_FORMATS 0x27 + +#define SDVO_CMD_GET_TV_FORMAT 0x28 + +#define SDVO_CMD_SET_TV_FORMAT 0x29 + +#define SDVO_CMD_GET_SUPPORTED_POWER_STATES 0x2a +#define SDVO_CMD_GET_ENCODER_POWER_STATE 0x2b +#define SDVO_CMD_SET_ENCODER_POWER_STATE 0x2c +# define SDVO_ENCODER_STATE_ON (1 << 0) +# define SDVO_ENCODER_STATE_STANDBY (1 << 1) +# define SDVO_ENCODER_STATE_SUSPEND (1 << 2) +# define SDVO_ENCODER_STATE_OFF (1 << 3) + +#define SDVO_CMD_SET_TV_RESOLUTION_SUPPORT 0x93 + +#define SDVO_CMD_SET_CONTROL_BUS_SWITCH 0x7a +# define SDVO_CONTROL_BUS_PROM 0x0 +# define SDVO_CONTROL_BUS_DDC1 0x1 +# define SDVO_CONTROL_BUS_DDC2 0x2 +# define SDVO_CONTROL_BUS_DDC3 0x3 + +/* SDVO Bus & SDVO Inputs wiring details*/ +/* Bit 0: Is SDVOB connected to In0 (1 = yes, 0 = no*/ +/* Bit 1: Is SDVOB connected to In1 (1 = yes, 0 = no*/ +/* Bit 2: Is SDVOC connected to In0 (1 = yes, 0 = no*/ +/* Bit 3: Is SDVOC connected to In1 (1 = yes, 0 = no*/ +#define SDVOB_IN0 0x01 +#define SDVOB_IN1 0x02 +#define SDVOC_IN0 0x04 +#define SDVOC_IN1 0x08 + +#define SDVO_DEVICE_NONE 0x00 +#define SDVO_DEVICE_CRT 0x01 +#define SDVO_DEVICE_TV 0x02 +#define SDVO_DEVICE_LVDS 0x04 +#define SDVO_DEVICE_TMDS 0x08 + diff --git a/trunk/drivers/staging/gma500/psb_irq.c b/trunk/drivers/staging/gma500/psb_irq.c new file mode 100644 index 000000000000..36dd63044b06 --- /dev/null +++ b/trunk/drivers/staging/gma500/psb_irq.c @@ -0,0 +1,627 @@ +/************************************************************************** + * Copyright (c) 2007, 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. + * + * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to + * develop this driver. + * + **************************************************************************/ +/* + */ + +#include +#include "psb_drv.h" +#include "psb_reg.h" +#include "psb_intel_reg.h" +#include "power.h" +#include "mdfld_output.h" + +/* + * inline functions + */ + +static inline u32 +psb_pipestat(int pipe) +{ + if (pipe == 0) + return PIPEASTAT; + if (pipe == 1) + return PIPEBSTAT; + if (pipe == 2) + return PIPECSTAT; + BUG(); +} + +static inline u32 +mid_pipe_event(int pipe) +{ + if (pipe == 0) + return _PSB_PIPEA_EVENT_FLAG; + if (pipe == 1) + return _MDFLD_PIPEB_EVENT_FLAG; + if (pipe == 2) + return _MDFLD_PIPEC_EVENT_FLAG; + BUG(); +} + +static inline u32 +mid_pipe_vsync(int pipe) +{ + if (pipe == 0) + return _PSB_VSYNC_PIPEA_FLAG; + if (pipe == 1) + return _PSB_VSYNC_PIPEB_FLAG; + if (pipe == 2) + return _MDFLD_PIPEC_VBLANK_FLAG; + BUG(); +} + +static inline u32 +mid_pipeconf(int pipe) +{ + if (pipe == 0) + return PIPEACONF; + if (pipe == 1) + return PIPEBCONF; + if (pipe == 2) + return PIPECCONF; + BUG(); +} + +void +psb_enable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask) +{ + if ((dev_priv->pipestat[pipe] & mask) != mask) { + u32 reg = psb_pipestat(pipe); + dev_priv->pipestat[pipe] |= mask; + /* Enable the interrupt, clear any pending status */ + if (gma_power_begin(dev_priv->dev, false)) { + u32 writeVal = PSB_RVDC32(reg); + writeVal |= (mask | (mask >> 16)); + PSB_WVDC32(writeVal, reg); + (void) PSB_RVDC32(reg); + gma_power_end(dev_priv->dev); + } + } +} + +void +psb_disable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask) +{ + if ((dev_priv->pipestat[pipe] & mask) != 0) { + u32 reg = psb_pipestat(pipe); + dev_priv->pipestat[pipe] &= ~mask; + if (gma_power_begin(dev_priv->dev, false)) { + u32 writeVal = PSB_RVDC32(reg); + writeVal &= ~mask; + PSB_WVDC32(writeVal, reg); + (void) PSB_RVDC32(reg); + gma_power_end(dev_priv->dev); + } + } +} + +void mid_enable_pipe_event(struct drm_psb_private *dev_priv, int pipe) +{ + if (gma_power_begin(dev_priv->dev, false)) { + u32 pipe_event = mid_pipe_event(pipe); + dev_priv->vdc_irq_mask |= pipe_event; + PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R); + PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); + gma_power_end(dev_priv->dev); + } +} + +void mid_disable_pipe_event(struct drm_psb_private *dev_priv, int pipe) +{ + if (dev_priv->pipestat[pipe] == 0) { + if (gma_power_begin(dev_priv->dev, false)) { + u32 pipe_event = mid_pipe_event(pipe); + dev_priv->vdc_irq_mask &= ~pipe_event; + PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R); + PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); + gma_power_end(dev_priv->dev); + } + } +} + +/** + * Display controller interrupt handler for pipe event. + * + */ +static void mid_pipe_event_handler(struct drm_device *dev, int pipe) +{ + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + + uint32_t pipe_stat_val = 0; + uint32_t pipe_stat_reg = psb_pipestat(pipe); + uint32_t pipe_enable = dev_priv->pipestat[pipe]; + uint32_t pipe_status = dev_priv->pipestat[pipe] >> 16; + uint32_t pipe_clear; + uint32_t i = 0; + + spin_lock(&dev_priv->irqmask_lock); + + pipe_stat_val = PSB_RVDC32(pipe_stat_reg); + pipe_stat_val &= pipe_enable | pipe_status; + pipe_stat_val &= pipe_stat_val >> 16; + + spin_unlock(&dev_priv->irqmask_lock); + + /* Clear the 2nd level interrupt status bits + * Sometimes the bits are very sticky so we repeat until they unstick */ + for (i = 0; i < 0xffff; i++) { + PSB_WVDC32(PSB_RVDC32(pipe_stat_reg), pipe_stat_reg); + pipe_clear = PSB_RVDC32(pipe_stat_reg) & pipe_status; + + if (pipe_clear == 0) + break; + } + + if (pipe_clear) + dev_err(dev->dev, + "%s, can't clear status bits for pipe %d, its value = 0x%x.\n", + __func__, pipe, PSB_RVDC32(pipe_stat_reg)); + + if (pipe_stat_val & PIPE_VBLANK_STATUS) + drm_handle_vblank(dev, pipe); + + if (pipe_stat_val & PIPE_TE_STATUS) + drm_handle_vblank(dev, pipe); +} + +/* + * Display controller interrupt handler. + */ +static void psb_vdc_interrupt(struct drm_device *dev, uint32_t vdc_stat) +{ + if (vdc_stat & _PSB_VSYNC_PIPEA_FLAG) + mid_pipe_event_handler(dev, 0); + + if (vdc_stat & _PSB_VSYNC_PIPEB_FLAG) + mid_pipe_event_handler(dev, 1); +} + +irqreturn_t psb_irq_handler(DRM_IRQ_ARGS) +{ + struct drm_device *dev = (struct drm_device *) arg; + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + + uint32_t vdc_stat, dsp_int = 0, sgx_int = 0; + int handled = 0; + + spin_lock(&dev_priv->irqmask_lock); + + vdc_stat = PSB_RVDC32(PSB_INT_IDENTITY_R); + + if (vdc_stat & _PSB_PIPE_EVENT_FLAG) + dsp_int = 1; + + /* FIXME: Handle Medfield + if (vdc_stat & _MDFLD_DISP_ALL_IRQ_FLAG) + dsp_int = 1; + */ + + if (vdc_stat & _PSB_IRQ_SGX_FLAG) + sgx_int = 1; + + vdc_stat &= dev_priv->vdc_irq_mask; + spin_unlock(&dev_priv->irqmask_lock); + + if (dsp_int && gma_power_is_on(dev)) { + psb_vdc_interrupt(dev, vdc_stat); + handled = 1; + } + + if (sgx_int) { + /* Not expected - we have it masked, shut it up */ + u32 s, s2; + s = PSB_RSGX32(PSB_CR_EVENT_STATUS); + s2 = PSB_RSGX32(PSB_CR_EVENT_STATUS2); + PSB_WSGX32(s, PSB_CR_EVENT_HOST_CLEAR); + PSB_WSGX32(s2, PSB_CR_EVENT_HOST_CLEAR2); + /* if s & _PSB_CE_TWOD_COMPLETE we have 2D done but + we may as well poll even if we add that ! */ + handled = 1; + } + + PSB_WVDC32(vdc_stat, PSB_INT_IDENTITY_R); + (void) PSB_RVDC32(PSB_INT_IDENTITY_R); + DRM_READMEMORYBARRIER(); + + if (!handled) + return IRQ_NONE; + + return IRQ_HANDLED; +} + +void psb_irq_preinstall(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + unsigned long irqflags; + + spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); + + if (gma_power_is_on(dev)) + PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM); + if (dev->vblank_enabled[0]) + dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEA_FLAG; + if (dev->vblank_enabled[1]) + dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEB_FLAG; + + /* FIXME: Handle Medfield irq mask + if (dev->vblank_enabled[1]) + dev_priv->vdc_irq_mask |= _MDFLD_PIPEB_EVENT_FLAG; + if (dev->vblank_enabled[2]) + dev_priv->vdc_irq_mask |= _MDFLD_PIPEC_EVENT_FLAG; + */ + + /* This register is safe even if display island is off */ + PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R); + spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); +} + +int psb_irq_postinstall(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + unsigned long irqflags; + + spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); + + /* This register is safe even if display island is off */ + PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); + PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM); + + if (dev->vblank_enabled[0]) + psb_enable_pipestat(dev_priv, 0, PIPE_VBLANK_INTERRUPT_ENABLE); + else + psb_disable_pipestat(dev_priv, 0, PIPE_VBLANK_INTERRUPT_ENABLE); + + if (dev->vblank_enabled[1]) + psb_enable_pipestat(dev_priv, 1, PIPE_VBLANK_INTERRUPT_ENABLE); + else + psb_disable_pipestat(dev_priv, 1, PIPE_VBLANK_INTERRUPT_ENABLE); + + if (dev->vblank_enabled[2]) + psb_enable_pipestat(dev_priv, 2, PIPE_VBLANK_INTERRUPT_ENABLE); + else + psb_disable_pipestat(dev_priv, 2, PIPE_VBLANK_INTERRUPT_ENABLE); + + spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); + return 0; +} + +void psb_irq_uninstall(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + unsigned long irqflags; + + spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); + + PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM); + + if (dev->vblank_enabled[0]) + psb_disable_pipestat(dev_priv, 0, PIPE_VBLANK_INTERRUPT_ENABLE); + + if (dev->vblank_enabled[1]) + psb_disable_pipestat(dev_priv, 1, PIPE_VBLANK_INTERRUPT_ENABLE); + + if (dev->vblank_enabled[2]) + psb_disable_pipestat(dev_priv, 2, PIPE_VBLANK_INTERRUPT_ENABLE); + + dev_priv->vdc_irq_mask &= _PSB_IRQ_SGX_FLAG | + _PSB_IRQ_MSVDX_FLAG | + _LNC_IRQ_TOPAZ_FLAG; + + /* These two registers are safe even if display island is off */ + PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R); + PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); + + wmb(); + + /* This register is safe even if display island is off */ + PSB_WVDC32(PSB_RVDC32(PSB_INT_IDENTITY_R), PSB_INT_IDENTITY_R); + spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); +} + +void psb_irq_turn_on_dpst(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + u32 hist_reg; + u32 pwm_reg; + + if (gma_power_begin(dev, false)) { + PSB_WVDC32(1 << 31, HISTOGRAM_LOGIC_CONTROL); + hist_reg = PSB_RVDC32(HISTOGRAM_LOGIC_CONTROL); + PSB_WVDC32(1 << 31, HISTOGRAM_INT_CONTROL); + hist_reg = PSB_RVDC32(HISTOGRAM_INT_CONTROL); + + PSB_WVDC32(0x80010100, PWM_CONTROL_LOGIC); + pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC); + PSB_WVDC32(pwm_reg | PWM_PHASEIN_ENABLE + | PWM_PHASEIN_INT_ENABLE, + PWM_CONTROL_LOGIC); + pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC); + + psb_enable_pipestat(dev_priv, 0, PIPE_DPST_EVENT_ENABLE); + + hist_reg = PSB_RVDC32(HISTOGRAM_INT_CONTROL); + PSB_WVDC32(hist_reg | HISTOGRAM_INT_CTRL_CLEAR, + HISTOGRAM_INT_CONTROL); + pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC); + PSB_WVDC32(pwm_reg | 0x80010100 | PWM_PHASEIN_ENABLE, + PWM_CONTROL_LOGIC); + + gma_power_end(dev); + } +} + +int psb_irq_enable_dpst(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + unsigned long irqflags; + + spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); + + /* enable DPST */ + mid_enable_pipe_event(dev_priv, 0); + psb_irq_turn_on_dpst(dev); + + spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); + return 0; +} + +void psb_irq_turn_off_dpst(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + u32 hist_reg; + u32 pwm_reg; + + if (gma_power_begin(dev, false)) { + PSB_WVDC32(0x00000000, HISTOGRAM_INT_CONTROL); + hist_reg = PSB_RVDC32(HISTOGRAM_INT_CONTROL); + + psb_disable_pipestat(dev_priv, 0, PIPE_DPST_EVENT_ENABLE); + + pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC); + PSB_WVDC32(pwm_reg & !(PWM_PHASEIN_INT_ENABLE), + PWM_CONTROL_LOGIC); + pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC); + + gma_power_end(dev); + } +} + +int psb_irq_disable_dpst(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + unsigned long irqflags; + + spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); + + mid_disable_pipe_event(dev_priv, 0); + psb_irq_turn_off_dpst(dev); + + spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); + + return 0; +} + +#ifdef PSB_FIXME +static int psb_vblank_do_wait(struct drm_device *dev, + unsigned int *sequence, atomic_t *counter) +{ + unsigned int cur_vblank; + int ret = 0; + DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, + (((cur_vblank = atomic_read(counter)) + - *sequence) <= (1 << 23))); + *sequence = cur_vblank; + + return ret; +} +#endif + +/* + * It is used to enable VBLANK interrupt + */ +int psb_enable_vblank(struct drm_device *dev, int pipe) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + unsigned long irqflags; + uint32_t reg_val = 0; + uint32_t pipeconf_reg = mid_pipeconf(pipe); + +#if defined(CONFIG_DRM_PSB_MFLD) + /* Medfield is different - we should perhaps extract out vblank + and blacklight etc ops */ + if (IS_MFLD(dev) && !mdfld_panel_dpi(dev)) + return mdfld_enable_te(dev, pipe); +#endif + if (gma_power_begin(dev, false)) { + reg_val = REG_READ(pipeconf_reg); + gma_power_end(dev); + } + + if (!(reg_val & PIPEACONF_ENABLE)) + return -EINVAL; + + spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); + + if (pipe == 0) + dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEA_FLAG; + else if (pipe == 1) + dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEB_FLAG; + + PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R); + PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); + psb_enable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_ENABLE); + + spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); + + return 0; +} + +/* + * It is used to disable VBLANK interrupt + */ +void psb_disable_vblank(struct drm_device *dev, int pipe) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + unsigned long irqflags; + +#if defined(CONFIG_DRM_PSB_MFLD) + if (IS_MFLD(dev) && !mdfld_panel_dpi(dev)) + mdfld_disable_te(dev, pipe); +#endif + spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); + + if (pipe == 0) + dev_priv->vdc_irq_mask &= ~_PSB_VSYNC_PIPEA_FLAG; + else if (pipe == 1) + dev_priv->vdc_irq_mask &= ~_PSB_VSYNC_PIPEB_FLAG; + + PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R); + PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); + psb_disable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_ENABLE); + + spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); +} + +/** + * mdfld_enable_te - enable TE events + * @dev: our DRM device + * @pipe: which pipe to work on + * + * Enable TE events on a Medfield display pipe. Medfield specific. + */ +int mdfld_enable_te(struct drm_device *dev, int pipe) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + unsigned long flags; + uint32_t reg_val = 0; + uint32_t pipeconf_reg = mid_pipeconf(pipe); + + if (gma_power_begin(dev, false)) { + reg_val = REG_READ(pipeconf_reg); + gma_power_end(dev); + } + + if (!(reg_val & PIPEACONF_ENABLE)) + return -EINVAL; + + spin_lock_irqsave(&dev_priv->irqmask_lock, flags); + + mid_enable_pipe_event(dev_priv, pipe); + psb_enable_pipestat(dev_priv, pipe, PIPE_TE_ENABLE); + + spin_unlock_irqrestore(&dev_priv->irqmask_lock, flags); + + return 0; +} + +/** + * mdfld_disable_te - disable TE events + * @dev: our DRM device + * @pipe: which pipe to work on + * + * Disable TE events on a Medfield display pipe. Medfield specific. + */ +void mdfld_disable_te(struct drm_device *dev, int pipe) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + unsigned long flags; + + spin_lock_irqsave(&dev_priv->irqmask_lock, flags); + + mid_disable_pipe_event(dev_priv, pipe); + psb_disable_pipestat(dev_priv, pipe, PIPE_TE_ENABLE); + + spin_unlock_irqrestore(&dev_priv->irqmask_lock, flags); +} + +/* Called from drm generic code, passed a 'crtc', which + * we use as a pipe index + */ +u32 psb_get_vblank_counter(struct drm_device *dev, int pipe) +{ + uint32_t high_frame = PIPEAFRAMEHIGH; + uint32_t low_frame = PIPEAFRAMEPIXEL; + uint32_t pipeconf_reg = PIPEACONF; + uint32_t reg_val = 0; + uint32_t high1 = 0, high2 = 0, low = 0, count = 0; + + switch (pipe) { + case 0: + break; + case 1: + high_frame = PIPEBFRAMEHIGH; + low_frame = PIPEBFRAMEPIXEL; + pipeconf_reg = PIPEBCONF; + break; + case 2: + high_frame = PIPECFRAMEHIGH; + low_frame = PIPECFRAMEPIXEL; + pipeconf_reg = PIPECCONF; + break; + default: + dev_err(dev->dev, "%s, invalid pipe.\n", __func__); + return 0; + } + + if (!gma_power_begin(dev, false)) + return 0; + + reg_val = REG_READ(pipeconf_reg); + + if (!(reg_val & PIPEACONF_ENABLE)) { + dev_err(dev->dev, "trying to get vblank count for disabled pipe %d\n", + pipe); + goto psb_get_vblank_counter_exit; + } + + /* + * High & low register fields aren't synchronized, so make sure + * we get a low value that's stable across two reads of the high + * register. + */ + do { + high1 = ((REG_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >> + PIPE_FRAME_HIGH_SHIFT); + low = ((REG_READ(low_frame) & PIPE_FRAME_LOW_MASK) >> + PIPE_FRAME_LOW_SHIFT); + high2 = ((REG_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >> + PIPE_FRAME_HIGH_SHIFT); + } while (high1 != high2); + + count = (high1 << 8) | low; + +psb_get_vblank_counter_exit: + + gma_power_end(dev); + + return count; +} + diff --git a/trunk/drivers/staging/gma500/psb_irq.h b/trunk/drivers/staging/gma500/psb_irq.h new file mode 100644 index 000000000000..216fda38b57d --- /dev/null +++ b/trunk/drivers/staging/gma500/psb_irq.h @@ -0,0 +1,45 @@ +/************************************************************************** + * Copyright (c) 2009-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. + * + * Authors: + * Benjamin Defnet + * Rajesh Poornachandran + * + **************************************************************************/ + +#ifndef _SYSIRQ_H_ +#define _SYSIRQ_H_ + +#include + +bool sysirq_init(struct drm_device *dev); +void sysirq_uninit(struct drm_device *dev); + +void psb_irq_preinstall(struct drm_device *dev); +int psb_irq_postinstall(struct drm_device *dev); +void psb_irq_uninstall(struct drm_device *dev); +irqreturn_t psb_irq_handler(DRM_IRQ_ARGS); + +int psb_irq_enable_dpst(struct drm_device *dev); +int psb_irq_disable_dpst(struct drm_device *dev); +void psb_irq_turn_on_dpst(struct drm_device *dev); +void psb_irq_turn_off_dpst(struct drm_device *dev); +int psb_enable_vblank(struct drm_device *dev, int pipe); +void psb_disable_vblank(struct drm_device *dev, int pipe); +u32 psb_get_vblank_counter(struct drm_device *dev, int pipe); + +#endif /* _SYSIRQ_H_ */ diff --git a/trunk/drivers/staging/gma500/psb_lid.c b/trunk/drivers/staging/gma500/psb_lid.c new file mode 100644 index 000000000000..b867aabe6bf3 --- /dev/null +++ b/trunk/drivers/staging/gma500/psb_lid.c @@ -0,0 +1,88 @@ +/************************************************************************** + * Copyright (c) 2007, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * 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. + * + * Authors: Thomas Hellstrom + **************************************************************************/ + +#include +#include "psb_drv.h" +#include "psb_reg.h" +#include "psb_intel_reg.h" +#include + +static void psb_lid_timer_func(unsigned long data) +{ + struct drm_psb_private * dev_priv = (struct drm_psb_private *)data; + struct drm_device *dev = (struct drm_device *)dev_priv->dev; + struct timer_list *lid_timer = &dev_priv->lid_timer; + unsigned long irq_flags; + u32 *lid_state = dev_priv->lid_state; + u32 pp_status; + + if (readl(lid_state) == dev_priv->lid_last_state) + goto lid_timer_schedule; + + if ((readl(lid_state)) & 0x01) { + /*lid state is open*/ + REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) | POWER_TARGET_ON); + do { + pp_status = REG_READ(PP_STATUS); + } while ((pp_status & PP_ON) == 0); + + /*FIXME: should be backlight level before*/ + psb_intel_lvds_set_brightness(dev, 100); + } else { + psb_intel_lvds_set_brightness(dev, 0); + + REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) & ~POWER_TARGET_ON); + do { + pp_status = REG_READ(PP_STATUS); + } while ((pp_status & PP_ON) == 0); + } + dev_priv->lid_last_state = readl(lid_state); + +lid_timer_schedule: + spin_lock_irqsave(&dev_priv->lid_lock, irq_flags); + if (!timer_pending(lid_timer)) { + lid_timer->expires = jiffies + PSB_LID_DELAY; + add_timer(lid_timer); + } + spin_unlock_irqrestore(&dev_priv->lid_lock, irq_flags); +} + +void psb_lid_timer_init(struct drm_psb_private *dev_priv) +{ + struct timer_list *lid_timer = &dev_priv->lid_timer; + unsigned long irq_flags; + + spin_lock_init(&dev_priv->lid_lock); + spin_lock_irqsave(&dev_priv->lid_lock, irq_flags); + + init_timer(lid_timer); + + lid_timer->data = (unsigned long)dev_priv; + lid_timer->function = psb_lid_timer_func; + lid_timer->expires = jiffies + PSB_LID_DELAY; + + add_timer(lid_timer); + spin_unlock_irqrestore(&dev_priv->lid_lock, irq_flags); +} + +void psb_lid_timer_takedown(struct drm_psb_private *dev_priv) +{ + del_timer_sync(&dev_priv->lid_timer); +} + diff --git a/trunk/drivers/staging/gma500/psb_reg.h b/trunk/drivers/staging/gma500/psb_reg.h new file mode 100644 index 000000000000..b81c7c1e9c2d --- /dev/null +++ b/trunk/drivers/staging/gma500/psb_reg.h @@ -0,0 +1,582 @@ +/************************************************************************** + * + * Copyright (c) (2005-2007) Imagination Technologies Limited. + * Copyright (c) 2007, 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.. + * + **************************************************************************/ + +#ifndef _PSB_REG_H_ +#define _PSB_REG_H_ + +#define PSB_CR_CLKGATECTL 0x0000 +#define _PSB_C_CLKGATECTL_AUTO_MAN_REG (1 << 24) +#define _PSB_C_CLKGATECTL_USE_CLKG_SHIFT (20) +#define _PSB_C_CLKGATECTL_USE_CLKG_MASK (0x3 << 20) +#define _PSB_C_CLKGATECTL_DPM_CLKG_SHIFT (16) +#define _PSB_C_CLKGATECTL_DPM_CLKG_MASK (0x3 << 16) +#define _PSB_C_CLKGATECTL_TA_CLKG_SHIFT (12) +#define _PSB_C_CLKGATECTL_TA_CLKG_MASK (0x3 << 12) +#define _PSB_C_CLKGATECTL_TSP_CLKG_SHIFT (8) +#define _PSB_C_CLKGATECTL_TSP_CLKG_MASK (0x3 << 8) +#define _PSB_C_CLKGATECTL_ISP_CLKG_SHIFT (4) +#define _PSB_C_CLKGATECTL_ISP_CLKG_MASK (0x3 << 4) +#define _PSB_C_CLKGATECTL_2D_CLKG_SHIFT (0) +#define _PSB_C_CLKGATECTL_2D_CLKG_MASK (0x3 << 0) +#define _PSB_C_CLKGATECTL_CLKG_ENABLED (0) +#define _PSB_C_CLKGATECTL_CLKG_DISABLED (1) +#define _PSB_C_CLKGATECTL_CLKG_AUTO (2) + +#define PSB_CR_CORE_ID 0x0010 +#define _PSB_CC_ID_ID_SHIFT (16) +#define _PSB_CC_ID_ID_MASK (0xFFFF << 16) +#define _PSB_CC_ID_CONFIG_SHIFT (0) +#define _PSB_CC_ID_CONFIG_MASK (0xFFFF << 0) + +#define PSB_CR_CORE_REVISION 0x0014 +#define _PSB_CC_REVISION_DESIGNER_SHIFT (24) +#define _PSB_CC_REVISION_DESIGNER_MASK (0xFF << 24) +#define _PSB_CC_REVISION_MAJOR_SHIFT (16) +#define _PSB_CC_REVISION_MAJOR_MASK (0xFF << 16) +#define _PSB_CC_REVISION_MINOR_SHIFT (8) +#define _PSB_CC_REVISION_MINOR_MASK (0xFF << 8) +#define _PSB_CC_REVISION_MAINTENANCE_SHIFT (0) +#define _PSB_CC_REVISION_MAINTENANCE_MASK (0xFF << 0) + +#define PSB_CR_DESIGNER_REV_FIELD1 0x0018 + +#define PSB_CR_SOFT_RESET 0x0080 +#define _PSB_CS_RESET_TSP_RESET (1 << 6) +#define _PSB_CS_RESET_ISP_RESET (1 << 5) +#define _PSB_CS_RESET_USE_RESET (1 << 4) +#define _PSB_CS_RESET_TA_RESET (1 << 3) +#define _PSB_CS_RESET_DPM_RESET (1 << 2) +#define _PSB_CS_RESET_TWOD_RESET (1 << 1) +#define _PSB_CS_RESET_BIF_RESET (1 << 0) + +#define PSB_CR_DESIGNER_REV_FIELD2 0x001C + +#define PSB_CR_EVENT_HOST_ENABLE2 0x0110 + +#define PSB_CR_EVENT_STATUS2 0x0118 + +#define PSB_CR_EVENT_HOST_CLEAR2 0x0114 +#define _PSB_CE2_BIF_REQUESTER_FAULT (1 << 4) + +#define PSB_CR_EVENT_STATUS 0x012C + +#define PSB_CR_EVENT_HOST_ENABLE 0x0130 + +#define PSB_CR_EVENT_HOST_CLEAR 0x0134 +#define _PSB_CE_MASTER_INTERRUPT (1 << 31) +#define _PSB_CE_TA_DPM_FAULT (1 << 28) +#define _PSB_CE_TWOD_COMPLETE (1 << 27) +#define _PSB_CE_DPM_OUT_OF_MEMORY_ZLS (1 << 25) +#define _PSB_CE_DPM_TA_MEM_FREE (1 << 24) +#define _PSB_CE_PIXELBE_END_RENDER (1 << 18) +#define _PSB_CE_SW_EVENT (1 << 14) +#define _PSB_CE_TA_FINISHED (1 << 13) +#define _PSB_CE_TA_TERMINATE (1 << 12) +#define _PSB_CE_DPM_REACHED_MEM_THRESH (1 << 3) +#define _PSB_CE_DPM_OUT_OF_MEMORY_GBL (1 << 2) +#define _PSB_CE_DPM_OUT_OF_MEMORY_MT (1 << 1) +#define _PSB_CE_DPM_3D_MEM_FREE (1 << 0) + + +#define PSB_USE_OFFSET_MASK 0x0007FFFF +#define PSB_USE_OFFSET_SIZE (PSB_USE_OFFSET_MASK + 1) +#define PSB_CR_USE_CODE_BASE0 0x0A0C +#define PSB_CR_USE_CODE_BASE1 0x0A10 +#define PSB_CR_USE_CODE_BASE2 0x0A14 +#define PSB_CR_USE_CODE_BASE3 0x0A18 +#define PSB_CR_USE_CODE_BASE4 0x0A1C +#define PSB_CR_USE_CODE_BASE5 0x0A20 +#define PSB_CR_USE_CODE_BASE6 0x0A24 +#define PSB_CR_USE_CODE_BASE7 0x0A28 +#define PSB_CR_USE_CODE_BASE8 0x0A2C +#define PSB_CR_USE_CODE_BASE9 0x0A30 +#define PSB_CR_USE_CODE_BASE10 0x0A34 +#define PSB_CR_USE_CODE_BASE11 0x0A38 +#define PSB_CR_USE_CODE_BASE12 0x0A3C +#define PSB_CR_USE_CODE_BASE13 0x0A40 +#define PSB_CR_USE_CODE_BASE14 0x0A44 +#define PSB_CR_USE_CODE_BASE15 0x0A48 +#define PSB_CR_USE_CODE_BASE(_i) (0x0A0C + ((_i) << 2)) +#define _PSB_CUC_BASE_DM_SHIFT (25) +#define _PSB_CUC_BASE_DM_MASK (0x3 << 25) +#define _PSB_CUC_BASE_ADDR_SHIFT (0) /* 1024-bit aligned address? */ +#define _PSB_CUC_BASE_ADDR_ALIGNSHIFT (7) +#define _PSB_CUC_BASE_ADDR_MASK (0x1FFFFFF << 0) +#define _PSB_CUC_DM_VERTEX (0) +#define _PSB_CUC_DM_PIXEL (1) +#define _PSB_CUC_DM_RESERVED (2) +#define _PSB_CUC_DM_EDM (3) + +#define PSB_CR_PDS_EXEC_BASE 0x0AB8 +#define _PSB_CR_PDS_EXEC_BASE_ADDR_SHIFT (20) /* 1MB aligned address */ +#define _PSB_CR_PDS_EXEC_BASE_ADDR_ALIGNSHIFT (20) + +#define PSB_CR_EVENT_KICKER 0x0AC4 +#define _PSB_CE_KICKER_ADDRESS_SHIFT (4) /* 128-bit aligned address */ + +#define PSB_CR_EVENT_KICK 0x0AC8 +#define _PSB_CE_KICK_NOW (1 << 0) + +#define PSB_CR_BIF_DIR_LIST_BASE1 0x0C38 + +#define PSB_CR_BIF_CTRL 0x0C00 +#define _PSB_CB_CTRL_CLEAR_FAULT (1 << 4) +#define _PSB_CB_CTRL_INVALDC (1 << 3) +#define _PSB_CB_CTRL_FLUSH (1 << 2) + +#define PSB_CR_BIF_INT_STAT 0x0C04 + +#define PSB_CR_BIF_FAULT 0x0C08 +#define _PSB_CBI_STAT_PF_N_RW (1 << 14) +#define _PSB_CBI_STAT_FAULT_SHIFT (0) +#define _PSB_CBI_STAT_FAULT_MASK (0x3FFF << 0) +#define _PSB_CBI_STAT_FAULT_CACHE (1 << 1) +#define _PSB_CBI_STAT_FAULT_TA (1 << 2) +#define _PSB_CBI_STAT_FAULT_VDM (1 << 3) +#define _PSB_CBI_STAT_FAULT_2D (1 << 4) +#define _PSB_CBI_STAT_FAULT_PBE (1 << 5) +#define _PSB_CBI_STAT_FAULT_TSP (1 << 6) +#define _PSB_CBI_STAT_FAULT_ISP (1 << 7) +#define _PSB_CBI_STAT_FAULT_USSEPDS (1 << 8) +#define _PSB_CBI_STAT_FAULT_HOST (1 << 9) + +#define PSB_CR_BIF_BANK0 0x0C78 +#define PSB_CR_BIF_BANK1 0x0C7C +#define PSB_CR_BIF_DIR_LIST_BASE0 0x0C84 +#define PSB_CR_BIF_TWOD_REQ_BASE 0x0C88 +#define PSB_CR_BIF_3D_REQ_BASE 0x0CAC + +#define PSB_CR_2D_SOCIF 0x0E18 +#define _PSB_C2_SOCIF_FREESPACE_SHIFT (0) +#define _PSB_C2_SOCIF_FREESPACE_MASK (0xFF << 0) +#define _PSB_C2_SOCIF_EMPTY (0x80 << 0) + +#define PSB_CR_2D_BLIT_STATUS 0x0E04 +#define _PSB_C2B_STATUS_BUSY (1 << 24) +#define _PSB_C2B_STATUS_COMPLETE_SHIFT (0) +#define _PSB_C2B_STATUS_COMPLETE_MASK (0xFFFFFF << 0) + +/* + * 2D defs. + */ + +/* + * 2D Slave Port Data : Block Header's Object Type + */ + +#define PSB_2D_CLIP_BH (0x00000000) +#define PSB_2D_PAT_BH (0x10000000) +#define PSB_2D_CTRL_BH (0x20000000) +#define PSB_2D_SRC_OFF_BH (0x30000000) +#define PSB_2D_MASK_OFF_BH (0x40000000) +#define PSB_2D_RESERVED1_BH (0x50000000) +#define PSB_2D_RESERVED2_BH (0x60000000) +#define PSB_2D_FENCE_BH (0x70000000) +#define PSB_2D_BLIT_BH (0x80000000) +#define PSB_2D_SRC_SURF_BH (0x90000000) +#define PSB_2D_DST_SURF_BH (0xA0000000) +#define PSB_2D_PAT_SURF_BH (0xB0000000) +#define PSB_2D_SRC_PAL_BH (0xC0000000) +#define PSB_2D_PAT_PAL_BH (0xD0000000) +#define PSB_2D_MASK_SURF_BH (0xE0000000) +#define PSB_2D_FLUSH_BH (0xF0000000) + +/* + * Clip Definition block (PSB_2D_CLIP_BH) + */ +#define PSB_2D_CLIPCOUNT_MAX (1) +#define PSB_2D_CLIPCOUNT_MASK (0x00000000) +#define PSB_2D_CLIPCOUNT_CLRMASK (0xFFFFFFFF) +#define PSB_2D_CLIPCOUNT_SHIFT (0) +/* clip rectangle min & max */ +#define PSB_2D_CLIP_XMAX_MASK (0x00FFF000) +#define PSB_2D_CLIP_XMAX_CLRMASK (0xFF000FFF) +#define PSB_2D_CLIP_XMAX_SHIFT (12) +#define PSB_2D_CLIP_XMIN_MASK (0x00000FFF) +#define PSB_2D_CLIP_XMIN_CLRMASK (0x00FFF000) +#define PSB_2D_CLIP_XMIN_SHIFT (0) +/* clip rectangle offset */ +#define PSB_2D_CLIP_YMAX_MASK (0x00FFF000) +#define PSB_2D_CLIP_YMAX_CLRMASK (0xFF000FFF) +#define PSB_2D_CLIP_YMAX_SHIFT (12) +#define PSB_2D_CLIP_YMIN_MASK (0x00000FFF) +#define PSB_2D_CLIP_YMIN_CLRMASK (0x00FFF000) +#define PSB_2D_CLIP_YMIN_SHIFT (0) + +/* + * Pattern Control (PSB_2D_PAT_BH) + */ +#define PSB_2D_PAT_HEIGHT_MASK (0x0000001F) +#define PSB_2D_PAT_HEIGHT_SHIFT (0) +#define PSB_2D_PAT_WIDTH_MASK (0x000003E0) +#define PSB_2D_PAT_WIDTH_SHIFT (5) +#define PSB_2D_PAT_YSTART_MASK (0x00007C00) +#define PSB_2D_PAT_YSTART_SHIFT (10) +#define PSB_2D_PAT_XSTART_MASK (0x000F8000) +#define PSB_2D_PAT_XSTART_SHIFT (15) + +/* + * 2D Control block (PSB_2D_CTRL_BH) + */ +/* Present Flags */ +#define PSB_2D_SRCCK_CTRL (0x00000001) +#define PSB_2D_DSTCK_CTRL (0x00000002) +#define PSB_2D_ALPHA_CTRL (0x00000004) +/* Colour Key Colour (SRC/DST)*/ +#define PSB_2D_CK_COL_MASK (0xFFFFFFFF) +#define PSB_2D_CK_COL_CLRMASK (0x00000000) +#define PSB_2D_CK_COL_SHIFT (0) +/* Colour Key Mask (SRC/DST)*/ +#define PSB_2D_CK_MASK_MASK (0xFFFFFFFF) +#define PSB_2D_CK_MASK_CLRMASK (0x00000000) +#define PSB_2D_CK_MASK_SHIFT (0) +/* Alpha Control (Alpha/RGB)*/ +#define PSB_2D_GBLALPHA_MASK (0x000FF000) +#define PSB_2D_GBLALPHA_CLRMASK (0xFFF00FFF) +#define PSB_2D_GBLALPHA_SHIFT (12) +#define PSB_2D_SRCALPHA_OP_MASK (0x00700000) +#define PSB_2D_SRCALPHA_OP_CLRMASK (0xFF8FFFFF) +#define PSB_2D_SRCALPHA_OP_SHIFT (20) +#define PSB_2D_SRCALPHA_OP_ONE (0x00000000) +#define PSB_2D_SRCALPHA_OP_SRC (0x00100000) +#define PSB_2D_SRCALPHA_OP_DST (0x00200000) +#define PSB_2D_SRCALPHA_OP_SG (0x00300000) +#define PSB_2D_SRCALPHA_OP_DG (0x00400000) +#define PSB_2D_SRCALPHA_OP_GBL (0x00500000) +#define PSB_2D_SRCALPHA_OP_ZERO (0x00600000) +#define PSB_2D_SRCALPHA_INVERT (0x00800000) +#define PSB_2D_SRCALPHA_INVERT_CLR (0xFF7FFFFF) +#define PSB_2D_DSTALPHA_OP_MASK (0x07000000) +#define PSB_2D_DSTALPHA_OP_CLRMASK (0xF8FFFFFF) +#define PSB_2D_DSTALPHA_OP_SHIFT (24) +#define PSB_2D_DSTALPHA_OP_ONE (0x00000000) +#define PSB_2D_DSTALPHA_OP_SRC (0x01000000) +#define PSB_2D_DSTALPHA_OP_DST (0x02000000) +#define PSB_2D_DSTALPHA_OP_SG (0x03000000) +#define PSB_2D_DSTALPHA_OP_DG (0x04000000) +#define PSB_2D_DSTALPHA_OP_GBL (0x05000000) +#define PSB_2D_DSTALPHA_OP_ZERO (0x06000000) +#define PSB_2D_DSTALPHA_INVERT (0x08000000) +#define PSB_2D_DSTALPHA_INVERT_CLR (0xF7FFFFFF) + +#define PSB_2D_PRE_MULTIPLICATION_ENABLE (0x10000000) +#define PSB_2D_PRE_MULTIPLICATION_CLRMASK (0xEFFFFFFF) +#define PSB_2D_ZERO_SOURCE_ALPHA_ENABLE (0x20000000) +#define PSB_2D_ZERO_SOURCE_ALPHA_CLRMASK (0xDFFFFFFF) + +/* + *Source Offset (PSB_2D_SRC_OFF_BH) + */ +#define PSB_2D_SRCOFF_XSTART_MASK ((0x00000FFF) << 12) +#define PSB_2D_SRCOFF_XSTART_SHIFT (12) +#define PSB_2D_SRCOFF_YSTART_MASK (0x00000FFF) +#define PSB_2D_SRCOFF_YSTART_SHIFT (0) + +/* + * Mask Offset (PSB_2D_MASK_OFF_BH) + */ +#define PSB_2D_MASKOFF_XSTART_MASK ((0x00000FFF) << 12) +#define PSB_2D_MASKOFF_XSTART_SHIFT (12) +#define PSB_2D_MASKOFF_YSTART_MASK (0x00000FFF) +#define PSB_2D_MASKOFF_YSTART_SHIFT (0) + +/* + * 2D Fence (see PSB_2D_FENCE_BH): bits 0:27 are ignored + */ + +/* + *Blit Rectangle (PSB_2D_BLIT_BH) + */ + +#define PSB_2D_ROT_MASK (3 << 25) +#define PSB_2D_ROT_CLRMASK (~PSB_2D_ROT_MASK) +#define PSB_2D_ROT_NONE (0 << 25) +#define PSB_2D_ROT_90DEGS (1 << 25) +#define PSB_2D_ROT_180DEGS (2 << 25) +#define PSB_2D_ROT_270DEGS (3 << 25) + +#define PSB_2D_COPYORDER_MASK (3 << 23) +#define PSB_2D_COPYORDER_CLRMASK (~PSB_2D_COPYORDER_MASK) +#define PSB_2D_COPYORDER_TL2BR (0 << 23) +#define PSB_2D_COPYORDER_BR2TL (1 << 23) +#define PSB_2D_COPYORDER_TR2BL (2 << 23) +#define PSB_2D_COPYORDER_BL2TR (3 << 23) + +#define PSB_2D_DSTCK_CLRMASK (0xFF9FFFFF) +#define PSB_2D_DSTCK_DISABLE (0x00000000) +#define PSB_2D_DSTCK_PASS (0x00200000) +#define PSB_2D_DSTCK_REJECT (0x00400000) + +#define PSB_2D_SRCCK_CLRMASK (0xFFE7FFFF) +#define PSB_2D_SRCCK_DISABLE (0x00000000) +#define PSB_2D_SRCCK_PASS (0x00080000) +#define PSB_2D_SRCCK_REJECT (0x00100000) + +#define PSB_2D_CLIP_ENABLE (0x00040000) + +#define PSB_2D_ALPHA_ENABLE (0x00020000) + +#define PSB_2D_PAT_CLRMASK (0xFFFEFFFF) +#define PSB_2D_PAT_MASK (0x00010000) +#define PSB_2D_USE_PAT (0x00010000) +#define PSB_2D_USE_FILL (0x00000000) +/* + * Tungsten Graphics note on rop codes: If rop A and rop B are + * identical, the mask surface will not be read and need not be + * set up. + */ + +#define PSB_2D_ROP3B_MASK (0x0000FF00) +#define PSB_2D_ROP3B_CLRMASK (0xFFFF00FF) +#define PSB_2D_ROP3B_SHIFT (8) +/* rop code A */ +#define PSB_2D_ROP3A_MASK (0x000000FF) +#define PSB_2D_ROP3A_CLRMASK (0xFFFFFF00) +#define PSB_2D_ROP3A_SHIFT (0) + +#define PSB_2D_ROP4_MASK (0x0000FFFF) +/* + * DWORD0: (Only pass if Pattern control == Use Fill Colour) + * Fill Colour RGBA8888 + */ +#define PSB_2D_FILLCOLOUR_MASK (0xFFFFFFFF) +#define PSB_2D_FILLCOLOUR_SHIFT (0) +/* + * DWORD1: (Always Present) + * X Start (Dest) + * Y Start (Dest) + */ +#define PSB_2D_DST_XSTART_MASK (0x00FFF000) +#define PSB_2D_DST_XSTART_CLRMASK (0xFF000FFF) +#define PSB_2D_DST_XSTART_SHIFT (12) +#define PSB_2D_DST_YSTART_MASK (0x00000FFF) +#define PSB_2D_DST_YSTART_CLRMASK (0xFFFFF000) +#define PSB_2D_DST_YSTART_SHIFT (0) +/* + * DWORD2: (Always Present) + * X Size (Dest) + * Y Size (Dest) + */ +#define PSB_2D_DST_XSIZE_MASK (0x00FFF000) +#define PSB_2D_DST_XSIZE_CLRMASK (0xFF000FFF) +#define PSB_2D_DST_XSIZE_SHIFT (12) +#define PSB_2D_DST_YSIZE_MASK (0x00000FFF) +#define PSB_2D_DST_YSIZE_CLRMASK (0xFFFFF000) +#define PSB_2D_DST_YSIZE_SHIFT (0) + +/* + * Source Surface (PSB_2D_SRC_SURF_BH) + */ +/* + * WORD 0 + */ + +#define PSB_2D_SRC_FORMAT_MASK (0x00078000) +#define PSB_2D_SRC_1_PAL (0x00000000) +#define PSB_2D_SRC_2_PAL (0x00008000) +#define PSB_2D_SRC_4_PAL (0x00010000) +#define PSB_2D_SRC_8_PAL (0x00018000) +#define PSB_2D_SRC_8_ALPHA (0x00020000) +#define PSB_2D_SRC_4_ALPHA (0x00028000) +#define PSB_2D_SRC_332RGB (0x00030000) +#define PSB_2D_SRC_4444ARGB (0x00038000) +#define PSB_2D_SRC_555RGB (0x00040000) +#define PSB_2D_SRC_1555ARGB (0x00048000) +#define PSB_2D_SRC_565RGB (0x00050000) +#define PSB_2D_SRC_0888ARGB (0x00058000) +#define PSB_2D_SRC_8888ARGB (0x00060000) +#define PSB_2D_SRC_8888UYVY (0x00068000) +#define PSB_2D_SRC_RESERVED (0x00070000) +#define PSB_2D_SRC_1555ARGB_LOOKUP (0x00078000) + + +#define PSB_2D_SRC_STRIDE_MASK (0x00007FFF) +#define PSB_2D_SRC_STRIDE_CLRMASK (0xFFFF8000) +#define PSB_2D_SRC_STRIDE_SHIFT (0) +/* + * WORD 1 - Base Address + */ +#define PSB_2D_SRC_ADDR_MASK (0x0FFFFFFC) +#define PSB_2D_SRC_ADDR_CLRMASK (0x00000003) +#define PSB_2D_SRC_ADDR_SHIFT (2) +#define PSB_2D_SRC_ADDR_ALIGNSHIFT (2) + +/* + * Pattern Surface (PSB_2D_PAT_SURF_BH) + */ +/* + * WORD 0 + */ + +#define PSB_2D_PAT_FORMAT_MASK (0x00078000) +#define PSB_2D_PAT_1_PAL (0x00000000) +#define PSB_2D_PAT_2_PAL (0x00008000) +#define PSB_2D_PAT_4_PAL (0x00010000) +#define PSB_2D_PAT_8_PAL (0x00018000) +#define PSB_2D_PAT_8_ALPHA (0x00020000) +#define PSB_2D_PAT_4_ALPHA (0x00028000) +#define PSB_2D_PAT_332RGB (0x00030000) +#define PSB_2D_PAT_4444ARGB (0x00038000) +#define PSB_2D_PAT_555RGB (0x00040000) +#define PSB_2D_PAT_1555ARGB (0x00048000) +#define PSB_2D_PAT_565RGB (0x00050000) +#define PSB_2D_PAT_0888ARGB (0x00058000) +#define PSB_2D_PAT_8888ARGB (0x00060000) + +#define PSB_2D_PAT_STRIDE_MASK (0x00007FFF) +#define PSB_2D_PAT_STRIDE_CLRMASK (0xFFFF8000) +#define PSB_2D_PAT_STRIDE_SHIFT (0) +/* + * WORD 1 - Base Address + */ +#define PSB_2D_PAT_ADDR_MASK (0x0FFFFFFC) +#define PSB_2D_PAT_ADDR_CLRMASK (0x00000003) +#define PSB_2D_PAT_ADDR_SHIFT (2) +#define PSB_2D_PAT_ADDR_ALIGNSHIFT (2) + +/* + * Destination Surface (PSB_2D_DST_SURF_BH) + */ +/* + * WORD 0 + */ + +#define PSB_2D_DST_FORMAT_MASK (0x00078000) +#define PSB_2D_DST_332RGB (0x00030000) +#define PSB_2D_DST_4444ARGB (0x00038000) +#define PSB_2D_DST_555RGB (0x00040000) +#define PSB_2D_DST_1555ARGB (0x00048000) +#define PSB_2D_DST_565RGB (0x00050000) +#define PSB_2D_DST_0888ARGB (0x00058000) +#define PSB_2D_DST_8888ARGB (0x00060000) +#define PSB_2D_DST_8888AYUV (0x00070000) + +#define PSB_2D_DST_STRIDE_MASK (0x00007FFF) +#define PSB_2D_DST_STRIDE_CLRMASK (0xFFFF8000) +#define PSB_2D_DST_STRIDE_SHIFT (0) +/* + * WORD 1 - Base Address + */ +#define PSB_2D_DST_ADDR_MASK (0x0FFFFFFC) +#define PSB_2D_DST_ADDR_CLRMASK (0x00000003) +#define PSB_2D_DST_ADDR_SHIFT (2) +#define PSB_2D_DST_ADDR_ALIGNSHIFT (2) + +/* + * Mask Surface (PSB_2D_MASK_SURF_BH) + */ +/* + * WORD 0 + */ +#define PSB_2D_MASK_STRIDE_MASK (0x00007FFF) +#define PSB_2D_MASK_STRIDE_CLRMASK (0xFFFF8000) +#define PSB_2D_MASK_STRIDE_SHIFT (0) +/* + * WORD 1 - Base Address + */ +#define PSB_2D_MASK_ADDR_MASK (0x0FFFFFFC) +#define PSB_2D_MASK_ADDR_CLRMASK (0x00000003) +#define PSB_2D_MASK_ADDR_SHIFT (2) +#define PSB_2D_MASK_ADDR_ALIGNSHIFT (2) + +/* + * Source Palette (PSB_2D_SRC_PAL_BH) + */ + +#define PSB_2D_SRCPAL_ADDR_SHIFT (0) +#define PSB_2D_SRCPAL_ADDR_CLRMASK (0xF0000007) +#define PSB_2D_SRCPAL_ADDR_MASK (0x0FFFFFF8) +#define PSB_2D_SRCPAL_BYTEALIGN (1024) + +/* + * Pattern Palette (PSB_2D_PAT_PAL_BH) + */ + +#define PSB_2D_PATPAL_ADDR_SHIFT (0) +#define PSB_2D_PATPAL_ADDR_CLRMASK (0xF0000007) +#define PSB_2D_PATPAL_ADDR_MASK (0x0FFFFFF8) +#define PSB_2D_PATPAL_BYTEALIGN (1024) + +/* + * Rop3 Codes (2 LS bytes) + */ + +#define PSB_2D_ROP3_SRCCOPY (0xCCCC) +#define PSB_2D_ROP3_PATCOPY (0xF0F0) +#define PSB_2D_ROP3_WHITENESS (0xFFFF) +#define PSB_2D_ROP3_BLACKNESS (0x0000) +#define PSB_2D_ROP3_SRC (0xCC) +#define PSB_2D_ROP3_PAT (0xF0) +#define PSB_2D_ROP3_DST (0xAA) + +/* + * Sizes. + */ + +#define PSB_SCENE_HW_COOKIE_SIZE 16 +#define PSB_TA_MEM_HW_COOKIE_SIZE 16 + +/* + * Scene stuff. + */ + +#define PSB_NUM_HW_SCENES 2 + +/* + * Scheduler completion actions. + */ + +#define PSB_RASTER_BLOCK 0 +#define PSB_RASTER 1 +#define PSB_RETURN 2 +#define PSB_TA 3 + +/* Power management */ +#define PSB_PUNIT_PORT 0x04 +#define PSB_OSPMBA 0x78 +#define PSB_APMBA 0x7a +#define PSB_APM_CMD 0x0 +#define PSB_APM_STS 0x04 +#define PSB_PWRGT_VID_ENC_MASK 0x30 +#define PSB_PWRGT_VID_DEC_MASK 0xc +#define PSB_PWRGT_GL3_MASK 0xc0 + +#define PSB_PM_SSC 0x20 +#define PSB_PM_SSS 0x30 +#define PSB_PWRGT_DISPLAY_MASK 0xc /*on a different BA than video/gfx*/ +#define MDFLD_PWRGT_DISPLAY_A_CNTR 0x0000000c +#define MDFLD_PWRGT_DISPLAY_B_CNTR 0x0000c000 +#define MDFLD_PWRGT_DISPLAY_C_CNTR 0x00030000 +#define MDFLD_PWRGT_DISP_MIPI_CNTR 0x000c0000 +#define MDFLD_PWRGT_DISPLAY_CNTR (MDFLD_PWRGT_DISPLAY_A_CNTR | MDFLD_PWRGT_DISPLAY_B_CNTR | MDFLD_PWRGT_DISPLAY_C_CNTR | MDFLD_PWRGT_DISP_MIPI_CNTR) /* 0x000fc00c */ +/* Display SSS register bits are different in A0 vs. B0 */ +#define PSB_PWRGT_GFX_MASK 0x3 +#define MDFLD_PWRGT_DISPLAY_A_STS 0x000000c0 +#define MDFLD_PWRGT_DISPLAY_B_STS 0x00000300 +#define MDFLD_PWRGT_DISPLAY_C_STS 0x00000c00 +#define PSB_PWRGT_GFX_MASK_B0 0xc3 +#define MDFLD_PWRGT_DISPLAY_A_STS_B0 0x0000000c +#define MDFLD_PWRGT_DISPLAY_B_STS_B0 0x0000c000 +#define MDFLD_PWRGT_DISPLAY_C_STS_B0 0x00030000 +#define MDFLD_PWRGT_DISP_MIPI_STS 0x000c0000 +#define MDFLD_PWRGT_DISPLAY_STS_A0 (MDFLD_PWRGT_DISPLAY_A_STS | MDFLD_PWRGT_DISPLAY_B_STS | MDFLD_PWRGT_DISPLAY_C_STS | MDFLD_PWRGT_DISP_MIPI_STS) /* 0x000fc00c */ +#define MDFLD_PWRGT_DISPLAY_STS_B0 (MDFLD_PWRGT_DISPLAY_A_STS_B0 | MDFLD_PWRGT_DISPLAY_B_STS_B0 | MDFLD_PWRGT_DISPLAY_C_STS_B0 | MDFLD_PWRGT_DISP_MIPI_STS) /* 0x000fc00c */ +#endif diff --git a/trunk/drivers/staging/omapdrm/Makefile b/trunk/drivers/staging/omapdrm/Makefile index d9cdc120d122..592cf69020cd 100644 --- a/trunk/drivers/staging/omapdrm/Makefile +++ b/trunk/drivers/staging/omapdrm/Makefile @@ -7,7 +7,6 @@ ccflags-y := -Iinclude/drm -Werror omapdrm-y := omap_drv.o \ omap_debugfs.o \ omap_crtc.o \ - omap_plane.o \ omap_encoder.o \ omap_connector.o \ omap_fb.o \ diff --git a/trunk/drivers/staging/omapdrm/omap_crtc.c b/trunk/drivers/staging/omapdrm/omap_crtc.c index 17ca163e5896..cffdf5e12394 100644 --- a/trunk/drivers/staging/omapdrm/omap_crtc.c +++ b/trunk/drivers/staging/omapdrm/omap_crtc.c @@ -27,95 +27,196 @@ struct omap_crtc { struct drm_crtc base; - struct drm_plane *plane; - const char *name; + struct omap_overlay *ovl; + struct omap_overlay_info info; int id; - /* if there is a pending flip, these will be non-null: */ + /* if there is a pending flip, this will be non-null: */ struct drm_pending_vblank_event *event; - struct drm_framebuffer *old_fb; }; +/* push changes down to dss2 */ +static int commit(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct omap_overlay *ovl = omap_crtc->ovl; + struct omap_overlay_info *info = &omap_crtc->info; + int ret; + + DBG("%s", omap_crtc->ovl->name); + DBG("%dx%d -> %dx%d (%d)", info->width, info->height, info->out_width, + info->out_height, info->screen_width); + DBG("%d,%d %08x", info->pos_x, info->pos_y, info->paddr); + + /* NOTE: do we want to do this at all here, or just wait + * for dpms(ON) since other CRTC's may not have their mode + * set yet, so fb dimensions may still change.. + */ + ret = ovl->set_overlay_info(ovl, info); + if (ret) { + dev_err(dev->dev, "could not set overlay info\n"); + return ret; + } + + /* our encoder doesn't necessarily get a commit() after this, in + * particular in the dpms() and mode_set_base() cases, so force the + * manager to update: + * + * could this be in the encoder somehow? + */ + if (ovl->manager) { + ret = ovl->manager->apply(ovl->manager); + if (ret) { + dev_err(dev->dev, "could not apply settings\n"); + return ret; + } + } + + if (info->enabled) { + omap_framebuffer_flush(crtc->fb, crtc->x, crtc->y, + crtc->fb->width, crtc->fb->height); + } + + return 0; +} + +/* update parameters that are dependent on the framebuffer dimensions and + * position within the fb that this crtc scans out from. This is called + * when framebuffer dimensions or x,y base may have changed, either due + * to our mode, or a change in another crtc that is scanning out of the + * same fb. + */ +static void update_scanout(struct drm_crtc *crtc) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + dma_addr_t paddr; + unsigned int screen_width; + + omap_framebuffer_get_buffer(crtc->fb, crtc->x, crtc->y, + NULL, &paddr, &screen_width); + + DBG("%s: %d,%d: %08x (%d)", omap_crtc->ovl->name, + crtc->x, crtc->y, (u32)paddr, screen_width); + + omap_crtc->info.paddr = paddr; + omap_crtc->info.screen_width = screen_width; +} + static void omap_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, uint32_t start, uint32_t size) { - /* not supported.. at least not yet */ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + DBG("%s", omap_crtc->ovl->name); } static void omap_crtc_destroy(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - omap_crtc->plane->funcs->destroy(omap_crtc->plane); + DBG("%s", omap_crtc->ovl->name); drm_crtc_cleanup(crtc); kfree(omap_crtc); } static void omap_crtc_dpms(struct drm_crtc *crtc, int mode) { - struct omap_drm_private *priv = crtc->dev->dev_private; struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - int i; - WARN_ON(omap_plane_dpms(omap_crtc->plane, mode)); + DBG("%s: %d", omap_crtc->ovl->name, mode); - for (i = 0; i < priv->num_planes; i++) { - struct drm_plane *plane = priv->planes[i]; - if (plane->crtc == crtc) - WARN_ON(omap_plane_dpms(plane, mode)); + if (mode == DRM_MODE_DPMS_ON) { + update_scanout(crtc); + omap_crtc->info.enabled = true; + } else { + omap_crtc->info.enabled = false; } + + WARN_ON(commit(crtc)); } static bool omap_crtc_mode_fixup(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) { + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + DBG("%s", omap_crtc->ovl->name); return true; } static int omap_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - int x, int y, - struct drm_framebuffer *old_fb) + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, + struct drm_framebuffer *old_fb) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - struct drm_plane *plane = omap_crtc->plane; - return omap_plane_mode_set(plane, crtc, crtc->fb, - 0, 0, mode->hdisplay, mode->vdisplay, - x << 16, y << 16, - mode->hdisplay << 16, mode->vdisplay << 16); + DBG("%s: %d,%d: %dx%d", omap_crtc->ovl->name, x, y, + mode->hdisplay, mode->vdisplay); + + /* just use adjusted mode */ + mode = adjusted_mode; + + omap_crtc->info.width = mode->hdisplay; + omap_crtc->info.height = mode->vdisplay; + omap_crtc->info.out_width = mode->hdisplay; + omap_crtc->info.out_height = mode->vdisplay; + omap_crtc->info.color_mode = OMAP_DSS_COLOR_RGB24U; + omap_crtc->info.rotation_type = OMAP_DSS_ROT_DMA; + omap_crtc->info.rotation = OMAP_DSS_ROT_0; + omap_crtc->info.global_alpha = 0xff; + omap_crtc->info.mirror = 0; + omap_crtc->info.mirror = 0; + omap_crtc->info.pos_x = 0; + omap_crtc->info.pos_y = 0; +#if 0 /* re-enable when these are available in DSS2 driver */ + omap_crtc->info.zorder = 3; /* GUI in the front, video behind */ + omap_crtc->info.min_x_decim = 1; + omap_crtc->info.max_x_decim = 1; + omap_crtc->info.min_y_decim = 1; + omap_crtc->info.max_y_decim = 1; +#endif + + update_scanout(crtc); + + return 0; } static void omap_crtc_prepare(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - DBG("%s", omap_crtc->name); + struct omap_overlay *ovl = omap_crtc->ovl; + + DBG("%s", omap_crtc->ovl->name); + + ovl->get_overlay_info(ovl, &omap_crtc->info); + omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); } static void omap_crtc_commit(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - DBG("%s", omap_crtc->name); + DBG("%s", omap_crtc->ovl->name); omap_crtc_dpms(crtc, DRM_MODE_DPMS_ON); } static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb) + struct drm_framebuffer *old_fb) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - struct drm_plane *plane = omap_crtc->plane; - struct drm_display_mode *mode = &crtc->mode; - return plane->funcs->update_plane(plane, crtc, crtc->fb, - 0, 0, mode->hdisplay, mode->vdisplay, - x << 16, y << 16, - mode->hdisplay << 16, mode->vdisplay << 16); + DBG("%s %d,%d: fb=%p", omap_crtc->ovl->name, x, y, old_fb); + + update_scanout(crtc); + + return commit(crtc); } static void omap_crtc_load_lut(struct drm_crtc *crtc) { + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + DBG("%s", omap_crtc->ovl->name); } static void page_flip_cb(void *arg) @@ -124,16 +225,15 @@ static void page_flip_cb(void *arg) struct drm_device *dev = crtc->dev; struct omap_crtc *omap_crtc = to_omap_crtc(crtc); struct drm_pending_vblank_event *event = omap_crtc->event; - struct drm_framebuffer *old_fb = omap_crtc->old_fb; struct timeval now; unsigned long flags; WARN_ON(!event); omap_crtc->event = NULL; - omap_crtc->old_fb = NULL; - omap_crtc_mode_set_base(crtc, crtc->x, crtc->y, old_fb); + update_scanout(crtc); + WARN_ON(commit(crtc)); /* wakeup userspace */ /* TODO: this should happen *after* flip in vsync IRQ handler */ @@ -164,11 +264,10 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc, return -EINVAL; } - omap_crtc->old_fb = crtc->fb; - omap_crtc->event = event; crtc->fb = fb; + omap_crtc->event = event; - omap_gem_op_async(omap_framebuffer_bo(fb, 0), OMAP_GEM_READ, + omap_gem_op_async(omap_framebuffer_bo(fb), OMAP_GEM_READ, page_flip_cb, crtc); return 0; @@ -191,6 +290,12 @@ static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = { .load_lut = omap_crtc_load_lut, }; +struct omap_overlay *omap_crtc_get_overlay(struct drm_crtc *crtc) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + return omap_crtc->ovl; +} + /* initialize crtc */ struct drm_crtc *omap_crtc_init(struct drm_device *dev, struct omap_overlay *ovl, int id) @@ -205,13 +310,9 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, goto fail; } - crtc = &omap_crtc->base; - - omap_crtc->plane = omap_plane_init(dev, ovl, (1 << id), true); - omap_crtc->plane->crtc = crtc; - omap_crtc->name = ovl->name; + omap_crtc->ovl = ovl; omap_crtc->id = id; - + crtc = &omap_crtc->base; drm_crtc_init(dev, crtc, &omap_crtc_funcs); drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs); diff --git a/trunk/drivers/staging/omapdrm/omap_drv.c b/trunk/drivers/staging/omapdrm/omap_drv.c index 3bbea9aac404..602aa2dd49c8 100644 --- a/trunk/drivers/staging/omapdrm/omap_drv.c +++ b/trunk/drivers/staging/omapdrm/omap_drv.c @@ -204,6 +204,12 @@ static int create_crtc(struct drm_device *dev, struct omap_overlay *ovl, struct omap_overlay_manager *mgr = NULL; struct drm_crtc *crtc; + if (ovl->manager) { + DBG("disconnecting %s from %s", ovl->name, + ovl->manager->name); + ovl->unset_manager(ovl); + } + /* find next best connector, ones with detected connection first */ while (*j < priv->num_connectors && !mgr) { @@ -239,6 +245,11 @@ static int create_crtc(struct drm_device *dev, struct omap_overlay *ovl, (*j)++; } + if (mgr) { + DBG("connecting %s to %s", ovl->name, mgr->name); + ovl->set_manager(ovl, mgr); + } + crtc = omap_crtc_init(dev, ovl, priv->num_crtcs); if (!crtc) { @@ -254,26 +265,6 @@ static int create_crtc(struct drm_device *dev, struct omap_overlay *ovl, return 0; } -static int create_plane(struct drm_device *dev, struct omap_overlay *ovl, - unsigned int possible_crtcs) -{ - struct omap_drm_private *priv = dev->dev_private; - struct drm_plane *plane = - omap_plane_init(dev, ovl, possible_crtcs, false); - - if (!plane) { - dev_err(dev->dev, "could not create plane: %s\n", - ovl->name); - return -ENOMEM; - } - - BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes)); - - priv->planes[priv->num_planes++] = plane; - - return 0; -} - static int match_dev_name(struct omap_dss_device *dssdev, void *data) { return !strcmp(dssdev->name, data); @@ -341,12 +332,6 @@ static int omap_modeset_init(struct drm_device *dev) omap_dss_get_overlay(kms_pdata->ovl_ids[i]); create_crtc(dev, ovl, &j, connected_connectors); } - - for (i = 0; i < kms_pdata->pln_cnt; i++) { - struct omap_overlay *ovl = - omap_dss_get_overlay(kms_pdata->pln_ids[i]); - create_plane(dev, ovl, (1 << priv->num_crtcs) - 1); - } } else { /* otherwise just grab up to CONFIG_DRM_OMAP_NUM_CRTCS and try * to make educated guesses about everything else @@ -368,12 +353,6 @@ static int omap_modeset_init(struct drm_device *dev) create_crtc(dev, omap_dss_get_overlay(i), &j, connected_connectors); } - - /* use any remaining overlays as drm planes */ - for (; i < omap_dss_get_num_overlays(); i++) { - struct omap_overlay *ovl = omap_dss_get_overlay(i); - create_plane(dev, ovl, (1 << priv->num_crtcs) - 1); - } } /* for now keep the mapping of CRTCs and encoders static.. */ @@ -382,7 +361,15 @@ static int omap_modeset_init(struct drm_device *dev) struct omap_overlay_manager *mgr = omap_encoder_get_manager(encoder); - encoder->possible_crtcs = (1 << priv->num_crtcs) - 1; + encoder->possible_crtcs = 0; + + for (j = 0; j < priv->num_crtcs; j++) { + struct omap_overlay *ovl = + omap_crtc_get_overlay(priv->crtcs[j]); + if (ovl->manager == mgr) { + encoder->possible_crtcs |= (1 << j); + } + } DBG("%s: possible_crtcs=%08x", mgr->name, encoder->possible_crtcs); @@ -390,8 +377,8 @@ static int omap_modeset_init(struct drm_device *dev) dump_video_chains(); - dev->mode_config.min_width = 32; - dev->mode_config.min_height = 32; + dev->mode_config.min_width = 256; + dev->mode_config.min_height = 256; /* note: eventually will need some cpu_is_omapXYZ() type stuff here * to fill in these limits properly on different OMAP generations.. @@ -721,18 +708,6 @@ static struct vm_operations_struct omap_gem_vm_ops = { .close = drm_gem_vm_close, }; -static const struct file_operations omapdriver_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .unlocked_ioctl = drm_ioctl, - .release = drm_release, - .mmap = omap_gem_mmap, - .poll = drm_poll, - .fasync = drm_fasync, - .read = drm_read, - .llseek = noop_llseek, -}; - static struct drm_driver omap_drm_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET | DRIVER_GEM, @@ -763,7 +738,17 @@ static struct drm_driver omap_drm_driver = { .dumb_destroy = omap_gem_dumb_destroy, .ioctls = ioctls, .num_ioctls = DRM_OMAP_NUM_IOCTLS, - .fops = &omapdriver_fops, + .fops = { + .owner = THIS_MODULE, + .open = drm_open, + .unlocked_ioctl = drm_ioctl, + .release = drm_release, + .mmap = omap_gem_mmap, + .poll = drm_poll, + .fasync = drm_fasync, + .read = drm_read, + .llseek = noop_llseek, + }, .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, diff --git a/trunk/drivers/staging/omapdrm/omap_drv.h b/trunk/drivers/staging/omapdrm/omap_drv.h index 61fe022dda5b..76c42515ecc5 100644 --- a/trunk/drivers/staging/omapdrm/omap_drv.h +++ b/trunk/drivers/staging/omapdrm/omap_drv.h @@ -24,7 +24,6 @@ #include #include #include -#include #include "omap_drm.h" #include "omap_priv.h" @@ -42,8 +41,6 @@ struct omap_drm_private { unsigned int num_crtcs; struct drm_crtc *crtcs[8]; - unsigned int num_planes; - struct drm_plane *planes[8]; unsigned int num_encoders; struct drm_encoder *encoders[8]; unsigned int num_connectors; @@ -64,17 +61,7 @@ void omap_fbdev_free(struct drm_device *dev); struct drm_crtc *omap_crtc_init(struct drm_device *dev, struct omap_overlay *ovl, int id); - -struct drm_plane *omap_plane_init(struct drm_device *dev, - struct omap_overlay *ovl, unsigned int possible_crtcs, - bool priv); -int omap_plane_dpms(struct drm_plane *plane, int mode); -int omap_plane_mode_set(struct drm_plane *plane, - struct drm_crtc *crtc, struct drm_framebuffer *fb, - int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h); +struct omap_overlay *omap_crtc_get_overlay(struct drm_crtc *crtc); struct drm_encoder *omap_encoder_init(struct drm_device *dev, struct omap_overlay_manager *mgr); @@ -93,14 +80,12 @@ void omap_connector_flush(struct drm_connector *connector, int x, int y, int w, int h); struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, - struct drm_file *file, struct drm_mode_fb_cmd2 *mode_cmd); + struct drm_file *file, struct drm_mode_fb_cmd *mode_cmd); struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, - struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos); -struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb, int p); -int omap_framebuffer_pin(struct drm_framebuffer *fb); -void omap_framebuffer_unpin(struct drm_framebuffer *fb); -void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, int x, int y, - struct omap_overlay_info *info); + struct drm_mode_fb_cmd *mode_cmd, struct drm_gem_object *bo); +struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb); +int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y, + void **vaddr, dma_addr_t *paddr, unsigned int *screen_width); struct drm_connector *omap_framebuffer_get_next_connector( struct drm_framebuffer *fb, struct drm_connector *from); void omap_framebuffer_flush(struct drm_framebuffer *fb, @@ -147,29 +132,4 @@ static inline int align_pitch(int pitch, int width, int bpp) return ALIGN(pitch, 8 * bytespp); } -/* should these be made into common util helpers? - */ - -static inline int objects_lookup(struct drm_device *dev, - struct drm_file *filp, uint32_t pixel_format, - struct drm_gem_object **bos, uint32_t *handles) -{ - int i, n = drm_format_num_planes(pixel_format); - - for (i = 0; i < n; i++) { - bos[i] = drm_gem_object_lookup(dev, filp, handles[i]); - if (!bos[i]) { - goto fail; - } - } - - return 0; - -fail: - while (--i > 0) { - drm_gem_object_unreference_unlocked(bos[i]); - } - return -ENOENT; -} - #endif /* __OMAP_DRV_H__ */ diff --git a/trunk/drivers/staging/omapdrm/omap_fb.c b/trunk/drivers/staging/omapdrm/omap_fb.c index d021a7ec58df..0b50c5b3b564 100644 --- a/trunk/drivers/staging/omapdrm/omap_fb.c +++ b/trunk/drivers/staging/omapdrm/omap_fb.c @@ -22,57 +22,18 @@ #include "drm_crtc.h" #include "drm_crtc_helper.h" + /* * framebuffer funcs */ -/* per-format info: */ -struct format { - enum omap_color_mode dss_format; - uint32_t pixel_format; - struct { - int stride_bpp; /* this times width is stride */ - int sub_y; /* sub-sample in y dimension */ - } planes[4]; - bool yuv; -}; - -static const struct format formats[] = { - /* 16bpp [A]RGB: */ - { OMAP_DSS_COLOR_RGB16, DRM_FORMAT_RGB565, {{2, 1}}, false }, /* RGB16-565 */ - { OMAP_DSS_COLOR_RGB12U, DRM_FORMAT_RGBX4444, {{2, 1}}, false }, /* RGB12x-4444 */ - { OMAP_DSS_COLOR_RGBX16, DRM_FORMAT_XRGB4444, {{2, 1}}, false }, /* xRGB12-4444 */ - { OMAP_DSS_COLOR_RGBA16, DRM_FORMAT_RGBA4444, {{2, 1}}, false }, /* RGBA12-4444 */ - { OMAP_DSS_COLOR_ARGB16, DRM_FORMAT_ARGB4444, {{2, 1}}, false }, /* ARGB16-4444 */ - { OMAP_DSS_COLOR_XRGB16_1555, DRM_FORMAT_XRGB1555, {{2, 1}}, false }, /* xRGB15-1555 */ - { OMAP_DSS_COLOR_ARGB16_1555, DRM_FORMAT_ARGB1555, {{2, 1}}, false }, /* ARGB16-1555 */ - /* 24bpp RGB: */ - { OMAP_DSS_COLOR_RGB24P, DRM_FORMAT_RGB888, {{3, 1}}, false }, /* RGB24-888 */ - /* 32bpp [A]RGB: */ - { OMAP_DSS_COLOR_RGBX32, DRM_FORMAT_RGBX8888, {{4, 1}}, false }, /* RGBx24-8888 */ - { OMAP_DSS_COLOR_RGB24U, DRM_FORMAT_XRGB8888, {{4, 1}}, false }, /* xRGB24-8888 */ - { OMAP_DSS_COLOR_RGBA32, DRM_FORMAT_RGBA8888, {{4, 1}}, false }, /* RGBA32-8888 */ - { OMAP_DSS_COLOR_ARGB32, DRM_FORMAT_ARGB8888, {{4, 1}}, false }, /* ARGB32-8888 */ - /* YUV: */ - { OMAP_DSS_COLOR_NV12, DRM_FORMAT_NV12, {{1, 1}, {1, 2}}, true }, - { OMAP_DSS_COLOR_YUV2, DRM_FORMAT_YUYV, {{2, 1}}, true }, - { OMAP_DSS_COLOR_UYVY, DRM_FORMAT_UYVY, {{2, 1}}, true }, -}; - -/* per-plane info for the fb: */ -struct plane { - struct drm_gem_object *bo; - uint32_t pitch; - uint32_t offset; - dma_addr_t paddr; -}; - #define to_omap_framebuffer(x) container_of(x, struct omap_framebuffer, base) struct omap_framebuffer { struct drm_framebuffer base; - const struct format *format; - struct plane planes[4]; + struct drm_gem_object *bo; + int size; + dma_addr_t paddr; }; static int omap_framebuffer_create_handle(struct drm_framebuffer *fb, @@ -80,23 +41,22 @@ static int omap_framebuffer_create_handle(struct drm_framebuffer *fb, unsigned int *handle) { struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); - return drm_gem_handle_create(file_priv, - omap_fb->planes[0].bo, handle); + return drm_gem_handle_create(file_priv, omap_fb->bo, handle); } static void omap_framebuffer_destroy(struct drm_framebuffer *fb) { + struct drm_device *dev = fb->dev; struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); - int i, n = drm_format_num_planes(omap_fb->format->pixel_format); DBG("destroy: FB ID: %d (%p)", fb->base.id, fb); drm_framebuffer_cleanup(fb); - for (i = 0; i < n; i++) { - struct plane *plane = &omap_fb->planes[i]; - if (plane->bo) - drm_gem_object_unreference_unlocked(plane->bo); + if (omap_fb->bo) { + if (omap_fb->paddr && omap_gem_put_paddr(omap_fb->bo)) + dev_err(dev->dev, "could not unmap!\n"); + drm_gem_object_unreference_unlocked(omap_fb->bo); } kfree(omap_fb); @@ -123,76 +83,37 @@ static const struct drm_framebuffer_funcs omap_framebuffer_funcs = { .dirty = omap_framebuffer_dirty, }; -/* pins buffer in preparation for scanout */ -int omap_framebuffer_pin(struct drm_framebuffer *fb) +/* returns the buffer size */ +int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y, + void **vaddr, dma_addr_t *paddr, unsigned int *screen_width) { struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); - int ret, i, n = drm_format_num_planes(omap_fb->format->pixel_format); + int bpp = fb->bits_per_pixel / 8; + unsigned long offset; - for (i = 0; i < n; i++) { - struct plane *plane = &omap_fb->planes[i]; - ret = omap_gem_get_paddr(plane->bo, &plane->paddr, true); - if (ret) - goto fail; - } + offset = (x * bpp) + (y * fb->pitch); - return 0; - -fail: - while (--i > 0) { - struct plane *plane = &omap_fb->planes[i]; - omap_gem_put_paddr(plane->bo); + if (vaddr) { + void *bo_vaddr = omap_gem_vaddr(omap_fb->bo); + /* note: we can only count on having a vaddr for buffers that + * are allocated physically contiguously to begin with (ie. + * dma_alloc_coherent()). But this should be ok because it + * is only used by legacy fbdev + */ + BUG_ON(IS_ERR_OR_NULL(bo_vaddr)); + *vaddr = bo_vaddr + offset; } - return ret; -} -/* releases buffer when done with scanout */ -void omap_framebuffer_unpin(struct drm_framebuffer *fb) -{ - struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); - int i, n = drm_format_num_planes(omap_fb->format->pixel_format); + *paddr = omap_fb->paddr + offset; + *screen_width = fb->pitch / bpp; - for (i = 0; i < n; i++) { - struct plane *plane = &omap_fb->planes[i]; - omap_gem_put_paddr(plane->bo); - } + return omap_fb->size - offset; } -/* update ovl info for scanout, handles cases of multi-planar fb's, etc. - */ -void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, int x, int y, - struct omap_overlay_info *info) +struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb) { struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); - const struct format *format = omap_fb->format; - struct plane *plane = &omap_fb->planes[0]; - unsigned int offset; - - offset = plane->offset + - (x * format->planes[0].stride_bpp) + - (y * plane->pitch / format->planes[0].sub_y); - - info->color_mode = format->dss_format; - info->paddr = plane->paddr + offset; - info->screen_width = plane->pitch / format->planes[0].stride_bpp; - - if (format->dss_format == OMAP_DSS_COLOR_NV12) { - plane = &omap_fb->planes[1]; - offset = plane->offset + - (x * format->planes[1].stride_bpp) + - (y * plane->pitch / format->planes[1].sub_y); - info->p_uv_addr = plane->paddr + offset; - } else { - info->p_uv_addr = 0; - } -} - -struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb, int p) -{ - struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); - if (p >= drm_format_num_planes(omap_fb->format->pixel_format)) - return NULL; - return omap_fb->planes[p].bo; + return omap_fb->bo; } /* iterate thru all the connectors, returning ones that are attached @@ -250,57 +171,39 @@ void omap_framebuffer_flush(struct drm_framebuffer *fb, } struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, - struct drm_file *file, struct drm_mode_fb_cmd2 *mode_cmd) + struct drm_file *file, struct drm_mode_fb_cmd *mode_cmd) { - struct drm_gem_object *bos[4]; + struct drm_gem_object *bo; struct drm_framebuffer *fb; - int ret; - - ret = objects_lookup(dev, file, mode_cmd->pixel_format, - bos, mode_cmd->handles); - if (ret) - return ERR_PTR(ret); - - fb = omap_framebuffer_init(dev, mode_cmd, bos); - if (IS_ERR(fb)) { - int i, n = drm_format_num_planes(mode_cmd->pixel_format); - for (i = 0; i < n; i++) - drm_gem_object_unreference_unlocked(bos[i]); - return fb; + bo = drm_gem_object_lookup(dev, file, mode_cmd->handle); + if (!bo) { + return ERR_PTR(-ENOENT); + } + fb = omap_framebuffer_init(dev, mode_cmd, bo); + if (!fb) { + return ERR_PTR(-ENOMEM); } return fb; } struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, - struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos) + struct drm_mode_fb_cmd *mode_cmd, struct drm_gem_object *bo) { struct omap_framebuffer *omap_fb; struct drm_framebuffer *fb = NULL; - const struct format *format = NULL; - int ret, i, n = drm_format_num_planes(mode_cmd->pixel_format); + int size, ret; - DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)", + DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%d)", dev, mode_cmd, mode_cmd->width, mode_cmd->height, - (char *)&mode_cmd->pixel_format); - - for (i = 0; i < ARRAY_SIZE(formats); i++) { - if (formats[i].pixel_format == mode_cmd->pixel_format) { - format = &formats[i]; - break; - } - } + mode_cmd->bpp); - if (!format) { - dev_err(dev->dev, "unsupported pixel format: %4.4s\n", - (char *)&mode_cmd->pixel_format); - ret = -EINVAL; - goto fail; - } + /* in case someone tries to feed us a completely bogus stride: */ + mode_cmd->pitch = align_pitch(mode_cmd->pitch, + mode_cmd->width, mode_cmd->bpp); omap_fb = kzalloc(sizeof(*omap_fb), GFP_KERNEL); if (!omap_fb) { dev_err(dev->dev, "could not allocate fb\n"); - ret = -ENOMEM; goto fail; } @@ -313,32 +216,19 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, DBG("create: FB ID: %d (%p)", fb->base.id, fb); - omap_fb->format = format; - - for (i = 0; i < n; i++) { - struct plane *plane = &omap_fb->planes[i]; - int size, pitch = mode_cmd->pitches[i]; - - if (pitch < (mode_cmd->width * format->planes[i].stride_bpp)) { - dev_err(dev->dev, "provided buffer pitch is too small! %d < %d\n", - pitch, mode_cmd->width * format->planes[i].stride_bpp); - ret = -EINVAL; - goto fail; - } + size = PAGE_ALIGN(mode_cmd->pitch * mode_cmd->height); - size = pitch * mode_cmd->height / format->planes[i].sub_y; + if (size > bo->size) { + dev_err(dev->dev, "provided buffer object is too small!\n"); + goto fail; + } - if (size > (bos[i]->size - mode_cmd->offsets[i])) { - dev_err(dev->dev, "provided buffer object is too small! %d < %d\n", - bos[i]->size - mode_cmd->offsets[i], size); - ret = -EINVAL; - goto fail; - } + omap_fb->bo = bo; + omap_fb->size = size; - plane->bo = bos[i]; - plane->offset = mode_cmd->offsets[i]; - plane->pitch = mode_cmd->pitches[i]; - plane->paddr = pitch; + if (omap_gem_get_paddr(bo, &omap_fb->paddr, true)) { + dev_err(dev->dev, "could not map (paddr)!\n"); + goto fail; } drm_helper_mode_fill_fb_struct(fb, mode_cmd); @@ -349,5 +239,5 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, if (fb) { omap_framebuffer_destroy(fb); } - return ERR_PTR(ret); + return NULL; } diff --git a/trunk/drivers/staging/omapdrm/omap_fbdev.c b/trunk/drivers/staging/omapdrm/omap_fbdev.c index 96940bbfc6f4..093ae2f87b20 100644 --- a/trunk/drivers/staging/omapdrm/omap_fbdev.c +++ b/trunk/drivers/staging/omapdrm/omap_fbdev.c @@ -129,8 +129,10 @@ static int omap_fbdev_create(struct drm_fb_helper *helper, struct drm_framebuffer *fb = NULL; union omap_gem_size gsize; struct fb_info *fbi = NULL; - struct drm_mode_fb_cmd2 mode_cmd = {0}; + struct drm_mode_fb_cmd mode_cmd = {0}; dma_addr_t paddr; + void __iomem *vaddr; + int size, screen_width; int ret; /* only doing ARGB32 since this is what is needed to alpha-blend @@ -143,56 +145,36 @@ static int omap_fbdev_create(struct drm_fb_helper *helper, sizes->surface_height, sizes->surface_bpp, sizes->fb_width, sizes->fb_height); - mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, - sizes->surface_depth); - mode_cmd.width = sizes->surface_width; mode_cmd.height = sizes->surface_height; - mode_cmd.pitches[0] = align_pitch( - mode_cmd.width * ((sizes->surface_bpp + 7) / 8), - mode_cmd.width, sizes->surface_bpp); + mode_cmd.bpp = sizes->surface_bpp; + mode_cmd.depth = sizes->surface_depth; + + mode_cmd.pitch = align_pitch( + mode_cmd.width * ((mode_cmd.bpp + 7) / 8), + mode_cmd.width, mode_cmd.bpp); fbdev->ywrap_enabled = priv->has_dmm && ywrap_enabled; if (fbdev->ywrap_enabled) { /* need to align pitch to page size if using DMM scrolling */ - mode_cmd.pitches[0] = ALIGN(mode_cmd.pitches[0], PAGE_SIZE); + mode_cmd.pitch = ALIGN(mode_cmd.pitch, PAGE_SIZE); } /* allocate backing bo */ gsize = (union omap_gem_size){ - .bytes = PAGE_ALIGN(mode_cmd.pitches[0] * mode_cmd.height), + .bytes = PAGE_ALIGN(mode_cmd.pitch * mode_cmd.height), }; DBG("allocating %d bytes for fb %d", gsize.bytes, dev->primary->index); fbdev->bo = omap_gem_new(dev, gsize, OMAP_BO_SCANOUT | OMAP_BO_WC); if (!fbdev->bo) { dev_err(dev->dev, "failed to allocate buffer object\n"); - ret = -ENOMEM; goto fail; } - fb = omap_framebuffer_init(dev, &mode_cmd, &fbdev->bo); - if (IS_ERR(fb)) { + fb = omap_framebuffer_init(dev, &mode_cmd, fbdev->bo); + if (!fb) { dev_err(dev->dev, "failed to allocate fb\n"); - /* note: if fb creation failed, we can't rely on fb destroy - * to unref the bo: - */ - drm_gem_object_unreference(fbdev->bo); - ret = PTR_ERR(fb); - goto fail; - } - - /* note: this keeps the bo pinned.. which is perhaps not ideal, - * but is needed as long as we use fb_mmap() to mmap to userspace - * (since this happens using fix.smem_start). Possibly we could - * implement our own mmap using GEM mmap support to avoid this - * (non-tiled buffer doesn't need to be pinned for fbcon to write - * to it). Then we just need to be sure that we are able to re- - * pin it in case of an opps. - */ - ret = omap_gem_get_paddr(fbdev->bo, &paddr, true); - if (ret) { - dev_err(dev->dev, "could not map (paddr)!\n"); ret = -ENOMEM; goto fail; } @@ -224,15 +206,18 @@ static int omap_fbdev_create(struct drm_fb_helper *helper, goto fail_unlock; } - drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); + drm_fb_helper_fill_fix(fbi, fb->pitch, fb->depth); drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height); + size = omap_framebuffer_get_buffer(fb, 0, 0, + &vaddr, &paddr, &screen_width); + dev->mode_config.fb_base = paddr; - fbi->screen_base = omap_gem_vaddr(fbdev->bo); - fbi->screen_size = fbdev->bo->size; + fbi->screen_base = vaddr; + fbi->screen_size = size; fbi->fix.smem_start = paddr; - fbi->fix.smem_len = fbdev->bo->size; + fbi->fix.smem_len = size; /* if we have DMM, then we can use it for scrolling by just * shuffling pages around in DMM rather than doing sw blit. @@ -377,11 +362,11 @@ void omap_fbdev_free(struct drm_device *dev) fbdev = to_omap_fbdev(priv->fbdev); + kfree(fbdev); + /* this will free the backing object */ if (fbdev->fb) fbdev->fb->funcs->destroy(fbdev->fb); - kfree(fbdev); - priv->fbdev = NULL; } diff --git a/trunk/drivers/staging/omapdrm/omap_gem.c b/trunk/drivers/staging/omapdrm/omap_gem.c index b7d6f886c5cf..e0ebd1d139f6 100644 --- a/trunk/drivers/staging/omapdrm/omap_gem.c +++ b/trunk/drivers/staging/omapdrm/omap_gem.c @@ -116,9 +116,6 @@ struct omap_gem_object { } *sync; }; -static int get_pages(struct drm_gem_object *obj, struct page ***pages); -static uint64_t mmap_offset(struct drm_gem_object *obj); - /* To deal with userspace mmap'ings of 2d tiled buffers, which (a) are * not necessarily pinned in TILER all the time, and (b) when they are * they are not necessarily page aligned, we reserve one or more small @@ -152,7 +149,7 @@ static void evict_entry(struct drm_gem_object *obj, { if (obj->dev->dev_mapping) { size_t size = PAGE_SIZE * usergart[fmt].height; - loff_t off = mmap_offset(obj) + + loff_t off = omap_gem_mmap_offset(obj) + (entry->obj_pgoff << PAGE_SHIFT); unmap_mapping_range(obj->dev->dev_mapping, off, size, 1); } @@ -192,6 +189,8 @@ static inline bool is_shmem(struct drm_gem_object *obj) return obj->filp != NULL; } +static int get_pages(struct drm_gem_object *obj, struct page ***pages); + static DEFINE_SPINLOCK(sync_lock); /** ensure backing pages are allocated */ @@ -252,7 +251,7 @@ static void omap_gem_detach_pages(struct drm_gem_object *obj) } /** get mmap offset */ -static uint64_t mmap_offset(struct drm_gem_object *obj) +uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj) { if (!obj->map_list.map) { /* Make it mmapable */ @@ -268,15 +267,6 @@ static uint64_t mmap_offset(struct drm_gem_object *obj) return (uint64_t)obj->map_list.hash.key << PAGE_SHIFT; } -uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj) -{ - uint64_t offset; - mutex_lock(&obj->dev->struct_mutex); - offset = mmap_offset(obj); - mutex_unlock(&obj->dev->struct_mutex); - return offset; -} - /** get mmap size */ size_t omap_gem_mmap_size(struct drm_gem_object *obj) { @@ -1044,11 +1034,6 @@ void omap_gem_free_object(struct drm_gem_object *obj) drm_gem_free_mmap_offset(obj); } - /* this means the object is still pinned.. which really should - * not happen. I think.. - */ - WARN_ON(omap_obj->paddr_cnt > 0); - /* don't free externally allocated backing memory */ if (!(omap_obj->flags & OMAP_BO_EXT_MEM)) { if (omap_obj->pages) { diff --git a/trunk/drivers/staging/omapdrm/omap_plane.c b/trunk/drivers/staging/omapdrm/omap_plane.c deleted file mode 100644 index 97909124a1fe..000000000000 --- a/trunk/drivers/staging/omapdrm/omap_plane.c +++ /dev/null @@ -1,344 +0,0 @@ -/* - * drivers/staging/omapdrm/omap_plane.c - * - * Copyright (C) 2011 Texas Instruments - * Author: Rob Clark - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -#include "omap_drv.h" - -/* some hackery because omapdss has an 'enum omap_plane' (which would be - * better named omap_plane_id).. and compiler seems unhappy about having - * both a 'struct omap_plane' and 'enum omap_plane' - */ -#define omap_plane _omap_plane - -/* - * plane funcs - */ - -#define to_omap_plane(x) container_of(x, struct omap_plane, base) - -struct omap_plane { - struct drm_plane base; - struct omap_overlay *ovl; - struct omap_overlay_info info; - - /* Source values, converted to integers because we don't support - * fractional positions: - */ - unsigned int src_x, src_y; - - /* last fb that we pinned: */ - struct drm_framebuffer *pinned_fb; -}; - - -/* push changes down to dss2 */ -static int commit(struct drm_plane *plane) -{ - struct drm_device *dev = plane->dev; - struct omap_plane *omap_plane = to_omap_plane(plane); - struct omap_overlay *ovl = omap_plane->ovl; - struct omap_overlay_info *info = &omap_plane->info; - int ret; - - DBG("%s", ovl->name); - DBG("%dx%d -> %dx%d (%d)", info->width, info->height, info->out_width, - info->out_height, info->screen_width); - DBG("%d,%d %08x %08x", info->pos_x, info->pos_y, - info->paddr, info->p_uv_addr); - - /* NOTE: do we want to do this at all here, or just wait - * for dpms(ON) since other CRTC's may not have their mode - * set yet, so fb dimensions may still change.. - */ - ret = ovl->set_overlay_info(ovl, info); - if (ret) { - dev_err(dev->dev, "could not set overlay info\n"); - return ret; - } - - /* our encoder doesn't necessarily get a commit() after this, in - * particular in the dpms() and mode_set_base() cases, so force the - * manager to update: - * - * could this be in the encoder somehow? - */ - if (ovl->manager) { - ret = ovl->manager->apply(ovl->manager); - if (ret) { - dev_err(dev->dev, "could not apply settings\n"); - return ret; - } - } - - if (ovl->is_enabled(ovl)) { - omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y, - info->out_width, info->out_height); - } - - return 0; -} - -/* when CRTC that we are attached to has potentially changed, this checks - * if we are attached to proper manager, and if necessary updates. - */ -static void update_manager(struct drm_plane *plane) -{ - struct omap_drm_private *priv = plane->dev->dev_private; - struct omap_plane *omap_plane = to_omap_plane(plane); - struct omap_overlay *ovl = omap_plane->ovl; - struct omap_overlay_manager *mgr = NULL; - int i; - - if (plane->crtc) { - for (i = 0; i < priv->num_encoders; i++) { - struct drm_encoder *encoder = priv->encoders[i]; - if (encoder->crtc == plane->crtc) { - mgr = omap_encoder_get_manager(encoder); - break; - } - } - } - - if (ovl->manager != mgr) { - bool enabled = ovl->is_enabled(ovl); - - /* don't switch things around with enabled overlays: */ - if (enabled) - omap_plane_dpms(plane, DRM_MODE_DPMS_OFF); - - if (ovl->manager) { - DBG("disconnecting %s from %s", ovl->name, - ovl->manager->name); - ovl->unset_manager(ovl); - } - - if (mgr) { - DBG("connecting %s to %s", ovl->name, mgr->name); - ovl->set_manager(ovl, mgr); - } - - if (enabled && mgr) - omap_plane_dpms(plane, DRM_MODE_DPMS_ON); - } -} - -/* update which fb (if any) is pinned for scanout */ -static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb) -{ - struct omap_plane *omap_plane = to_omap_plane(plane); - int ret = 0; - - if (omap_plane->pinned_fb != fb) { - if (omap_plane->pinned_fb) - omap_framebuffer_unpin(omap_plane->pinned_fb); - omap_plane->pinned_fb = fb; - if (fb) - ret = omap_framebuffer_pin(fb); - } - - return ret; -} - -/* update parameters that are dependent on the framebuffer dimensions and - * position within the fb that this plane scans out from. This is called - * when framebuffer or x,y base may have changed. - */ -static void update_scanout(struct drm_plane *plane) -{ - struct omap_plane *omap_plane = to_omap_plane(plane); - struct omap_overlay_info *info = &omap_plane->info; - int ret; - - ret = update_pin(plane, plane->fb); - if (ret) { - dev_err(plane->dev->dev, - "could not pin fb: %d\n", ret); - omap_plane_dpms(plane, DRM_MODE_DPMS_OFF); - return; - } - - omap_framebuffer_update_scanout(plane->fb, - omap_plane->src_x, omap_plane->src_y, info); - - DBG("%s: %d,%d: %08x %08x (%d)", omap_plane->ovl->name, - omap_plane->src_x, omap_plane->src_y, - (u32)info->paddr, (u32)info->p_uv_addr, - info->screen_width); -} - -int omap_plane_mode_set(struct drm_plane *plane, - struct drm_crtc *crtc, struct drm_framebuffer *fb, - int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h) -{ - struct omap_plane *omap_plane = to_omap_plane(plane); - - /* src values are in Q16 fixed point, convert to integer: */ - src_x = src_x >> 16; - src_y = src_y >> 16; - src_w = src_w >> 16; - src_h = src_h >> 16; - - omap_plane->info.pos_x = crtc_x; - omap_plane->info.pos_y = crtc_y; - omap_plane->info.out_width = crtc_w; - omap_plane->info.out_height = crtc_h; - omap_plane->info.width = src_w; - omap_plane->info.height = src_h; - omap_plane->src_x = src_x; - omap_plane->src_y = src_y; - - /* note: this is done after this fxn returns.. but if we need - * to do a commit/update_scanout, etc before this returns we - * need the current value. - */ - plane->fb = fb; - plane->crtc = crtc; - - update_scanout(plane); - update_manager(plane); - - return 0; -} - -static int omap_plane_update(struct drm_plane *plane, - struct drm_crtc *crtc, struct drm_framebuffer *fb, - int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h) -{ - omap_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, - src_x, src_y, src_w, src_h); - return omap_plane_dpms(plane, DRM_MODE_DPMS_ON); -} - -static int omap_plane_disable(struct drm_plane *plane) -{ - return omap_plane_dpms(plane, DRM_MODE_DPMS_OFF); -} - -static void omap_plane_destroy(struct drm_plane *plane) -{ - struct omap_plane *omap_plane = to_omap_plane(plane); - DBG("%s", omap_plane->ovl->name); - omap_plane_disable(plane); - drm_plane_cleanup(plane); - kfree(omap_plane); -} - -int omap_plane_dpms(struct drm_plane *plane, int mode) -{ - struct omap_plane *omap_plane = to_omap_plane(plane); - struct omap_overlay *ovl = omap_plane->ovl; - int r; - - DBG("%s: %d", omap_plane->ovl->name, mode); - - if (mode == DRM_MODE_DPMS_ON) { - update_scanout(plane); - r = commit(plane); - if (!r) - r = ovl->enable(ovl); - } else { - r = ovl->disable(ovl); - update_pin(plane, NULL); - } - - return r; -} - -static const struct drm_plane_funcs omap_plane_funcs = { - .update_plane = omap_plane_update, - .disable_plane = omap_plane_disable, - .destroy = omap_plane_destroy, -}; - -static const uint32_t formats[] = { - DRM_FORMAT_RGB565, - DRM_FORMAT_RGBX4444, - DRM_FORMAT_XRGB4444, - DRM_FORMAT_RGBA4444, - DRM_FORMAT_ABGR4444, - DRM_FORMAT_XRGB1555, - DRM_FORMAT_ARGB1555, - DRM_FORMAT_RGB888, - DRM_FORMAT_RGBX8888, - DRM_FORMAT_XRGB8888, - DRM_FORMAT_RGBA8888, - DRM_FORMAT_ARGB8888, - DRM_FORMAT_NV12, - DRM_FORMAT_YUYV, - DRM_FORMAT_UYVY, -}; - -/* initialize plane */ -struct drm_plane *omap_plane_init(struct drm_device *dev, - struct omap_overlay *ovl, unsigned int possible_crtcs, - bool priv) -{ - struct drm_plane *plane = NULL; - struct omap_plane *omap_plane; - - DBG("%s: possible_crtcs=%08x, priv=%d", ovl->name, - possible_crtcs, priv); - - omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL); - if (!omap_plane) { - dev_err(dev->dev, "could not allocate plane\n"); - goto fail; - } - - omap_plane->ovl = ovl; - plane = &omap_plane->base; - - drm_plane_init(dev, plane, possible_crtcs, &omap_plane_funcs, - formats, ARRAY_SIZE(formats), priv); - - /* get our starting configuration, set defaults for parameters - * we don't currently use, etc: - */ - ovl->get_overlay_info(ovl, &omap_plane->info); - omap_plane->info.rotation_type = OMAP_DSS_ROT_DMA; - omap_plane->info.rotation = OMAP_DSS_ROT_0; - omap_plane->info.global_alpha = 0xff; - omap_plane->info.mirror = 0; - omap_plane->info.mirror = 0; - - /* Set defaults depending on whether we are a CRTC or overlay - * layer. - * TODO add ioctl to give userspace an API to change this.. this - * will come in a subsequent patch. - */ - if (priv) - omap_plane->info.zorder = 0; - else - omap_plane->info.zorder = 1; - - update_manager(plane); - - return plane; - -fail: - if (plane) { - omap_plane_destroy(plane); - } - return NULL; -} diff --git a/trunk/drivers/staging/omapdrm/omap_priv.h b/trunk/drivers/staging/omapdrm/omap_priv.h index ef6441447147..c324709aa9a1 100644 --- a/trunk/drivers/staging/omapdrm/omap_priv.h +++ b/trunk/drivers/staging/omapdrm/omap_priv.h @@ -27,22 +27,14 @@ * pipes/overlays/CRTCs are used.. if this is not provided, then instead the * first CONFIG_DRM_OMAP_NUM_CRTCS are used, and they are each connected to * one manager, with priority given to managers that are connected to - * detected devices. Remaining overlays are used as video planes. This - * should be a good default behavior for most cases, but yet there still - * might be times when you wish to do something different. + * detected devices. This should be a good default behavior for most cases, + * but yet there still might be times when you wish to do something different. */ struct omap_kms_platform_data { - /* overlays to use as CRTCs: */ int ovl_cnt; const int *ovl_ids; - - /* overlays to use as video planes: */ - int pln_cnt; - const int *pln_ids; - int mgr_cnt; const int *mgr_ids; - int dev_cnt; const char **dev_names; }; diff --git a/trunk/drivers/staging/pohmelfs/Kconfig b/trunk/drivers/staging/pohmelfs/Kconfig new file mode 100644 index 000000000000..8d53b1a1e715 --- /dev/null +++ b/trunk/drivers/staging/pohmelfs/Kconfig @@ -0,0 +1,20 @@ +config POHMELFS + tristate "POHMELFS filesystem support" + depends on NET + select CONNECTOR + select CRYPTO + select CRYPTO_BLKCIPHER + select CRYPTO_HMAC + help + POHMELFS stands for Parallel Optimized Host Message Exchange Layered + File System. This is a network filesystem which supports coherent + caching of data and metadata on clients. + +config POHMELFS_DEBUG + bool "POHMELFS debugging" + depends on POHMELFS + default n + help + Turns on excessive POHMELFS debugging facilities. + You usually do not want to slow things down noticeably and get really + lots of kernel messages in syslog. diff --git a/trunk/drivers/staging/pohmelfs/Makefile b/trunk/drivers/staging/pohmelfs/Makefile new file mode 100644 index 000000000000..196561ca26bc --- /dev/null +++ b/trunk/drivers/staging/pohmelfs/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_POHMELFS) += pohmelfs.o + +pohmelfs-y := inode.o config.o dir.o net.o path_entry.o trans.o crypto.o lock.o mcache.o diff --git a/trunk/drivers/staging/pohmelfs/config.c b/trunk/drivers/staging/pohmelfs/config.c new file mode 100644 index 000000000000..b6c42cb0d1c6 --- /dev/null +++ b/trunk/drivers/staging/pohmelfs/config.c @@ -0,0 +1,611 @@ +/* + * 2007+ Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netfs.h" + +/* + * Global configuration list. + * Each client can be asked to get one of them. + * + * Allows to provide remote server address (ipv4/v6/whatever), port + * and so on via kernel connector. + */ + +static struct cb_id pohmelfs_cn_id = {.idx = POHMELFS_CN_IDX, .val = POHMELFS_CN_VAL}; +static LIST_HEAD(pohmelfs_config_list); +static DEFINE_MUTEX(pohmelfs_config_lock); + +static inline int pohmelfs_config_eql(struct pohmelfs_ctl *sc, struct pohmelfs_ctl *ctl) +{ + if (sc->idx == ctl->idx && sc->type == ctl->type && + sc->proto == ctl->proto && + sc->addrlen == ctl->addrlen && + !memcmp(&sc->addr, &ctl->addr, ctl->addrlen)) + return 1; + + return 0; +} + +static struct pohmelfs_config_group *pohmelfs_find_config_group(unsigned int idx) +{ + struct pohmelfs_config_group *g, *group = NULL; + + list_for_each_entry(g, &pohmelfs_config_list, group_entry) { + if (g->idx == idx) { + group = g; + break; + } + } + + return group; +} + +static struct pohmelfs_config_group *pohmelfs_find_create_config_group(unsigned int idx) +{ + struct pohmelfs_config_group *g; + + g = pohmelfs_find_config_group(idx); + if (g) + return g; + + g = kzalloc(sizeof(struct pohmelfs_config_group), GFP_KERNEL); + if (!g) + return NULL; + + INIT_LIST_HEAD(&g->config_list); + g->idx = idx; + g->num_entry = 0; + + list_add_tail(&g->group_entry, &pohmelfs_config_list); + + return g; +} + +static inline void pohmelfs_insert_config_entry(struct pohmelfs_sb *psb, struct pohmelfs_config *dst) +{ + struct pohmelfs_config *tmp; + + INIT_LIST_HEAD(&dst->config_entry); + + list_for_each_entry(tmp, &psb->state_list, config_entry) { + if (dst->state.ctl.prio > tmp->state.ctl.prio) + list_add_tail(&dst->config_entry, &tmp->config_entry); + } + if (list_empty(&dst->config_entry)) + list_add_tail(&dst->config_entry, &psb->state_list); +} + +static int pohmelfs_move_config_entry(struct pohmelfs_sb *psb, + struct pohmelfs_config *dst, struct pohmelfs_config *new) +{ + if ((dst->state.ctl.prio == new->state.ctl.prio) && + (dst->state.ctl.perm == new->state.ctl.perm)) + return 0; + + dprintk("%s: dst: prio: %d, perm: %x, new: prio: %d, perm: %d.\n", + __func__, dst->state.ctl.prio, dst->state.ctl.perm, + new->state.ctl.prio, new->state.ctl.perm); + dst->state.ctl.prio = new->state.ctl.prio; + dst->state.ctl.perm = new->state.ctl.perm; + + list_del_init(&dst->config_entry); + pohmelfs_insert_config_entry(psb, dst); + return 0; +} + +/* + * pohmelfs_copy_config() is used to copy new state configs from the + * config group (controlled by the netlink messages) into the superblock. + * This happens either at startup time where no transactions can access + * the list of the configs (and thus list of the network states), or at + * run-time, where it is protected by the psb->state_lock. + */ +int pohmelfs_copy_config(struct pohmelfs_sb *psb) +{ + struct pohmelfs_config_group *g; + struct pohmelfs_config *c, *dst; + int err = -ENODEV; + + mutex_lock(&pohmelfs_config_lock); + + g = pohmelfs_find_config_group(psb->idx); + if (!g) + goto out_unlock; + + /* + * Run over all entries in given config group and try to create and + * initialize those, which do not exist in superblock list. + * Skip all existing entries. + */ + + list_for_each_entry(c, &g->config_list, config_entry) { + err = 0; + list_for_each_entry(dst, &psb->state_list, config_entry) { + if (pohmelfs_config_eql(&dst->state.ctl, &c->state.ctl)) { + err = pohmelfs_move_config_entry(psb, dst, c); + if (!err) + err = -EEXIST; + break; + } + } + + if (err) + continue; + + dst = kzalloc(sizeof(struct pohmelfs_config), GFP_KERNEL); + if (!dst) { + err = -ENOMEM; + break; + } + + memcpy(&dst->state.ctl, &c->state.ctl, sizeof(struct pohmelfs_ctl)); + + pohmelfs_insert_config_entry(psb, dst); + + err = pohmelfs_state_init_one(psb, dst); + if (err) { + list_del(&dst->config_entry); + kfree(dst); + } + + err = 0; + } + +out_unlock: + mutex_unlock(&pohmelfs_config_lock); + + return err; +} + +int pohmelfs_copy_crypto(struct pohmelfs_sb *psb) +{ + struct pohmelfs_config_group *g; + int err = -ENOENT; + + mutex_lock(&pohmelfs_config_lock); + g = pohmelfs_find_config_group(psb->idx); + if (!g) + goto err_out_exit; + + if (g->hash_string) { + err = -ENOMEM; + psb->hash_string = kstrdup(g->hash_string, GFP_KERNEL); + if (!psb->hash_string) + goto err_out_exit; + psb->hash_strlen = g->hash_strlen; + } + + if (g->cipher_string) { + psb->cipher_string = kstrdup(g->cipher_string, GFP_KERNEL); + if (!psb->cipher_string) + goto err_out_free_hash_string; + psb->cipher_strlen = g->cipher_strlen; + } + + if (g->hash_keysize) { + psb->hash_key = kmemdup(g->hash_key, g->hash_keysize, + GFP_KERNEL); + if (!psb->hash_key) + goto err_out_free_cipher_string; + psb->hash_keysize = g->hash_keysize; + } + + if (g->cipher_keysize) { + psb->cipher_key = kmemdup(g->cipher_key, g->cipher_keysize, + GFP_KERNEL); + if (!psb->cipher_key) + goto err_out_free_hash; + psb->cipher_keysize = g->cipher_keysize; + } + + mutex_unlock(&pohmelfs_config_lock); + + return 0; + +err_out_free_hash: + kfree(psb->hash_key); +err_out_free_cipher_string: + kfree(psb->cipher_string); +err_out_free_hash_string: + kfree(psb->hash_string); +err_out_exit: + mutex_unlock(&pohmelfs_config_lock); + return err; +} + +static int pohmelfs_send_reply(int err, int msg_num, int action, struct cn_msg *msg, struct pohmelfs_ctl *ctl) +{ + struct pohmelfs_cn_ack *ack; + + ack = kzalloc(sizeof(struct pohmelfs_cn_ack), GFP_KERNEL); + if (!ack) + return -ENOMEM; + + memcpy(&ack->msg, msg, sizeof(struct cn_msg)); + + if (action == POHMELFS_CTLINFO_ACK) + memcpy(&ack->ctl, ctl, sizeof(struct pohmelfs_ctl)); + + ack->msg.len = sizeof(struct pohmelfs_cn_ack) - sizeof(struct cn_msg); + ack->msg.ack = msg->ack + 1; + ack->error = err; + ack->msg_num = msg_num; + + cn_netlink_send(&ack->msg, 0, GFP_KERNEL); + kfree(ack); + return 0; +} + +static int pohmelfs_cn_disp(struct cn_msg *msg) +{ + struct pohmelfs_config_group *g; + struct pohmelfs_ctl *ctl = (struct pohmelfs_ctl *)msg->data; + struct pohmelfs_config *c, *tmp; + int err = 0, i = 1; + + if (msg->len != sizeof(struct pohmelfs_ctl)) + return -EBADMSG; + + mutex_lock(&pohmelfs_config_lock); + + g = pohmelfs_find_config_group(ctl->idx); + if (!g) { + pohmelfs_send_reply(err, 0, POHMELFS_NOINFO_ACK, msg, NULL); + goto out_unlock; + } + + list_for_each_entry_safe(c, tmp, &g->config_list, config_entry) { + struct pohmelfs_ctl *sc = &c->state.ctl; + if (pohmelfs_send_reply(err, g->num_entry - i, POHMELFS_CTLINFO_ACK, msg, sc)) { + err = -ENOMEM; + goto out_unlock; + } + i += 1; + } + + out_unlock: + mutex_unlock(&pohmelfs_config_lock); + return err; +} + +static int pohmelfs_cn_dump(struct cn_msg *msg) +{ + struct pohmelfs_config_group *g; + struct pohmelfs_config *c, *tmp; + int err = 0, i = 1; + int total_msg = 0; + + if (msg->len != sizeof(struct pohmelfs_ctl)) + return -EBADMSG; + + mutex_lock(&pohmelfs_config_lock); + + list_for_each_entry(g, &pohmelfs_config_list, group_entry) + total_msg += g->num_entry; + if (total_msg == 0) { + if (pohmelfs_send_reply(err, 0, POHMELFS_NOINFO_ACK, msg, NULL)) + err = -ENOMEM; + goto out_unlock; + } + + list_for_each_entry(g, &pohmelfs_config_list, group_entry) { + list_for_each_entry_safe(c, tmp, &g->config_list, + config_entry) { + struct pohmelfs_ctl *sc = &c->state.ctl; + if (pohmelfs_send_reply(err, total_msg - i, + POHMELFS_CTLINFO_ACK, msg, + sc)) { + err = -ENOMEM; + goto out_unlock; + } + i += 1; + } + } + +out_unlock: + mutex_unlock(&pohmelfs_config_lock); + return err; +} + +static int pohmelfs_cn_flush(struct cn_msg *msg) +{ + struct pohmelfs_config_group *g; + struct pohmelfs_ctl *ctl = (struct pohmelfs_ctl *)msg->data; + struct pohmelfs_config *c, *tmp; + int err = 0; + + if (msg->len != sizeof(struct pohmelfs_ctl)) + return -EBADMSG; + + mutex_lock(&pohmelfs_config_lock); + + if (ctl->idx != POHMELFS_NULL_IDX) { + g = pohmelfs_find_config_group(ctl->idx); + + if (!g) + goto out_unlock; + + list_for_each_entry_safe(c, tmp, &g->config_list, config_entry) { + list_del(&c->config_entry); + g->num_entry--; + kfree(c); + } + } else { + list_for_each_entry(g, &pohmelfs_config_list, group_entry) { + list_for_each_entry_safe(c, tmp, &g->config_list, + config_entry) { + list_del(&c->config_entry); + g->num_entry--; + kfree(c); + } + } + } + +out_unlock: + mutex_unlock(&pohmelfs_config_lock); + pohmelfs_cn_dump(msg); + + return err; +} + +static int pohmelfs_modify_config(struct pohmelfs_ctl *old, struct pohmelfs_ctl *new) +{ + old->perm = new->perm; + old->prio = new->prio; + return 0; +} + +static int pohmelfs_cn_ctl(struct cn_msg *msg, int action) +{ + struct pohmelfs_config_group *g; + struct pohmelfs_ctl *ctl = (struct pohmelfs_ctl *)msg->data; + struct pohmelfs_config *c, *tmp; + int err = 0; + + if (msg->len != sizeof(struct pohmelfs_ctl)) + return -EBADMSG; + + mutex_lock(&pohmelfs_config_lock); + + g = pohmelfs_find_create_config_group(ctl->idx); + if (!g) { + err = -ENOMEM; + goto out_unlock; + } + + list_for_each_entry_safe(c, tmp, &g->config_list, config_entry) { + struct pohmelfs_ctl *sc = &c->state.ctl; + + if (pohmelfs_config_eql(sc, ctl)) { + if (action == POHMELFS_FLAGS_ADD) { + err = -EEXIST; + goto out_unlock; + } else if (action == POHMELFS_FLAGS_DEL) { + list_del(&c->config_entry); + g->num_entry--; + kfree(c); + goto out_unlock; + } else if (action == POHMELFS_FLAGS_MODIFY) { + err = pohmelfs_modify_config(sc, ctl); + goto out_unlock; + } else { + err = -EEXIST; + goto out_unlock; + } + } + } + if (action == POHMELFS_FLAGS_DEL) { + err = -EBADMSG; + goto out_unlock; + } + + c = kzalloc(sizeof(struct pohmelfs_config), GFP_KERNEL); + if (!c) { + err = -ENOMEM; + goto out_unlock; + } + memcpy(&c->state.ctl, ctl, sizeof(struct pohmelfs_ctl)); + g->num_entry++; + + list_add_tail(&c->config_entry, &g->config_list); + + out_unlock: + mutex_unlock(&pohmelfs_config_lock); + if (pohmelfs_send_reply(err, 0, POHMELFS_NOINFO_ACK, msg, NULL)) + err = -ENOMEM; + + return err; +} + +static int pohmelfs_crypto_hash_init(struct pohmelfs_config_group *g, struct pohmelfs_crypto *c) +{ + char *algo = (char *)c->data; + u8 *key = (u8 *)(algo + c->strlen); + + if (g->hash_string) + return -EEXIST; + + g->hash_string = kstrdup(algo, GFP_KERNEL); + if (!g->hash_string) + return -ENOMEM; + g->hash_strlen = c->strlen; + g->hash_keysize = c->keysize; + + g->hash_key = kmemdup(key, c->keysize, GFP_KERNEL); + if (!g->hash_key) { + kfree(g->hash_string); + return -ENOMEM; + } + + return 0; +} + +static int pohmelfs_crypto_cipher_init(struct pohmelfs_config_group *g, struct pohmelfs_crypto *c) +{ + char *algo = (char *)c->data; + u8 *key = (u8 *)(algo + c->strlen); + + if (g->cipher_string) + return -EEXIST; + + g->cipher_string = kstrdup(algo, GFP_KERNEL); + if (!g->cipher_string) + return -ENOMEM; + g->cipher_strlen = c->strlen; + g->cipher_keysize = c->keysize; + + g->cipher_key = kmemdup(key, c->keysize, GFP_KERNEL); + if (!g->cipher_key) { + kfree(g->cipher_string); + return -ENOMEM; + } + + return 0; +} + +static int pohmelfs_cn_crypto(struct cn_msg *msg) +{ + struct pohmelfs_crypto *crypto = (struct pohmelfs_crypto *)msg->data; + struct pohmelfs_config_group *g; + int err = 0; + + dprintk("%s: idx: %u, strlen: %u, type: %u, keysize: %u, algo: %s.\n", + __func__, crypto->idx, crypto->strlen, crypto->type, + crypto->keysize, (char *)crypto->data); + + mutex_lock(&pohmelfs_config_lock); + g = pohmelfs_find_create_config_group(crypto->idx); + if (!g) { + err = -ENOMEM; + goto out_unlock; + } + + switch (crypto->type) { + case POHMELFS_CRYPTO_HASH: + err = pohmelfs_crypto_hash_init(g, crypto); + break; + case POHMELFS_CRYPTO_CIPHER: + err = pohmelfs_crypto_cipher_init(g, crypto); + break; + default: + err = -ENOTSUPP; + break; + } + +out_unlock: + mutex_unlock(&pohmelfs_config_lock); + if (pohmelfs_send_reply(err, 0, POHMELFS_NOINFO_ACK, msg, NULL)) + err = -ENOMEM; + + return err; +} + +static void pohmelfs_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) +{ + int err; + + if (!cap_raised(current_cap(), CAP_SYS_ADMIN)) + return; + + switch (msg->flags) { + case POHMELFS_FLAGS_ADD: + case POHMELFS_FLAGS_DEL: + case POHMELFS_FLAGS_MODIFY: + err = pohmelfs_cn_ctl(msg, msg->flags); + break; + case POHMELFS_FLAGS_FLUSH: + err = pohmelfs_cn_flush(msg); + break; + case POHMELFS_FLAGS_SHOW: + err = pohmelfs_cn_disp(msg); + break; + case POHMELFS_FLAGS_DUMP: + err = pohmelfs_cn_dump(msg); + break; + case POHMELFS_FLAGS_CRYPTO: + err = pohmelfs_cn_crypto(msg); + break; + default: + err = -ENOSYS; + break; + } +} + +int pohmelfs_config_check(struct pohmelfs_config *config, int idx) +{ + struct pohmelfs_ctl *ctl = &config->state.ctl; + struct pohmelfs_config *tmp; + int err = -ENOENT; + struct pohmelfs_ctl *sc; + struct pohmelfs_config_group *g; + + mutex_lock(&pohmelfs_config_lock); + + g = pohmelfs_find_config_group(ctl->idx); + if (g) { + list_for_each_entry(tmp, &g->config_list, config_entry) { + sc = &tmp->state.ctl; + + if (pohmelfs_config_eql(sc, ctl)) { + err = 0; + break; + } + } + } + + mutex_unlock(&pohmelfs_config_lock); + + return err; +} + +int __init pohmelfs_config_init(void) +{ + /* XXX remove (void *) cast when vanilla connector got synced */ + return cn_add_callback(&pohmelfs_cn_id, "pohmelfs", (void *)pohmelfs_cn_callback); +} + +void pohmelfs_config_exit(void) +{ + struct pohmelfs_config *c, *tmp; + struct pohmelfs_config_group *g, *gtmp; + + cn_del_callback(&pohmelfs_cn_id); + + mutex_lock(&pohmelfs_config_lock); + list_for_each_entry_safe(g, gtmp, &pohmelfs_config_list, group_entry) { + list_for_each_entry_safe(c, tmp, &g->config_list, config_entry) { + list_del(&c->config_entry); + kfree(c); + } + + list_del(&g->group_entry); + + kfree(g->hash_string); + + kfree(g->cipher_string); + + kfree(g); + } + mutex_unlock(&pohmelfs_config_lock); +} diff --git a/trunk/drivers/staging/pohmelfs/crypto.c b/trunk/drivers/staging/pohmelfs/crypto.c new file mode 100644 index 000000000000..ad92771dce57 --- /dev/null +++ b/trunk/drivers/staging/pohmelfs/crypto.c @@ -0,0 +1,878 @@ +/* + * 2007+ Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "netfs.h" + +static struct crypto_hash *pohmelfs_init_hash(struct pohmelfs_sb *psb) +{ + int err; + struct crypto_hash *hash; + + hash = crypto_alloc_hash(psb->hash_string, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(hash)) { + err = PTR_ERR(hash); + dprintk("%s: idx: %u: failed to allocate hash '%s', err: %d.\n", + __func__, psb->idx, psb->hash_string, err); + goto err_out_exit; + } + + psb->crypto_attached_size = crypto_hash_digestsize(hash); + + if (!psb->hash_keysize) + return hash; + + err = crypto_hash_setkey(hash, psb->hash_key, psb->hash_keysize); + if (err) { + dprintk("%s: idx: %u: failed to set key for hash '%s', err: %d.\n", + __func__, psb->idx, psb->hash_string, err); + goto err_out_free; + } + + return hash; + +err_out_free: + crypto_free_hash(hash); +err_out_exit: + return ERR_PTR(err); +} + +static struct crypto_ablkcipher *pohmelfs_init_cipher(struct pohmelfs_sb *psb) +{ + int err = -EINVAL; + struct crypto_ablkcipher *cipher; + + if (!psb->cipher_keysize) + goto err_out_exit; + + cipher = crypto_alloc_ablkcipher(psb->cipher_string, 0, 0); + if (IS_ERR(cipher)) { + err = PTR_ERR(cipher); + dprintk("%s: idx: %u: failed to allocate cipher '%s', err: %d.\n", + __func__, psb->idx, psb->cipher_string, err); + goto err_out_exit; + } + + crypto_ablkcipher_clear_flags(cipher, ~0); + + err = crypto_ablkcipher_setkey(cipher, psb->cipher_key, psb->cipher_keysize); + if (err) { + dprintk("%s: idx: %u: failed to set key for cipher '%s', err: %d.\n", + __func__, psb->idx, psb->cipher_string, err); + goto err_out_free; + } + + return cipher; + +err_out_free: + crypto_free_ablkcipher(cipher); +err_out_exit: + return ERR_PTR(err); +} + +int pohmelfs_crypto_engine_init(struct pohmelfs_crypto_engine *e, struct pohmelfs_sb *psb) +{ + int err; + + e->page_num = 0; + + e->size = PAGE_SIZE; + e->data = kmalloc(e->size, GFP_KERNEL); + if (!e->data) { + err = -ENOMEM; + goto err_out_exit; + } + + if (psb->hash_string) { + e->hash = pohmelfs_init_hash(psb); + if (IS_ERR(e->hash)) { + err = PTR_ERR(e->hash); + e->hash = NULL; + goto err_out_free; + } + } + + if (psb->cipher_string) { + e->cipher = pohmelfs_init_cipher(psb); + if (IS_ERR(e->cipher)) { + err = PTR_ERR(e->cipher); + e->cipher = NULL; + goto err_out_free_hash; + } + } + + return 0; + +err_out_free_hash: + crypto_free_hash(e->hash); +err_out_free: + kfree(e->data); +err_out_exit: + return err; +} + +void pohmelfs_crypto_engine_exit(struct pohmelfs_crypto_engine *e) +{ + crypto_free_hash(e->hash); + crypto_free_ablkcipher(e->cipher); + kfree(e->data); +} + +static void pohmelfs_crypto_complete(struct crypto_async_request *req, int err) +{ + struct pohmelfs_crypto_completion *c = req->data; + + if (err == -EINPROGRESS) + return; + + dprintk("%s: req: %p, err: %d.\n", __func__, req, err); + c->error = err; + complete(&c->complete); +} + +static int pohmelfs_crypto_process(struct ablkcipher_request *req, + struct scatterlist *sg_dst, struct scatterlist *sg_src, + void *iv, int enc, unsigned long timeout) +{ + struct pohmelfs_crypto_completion complete; + int err; + + init_completion(&complete.complete); + complete.error = -EINPROGRESS; + + ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + pohmelfs_crypto_complete, &complete); + + ablkcipher_request_set_crypt(req, sg_src, sg_dst, sg_src->length, iv); + + if (enc) + err = crypto_ablkcipher_encrypt(req); + else + err = crypto_ablkcipher_decrypt(req); + + switch (err) { + case -EINPROGRESS: + case -EBUSY: + err = wait_for_completion_interruptible_timeout(&complete.complete, + timeout); + if (!err) + err = -ETIMEDOUT; + else if (err > 0) + err = complete.error; + break; + default: + break; + } + + return err; +} + +int pohmelfs_crypto_process_input_data(struct pohmelfs_crypto_engine *e, u64 cmd_iv, + void *data, struct page *page, unsigned int size) +{ + int err; + struct scatterlist sg; + + if (!e->cipher && !e->hash) + return 0; + + dprintk("%s: eng: %p, iv: %llx, data: %p, page: %p/%lu, size: %u.\n", + __func__, e, cmd_iv, data, page, (page) ? page->index : 0, size); + + if (data) { + sg_init_one(&sg, data, size); + } else { + sg_init_table(&sg, 1); + sg_set_page(&sg, page, size, 0); + } + + if (e->cipher) { + struct ablkcipher_request *req = e->data + crypto_hash_digestsize(e->hash); + u8 iv[32]; + + memset(iv, 0, sizeof(iv)); + memcpy(iv, &cmd_iv, sizeof(cmd_iv)); + + ablkcipher_request_set_tfm(req, e->cipher); + + err = pohmelfs_crypto_process(req, &sg, &sg, iv, 0, e->timeout); + if (err) + goto err_out_exit; + } + + if (e->hash) { + struct hash_desc desc; + void *dst = e->data + e->size/2; + + desc.tfm = e->hash; + desc.flags = 0; + + err = crypto_hash_init(&desc); + if (err) + goto err_out_exit; + + err = crypto_hash_update(&desc, &sg, size); + if (err) + goto err_out_exit; + + err = crypto_hash_final(&desc, dst); + if (err) + goto err_out_exit; + + err = !!memcmp(dst, e->data, crypto_hash_digestsize(e->hash)); + + if (err) { +#ifdef CONFIG_POHMELFS_DEBUG + unsigned int i; + unsigned char *recv = e->data, *calc = dst; + + dprintk("%s: eng: %p, hash: %p, cipher: %p: iv : %llx, hash mismatch (recv/calc): ", + __func__, e, e->hash, e->cipher, cmd_iv); + for (i = 0; i < crypto_hash_digestsize(e->hash); ++i) { +#if 0 + dprintka("%02x ", recv[i]); + if (recv[i] != calc[i]) { + dprintka("| calc byte: %02x.\n", calc[i]); + break; + } +#else + dprintka("%02x/%02x ", recv[i], calc[i]); +#endif + } + dprintk("\n"); +#endif + goto err_out_exit; + } else { + dprintk("%s: eng: %p, hash: %p, cipher: %p: hashes matched.\n", + __func__, e, e->hash, e->cipher); + } + } + + dprintk("%s: eng: %p, size: %u, hash: %p, cipher: %p: completed.\n", + __func__, e, e->size, e->hash, e->cipher); + + return 0; + +err_out_exit: + dprintk("%s: eng: %p, hash: %p, cipher: %p: err: %d.\n", + __func__, e, e->hash, e->cipher, err); + return err; +} + +static int pohmelfs_trans_iter(struct netfs_trans *t, struct pohmelfs_crypto_engine *e, + int (*iterator) (struct pohmelfs_crypto_engine *e, + struct scatterlist *dst, + struct scatterlist *src)) +{ + void *data = t->iovec.iov_base + sizeof(struct netfs_cmd) + t->psb->crypto_attached_size; + unsigned int size = t->iovec.iov_len - sizeof(struct netfs_cmd) - t->psb->crypto_attached_size; + struct netfs_cmd *cmd = data; + unsigned int sz, pages = t->attached_pages, i, csize, cmd_cmd, dpage_idx; + struct scatterlist sg_src, sg_dst; + int err; + + while (size) { + cmd = data; + cmd_cmd = __be16_to_cpu(cmd->cmd); + csize = __be32_to_cpu(cmd->size); + cmd->iv = __cpu_to_be64(e->iv); + + if (cmd_cmd == NETFS_READ_PAGES || cmd_cmd == NETFS_READ_PAGE) + csize = __be16_to_cpu(cmd->ext); + + sz = csize + __be16_to_cpu(cmd->cpad) + sizeof(struct netfs_cmd); + + dprintk("%s: size: %u, sz: %u, cmd_size: %u, cmd_cpad: %u.\n", + __func__, size, sz, __be32_to_cpu(cmd->size), __be16_to_cpu(cmd->cpad)); + + data += sz; + size -= sz; + + sg_init_one(&sg_src, cmd->data, sz - sizeof(struct netfs_cmd)); + sg_init_one(&sg_dst, cmd->data, sz - sizeof(struct netfs_cmd)); + + err = iterator(e, &sg_dst, &sg_src); + if (err) + return err; + } + + if (!pages) + return 0; + + dpage_idx = 0; + for (i = 0; i < t->page_num; ++i) { + struct page *page = t->pages[i]; + struct page *dpage = e->pages[dpage_idx]; + + if (!page) + continue; + + sg_init_table(&sg_src, 1); + sg_init_table(&sg_dst, 1); + sg_set_page(&sg_src, page, page_private(page), 0); + sg_set_page(&sg_dst, dpage, page_private(page), 0); + + err = iterator(e, &sg_dst, &sg_src); + if (err) + return err; + + pages--; + if (!pages) + break; + dpage_idx++; + } + + return 0; +} + +static int pohmelfs_encrypt_iterator(struct pohmelfs_crypto_engine *e, + struct scatterlist *sg_dst, struct scatterlist *sg_src) +{ + struct ablkcipher_request *req = e->data; + u8 iv[32]; + + memset(iv, 0, sizeof(iv)); + + memcpy(iv, &e->iv, sizeof(e->iv)); + + return pohmelfs_crypto_process(req, sg_dst, sg_src, iv, 1, e->timeout); +} + +static int pohmelfs_encrypt(struct pohmelfs_crypto_thread *tc) +{ + struct netfs_trans *t = tc->trans; + struct pohmelfs_crypto_engine *e = &tc->eng; + struct ablkcipher_request *req = e->data; + + memset(req, 0, sizeof(struct ablkcipher_request)); + ablkcipher_request_set_tfm(req, e->cipher); + + e->iv = pohmelfs_gen_iv(t); + + return pohmelfs_trans_iter(t, e, pohmelfs_encrypt_iterator); +} + +static int pohmelfs_hash_iterator(struct pohmelfs_crypto_engine *e, + struct scatterlist *sg_dst, struct scatterlist *sg_src) +{ + return crypto_hash_update(e->data, sg_src, sg_src->length); +} + +static int pohmelfs_hash(struct pohmelfs_crypto_thread *tc) +{ + struct pohmelfs_crypto_engine *e = &tc->eng; + struct hash_desc *desc = e->data; + unsigned char *dst = tc->trans->iovec.iov_base + sizeof(struct netfs_cmd); + int err; + + desc->tfm = e->hash; + desc->flags = 0; + + err = crypto_hash_init(desc); + if (err) + return err; + + err = pohmelfs_trans_iter(tc->trans, e, pohmelfs_hash_iterator); + if (err) + return err; + + err = crypto_hash_final(desc, dst); + if (err) + return err; + + { + unsigned int i; + dprintk("%s: ", __func__); + for (i = 0; i < tc->psb->crypto_attached_size; ++i) + dprintka("%02x ", dst[i]); + dprintka("\n"); + } + + return 0; +} + +static void pohmelfs_crypto_pages_free(struct pohmelfs_crypto_engine *e) +{ + unsigned int i; + + for (i = 0; i < e->page_num; ++i) + __free_page(e->pages[i]); + kfree(e->pages); +} + +static int pohmelfs_crypto_pages_alloc(struct pohmelfs_crypto_engine *e, struct pohmelfs_sb *psb) +{ + unsigned int i; + + e->pages = kmalloc(psb->trans_max_pages * sizeof(struct page *), GFP_KERNEL); + if (!e->pages) + return -ENOMEM; + + for (i = 0; i < psb->trans_max_pages; ++i) { + e->pages[i] = alloc_page(GFP_KERNEL); + if (!e->pages[i]) + break; + } + + e->page_num = i; + if (!e->page_num) + goto err_out_free; + + return 0; + +err_out_free: + kfree(e->pages); + return -ENOMEM; +} + +static void pohmelfs_sys_crypto_exit_one(struct pohmelfs_crypto_thread *t) +{ + struct pohmelfs_sb *psb = t->psb; + + if (t->thread) + kthread_stop(t->thread); + + mutex_lock(&psb->crypto_thread_lock); + list_del(&t->thread_entry); + psb->crypto_thread_num--; + mutex_unlock(&psb->crypto_thread_lock); + + pohmelfs_crypto_engine_exit(&t->eng); + pohmelfs_crypto_pages_free(&t->eng); + kfree(t); +} + +static int pohmelfs_crypto_finish(struct netfs_trans *t, struct pohmelfs_sb *psb, int err) +{ + struct netfs_cmd *cmd = t->iovec.iov_base; + netfs_convert_cmd(cmd); + + if (likely(!err)) + err = netfs_trans_finish_send(t, psb); + + t->result = err; + netfs_trans_put(t); + + return err; +} + +void pohmelfs_crypto_thread_make_ready(struct pohmelfs_crypto_thread *th) +{ + struct pohmelfs_sb *psb = th->psb; + + th->page = NULL; + th->trans = NULL; + + mutex_lock(&psb->crypto_thread_lock); + list_move_tail(&th->thread_entry, &psb->crypto_ready_list); + mutex_unlock(&psb->crypto_thread_lock); + wake_up(&psb->wait); +} + +static int pohmelfs_crypto_thread_trans(struct pohmelfs_crypto_thread *t) +{ + struct netfs_trans *trans; + int err = 0; + + trans = t->trans; + trans->eng = NULL; + + if (t->eng.hash) { + err = pohmelfs_hash(t); + if (err) + goto out_complete; + } + + if (t->eng.cipher) { + err = pohmelfs_encrypt(t); + if (err) + goto out_complete; + trans->eng = &t->eng; + } + +out_complete: + t->page = NULL; + t->trans = NULL; + + if (!trans->eng) + pohmelfs_crypto_thread_make_ready(t); + + pohmelfs_crypto_finish(trans, t->psb, err); + return err; +} + +static int pohmelfs_crypto_thread_page(struct pohmelfs_crypto_thread *t) +{ + struct pohmelfs_crypto_engine *e = &t->eng; + struct page *page = t->page; + int err; + + WARN_ON(!PageChecked(page)); + + err = pohmelfs_crypto_process_input_data(e, e->iv, NULL, page, t->size); + if (!err) + SetPageUptodate(page); + else + SetPageError(page); + unlock_page(page); + page_cache_release(page); + + pohmelfs_crypto_thread_make_ready(t); + + return err; +} + +static int pohmelfs_crypto_thread_func(void *data) +{ + struct pohmelfs_crypto_thread *t = data; + + while (!kthread_should_stop()) { + wait_event_interruptible(t->wait, kthread_should_stop() || + t->trans || t->page); + + if (kthread_should_stop()) + break; + + if (!t->trans && !t->page) + continue; + + dprintk("%s: thread: %p, trans: %p, page: %p.\n", + __func__, t, t->trans, t->page); + + if (t->trans) + pohmelfs_crypto_thread_trans(t); + else if (t->page) + pohmelfs_crypto_thread_page(t); + } + + return 0; +} + +static void pohmelfs_crypto_flush(struct pohmelfs_sb *psb, struct list_head *head) +{ + while (!list_empty(head)) { + struct pohmelfs_crypto_thread *t = NULL; + + mutex_lock(&psb->crypto_thread_lock); + if (!list_empty(head)) { + t = list_first_entry(head, struct pohmelfs_crypto_thread, thread_entry); + list_del_init(&t->thread_entry); + } + mutex_unlock(&psb->crypto_thread_lock); + + if (t) + pohmelfs_sys_crypto_exit_one(t); + } +} + +static void pohmelfs_sys_crypto_exit(struct pohmelfs_sb *psb) +{ + while (!list_empty(&psb->crypto_active_list) || !list_empty(&psb->crypto_ready_list)) { + dprintk("%s: crypto_thread_num: %u.\n", __func__, psb->crypto_thread_num); + pohmelfs_crypto_flush(psb, &psb->crypto_active_list); + pohmelfs_crypto_flush(psb, &psb->crypto_ready_list); + } +} + +static int pohmelfs_sys_crypto_init(struct pohmelfs_sb *psb) +{ + unsigned int i; + struct pohmelfs_crypto_thread *t; + struct pohmelfs_config *c; + struct netfs_state *st; + int err; + + list_for_each_entry(c, &psb->state_list, config_entry) { + st = &c->state; + + err = pohmelfs_crypto_engine_init(&st->eng, psb); + if (err) + goto err_out_exit; + + dprintk("%s: st: %p, eng: %p, hash: %p, cipher: %p.\n", + __func__, st, &st->eng, &st->eng.hash, &st->eng.cipher); + } + + for (i = 0; i < psb->crypto_thread_num; ++i) { + err = -ENOMEM; + t = kzalloc(sizeof(struct pohmelfs_crypto_thread), GFP_KERNEL); + if (!t) + goto err_out_free_state_engines; + + init_waitqueue_head(&t->wait); + + t->psb = psb; + t->trans = NULL; + t->eng.thread = t; + + err = pohmelfs_crypto_engine_init(&t->eng, psb); + if (err) + goto err_out_free_state_engines; + + err = pohmelfs_crypto_pages_alloc(&t->eng, psb); + if (err) + goto err_out_free; + + t->thread = kthread_run(pohmelfs_crypto_thread_func, t, + "pohmelfs-crypto-%d-%d", psb->idx, i); + if (IS_ERR(t->thread)) { + err = PTR_ERR(t->thread); + t->thread = NULL; + goto err_out_free; + } + + if (t->eng.cipher) + psb->crypto_align_size = crypto_ablkcipher_blocksize(t->eng.cipher); + + mutex_lock(&psb->crypto_thread_lock); + list_add_tail(&t->thread_entry, &psb->crypto_ready_list); + mutex_unlock(&psb->crypto_thread_lock); + } + + psb->crypto_thread_num = i; + return 0; + +err_out_free: + pohmelfs_sys_crypto_exit_one(t); +err_out_free_state_engines: + list_for_each_entry(c, &psb->state_list, config_entry) { + st = &c->state; + pohmelfs_crypto_engine_exit(&st->eng); + } +err_out_exit: + pohmelfs_sys_crypto_exit(psb); + return err; +} + +void pohmelfs_crypto_exit(struct pohmelfs_sb *psb) +{ + pohmelfs_sys_crypto_exit(psb); + + kfree(psb->hash_string); + kfree(psb->cipher_string); +} + +static int pohmelfs_crypt_init_complete(struct page **pages, unsigned int page_num, + void *private, int err) +{ + struct pohmelfs_sb *psb = private; + + psb->flags = -err; + dprintk("%s: err: %d.\n", __func__, err); + + wake_up(&psb->wait); + + return err; +} + +static int pohmelfs_crypto_init_handshake(struct pohmelfs_sb *psb) +{ + struct netfs_trans *t; + struct netfs_crypto_capabilities *cap; + struct netfs_cmd *cmd; + char *str; + int err = -ENOMEM, size; + + size = sizeof(struct netfs_crypto_capabilities) + + psb->cipher_strlen + psb->hash_strlen + 2; /* 0 bytes */ + + t = netfs_trans_alloc(psb, size, 0, 0); + if (!t) + goto err_out_exit; + + t->complete = pohmelfs_crypt_init_complete; + t->private = psb; + + cmd = netfs_trans_current(t); + cap = (struct netfs_crypto_capabilities *)(cmd + 1); + str = (char *)(cap + 1); + + cmd->cmd = NETFS_CAPABILITIES; + cmd->id = POHMELFS_CRYPTO_CAPABILITIES; + cmd->size = size; + cmd->start = 0; + cmd->ext = 0; + cmd->csize = 0; + + netfs_convert_cmd(cmd); + netfs_trans_update(cmd, t, size); + + cap->hash_strlen = psb->hash_strlen; + if (cap->hash_strlen) { + sprintf(str, "%s", psb->hash_string); + str += cap->hash_strlen; + } + + cap->cipher_strlen = psb->cipher_strlen; + cap->cipher_keysize = psb->cipher_keysize; + if (cap->cipher_strlen) + sprintf(str, "%s", psb->cipher_string); + + netfs_convert_crypto_capabilities(cap); + + psb->flags = ~0; + err = netfs_trans_finish(t, psb); + if (err) + goto err_out_exit; + + err = wait_event_interruptible_timeout(psb->wait, (psb->flags != ~0), + psb->wait_on_page_timeout); + if (!err) + err = -ETIMEDOUT; + else if (err > 0) + err = -psb->flags; + + if (!err) + psb->perform_crypto = 1; + psb->flags = 0; + + /* + * At this point NETFS_CAPABILITIES response command + * should setup superblock in a way, which is acceptable + * for both client and server, so if server refuses connection, + * it will send error in transaction response. + */ + + if (err) + goto err_out_exit; + + return 0; + +err_out_exit: + return err; +} + +int pohmelfs_crypto_init(struct pohmelfs_sb *psb) +{ + int err; + + if (!psb->cipher_string && !psb->hash_string) + return 0; + + err = pohmelfs_crypto_init_handshake(psb); + if (err) + return err; + + err = pohmelfs_sys_crypto_init(psb); + if (err) + return err; + + return 0; +} + +static int pohmelfs_crypto_thread_get(struct pohmelfs_sb *psb, + int (*action)(struct pohmelfs_crypto_thread *t, void *data), void *data) +{ + struct pohmelfs_crypto_thread *t = NULL; + int err; + + while (!t) { + err = wait_event_interruptible_timeout(psb->wait, + !list_empty(&psb->crypto_ready_list), + psb->wait_on_page_timeout); + + t = NULL; + err = 0; + mutex_lock(&psb->crypto_thread_lock); + if (!list_empty(&psb->crypto_ready_list)) { + t = list_entry(psb->crypto_ready_list.prev, + struct pohmelfs_crypto_thread, + thread_entry); + + list_move_tail(&t->thread_entry, + &psb->crypto_active_list); + + action(t, data); + wake_up(&t->wait); + + } + mutex_unlock(&psb->crypto_thread_lock); + } + + return err; +} + +static int pohmelfs_trans_crypt_action(struct pohmelfs_crypto_thread *t, void *data) +{ + struct netfs_trans *trans = data; + + netfs_trans_get(trans); + t->trans = trans; + + dprintk("%s: t: %p, gen: %u, thread: %p.\n", __func__, trans, trans->gen, t); + return 0; +} + +int pohmelfs_trans_crypt(struct netfs_trans *trans, struct pohmelfs_sb *psb) +{ + if ((!psb->hash_string && !psb->cipher_string) || !psb->perform_crypto) { + netfs_trans_get(trans); + return pohmelfs_crypto_finish(trans, psb, 0); + } + + return pohmelfs_crypto_thread_get(psb, pohmelfs_trans_crypt_action, trans); +} + +struct pohmelfs_crypto_input_action_data { + struct page *page; + struct pohmelfs_crypto_engine *e; + u64 iv; + unsigned int size; +}; + +static int pohmelfs_crypt_input_page_action(struct pohmelfs_crypto_thread *t, void *data) +{ + struct pohmelfs_crypto_input_action_data *act = data; + + memcpy(t->eng.data, act->e->data, t->psb->crypto_attached_size); + + t->size = act->size; + t->eng.iv = act->iv; + + t->page = act->page; + return 0; +} + +int pohmelfs_crypto_process_input_page(struct pohmelfs_crypto_engine *e, + struct page *page, unsigned int size, u64 iv) +{ + struct inode *inode = page->mapping->host; + struct pohmelfs_crypto_input_action_data act; + int err = -ENOENT; + + act.page = page; + act.e = e; + act.size = size; + act.iv = iv; + + err = pohmelfs_crypto_thread_get(POHMELFS_SB(inode->i_sb), + pohmelfs_crypt_input_page_action, &act); + if (err) + goto err_out_exit; + + return 0; + +err_out_exit: + SetPageUptodate(page); + page_cache_release(page); + + return err; +} diff --git a/trunk/drivers/staging/pohmelfs/dir.c b/trunk/drivers/staging/pohmelfs/dir.c new file mode 100644 index 000000000000..2ee4491b7136 --- /dev/null +++ b/trunk/drivers/staging/pohmelfs/dir.c @@ -0,0 +1,1102 @@ +/* + * 2007+ Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "netfs.h" + +static int pohmelfs_cmp_hash(struct pohmelfs_name *n, u32 hash) +{ + if (n->hash > hash) + return -1; + if (n->hash < hash) + return 1; + + return 0; +} + +static struct pohmelfs_name *pohmelfs_search_hash_unprecise(struct pohmelfs_inode *pi, u32 hash) +{ + struct rb_node *n = pi->hash_root.rb_node; + struct pohmelfs_name *tmp = NULL; + int cmp; + + while (n) { + tmp = rb_entry(n, struct pohmelfs_name, hash_node); + + cmp = pohmelfs_cmp_hash(tmp, hash); + if (cmp < 0) + n = n->rb_left; + else if (cmp > 0) + n = n->rb_right; + else + break; + + } + + return tmp; +} + +struct pohmelfs_name *pohmelfs_search_hash(struct pohmelfs_inode *pi, u32 hash) +{ + struct pohmelfs_name *tmp; + + tmp = pohmelfs_search_hash_unprecise(pi, hash); + if (tmp && (tmp->hash == hash)) + return tmp; + + return NULL; +} + +static void __pohmelfs_name_del(struct pohmelfs_inode *parent, struct pohmelfs_name *node) +{ + rb_erase(&node->hash_node, &parent->hash_root); +} + +/* + * Remove name cache entry from its caches and free it. + */ +static void pohmelfs_name_free(struct pohmelfs_inode *parent, struct pohmelfs_name *node) +{ + __pohmelfs_name_del(parent, node); + list_del(&node->sync_create_entry); + kfree(node); +} + +static struct pohmelfs_name *pohmelfs_insert_hash(struct pohmelfs_inode *pi, + struct pohmelfs_name *new) +{ + struct rb_node **n = &pi->hash_root.rb_node, *parent = NULL; + struct pohmelfs_name *ret = NULL, *tmp; + int cmp; + + while (*n) { + parent = *n; + + tmp = rb_entry(parent, struct pohmelfs_name, hash_node); + + cmp = pohmelfs_cmp_hash(tmp, new->hash); + if (cmp < 0) + n = &parent->rb_left; + else if (cmp > 0) + n = &parent->rb_right; + else { + ret = tmp; + break; + } + } + + if (ret) { + printk("%s: exist: parent: %llu, ino: %llu, hash: %x, len: %u, data: '%s', " + "new: ino: %llu, hash: %x, len: %u, data: '%s'.\n", + __func__, pi->ino, + ret->ino, ret->hash, ret->len, ret->data, + new->ino, new->hash, new->len, new->data); + ret->ino = new->ino; + return ret; + } + + rb_link_node(&new->hash_node, parent, n); + rb_insert_color(&new->hash_node, &pi->hash_root); + + return NULL; +} + +/* + * Free name cache for given inode. + */ +void pohmelfs_free_names(struct pohmelfs_inode *parent) +{ + struct rb_node *rb_node; + struct pohmelfs_name *n; + + for (rb_node = rb_first(&parent->hash_root); rb_node;) { + n = rb_entry(rb_node, struct pohmelfs_name, hash_node); + rb_node = rb_next(rb_node); + + pohmelfs_name_free(parent, n); + } +} + +static void pohmelfs_fix_offset(struct pohmelfs_inode *parent, struct pohmelfs_name *node) +{ + parent->total_len -= node->len; +} + +/* + * Free name cache entry helper. + */ +void pohmelfs_name_del(struct pohmelfs_inode *parent, struct pohmelfs_name *node) +{ + pohmelfs_fix_offset(parent, node); + pohmelfs_name_free(parent, node); +} + +/* + * Insert new name cache entry into all hash cache. + */ +static int pohmelfs_insert_name(struct pohmelfs_inode *parent, struct pohmelfs_name *n) +{ + struct pohmelfs_name *name; + + name = pohmelfs_insert_hash(parent, n); + if (name) + return -EEXIST; + + parent->total_len += n->len; + list_add_tail(&n->sync_create_entry, &parent->sync_create_list); + + return 0; +} + +/* + * Allocate new name cache entry. + */ +static struct pohmelfs_name *pohmelfs_name_alloc(unsigned int len) +{ + struct pohmelfs_name *n; + + n = kzalloc(sizeof(struct pohmelfs_name) + len, GFP_KERNEL); + if (!n) + return NULL; + + INIT_LIST_HEAD(&n->sync_create_entry); + + n->data = (char *)(n+1); + + return n; +} + +/* + * Add new name entry into directory's cache. + */ +static int pohmelfs_add_dir(struct pohmelfs_sb *psb, struct pohmelfs_inode *parent, + struct pohmelfs_inode *npi, struct qstr *str, unsigned int mode, int link) +{ + int err = -ENOMEM; + struct pohmelfs_name *n; + + n = pohmelfs_name_alloc(str->len + 1); + if (!n) + goto err_out_exit; + + n->ino = npi->ino; + n->mode = mode; + n->len = str->len; + n->hash = str->hash; + sprintf(n->data, "%s", str->name); + + mutex_lock(&parent->offset_lock); + err = pohmelfs_insert_name(parent, n); + mutex_unlock(&parent->offset_lock); + + if (err) { + if (err != -EEXIST) + goto err_out_free; + kfree(n); + } + + return 0; + +err_out_free: + kfree(n); +err_out_exit: + return err; +} + +/* + * Create new inode for given parameters (name, inode info, parent). + * This does not create object on the server, it will be synced there during writeback. + */ +struct pohmelfs_inode *pohmelfs_new_inode(struct pohmelfs_sb *psb, + struct pohmelfs_inode *parent, struct qstr *str, + struct netfs_inode_info *info, int link) +{ + struct inode *new = NULL; + struct pohmelfs_inode *npi; + int err = -EEXIST; + + dprintk("%s: creating inode: parent: %llu, ino: %llu, str: %p.\n", + __func__, (parent) ? parent->ino : 0, info->ino, str); + + err = -ENOMEM; + new = iget_locked(psb->sb, info->ino); + if (!new) + goto err_out_exit; + + npi = POHMELFS_I(new); + npi->ino = info->ino; + err = 0; + + if (new->i_state & I_NEW) { + dprintk("%s: filling VFS inode: %lu/%llu.\n", + __func__, new->i_ino, info->ino); + pohmelfs_fill_inode(new, info); + + if (S_ISDIR(info->mode)) { + struct qstr s; + + s.name = "."; + s.len = 1; + s.hash = jhash(s.name, s.len, 0); + + err = pohmelfs_add_dir(psb, npi, npi, &s, info->mode, 0); + if (err) + goto err_out_put; + + s.name = ".."; + s.len = 2; + s.hash = jhash(s.name, s.len, 0); + + err = pohmelfs_add_dir(psb, npi, (parent) ? parent : npi, &s, + (parent) ? parent->vfs_inode.i_mode : npi->vfs_inode.i_mode, 0); + if (err) + goto err_out_put; + } + } + + if (str) { + if (parent) { + err = pohmelfs_add_dir(psb, parent, npi, str, info->mode, link); + + dprintk("%s: %s inserted name: '%s', new_offset: %llu, ino: %llu, parent: %llu.\n", + __func__, (err) ? "unsuccessfully" : "successfully", + str->name, parent->total_len, info->ino, parent->ino); + + if (err && err != -EEXIST) + goto err_out_put; + } + } + + if (new->i_state & I_NEW) { + if (parent) + mark_inode_dirty(&parent->vfs_inode); + mark_inode_dirty(new); + } + + set_bit(NETFS_INODE_OWNED, &npi->state); + npi->lock_type = POHMELFS_WRITE_LOCK; + unlock_new_inode(new); + + return npi; + +err_out_put: + printk("%s: putting inode: %p, npi: %p, error: %d.\n", __func__, new, npi, err); + iput(new); +err_out_exit: + return ERR_PTR(err); +} + +static int pohmelfs_remote_sync_complete(struct page **pages, unsigned int page_num, + void *private, int err) +{ + struct pohmelfs_inode *pi = private; + struct pohmelfs_sb *psb = POHMELFS_SB(pi->vfs_inode.i_sb); + + dprintk("%s: ino: %llu, err: %d.\n", __func__, pi->ino, err); + + if (err) + pi->error = err; + wake_up(&psb->wait); + pohmelfs_put_inode(pi); + + return err; +} + +/* + * Receive directory content from the server. + * This should be only done for objects, which were not created locally, + * and which were not synced previously. + */ +static int pohmelfs_sync_remote_dir(struct pohmelfs_inode *pi) +{ + struct inode *inode = &pi->vfs_inode; + struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); + long ret = psb->wait_on_page_timeout; + int err; + + dprintk("%s: dir: %llu, state: %lx: remote_synced: %d.\n", + __func__, pi->ino, pi->state, test_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state)); + + if (test_bit(NETFS_INODE_REMOTE_DIR_SYNCED, &pi->state)) + return 0; + + if (!igrab(inode)) { + err = -ENOENT; + goto err_out_exit; + } + + err = pohmelfs_meta_command(pi, NETFS_READDIR, NETFS_TRANS_SINGLE_DST, + pohmelfs_remote_sync_complete, pi, 0); + if (err) + goto err_out_exit; + + pi->error = 0; + ret = wait_event_interruptible_timeout(psb->wait, + test_bit(NETFS_INODE_REMOTE_DIR_SYNCED, &pi->state) || pi->error, ret); + dprintk("%s: awake dir: %llu, ret: %ld, err: %d.\n", __func__, pi->ino, ret, pi->error); + if (ret <= 0) { + err = ret; + if (!err) + err = -ETIMEDOUT; + goto err_out_exit; + } + + if (pi->error) + return pi->error; + + return 0; + +err_out_exit: + clear_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state); + + return err; +} + +static int pohmelfs_dir_open(struct inode *inode, struct file *file) +{ + file->private_data = NULL; + return 0; +} + +/* + * VFS readdir callback. Syncs directory content from server if needed, + * and provides direntry info to the userspace. + */ +static int pohmelfs_readdir(struct file *file, void *dirent, filldir_t filldir) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct pohmelfs_inode *pi = POHMELFS_I(inode); + struct pohmelfs_name *n; + struct rb_node *rb_node; + int err = 0, mode; + u64 len; + + dprintk("%s: parent: %llu, fpos: %llu, hash: %08lx.\n", + __func__, pi->ino, (u64)file->f_pos, + (unsigned long)file->private_data); +#if 0 + err = pohmelfs_data_lock(pi, 0, ~0, POHMELFS_READ_LOCK); + if (err) + return err; +#endif + err = pohmelfs_sync_remote_dir(pi); + if (err) + return err; + + if (file->private_data && (file->private_data == (void *)(unsigned long)file->f_pos)) + return 0; + + mutex_lock(&pi->offset_lock); + n = pohmelfs_search_hash_unprecise(pi, (unsigned long)file->private_data); + + while (n) { + mode = (n->mode >> 12) & 15; + + dprintk("%s: offset: %llu, parent ino: %llu, name: '%s', len: %u, ino: %llu, " + "mode: %o/%o, fpos: %llu, hash: %08x.\n", + __func__, file->f_pos, pi->ino, n->data, n->len, + n->ino, n->mode, mode, file->f_pos, n->hash); + + file->private_data = (void *)(unsigned long)n->hash; + + len = n->len; + err = filldir(dirent, n->data, n->len, file->f_pos, n->ino, mode); + + if (err < 0) { + dprintk("%s: err: %d.\n", __func__, err); + err = 0; + break; + } + + file->f_pos += len; + + rb_node = rb_next(&n->hash_node); + + if (!rb_node || (rb_node == &n->hash_node)) { + file->private_data = (void *)(unsigned long)file->f_pos; + break; + } + + n = rb_entry(rb_node, struct pohmelfs_name, hash_node); + } + mutex_unlock(&pi->offset_lock); + + return err; +} + +static loff_t pohmelfs_dir_lseek(struct file *file, loff_t offset, int origin) +{ + file->f_pos = offset; + file->private_data = NULL; + return offset; +} + +const struct file_operations pohmelfs_dir_fops = { + .open = pohmelfs_dir_open, + .read = generic_read_dir, + .llseek = pohmelfs_dir_lseek, + .readdir = pohmelfs_readdir, +}; + +/* + * Lookup single object on server. + */ +static int pohmelfs_lookup_single(struct pohmelfs_inode *parent, + struct qstr *str, u64 ino) +{ + struct pohmelfs_sb *psb = POHMELFS_SB(parent->vfs_inode.i_sb); + long ret = msecs_to_jiffies(5000); + int err; + + set_bit(NETFS_COMMAND_PENDING, &parent->state); + err = pohmelfs_meta_command_data(parent, parent->ino, NETFS_LOOKUP, + (char *)str->name, NETFS_TRANS_SINGLE_DST, NULL, NULL, ino); + if (err) + goto err_out_exit; + + err = 0; + ret = wait_event_interruptible_timeout(psb->wait, + !test_bit(NETFS_COMMAND_PENDING, &parent->state), ret); + if (ret <= 0) { + err = ret; + if (!err) + err = -ETIMEDOUT; + } + + if (err) + goto err_out_exit; + + return 0; + +err_out_exit: + clear_bit(NETFS_COMMAND_PENDING, &parent->state); + + printk("%s: failed: parent: %llu, ino: %llu, name: '%s', err: %d.\n", + __func__, parent->ino, ino, str->name, err); + + return err; +} + +/* + * VFS lookup callback. + * We first try to get inode number from local name cache, if we have one, + * then inode can be found in inode cache. If there is no inode or no object in + * local cache, try to lookup it on server. This only should be done for directories, + * which were not created locally, otherwise remote server does not know about dir at all, + * so no need to try to know that. + */ +struct dentry *pohmelfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) +{ + struct pohmelfs_inode *parent = POHMELFS_I(dir); + struct pohmelfs_name *n; + struct inode *inode = NULL; + unsigned long ino = 0; + int err, lock_type = POHMELFS_READ_LOCK, need_lock = 1; + struct qstr str = dentry->d_name; + + if ((nd->intent.open.flags & O_ACCMODE) != O_RDONLY) + lock_type = POHMELFS_WRITE_LOCK; + + if (test_bit(NETFS_INODE_OWNED, &parent->state)) { + if (lock_type == parent->lock_type) + need_lock = 0; + if ((lock_type == POHMELFS_READ_LOCK) && (parent->lock_type == POHMELFS_WRITE_LOCK)) + need_lock = 0; + } + + if ((lock_type == POHMELFS_READ_LOCK) && !test_bit(NETFS_INODE_REMOTE_DIR_SYNCED, &parent->state)) + need_lock = 1; + + str.hash = jhash(dentry->d_name.name, dentry->d_name.len, 0); + + mutex_lock(&parent->offset_lock); + n = pohmelfs_search_hash(parent, str.hash); + if (n) + ino = n->ino; + mutex_unlock(&parent->offset_lock); + + dprintk("%s: start ino: %lu, inode: %p, name: '%s', hash: %x, parent_state: %lx, need_lock: %d.\n", + __func__, ino, inode, str.name, str.hash, parent->state, need_lock); + + if (ino) { + inode = ilookup(dir->i_sb, ino); + if (inode) + goto out; + } + + dprintk("%s: no inode dir: %p, dir_ino: %llu, name: '%s', len: %u, dir_state: %lx, ino: %lu.\n", + __func__, dir, parent->ino, + str.name, str.len, parent->state, ino); + + if (!ino) { + if (!need_lock) + goto out; + } + + err = pohmelfs_data_lock(parent, 0, ~0, lock_type); + if (err) + goto out; + + err = pohmelfs_lookup_single(parent, &str, ino); + if (err) + goto out; + + if (!ino) { + mutex_lock(&parent->offset_lock); + n = pohmelfs_search_hash(parent, str.hash); + if (n) + ino = n->ino; + mutex_unlock(&parent->offset_lock); + } + + if (ino) { + inode = ilookup(dir->i_sb, ino); + dprintk("%s: second lookup ino: %lu, inode: %p, name: '%s', hash: %x.\n", + __func__, ino, inode, str.name, str.hash); + if (!inode) { + dprintk("%s: No inode for ino: %lu, name: '%s', hash: %x.\n", + __func__, ino, str.name, str.hash); + /* return NULL; */ + return ERR_PTR(-EACCES); + } + } else { + printk("%s: No inode number : name: '%s', hash: %x.\n", + __func__, str.name, str.hash); + } +out: + return d_splice_alias(inode, dentry); +} + +/* + * Create new object in local cache. Object will be synced to server + * during writeback for given inode. + */ +struct pohmelfs_inode *pohmelfs_create_entry_local(struct pohmelfs_sb *psb, + struct pohmelfs_inode *parent, struct qstr *str, u64 start, umode_t mode) +{ + struct pohmelfs_inode *npi; + int err = -ENOMEM; + struct netfs_inode_info info; + + dprintk("%s: name: '%s', mode: %ho, start: %llu.\n", + __func__, str->name, mode, start); + + info.mode = mode; + info.ino = start; + + if (!start) + info.ino = pohmelfs_new_ino(psb); + + info.nlink = S_ISDIR(mode) ? 2 : 1; + info.uid = current_fsuid(); + info.gid = current_fsgid(); + info.size = 0; + info.blocksize = 512; + info.blocks = 0; + info.rdev = 0; + info.version = 0; + + npi = pohmelfs_new_inode(psb, parent, str, &info, !!start); + if (IS_ERR(npi)) { + err = PTR_ERR(npi); + goto err_out_unlock; + } + + return npi; + +err_out_unlock: + dprintk("%s: err: %d.\n", __func__, err); + return ERR_PTR(err); +} + +/* + * Create local object and bind it to dentry. + */ +static int pohmelfs_create_entry(struct inode *dir, struct dentry *dentry, + u64 start, umode_t mode) +{ + struct pohmelfs_sb *psb = POHMELFS_SB(dir->i_sb); + struct pohmelfs_inode *npi, *parent; + struct qstr str = dentry->d_name; + int err; + + parent = POHMELFS_I(dir); + + err = pohmelfs_data_lock(parent, 0, ~0, POHMELFS_WRITE_LOCK); + if (err) + return err; + + str.hash = jhash(dentry->d_name.name, dentry->d_name.len, 0); + + npi = pohmelfs_create_entry_local(psb, parent, &str, start, mode); + if (IS_ERR(npi)) + return PTR_ERR(npi); + + d_instantiate(dentry, &npi->vfs_inode); + + dprintk("%s: parent: %llu, inode: %llu, name: '%s', parent_nlink: %d, nlink: %d.\n", + __func__, parent->ino, npi->ino, dentry->d_name.name, + (signed)dir->i_nlink, (signed)npi->vfs_inode.i_nlink); + + return 0; +} + +/* + * VFS create and mkdir callbacks. + */ +static int pohmelfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, + struct nameidata *nd) +{ + return pohmelfs_create_entry(dir, dentry, 0, mode); +} + +static int pohmelfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + int err; + + inode_inc_link_count(dir); + err = pohmelfs_create_entry(dir, dentry, 0, mode | S_IFDIR); + if (err) + inode_dec_link_count(dir); + + return err; +} + +static int pohmelfs_remove_entry(struct inode *dir, struct dentry *dentry) +{ + struct pohmelfs_sb *psb = POHMELFS_SB(dir->i_sb); + struct inode *inode = dentry->d_inode; + struct pohmelfs_inode *parent = POHMELFS_I(dir), *pi = POHMELFS_I(inode); + struct pohmelfs_name *n; + int err = -ENOENT; + struct qstr str = dentry->d_name; + + err = pohmelfs_data_lock(parent, 0, ~0, POHMELFS_WRITE_LOCK); + if (err) + return err; + + str.hash = jhash(dentry->d_name.name, dentry->d_name.len, 0); + + dprintk("%s: dir_ino: %llu, inode: %llu, name: '%s', nlink: %d.\n", + __func__, parent->ino, pi->ino, + str.name, (signed)inode->i_nlink); + + BUG_ON(!inode); + + mutex_lock(&parent->offset_lock); + n = pohmelfs_search_hash(parent, str.hash); + if (n) { + pohmelfs_fix_offset(parent, n); + if (test_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state)) + pohmelfs_remove_child(pi, n); + + pohmelfs_name_free(parent, n); + err = 0; + } + mutex_unlock(&parent->offset_lock); + + if (!err) { + psb->avail_size += inode->i_size; + + pohmelfs_inode_del_inode(psb, pi); + + mark_inode_dirty(dir); + + inode->i_ctime = dir->i_ctime; + if (inode->i_nlink) + inode_dec_link_count(inode); + } + + return err; +} + +/* + * Unlink and rmdir VFS callbacks. + */ +static int pohmelfs_unlink(struct inode *dir, struct dentry *dentry) +{ + return pohmelfs_remove_entry(dir, dentry); +} + +static int pohmelfs_rmdir(struct inode *dir, struct dentry *dentry) +{ + int err; + struct inode *inode = dentry->d_inode; + + dprintk("%s: parent: %llu, inode: %llu, name: '%s', parent_nlink: %d, nlink: %d.\n", + __func__, POHMELFS_I(dir)->ino, POHMELFS_I(inode)->ino, + dentry->d_name.name, (signed)dir->i_nlink, (signed)inode->i_nlink); + + err = pohmelfs_remove_entry(dir, dentry); + if (!err) { + inode_dec_link_count(dir); + inode_dec_link_count(inode); + } + + return err; +} + +/* + * Link creation is synchronous. + * I'm lazy. + * Earth is somewhat round. + */ +static int pohmelfs_create_link(struct pohmelfs_inode *parent, struct qstr *obj, + struct pohmelfs_inode *target, struct qstr *tstr) +{ + struct super_block *sb = parent->vfs_inode.i_sb; + struct pohmelfs_sb *psb = POHMELFS_SB(sb); + struct netfs_cmd *cmd; + struct netfs_trans *t; + void *data; + int err, parent_len, target_len = 0, cur_len, path_size = 0; + + err = pohmelfs_data_lock(parent, 0, ~0, POHMELFS_WRITE_LOCK); + if (err) + return err; + + err = sb->s_op->write_inode(&parent->vfs_inode, 0); + if (err) + goto err_out_exit; + + if (tstr) + target_len = tstr->len; + + parent_len = pohmelfs_path_length(parent); + if (target) + target_len += pohmelfs_path_length(target); + + if (parent_len < 0) { + err = parent_len; + goto err_out_exit; + } + + if (target_len < 0) { + err = target_len; + goto err_out_exit; + } + + t = netfs_trans_alloc(psb, parent_len + target_len + obj->len + 2, 0, 0); + if (!t) { + err = -ENOMEM; + goto err_out_exit; + } + cur_len = netfs_trans_cur_len(t); + + cmd = netfs_trans_current(t); + if (IS_ERR(cmd)) { + err = PTR_ERR(cmd); + goto err_out_free; + } + + data = (void *)(cmd + 1); + cur_len -= sizeof(struct netfs_cmd); + + err = pohmelfs_construct_path_string(parent, data, parent_len); + if (err > 0) { + /* Do not place null-byte before the slash */ + path_size = err - 1; + cur_len -= path_size; + + err = snprintf(data + path_size, cur_len, "/%s|", obj->name); + + path_size += err; + cur_len -= err; + + cmd->ext = path_size - 1; /* No | symbol */ + + if (target) { + err = pohmelfs_construct_path_string(target, data + path_size, target_len); + if (err > 0) { + path_size += err; + cur_len -= err; + } + } + } + + if (err < 0) + goto err_out_free; + + cmd->start = 0; + + if (!target && tstr) { + if (tstr->len > cur_len - 1) { + err = -ENAMETOOLONG; + goto err_out_free; + } + + err = snprintf(data + path_size, cur_len, "%s", tstr->name) + 1; /* 0-byte */ + path_size += err; + cur_len -= err; + cmd->start = 1; + } + + dprintk("%s: parent: %llu, obj: '%s', target_inode: %llu, target_str: '%s', full: '%s'.\n", + __func__, parent->ino, obj->name, (target) ? target->ino : 0, (tstr) ? tstr->name : NULL, + (char *)data); + + cmd->cmd = NETFS_LINK; + cmd->size = path_size; + cmd->id = parent->ino; + + netfs_convert_cmd(cmd); + + netfs_trans_update(cmd, t, path_size); + + err = netfs_trans_finish(t, psb); + if (err) + goto err_out_exit; + + return 0; + +err_out_free: + t->result = err; + netfs_trans_put(t); +err_out_exit: + return err; +} + +/* + * VFS hard and soft link callbacks. + */ +static int pohmelfs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry) +{ + struct inode *inode = old_dentry->d_inode; + struct pohmelfs_inode *pi = POHMELFS_I(inode); + int err; + struct qstr str = dentry->d_name; + + str.hash = jhash(dentry->d_name.name, dentry->d_name.len, 0); + + err = inode->i_sb->s_op->write_inode(inode, 0); + if (err) + return err; + + err = pohmelfs_create_link(POHMELFS_I(dir), &str, pi, NULL); + if (err) + return err; + + return pohmelfs_create_entry(dir, dentry, pi->ino, inode->i_mode); +} + +static int pohmelfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) +{ + struct qstr sym_str; + struct qstr str = dentry->d_name; + struct inode *inode; + int err; + + str.hash = jhash(dentry->d_name.name, dentry->d_name.len, 0); + + sym_str.name = symname; + sym_str.len = strlen(symname); + + err = pohmelfs_create_link(POHMELFS_I(dir), &str, NULL, &sym_str); + if (err) + goto err_out_exit; + + err = pohmelfs_create_entry(dir, dentry, 0, S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO); + if (err) + goto err_out_exit; + + inode = dentry->d_inode; + + err = page_symlink(inode, symname, sym_str.len + 1); + if (err) + goto err_out_put; + + return 0; + +err_out_put: + iput(inode); +err_out_exit: + return err; +} + +static int pohmelfs_send_rename(struct pohmelfs_inode *pi, struct pohmelfs_inode *parent, + struct qstr *str) +{ + int path_len, err, total_len = 0, inode_len, parent_len; + char *path; + struct netfs_trans *t; + struct netfs_cmd *cmd; + struct pohmelfs_sb *psb = POHMELFS_SB(pi->vfs_inode.i_sb); + + parent_len = pohmelfs_path_length(parent); + inode_len = pohmelfs_path_length(pi); + + if (parent_len < 0 || inode_len < 0) + return -EINVAL; + + path_len = parent_len + inode_len + str->len + 3; + + t = netfs_trans_alloc(psb, path_len, 0, 0); + if (!t) + return -ENOMEM; + + cmd = netfs_trans_current(t); + path = (char *)(cmd + 1); + + err = pohmelfs_construct_path_string(pi, path, inode_len); + if (err < 0) + goto err_out_unlock; + + cmd->ext = err; + + path += err; + total_len += err; + path_len -= err; + + *path = '|'; + path++; + total_len++; + path_len--; + + err = pohmelfs_construct_path_string(parent, path, parent_len); + if (err < 0) + goto err_out_unlock; + + /* + * Do not place a null-byte before the final slash and the name. + */ + err--; + path += err; + total_len += err; + path_len -= err; + + err = snprintf(path, path_len - 1, "/%s", str->name); + + total_len += err + 1; /* 0 symbol */ + path_len -= err + 1; + + cmd->cmd = NETFS_RENAME; + cmd->id = pi->ino; + cmd->start = parent->ino; + cmd->size = total_len; + + netfs_convert_cmd(cmd); + + netfs_trans_update(cmd, t, total_len); + + return netfs_trans_finish(t, psb); + +err_out_unlock: + netfs_trans_free(t); + return err; +} + +static int pohmelfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + struct inode *inode = old_dentry->d_inode; + struct pohmelfs_inode *old_parent, *pi, *new_parent; + struct qstr str = new_dentry->d_name; + struct pohmelfs_name *n; + unsigned int old_hash; + int err = -ENOENT; + + pi = POHMELFS_I(inode); + old_parent = POHMELFS_I(old_dir); + + if (new_dir) + new_dir->i_sb->s_op->write_inode(new_dir, 0); + + old_hash = jhash(old_dentry->d_name.name, old_dentry->d_name.len, 0); + str.hash = jhash(new_dentry->d_name.name, new_dentry->d_name.len, 0); + + str.len = new_dentry->d_name.len; + str.name = new_dentry->d_name.name; + str.hash = jhash(new_dentry->d_name.name, new_dentry->d_name.len, 0); + + if (new_dir) { + new_parent = POHMELFS_I(new_dir); + err = -ENOTEMPTY; + + if (S_ISDIR(inode->i_mode) && + new_parent->total_len <= 3) + goto err_out_exit; + } else { + new_parent = old_parent; + } + + dprintk("%s: ino: %llu, parent: %llu, name: '%s' -> parent: %llu, name: '%s', i_size: %llu.\n", + __func__, pi->ino, old_parent->ino, old_dentry->d_name.name, + new_parent->ino, new_dentry->d_name.name, inode->i_size); + + if (test_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state) && + test_bit(NETFS_INODE_OWNED, &pi->state)) { + err = pohmelfs_send_rename(pi, new_parent, &str); + if (err) + goto err_out_exit; + } + + n = pohmelfs_name_alloc(str.len + 1); + if (!n) + goto err_out_exit; + + mutex_lock(&new_parent->offset_lock); + n->ino = pi->ino; + n->mode = inode->i_mode; + n->len = str.len; + n->hash = str.hash; + sprintf(n->data, "%s", str.name); + + err = pohmelfs_insert_name(new_parent, n); + mutex_unlock(&new_parent->offset_lock); + + if (err) + goto err_out_exit; + + mutex_lock(&old_parent->offset_lock); + n = pohmelfs_search_hash(old_parent, old_hash); + if (n) + pohmelfs_name_del(old_parent, n); + mutex_unlock(&old_parent->offset_lock); + + mark_inode_dirty(inode); + mark_inode_dirty(&new_parent->vfs_inode); + + WARN_ON_ONCE(list_empty(&inode->i_dentry)); + + return 0; + +err_out_exit: + + clear_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state); + + return err; +} + +/* + * POHMELFS directory inode operations. + */ +const struct inode_operations pohmelfs_dir_inode_ops = { + .link = pohmelfs_link, + .symlink = pohmelfs_symlink, + .unlink = pohmelfs_unlink, + .mkdir = pohmelfs_mkdir, + .rmdir = pohmelfs_rmdir, + .create = pohmelfs_create, + .lookup = pohmelfs_lookup, + .setattr = pohmelfs_setattr, + .rename = pohmelfs_rename, +}; diff --git a/trunk/drivers/staging/pohmelfs/inode.c b/trunk/drivers/staging/pohmelfs/inode.c new file mode 100644 index 000000000000..807e3f324113 --- /dev/null +++ b/trunk/drivers/staging/pohmelfs/inode.c @@ -0,0 +1,2055 @@ +/* + * 2007+ Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netfs.h" + +#define POHMELFS_MAGIC_NUM 0x504f482e + +static struct kmem_cache *pohmelfs_inode_cache; +static atomic_t psb_bdi_num = ATOMIC_INIT(0); + +/* + * Removes inode from all trees, drops local name cache and removes all queued + * requests for object removal. + */ +void pohmelfs_inode_del_inode(struct pohmelfs_sb *psb, struct pohmelfs_inode *pi) +{ + mutex_lock(&pi->offset_lock); + pohmelfs_free_names(pi); + mutex_unlock(&pi->offset_lock); + + dprintk("%s: deleted stuff in ino: %llu.\n", __func__, pi->ino); +} + +/* + * Sync inode to server. + * Returns zero in success and negative error value otherwise. + * It will gather path to root directory into structures containing + * creation mode, permissions and names, so that the whole path + * to given inode could be created using only single network command. + */ +int pohmelfs_write_inode_create(struct inode *inode, struct netfs_trans *trans) +{ + struct pohmelfs_inode *pi = POHMELFS_I(inode); + int err = -ENOMEM, size; + struct netfs_cmd *cmd; + void *data; + int cur_len = netfs_trans_cur_len(trans); + + if (unlikely(cur_len < 0)) + return -ETOOSMALL; + + cmd = netfs_trans_current(trans); + cur_len -= sizeof(struct netfs_cmd); + + data = (void *)(cmd + 1); + + err = pohmelfs_construct_path_string(pi, data, cur_len); + if (err < 0) + goto err_out_exit; + + size = err; + + cmd->start = i_size_read(inode); + cmd->cmd = NETFS_CREATE; + cmd->size = size; + cmd->id = pi->ino; + cmd->ext = inode->i_mode; + + netfs_convert_cmd(cmd); + + netfs_trans_update(cmd, trans, size); + + return 0; + +err_out_exit: + printk("%s: completed ino: %llu, err: %d.\n", __func__, pi->ino, err); + return err; +} + +static int pohmelfs_write_trans_complete(struct page **pages, unsigned int page_num, + void *private, int err) +{ + unsigned i; + + dprintk("%s: pages: %lu-%lu, page_num: %u, err: %d.\n", + __func__, pages[0]->index, pages[page_num-1]->index, + page_num, err); + + for (i = 0; i < page_num; i++) { + struct page *page = pages[i]; + + if (!page) + continue; + + end_page_writeback(page); + + if (err < 0) { + SetPageError(page); + set_page_dirty(page); + } + + unlock_page(page); + page_cache_release(page); + + /* dprintk("%s: %3u/%u: page: %p.\n", __func__, i, page_num, page); */ + } + return err; +} + +static int pohmelfs_inode_has_dirty_pages(struct address_space *mapping, pgoff_t index) +{ + int ret; + struct page *page; + + rcu_read_lock(); + ret = radix_tree_gang_lookup_tag(&mapping->page_tree, + (void **)&page, index, 1, PAGECACHE_TAG_DIRTY); + rcu_read_unlock(); + return ret; +} + +static int pohmelfs_writepages(struct address_space *mapping, struct writeback_control *wbc) +{ + struct inode *inode = mapping->host; + struct pohmelfs_inode *pi = POHMELFS_I(inode); + struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); + int err = 0; + int done = 0; + int nr_pages; + pgoff_t index; + pgoff_t end; /* Inclusive */ + int scanned = 0; + int range_whole = 0; + + if (wbc->range_cyclic) { + index = mapping->writeback_index; /* Start from prev offset */ + end = -1; + } else { + index = wbc->range_start >> PAGE_CACHE_SHIFT; + end = wbc->range_end >> PAGE_CACHE_SHIFT; + if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) + range_whole = 1; + scanned = 1; + } +retry: + while (!done && (index <= end)) { + unsigned int i = min(end - index, (pgoff_t)psb->trans_max_pages); + int path_len; + struct netfs_trans *trans; + + err = pohmelfs_inode_has_dirty_pages(mapping, index); + if (!err) + break; + + err = pohmelfs_path_length(pi); + if (err < 0) + break; + + path_len = err; + + if (path_len <= 2) { + err = -ENOENT; + break; + } + + trans = netfs_trans_alloc(psb, path_len, 0, i); + if (!trans) { + err = -ENOMEM; + break; + } + trans->complete = &pohmelfs_write_trans_complete; + + trans->page_num = nr_pages = find_get_pages_tag(mapping, &index, + PAGECACHE_TAG_DIRTY, trans->page_num, + trans->pages); + + dprintk("%s: t: %p, nr_pages: %u, end: %lu, index: %lu, max: %u.\n", + __func__, trans, nr_pages, end, index, trans->page_num); + + if (!nr_pages) + goto err_out_reset; + + err = pohmelfs_write_inode_create(inode, trans); + if (err) + goto err_out_reset; + + err = 0; + scanned = 1; + + for (i = 0; i < trans->page_num; i++) { + struct page *page = trans->pages[i]; + + lock_page(page); + + if (unlikely(page->mapping != mapping)) + goto out_continue; + + if (!wbc->range_cyclic && page->index > end) { + done = 1; + goto out_continue; + } + + if (wbc->sync_mode != WB_SYNC_NONE) + wait_on_page_writeback(page); + + if (PageWriteback(page) || + !clear_page_dirty_for_io(page)) { + dprintk("%s: not clear for io page: %p, writeback: %d.\n", + __func__, page, PageWriteback(page)); + goto out_continue; + } + + set_page_writeback(page); + + trans->attached_size += page_private(page); + trans->attached_pages++; +#if 0 + dprintk("%s: %u/%u added trans: %p, gen: %u, page: %p, [High: %d], size: %lu, idx: %lu.\n", + __func__, i, trans->page_num, trans, trans->gen, page, + !!PageHighMem(page), page_private(page), page->index); +#endif + wbc->nr_to_write--; + + if (wbc->nr_to_write <= 0) + done = 1; + + continue; +out_continue: + unlock_page(page); + trans->pages[i] = NULL; + } + + err = netfs_trans_finish(trans, psb); + if (err) + break; + + continue; + +err_out_reset: + trans->result = err; + netfs_trans_reset(trans); + netfs_trans_put(trans); + break; + } + + if (!scanned && !done) { + /* + * We hit the last page and there is more work to be done: wrap + * back to the start of the file + */ + scanned = 1; + index = 0; + goto retry; + } + + if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0)) + mapping->writeback_index = index; + + return err; +} + +/* + * Inode writeback creation completion callback. + * Only invoked for just created inodes, which do not have pages attached, + * like dirs and empty files. + */ +static int pohmelfs_write_inode_complete(struct page **pages, unsigned int page_num, + void *private, int err) +{ + struct inode *inode = private; + struct pohmelfs_inode *pi = POHMELFS_I(inode); + + if (inode) { + if (err) { + mark_inode_dirty(inode); + clear_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state); + } else { + set_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state); + } + + pohmelfs_put_inode(pi); + } + + return err; +} + +int pohmelfs_write_create_inode(struct pohmelfs_inode *pi) +{ + struct netfs_trans *t; + struct inode *inode = &pi->vfs_inode; + struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); + int err; + + if (test_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state)) + return 0; + + dprintk("%s: started ino: %llu.\n", __func__, pi->ino); + + err = pohmelfs_path_length(pi); + if (err < 0) + goto err_out_exit; + + t = netfs_trans_alloc(psb, err + 1, 0, 0); + if (!t) { + err = -ENOMEM; + goto err_out_exit; + } + t->complete = pohmelfs_write_inode_complete; + t->private = igrab(inode); + if (!t->private) { + err = -ENOENT; + goto err_out_put; + } + + err = pohmelfs_write_inode_create(inode, t); + if (err) + goto err_out_put; + + netfs_trans_finish(t, POHMELFS_SB(inode->i_sb)); + + return 0; + +err_out_put: + t->result = err; + netfs_trans_put(t); +err_out_exit: + return err; +} + +/* + * Sync all not-yet-created children in given directory to the server. + */ +static int pohmelfs_write_inode_create_children(struct inode *inode) +{ + struct pohmelfs_inode *parent = POHMELFS_I(inode); + struct super_block *sb = inode->i_sb; + struct pohmelfs_name *n; + + while (!list_empty(&parent->sync_create_list)) { + n = NULL; + mutex_lock(&parent->offset_lock); + if (!list_empty(&parent->sync_create_list)) { + n = list_first_entry(&parent->sync_create_list, + struct pohmelfs_name, sync_create_entry); + list_del_init(&n->sync_create_entry); + } + mutex_unlock(&parent->offset_lock); + + if (!n) + break; + + inode = ilookup(sb, n->ino); + + dprintk("%s: parent: %llu, ino: %llu, inode: %p.\n", + __func__, parent->ino, n->ino, inode); + + if (inode && (inode->i_state & I_DIRTY)) { + struct pohmelfs_inode *pi = POHMELFS_I(inode); + pohmelfs_write_create_inode(pi); + /* pohmelfs_meta_command(pi, NETFS_INODE_INFO, 0, NULL, NULL, 0); */ + iput(inode); + } + } + + return 0; +} + +/* + * Removes given child from given inode on server. + */ +int pohmelfs_remove_child(struct pohmelfs_inode *pi, struct pohmelfs_name *n) +{ + return pohmelfs_meta_command_data(pi, pi->ino, NETFS_REMOVE, NULL, 0, NULL, NULL, 0); +} + +/* + * Writeback for given inode. + */ +static int pohmelfs_write_inode(struct inode *inode, + struct writeback_control *wbc) +{ + struct pohmelfs_inode *pi = POHMELFS_I(inode); + + pohmelfs_write_create_inode(pi); + pohmelfs_write_inode_create_children(inode); + + return 0; +} + +/* + * It is not exported, sorry... + */ +static inline wait_queue_head_t *page_waitqueue(struct page *page) +{ + const struct zone *zone = page_zone(page); + + return &zone->wait_table[hash_ptr(page, zone->wait_table_bits)]; +} + +static int pohmelfs_wait_on_page_locked(struct page *page) +{ + struct pohmelfs_sb *psb = POHMELFS_SB(page->mapping->host->i_sb); + long ret = psb->wait_on_page_timeout; + DEFINE_WAIT_BIT(wait, &page->flags, PG_locked); + int err = 0; + + if (!PageLocked(page)) + return 0; + + for (;;) { + prepare_to_wait(page_waitqueue(page), + &wait.wait, TASK_INTERRUPTIBLE); + + dprintk("%s: page: %p, locked: %d, uptodate: %d, error: %d, flags: %lx.\n", + __func__, page, PageLocked(page), PageUptodate(page), + PageError(page), page->flags); + + if (!PageLocked(page)) + break; + + if (!signal_pending(current)) { + ret = schedule_timeout(ret); + if (!ret) + break; + continue; + } + ret = -ERESTARTSYS; + break; + } + finish_wait(page_waitqueue(page), &wait.wait); + + if (!ret) + err = -ETIMEDOUT; + + + if (!err) + SetPageUptodate(page); + + if (err) + printk("%s: page: %p, uptodate: %d, locked: %d, err: %d.\n", + __func__, page, PageUptodate(page), PageLocked(page), err); + + return err; +} + +static int pohmelfs_read_page_complete(struct page **pages, unsigned int page_num, + void *private, int err) +{ + struct page *page = private; + + if (PageChecked(page)) + return err; + + if (err < 0) { + dprintk("%s: page: %p, err: %d.\n", __func__, page, err); + SetPageError(page); + } + + unlock_page(page); + + return err; +} + +/* + * Read a page from remote server. + * Function will wait until page is unlocked. + */ +static int pohmelfs_readpage(struct file *file, struct page *page) +{ + struct inode *inode = page->mapping->host; + struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); + struct pohmelfs_inode *pi = POHMELFS_I(inode); + struct netfs_trans *t; + struct netfs_cmd *cmd; + int err, path_len; + void *data; + u64 isize; + + err = pohmelfs_data_lock(pi, page->index << PAGE_CACHE_SHIFT, + PAGE_SIZE, POHMELFS_READ_LOCK); + if (err) + goto err_out_exit; + + isize = i_size_read(inode); + if (isize <= page->index << PAGE_CACHE_SHIFT) { + SetPageUptodate(page); + unlock_page(page); + return 0; + } + + path_len = pohmelfs_path_length(pi); + if (path_len < 0) { + err = path_len; + goto err_out_exit; + } + + t = netfs_trans_alloc(psb, path_len, NETFS_TRANS_SINGLE_DST, 0); + if (!t) { + err = -ENOMEM; + goto err_out_exit; + } + + t->complete = pohmelfs_read_page_complete; + t->private = page; + + cmd = netfs_trans_current(t); + data = (void *)(cmd + 1); + + err = pohmelfs_construct_path_string(pi, data, path_len); + if (err < 0) + goto err_out_free; + + path_len = err; + + cmd->id = pi->ino; + cmd->start = page->index; + cmd->start <<= PAGE_CACHE_SHIFT; + cmd->size = PAGE_CACHE_SIZE + path_len; + cmd->cmd = NETFS_READ_PAGE; + cmd->ext = path_len; + + dprintk("%s: path: '%s', page: %p, ino: %llu, start: %llu, size: %lu.\n", + __func__, (char *)data, page, pi->ino, cmd->start, PAGE_CACHE_SIZE); + + netfs_convert_cmd(cmd); + netfs_trans_update(cmd, t, path_len); + + err = netfs_trans_finish(t, psb); + if (err) + goto err_out_return; + + return pohmelfs_wait_on_page_locked(page); + +err_out_free: + t->result = err; + netfs_trans_put(t); +err_out_exit: + SetPageError(page); + if (PageLocked(page)) + unlock_page(page); +err_out_return: + printk("%s: page: %p, start: %lu, size: %lu, err: %d.\n", + __func__, page, page->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE, err); + + return err; +} + +/* + * Write begin/end magic. + * Allocates a page and writes inode if it was not synced to server before. + */ +static int pohmelfs_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned flags, + struct page **pagep, void **fsdata) +{ + struct inode *inode = mapping->host; + struct page *page; + pgoff_t index; + unsigned start, end; + int err; + + *pagep = NULL; + + index = pos >> PAGE_CACHE_SHIFT; + start = pos & (PAGE_CACHE_SIZE - 1); + end = start + len; + + page = grab_cache_page(mapping, index); +#if 0 + dprintk("%s: page: %p pos: %llu, len: %u, index: %lu, start: %u, end: %u, uptodate: %d.\n", + __func__, page, pos, len, index, start, end, PageUptodate(page)); +#endif + if (!page) { + err = -ENOMEM; + goto err_out_exit; + } + + while (!PageUptodate(page)) { + if (start && test_bit(NETFS_INODE_REMOTE_SYNCED, &POHMELFS_I(inode)->state)) { + err = pohmelfs_readpage(file, page); + if (err) + goto err_out_exit; + + lock_page(page); + continue; + } + + if (len != PAGE_CACHE_SIZE) { + void *kaddr = kmap_atomic(page, KM_USER0); + + memset(kaddr + start, 0, PAGE_CACHE_SIZE - start); + flush_dcache_page(page); + kunmap_atomic(kaddr, KM_USER0); + } + SetPageUptodate(page); + } + + set_page_private(page, end); + + *pagep = page; + + return 0; + +err_out_exit: + page_cache_release(page); + *pagep = NULL; + + return err; +} + +static int pohmelfs_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *page, void *fsdata) +{ + struct inode *inode = mapping->host; + + if (copied != len) { + unsigned from = pos & (PAGE_CACHE_SIZE - 1); + void *kaddr = kmap_atomic(page, KM_USER0); + + memset(kaddr + from + copied, 0, len - copied); + flush_dcache_page(page); + kunmap_atomic(kaddr, KM_USER0); + } + + SetPageUptodate(page); + set_page_dirty(page); +#if 0 + dprintk("%s: page: %p [U: %d, D: %d, L: %d], pos: %llu, len: %u, copied: %u.\n", + __func__, page, + PageUptodate(page), PageDirty(page), PageLocked(page), + pos, len, copied); +#endif + flush_dcache_page(page); + + unlock_page(page); + page_cache_release(page); + + if (pos + copied > inode->i_size) { + struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); + + psb->avail_size -= pos + copied - inode->i_size; + + i_size_write(inode, pos + copied); + } + + return copied; +} + +static int pohmelfs_readpages_trans_complete(struct page **__pages, unsigned int page_num, + void *private, int err) +{ + struct pohmelfs_inode *pi = private; + unsigned int i, num; + struct page **pages, *page = (struct page *)__pages; + loff_t index = page->index; + + pages = kzalloc(sizeof(void *) * page_num, GFP_NOIO); + if (!pages) + return -ENOMEM; + + num = find_get_pages_contig(pi->vfs_inode.i_mapping, index, page_num, pages); + if (num <= 0) { + err = num; + goto err_out_free; + } + + for (i = 0; i < num; ++i) { + page = pages[i]; + + if (err) + printk("%s: %u/%u: page: %p, index: %lu, uptodate: %d, locked: %d, err: %d.\n", + __func__, i, num, page, page->index, + PageUptodate(page), PageLocked(page), err); + + if (!PageChecked(page)) { + if (err < 0) + SetPageError(page); + unlock_page(page); + } + page_cache_release(page); + page_cache_release(page); + } + +err_out_free: + kfree(pages); + return err; +} + +static int pohmelfs_send_readpages(struct pohmelfs_inode *pi, struct page *first, unsigned int num) +{ + struct netfs_trans *t; + struct netfs_cmd *cmd; + struct pohmelfs_sb *psb = POHMELFS_SB(pi->vfs_inode.i_sb); + int err, path_len; + void *data; + + err = pohmelfs_data_lock(pi, first->index << PAGE_CACHE_SHIFT, + num * PAGE_SIZE, POHMELFS_READ_LOCK); + if (err) + goto err_out_exit; + + path_len = pohmelfs_path_length(pi); + if (path_len < 0) { + err = path_len; + goto err_out_exit; + } + + t = netfs_trans_alloc(psb, path_len, NETFS_TRANS_SINGLE_DST, 0); + if (!t) { + err = -ENOMEM; + goto err_out_exit; + } + + cmd = netfs_trans_current(t); + data = (void *)(cmd + 1); + + t->complete = pohmelfs_readpages_trans_complete; + t->private = pi; + t->page_num = num; + t->pages = (struct page **)first; + + err = pohmelfs_construct_path_string(pi, data, path_len); + if (err < 0) + goto err_out_put; + + path_len = err; + + cmd->cmd = NETFS_READ_PAGES; + cmd->start = first->index; + cmd->start <<= PAGE_CACHE_SHIFT; + cmd->size = (num << 8 | PAGE_CACHE_SHIFT); + cmd->id = pi->ino; + cmd->ext = path_len; + + dprintk("%s: t: %p, gen: %u, path: '%s', path_len: %u, " + "start: %lu, num: %u.\n", + __func__, t, t->gen, (char *)data, path_len, + first->index, num); + + netfs_convert_cmd(cmd); + netfs_trans_update(cmd, t, path_len); + + return netfs_trans_finish(t, psb); + +err_out_put: + netfs_trans_free(t); +err_out_exit: + pohmelfs_readpages_trans_complete((struct page **)first, num, pi, err); + return err; +} + +#define list_to_page(head) (list_entry((head)->prev, struct page, lru)) + +static int pohmelfs_readpages(struct file *file, struct address_space *mapping, + struct list_head *pages, unsigned nr_pages) +{ + unsigned int page_idx, num = 0; + struct page *page = NULL, *first = NULL; + + for (page_idx = 0; page_idx < nr_pages; page_idx++) { + page = list_to_page(pages); + + prefetchw(&page->flags); + list_del(&page->lru); + + if (!add_to_page_cache_lru(page, mapping, + page->index, GFP_KERNEL)) { + + if (!num) { + num = 1; + first = page; + continue; + } + + dprintk("%s: added to lru page: %p, page_index: %lu, first_index: %lu.\n", + __func__, page, page->index, first->index); + + if (unlikely(first->index + num != page->index) || (num > 500)) { + pohmelfs_send_readpages(POHMELFS_I(mapping->host), + first, num); + first = page; + num = 0; + } + + num++; + } + } + pohmelfs_send_readpages(POHMELFS_I(mapping->host), first, num); + + /* + * This will be sync read, so when last page is processed, + * all previous are alerady unlocked and ready to be used. + */ + return 0; +} + +/* + * Small address space operations for POHMELFS. + */ +const struct address_space_operations pohmelfs_aops = { + .readpage = pohmelfs_readpage, + .readpages = pohmelfs_readpages, + .writepages = pohmelfs_writepages, + .write_begin = pohmelfs_write_begin, + .write_end = pohmelfs_write_end, + .set_page_dirty = __set_page_dirty_nobuffers, +}; + +static void pohmelfs_i_callback(struct rcu_head *head) +{ + struct inode *inode = container_of(head, struct inode, i_rcu); + kmem_cache_free(pohmelfs_inode_cache, POHMELFS_I(inode)); +} + +/* + * ->destroy_inode() callback. Deletes inode from the caches + * and frees private data. + */ +static void pohmelfs_destroy_inode(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + struct pohmelfs_sb *psb = POHMELFS_SB(sb); + struct pohmelfs_inode *pi = POHMELFS_I(inode); + + /* pohmelfs_data_unlock(pi, 0, inode->i_size, POHMELFS_READ_LOCK); */ + + pohmelfs_inode_del_inode(psb, pi); + + dprintk("%s: pi: %p, inode: %p, ino: %llu.\n", + __func__, pi, &pi->vfs_inode, pi->ino); + atomic_long_dec(&psb->total_inodes); + call_rcu(&inode->i_rcu, pohmelfs_i_callback); +} + +/* + * ->alloc_inode() callback. Allocates inode and initializes private data. + */ +static struct inode *pohmelfs_alloc_inode(struct super_block *sb) +{ + struct pohmelfs_inode *pi; + + pi = kmem_cache_alloc(pohmelfs_inode_cache, GFP_NOIO); + if (!pi) + return NULL; + + pi->hash_root = RB_ROOT; + mutex_init(&pi->offset_lock); + + INIT_LIST_HEAD(&pi->sync_create_list); + + INIT_LIST_HEAD(&pi->inode_entry); + + pi->lock_type = 0; + pi->state = 0; + pi->total_len = 0; + pi->drop_count = 0; + + dprintk("%s: pi: %p, inode: %p.\n", __func__, pi, &pi->vfs_inode); + + atomic_long_inc(&POHMELFS_SB(sb)->total_inodes); + + return &pi->vfs_inode; +} + +/* + * We want fsync() to work on POHMELFS. + */ +static int pohmelfs_fsync(struct file *file, loff_t start, loff_t end, int datasync) +{ + struct inode *inode = file->f_mapping->host; + int err = filemap_write_and_wait_range(inode->i_mapping, start, end); + if (!err) { + mutex_lock(&inode->i_mutex); + err = sync_inode_metadata(inode, 1); + mutex_unlock(&inode->i_mutex); + } + return err; +} + +ssize_t pohmelfs_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + struct address_space *mapping = file->f_mapping; + struct inode *inode = mapping->host; + struct pohmelfs_inode *pi = POHMELFS_I(inode); + struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = len }; + struct kiocb kiocb; + ssize_t ret; + loff_t pos = *ppos; + + init_sync_kiocb(&kiocb, file); + kiocb.ki_pos = pos; + kiocb.ki_left = len; + + dprintk("%s: len: %zu, pos: %llu.\n", __func__, len, pos); + + mutex_lock(&inode->i_mutex); + ret = pohmelfs_data_lock(pi, pos, len, POHMELFS_WRITE_LOCK); + if (ret) + goto err_out_unlock; + + ret = __generic_file_aio_write(&kiocb, &iov, 1, &kiocb.ki_pos); + *ppos = kiocb.ki_pos; + + mutex_unlock(&inode->i_mutex); + WARN_ON(ret < 0); + + if (ret > 0) { + ssize_t err; + + err = generic_write_sync(file, pos, ret); + if (err < 0) + ret = err; + WARN_ON(ret < 0); + } + + return ret; + +err_out_unlock: + mutex_unlock(&inode->i_mutex); + return ret; +} + +static const struct file_operations pohmelfs_file_ops = { + .open = generic_file_open, + .fsync = pohmelfs_fsync, + + .llseek = generic_file_llseek, + + .read = do_sync_read, + .aio_read = generic_file_aio_read, + + .mmap = generic_file_mmap, + + .splice_read = generic_file_splice_read, + .splice_write = generic_file_splice_write, + + .write = pohmelfs_write, + .aio_write = generic_file_aio_write, +}; + +const struct inode_operations pohmelfs_symlink_inode_operations = { + .readlink = generic_readlink, + .follow_link = page_follow_link_light, + .put_link = page_put_link, +}; + +int pohmelfs_setattr_raw(struct inode *inode, struct iattr *attr) +{ + int err; + + err = inode_change_ok(inode, attr); + if (err) { + dprintk("%s: ino: %llu, inode changes are not allowed.\n", __func__, POHMELFS_I(inode)->ino); + goto err_out_exit; + } + + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) { + err = vmtruncate(inode, attr->ia_size); + if (err) { + dprintk("%s: ino: %llu, failed to set the attributes.\n", __func__, POHMELFS_I(inode)->ino); + goto err_out_exit; + } + } + + setattr_copy(inode, attr); + mark_inode_dirty(inode); + + dprintk("%s: ino: %llu, mode: %o -> %o, uid: %u -> %u, gid: %u -> %u, size: %llu -> %llu.\n", + __func__, POHMELFS_I(inode)->ino, inode->i_mode, attr->ia_mode, + inode->i_uid, attr->ia_uid, inode->i_gid, attr->ia_gid, inode->i_size, attr->ia_size); + + return 0; + +err_out_exit: + return err; +} + +int pohmelfs_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + struct pohmelfs_inode *pi = POHMELFS_I(inode); + int err; + + err = pohmelfs_data_lock(pi, 0, ~0, POHMELFS_WRITE_LOCK); + if (err) + goto err_out_exit; + + err = security_inode_setattr(dentry, attr); + if (err) + goto err_out_exit; + + err = pohmelfs_setattr_raw(inode, attr); + if (err) + goto err_out_exit; + + return 0; + +err_out_exit: + return err; +} + +static int pohmelfs_send_xattr_req(struct pohmelfs_inode *pi, u64 id, u64 start, + const char *name, const void *value, size_t attrsize, int command) +{ + struct pohmelfs_sb *psb = POHMELFS_SB(pi->vfs_inode.i_sb); + int err, path_len, namelen = strlen(name) + 1; /* 0-byte */ + struct netfs_trans *t; + struct netfs_cmd *cmd; + void *data; + + dprintk("%s: id: %llu, start: %llu, name: '%s', attrsize: %zu, cmd: %d.\n", + __func__, id, start, name, attrsize, command); + + path_len = pohmelfs_path_length(pi); + if (path_len < 0) { + err = path_len; + goto err_out_exit; + } + + t = netfs_trans_alloc(psb, namelen + path_len + attrsize, 0, 0); + if (!t) { + err = -ENOMEM; + goto err_out_exit; + } + + cmd = netfs_trans_current(t); + data = cmd + 1; + + path_len = pohmelfs_construct_path_string(pi, data, path_len); + if (path_len < 0) { + err = path_len; + goto err_out_put; + } + data += path_len; + + /* + * 'name' is a NUL-terminated string already and + * 'namelen' includes 0-byte. + */ + memcpy(data, name, namelen); + data += namelen; + + memcpy(data, value, attrsize); + + cmd->cmd = command; + cmd->id = id; + cmd->start = start; + cmd->size = attrsize + namelen + path_len; + cmd->ext = path_len; + cmd->csize = 0; + cmd->cpad = 0; + + netfs_convert_cmd(cmd); + netfs_trans_update(cmd, t, namelen + path_len + attrsize); + + return netfs_trans_finish(t, psb); + +err_out_put: + t->result = err; + netfs_trans_put(t); +err_out_exit: + return err; +} + +static int pohmelfs_setxattr(struct dentry *dentry, const char *name, + const void *value, size_t attrsize, int flags) +{ + struct inode *inode = dentry->d_inode; + struct pohmelfs_inode *pi = POHMELFS_I(inode); + struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); + + if (!(psb->state_flags & POHMELFS_FLAGS_XATTR)) + return -EOPNOTSUPP; + + return pohmelfs_send_xattr_req(pi, flags, attrsize, name, + value, attrsize, NETFS_XATTR_SET); +} + +static ssize_t pohmelfs_getxattr(struct dentry *dentry, const char *name, + void *value, size_t attrsize) +{ + struct inode *inode = dentry->d_inode; + struct pohmelfs_inode *pi = POHMELFS_I(inode); + struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); + struct pohmelfs_mcache *m; + int err; + long timeout = psb->mcache_timeout; + + if (!(psb->state_flags & POHMELFS_FLAGS_XATTR)) + return -EOPNOTSUPP; + + m = pohmelfs_mcache_alloc(psb, 0, attrsize, value); + if (IS_ERR(m)) + return PTR_ERR(m); + + dprintk("%s: ino: %llu, name: '%s', size: %zu.\n", + __func__, pi->ino, name, attrsize); + + err = pohmelfs_send_xattr_req(pi, m->gen, attrsize, name, value, 0, NETFS_XATTR_GET); + if (err) + goto err_out_put; + + do { + err = wait_for_completion_timeout(&m->complete, timeout); + if (err) { + err = m->err; + break; + } + + /* + * This loop is a bit ugly, since it waits until reference counter + * hits 1 and then puts the object here. Main goal is to prevent race with + * the network thread, when it can start processing the given request, i.e. + * increase its reference counter but yet not complete it, while + * we will exit from ->getxattr() with timeout, and although request + * will not be freed (its reference counter was increased by network + * thread), data pointer provided by user may be released, so we will + * overwrite an already freed area in the network thread. + * + * Now after timeout we remove request from the cache, so it can not be + * found by network thread, and wait for its reference counter to hit 1, + * i.e. if network thread already started to process this request, we wait + * for it to finish, and then free object locally. If reference counter is + * already 1, i.e. request is not used by anyone else, we can free it without + * problem. + */ + err = -ETIMEDOUT; + timeout = HZ; + + pohmelfs_mcache_remove_locked(psb, m); + } while (atomic_read(&m->refcnt) != 1); + + pohmelfs_mcache_put(psb, m); + + dprintk("%s: ino: %llu, err: %d.\n", __func__, pi->ino, err); + + return err; + +err_out_put: + pohmelfs_mcache_put(psb, m); + return err; +} + +static int pohmelfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) +{ + struct inode *inode = dentry->d_inode; +#if 0 + struct pohmelfs_inode *pi = POHMELFS_I(inode); + int err; + + err = pohmelfs_data_lock(pi, 0, ~0, POHMELFS_READ_LOCK); + if (err) + return err; + dprintk("%s: ino: %llu, mode: %o, uid: %u, gid: %u, size: %llu.\n", + __func__, pi->ino, inode->i_mode, inode->i_uid, + inode->i_gid, inode->i_size); +#endif + + generic_fillattr(inode, stat); + return 0; +} + +const struct inode_operations pohmelfs_file_inode_operations = { + .setattr = pohmelfs_setattr, + .getattr = pohmelfs_getattr, + .setxattr = pohmelfs_setxattr, + .getxattr = pohmelfs_getxattr, +}; + +/* + * Fill inode data: mode, size, operation callbacks and so on... + */ +void pohmelfs_fill_inode(struct inode *inode, struct netfs_inode_info *info) +{ + inode->i_mode = info->mode; + set_nlink(inode, info->nlink); + inode->i_uid = info->uid; + inode->i_gid = info->gid; + inode->i_blocks = info->blocks; + inode->i_rdev = info->rdev; + inode->i_size = info->size; + inode->i_version = info->version; + inode->i_blkbits = ffs(info->blocksize); + + dprintk("%s: inode: %p, num: %lu/%llu inode is regular: %d, dir: %d, link: %d, mode: %o, size: %llu.\n", + __func__, inode, inode->i_ino, info->ino, + S_ISREG(inode->i_mode), S_ISDIR(inode->i_mode), + S_ISLNK(inode->i_mode), inode->i_mode, inode->i_size); + + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC; + + /* + * i_mapping is a pointer to i_data during inode initialization. + */ + inode->i_data.a_ops = &pohmelfs_aops; + + if (S_ISREG(inode->i_mode)) { + inode->i_fop = &pohmelfs_file_ops; + inode->i_op = &pohmelfs_file_inode_operations; + } else if (S_ISDIR(inode->i_mode)) { + inode->i_fop = &pohmelfs_dir_fops; + inode->i_op = &pohmelfs_dir_inode_ops; + } else if (S_ISLNK(inode->i_mode)) { + inode->i_op = &pohmelfs_symlink_inode_operations; + inode->i_fop = &pohmelfs_file_ops; + } else { + inode->i_fop = &generic_ro_fops; + } +} + +static int pohmelfs_drop_inode(struct inode *inode) +{ + struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); + struct pohmelfs_inode *pi = POHMELFS_I(inode); + + spin_lock(&psb->ino_lock); + list_del_init(&pi->inode_entry); + spin_unlock(&psb->ino_lock); + + return generic_drop_inode(inode); +} + +static struct pohmelfs_inode *pohmelfs_get_inode_from_list(struct pohmelfs_sb *psb, + struct list_head *head, unsigned int *count) +{ + struct pohmelfs_inode *pi = NULL; + + spin_lock(&psb->ino_lock); + if (!list_empty(head)) { + pi = list_entry(head->next, struct pohmelfs_inode, + inode_entry); + list_del_init(&pi->inode_entry); + *count = pi->drop_count; + pi->drop_count = 0; + } + spin_unlock(&psb->ino_lock); + + return pi; +} + +static void pohmelfs_flush_transactions(struct pohmelfs_sb *psb) +{ + struct pohmelfs_config *c; + + mutex_lock(&psb->state_lock); + list_for_each_entry(c, &psb->state_list, config_entry) { + pohmelfs_state_flush_transactions(&c->state); + } + mutex_unlock(&psb->state_lock); +} + +/* + * ->put_super() callback. Invoked before superblock is destroyed, + * so it has to clean all private data. + */ +static void pohmelfs_put_super(struct super_block *sb) +{ + struct pohmelfs_sb *psb = POHMELFS_SB(sb); + struct pohmelfs_inode *pi; + unsigned int count = 0; + unsigned int in_drop_list = 0; + struct inode *inode, *tmp; + + dprintk("%s.\n", __func__); + + /* + * Kill pending transactions, which could affect inodes in-flight. + */ + pohmelfs_flush_transactions(psb); + + while ((pi = pohmelfs_get_inode_from_list(psb, &psb->drop_list, &count))) { + inode = &pi->vfs_inode; + + dprintk("%s: ino: %llu, pi: %p, inode: %p, count: %u.\n", + __func__, pi->ino, pi, inode, count); + + if (atomic_read(&inode->i_count) != count) { + printk("%s: ino: %llu, pi: %p, inode: %p, count: %u, i_count: %d.\n", + __func__, pi->ino, pi, inode, count, + atomic_read(&inode->i_count)); + count = atomic_read(&inode->i_count); + in_drop_list++; + } + + while (count--) + iput(&pi->vfs_inode); + } + + list_for_each_entry_safe(inode, tmp, &sb->s_inodes, i_sb_list) { + pi = POHMELFS_I(inode); + + dprintk("%s: ino: %llu, pi: %p, inode: %p, i_count: %u.\n", + __func__, pi->ino, pi, inode, atomic_read(&inode->i_count)); + + /* + * These are special inodes, they were created during + * directory reading or lookup, and were not bound to dentry, + * so they live here with reference counter being 1 and prevent + * umount from succeed since it believes that they are busy. + */ + count = atomic_read(&inode->i_count); + if (count) { + list_del_init(&inode->i_sb_list); + while (count--) + iput(&pi->vfs_inode); + } + } + + psb->trans_scan_timeout = psb->drop_scan_timeout = 0; + cancel_delayed_work_sync(&psb->dwork); + cancel_delayed_work_sync(&psb->drop_dwork); + flush_scheduled_work(); + + dprintk("%s: stopped workqueues.\n", __func__); + + pohmelfs_crypto_exit(psb); + pohmelfs_state_exit(psb); + + bdi_destroy(&psb->bdi); + + kfree(psb); + sb->s_fs_info = NULL; +} + +static int pohmelfs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + struct super_block *sb = dentry->d_sb; + struct pohmelfs_sb *psb = POHMELFS_SB(sb); + + /* + * There are no filesystem size limits yet. + */ + memset(buf, 0, sizeof(struct kstatfs)); + + buf->f_type = POHMELFS_MAGIC_NUM; /* 'POH.' */ + buf->f_bsize = sb->s_blocksize; + buf->f_files = psb->ino; + buf->f_namelen = 255; + buf->f_files = atomic_long_read(&psb->total_inodes); + buf->f_bfree = buf->f_bavail = psb->avail_size >> PAGE_SHIFT; + buf->f_blocks = psb->total_size >> PAGE_SHIFT; + + dprintk("%s: total: %llu, avail: %llu, inodes: %llu, bsize: %lu.\n", + __func__, psb->total_size, psb->avail_size, buf->f_files, sb->s_blocksize); + + return 0; +} + +static int pohmelfs_show_options(struct seq_file *seq, struct dentry *root) +{ + struct pohmelfs_sb *psb = POHMELFS_SB(root->d_sb); + + seq_printf(seq, ",idx=%u", psb->idx); + seq_printf(seq, ",trans_scan_timeout=%u", jiffies_to_msecs(psb->trans_scan_timeout)); + seq_printf(seq, ",drop_scan_timeout=%u", jiffies_to_msecs(psb->drop_scan_timeout)); + seq_printf(seq, ",wait_on_page_timeout=%u", jiffies_to_msecs(psb->wait_on_page_timeout)); + seq_printf(seq, ",trans_retries=%u", psb->trans_retries); + seq_printf(seq, ",crypto_thread_num=%u", psb->crypto_thread_num); + seq_printf(seq, ",trans_max_pages=%u", psb->trans_max_pages); + seq_printf(seq, ",mcache_timeout=%u", jiffies_to_msecs(psb->mcache_timeout)); + if (psb->crypto_fail_unsupported) + seq_printf(seq, ",crypto_fail_unsupported"); + + return 0; +} + +enum { + pohmelfs_opt_idx, + pohmelfs_opt_crypto_thread_num, + pohmelfs_opt_trans_max_pages, + pohmelfs_opt_crypto_fail_unsupported, + + /* Remountable options */ + pohmelfs_opt_trans_scan_timeout, + pohmelfs_opt_drop_scan_timeout, + pohmelfs_opt_wait_on_page_timeout, + pohmelfs_opt_trans_retries, + pohmelfs_opt_mcache_timeout, +}; + +static struct match_token pohmelfs_tokens[] = { + {pohmelfs_opt_idx, "idx=%u"}, + {pohmelfs_opt_crypto_thread_num, "crypto_thread_num=%u"}, + {pohmelfs_opt_trans_max_pages, "trans_max_pages=%u"}, + {pohmelfs_opt_crypto_fail_unsupported, "crypto_fail_unsupported"}, + {pohmelfs_opt_trans_scan_timeout, "trans_scan_timeout=%u"}, + {pohmelfs_opt_drop_scan_timeout, "drop_scan_timeout=%u"}, + {pohmelfs_opt_wait_on_page_timeout, "wait_on_page_timeout=%u"}, + {pohmelfs_opt_trans_retries, "trans_retries=%u"}, + {pohmelfs_opt_mcache_timeout, "mcache_timeout=%u"}, +}; + +static int pohmelfs_parse_options(char *options, struct pohmelfs_sb *psb, int remount) +{ + char *p; + substring_t args[MAX_OPT_ARGS]; + int option, err; + + if (!options) + return 0; + + while ((p = strsep(&options, ",")) != NULL) { + int token; + if (!*p) + continue; + + token = match_token(p, pohmelfs_tokens, args); + + err = match_int(&args[0], &option); + if (err) + return err; + + if (remount && token <= pohmelfs_opt_crypto_fail_unsupported) + continue; + + switch (token) { + case pohmelfs_opt_idx: + psb->idx = option; + break; + case pohmelfs_opt_trans_scan_timeout: + psb->trans_scan_timeout = msecs_to_jiffies(option); + break; + case pohmelfs_opt_drop_scan_timeout: + psb->drop_scan_timeout = msecs_to_jiffies(option); + break; + case pohmelfs_opt_wait_on_page_timeout: + psb->wait_on_page_timeout = msecs_to_jiffies(option); + break; + case pohmelfs_opt_mcache_timeout: + psb->mcache_timeout = msecs_to_jiffies(option); + break; + case pohmelfs_opt_trans_retries: + psb->trans_retries = option; + break; + case pohmelfs_opt_crypto_thread_num: + psb->crypto_thread_num = option; + break; + case pohmelfs_opt_trans_max_pages: + psb->trans_max_pages = option; + break; + case pohmelfs_opt_crypto_fail_unsupported: + psb->crypto_fail_unsupported = 1; + break; + default: + return -EINVAL; + } + } + + return 0; +} + +static int pohmelfs_remount(struct super_block *sb, int *flags, char *data) +{ + int err; + struct pohmelfs_sb *psb = POHMELFS_SB(sb); + unsigned long old_sb_flags = sb->s_flags; + + err = pohmelfs_parse_options(data, psb, 1); + if (err) + goto err_out_restore; + + if (!(*flags & MS_RDONLY)) + sb->s_flags &= ~MS_RDONLY; + return 0; + +err_out_restore: + sb->s_flags = old_sb_flags; + return err; +} + +static void pohmelfs_flush_inode(struct pohmelfs_inode *pi, unsigned int count) +{ + struct inode *inode = &pi->vfs_inode; + + dprintk("%s: %p: ino: %llu, owned: %d.\n", + __func__, inode, pi->ino, test_bit(NETFS_INODE_OWNED, &pi->state)); + + mutex_lock(&inode->i_mutex); + if (test_and_clear_bit(NETFS_INODE_OWNED, &pi->state)) { + filemap_fdatawrite(inode->i_mapping); + inode->i_sb->s_op->write_inode(inode, 0); + } + +#ifdef POHMELFS_TRUNCATE_ON_INODE_FLUSH + truncate_inode_pages(inode->i_mapping, 0); +#endif + + pohmelfs_data_unlock(pi, 0, ~0, POHMELFS_WRITE_LOCK); + mutex_unlock(&inode->i_mutex); +} + +static void pohmelfs_put_inode_count(struct pohmelfs_inode *pi, unsigned int count) +{ + dprintk("%s: ino: %llu, pi: %p, inode: %p, count: %u.\n", + __func__, pi->ino, pi, &pi->vfs_inode, count); + + if (test_and_clear_bit(NETFS_INODE_NEED_FLUSH, &pi->state)) + pohmelfs_flush_inode(pi, count); + + while (count--) + iput(&pi->vfs_inode); +} + +static void pohmelfs_drop_scan(struct work_struct *work) +{ + struct pohmelfs_sb *psb = + container_of(work, struct pohmelfs_sb, drop_dwork.work); + struct pohmelfs_inode *pi; + unsigned int count = 0; + + while ((pi = pohmelfs_get_inode_from_list(psb, &psb->drop_list, &count))) + pohmelfs_put_inode_count(pi, count); + + pohmelfs_check_states(psb); + + if (psb->drop_scan_timeout) + schedule_delayed_work(&psb->drop_dwork, psb->drop_scan_timeout); +} + +/* + * Run through all transactions starting from the oldest, + * drop transaction from current state and try to send it + * to all remote nodes, which are currently installed. + */ +static void pohmelfs_trans_scan_state(struct netfs_state *st) +{ + struct rb_node *rb_node; + struct netfs_trans_dst *dst; + struct pohmelfs_sb *psb = st->psb; + unsigned int timeout = psb->trans_scan_timeout; + struct netfs_trans *t; + int err; + + mutex_lock(&st->trans_lock); + for (rb_node = rb_first(&st->trans_root); rb_node; ) { + dst = rb_entry(rb_node, struct netfs_trans_dst, state_entry); + t = dst->trans; + + if (timeout && time_after(dst->send_time + timeout, jiffies) + && dst->retries == 0) + break; + + dprintk("%s: t: %p, gen: %u, st: %p, retries: %u, max: %u.\n", + __func__, t, t->gen, st, dst->retries, psb->trans_retries); + netfs_trans_get(t); + + rb_node = rb_next(rb_node); + + err = -ETIMEDOUT; + if (timeout && (++dst->retries < psb->trans_retries)) + err = netfs_trans_resend(t, psb); + + if (err || (t->flags & NETFS_TRANS_SINGLE_DST)) { + if (netfs_trans_remove_nolock(dst, st)) + netfs_trans_drop_dst_nostate(dst); + } + + t->result = err; + netfs_trans_put(t); + } + mutex_unlock(&st->trans_lock); +} + +/* + * Walk through all installed network states and resend all + * transactions, which are old enough. + */ +static void pohmelfs_trans_scan(struct work_struct *work) +{ + struct pohmelfs_sb *psb = + container_of(work, struct pohmelfs_sb, dwork.work); + struct netfs_state *st; + struct pohmelfs_config *c; + + mutex_lock(&psb->state_lock); + list_for_each_entry(c, &psb->state_list, config_entry) { + st = &c->state; + + pohmelfs_trans_scan_state(st); + } + mutex_unlock(&psb->state_lock); + + /* + * If no timeout specified then system is in the middle of umount process, + * so no need to reschedule scanning process again. + */ + if (psb->trans_scan_timeout) + schedule_delayed_work(&psb->dwork, psb->trans_scan_timeout); +} + +int pohmelfs_meta_command_data(struct pohmelfs_inode *pi, u64 id, unsigned int cmd_op, char *addon, + unsigned int flags, netfs_trans_complete_t complete, void *priv, u64 start) +{ + struct inode *inode = &pi->vfs_inode; + struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); + int err = 0, sz; + struct netfs_trans *t; + int path_len, addon_len = 0; + void *data; + struct netfs_inode_info *info; + struct netfs_cmd *cmd; + + dprintk("%s: ino: %llu, cmd: %u, addon: %p.\n", __func__, pi->ino, cmd_op, addon); + + path_len = pohmelfs_path_length(pi); + if (path_len < 0) { + err = path_len; + goto err_out_exit; + } + + if (addon) + addon_len = strlen(addon) + 1; /* 0-byte */ + sz = addon_len; + + if (cmd_op == NETFS_INODE_INFO) + sz += sizeof(struct netfs_inode_info); + + t = netfs_trans_alloc(psb, sz + path_len, flags, 0); + if (!t) { + err = -ENOMEM; + goto err_out_exit; + } + t->complete = complete; + t->private = priv; + + cmd = netfs_trans_current(t); + data = (void *)(cmd + 1); + + if (cmd_op == NETFS_INODE_INFO) { + info = (struct netfs_inode_info *)(cmd + 1); + data = (void *)(info + 1); + + /* + * We are under i_mutex, can read and change whatever we want... + */ + info->mode = inode->i_mode; + info->nlink = inode->i_nlink; + info->uid = inode->i_uid; + info->gid = inode->i_gid; + info->blocks = inode->i_blocks; + info->rdev = inode->i_rdev; + info->size = inode->i_size; + info->version = inode->i_version; + + netfs_convert_inode_info(info); + } + + path_len = pohmelfs_construct_path_string(pi, data, path_len); + if (path_len < 0) + goto err_out_free; + + dprintk("%s: path_len: %d.\n", __func__, path_len); + + if (addon) { + path_len--; /* Do not place null-byte before the addon */ + path_len += sprintf(data + path_len, "/%s", addon) + 1; /* 0 - byte */ + } + + sz += path_len; + + cmd->cmd = cmd_op; + cmd->ext = path_len; + cmd->size = sz; + cmd->id = id; + cmd->start = start; + + netfs_convert_cmd(cmd); + netfs_trans_update(cmd, t, sz); + + /* + * Note, that it is possible to leak error here: transaction callback will not + * be invoked for allocation path failure. + */ + return netfs_trans_finish(t, psb); + +err_out_free: + netfs_trans_free(t); +err_out_exit: + if (complete) + complete(NULL, 0, priv, err); + return err; +} + +int pohmelfs_meta_command(struct pohmelfs_inode *pi, unsigned int cmd_op, unsigned int flags, + netfs_trans_complete_t complete, void *priv, u64 start) +{ + return pohmelfs_meta_command_data(pi, pi->ino, cmd_op, NULL, flags, complete, priv, start); +} + +/* + * Send request and wait for POHMELFS root capabilities response, + * which will update server's informaion about size of the export, + * permissions, number of objects, available size and so on. + */ +static int pohmelfs_root_handshake(struct pohmelfs_sb *psb) +{ + struct netfs_trans *t; + struct netfs_cmd *cmd; + int err = -ENOMEM; + + t = netfs_trans_alloc(psb, 0, 0, 0); + if (!t) + goto err_out_exit; + + cmd = netfs_trans_current(t); + + cmd->cmd = NETFS_CAPABILITIES; + cmd->id = POHMELFS_ROOT_CAPABILITIES; + cmd->size = 0; + cmd->start = 0; + cmd->ext = 0; + cmd->csize = 0; + + netfs_convert_cmd(cmd); + netfs_trans_update(cmd, t, 0); + + err = netfs_trans_finish(t, psb); + if (err) + goto err_out_exit; + + psb->flags = ~0; + err = wait_event_interruptible_timeout(psb->wait, + (psb->flags != ~0), + psb->wait_on_page_timeout); + if (!err) + err = -ETIMEDOUT; + else if (err > 0) + err = -psb->flags; + + if (err) + goto err_out_exit; + + return 0; + +err_out_exit: + return err; +} + +static int pohmelfs_show_stats(struct seq_file *m, struct dentry *root) +{ + struct netfs_state *st; + struct pohmelfs_ctl *ctl; + struct pohmelfs_sb *psb = POHMELFS_SB(root->d_sb); + struct pohmelfs_config *c; + + mutex_lock(&psb->state_lock); + + seq_printf(m, "\nidx addr(:port) socket_type protocol active priority permissions\n"); + + list_for_each_entry(c, &psb->state_list, config_entry) { + st = &c->state; + ctl = &st->ctl; + + seq_printf(m, "%u ", ctl->idx); + if (ctl->addr.sa_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)&st->ctl.addr; + seq_printf(m, "%pI4:%u", &sin->sin_addr.s_addr, ntohs(sin->sin_port)); + } else if (ctl->addr.sa_family == AF_INET6) { + struct sockaddr_in6 *sin = (struct sockaddr_in6 *)&st->ctl.addr; + seq_printf(m, "%pi6:%u", &sin->sin6_addr, ntohs(sin->sin6_port)); + } else { + unsigned int i; + for (i = 0; i < ctl->addrlen; ++i) + seq_printf(m, "%02x.", ctl->addr.addr[i]); + } + + seq_printf(m, " %u %u %d %u %x\n", + ctl->type, ctl->proto, + st->socket != NULL, + ctl->prio, ctl->perm); + } + mutex_unlock(&psb->state_lock); + + return 0; +} + +static const struct super_operations pohmelfs_sb_ops = { + .alloc_inode = pohmelfs_alloc_inode, + .destroy_inode = pohmelfs_destroy_inode, + .drop_inode = pohmelfs_drop_inode, + .write_inode = pohmelfs_write_inode, + .put_super = pohmelfs_put_super, + .remount_fs = pohmelfs_remount, + .statfs = pohmelfs_statfs, + .show_options = pohmelfs_show_options, + .show_stats = pohmelfs_show_stats, +}; + +/* + * Allocate private superblock and create root dir. + */ +static int pohmelfs_fill_super(struct super_block *sb, void *data, int silent) +{ + struct pohmelfs_sb *psb; + int err = -ENOMEM; + struct inode *root; + struct pohmelfs_inode *npi; + struct qstr str; + + psb = kzalloc(sizeof(struct pohmelfs_sb), GFP_KERNEL); + if (!psb) + goto err_out_exit; + + err = bdi_init(&psb->bdi); + if (err) + goto err_out_free_sb; + + err = bdi_register(&psb->bdi, NULL, "pfs-%d", atomic_inc_return(&psb_bdi_num)); + if (err) { + bdi_destroy(&psb->bdi); + goto err_out_free_sb; + } + + sb->s_fs_info = psb; + sb->s_op = &pohmelfs_sb_ops; + sb->s_magic = POHMELFS_MAGIC_NUM; + sb->s_maxbytes = MAX_LFS_FILESIZE; + sb->s_blocksize = PAGE_SIZE; + sb->s_bdi = &psb->bdi; + + psb->sb = sb; + + psb->ino = 2; + psb->idx = 0; + psb->active_state = NULL; + psb->trans_retries = 5; + psb->trans_data_size = PAGE_SIZE; + psb->drop_scan_timeout = msecs_to_jiffies(1000); + psb->trans_scan_timeout = msecs_to_jiffies(5000); + psb->wait_on_page_timeout = msecs_to_jiffies(5000); + init_waitqueue_head(&psb->wait); + + spin_lock_init(&psb->ino_lock); + + INIT_LIST_HEAD(&psb->drop_list); + + mutex_init(&psb->mcache_lock); + psb->mcache_root = RB_ROOT; + psb->mcache_timeout = msecs_to_jiffies(5000); + atomic_long_set(&psb->mcache_gen, 0); + + psb->trans_max_pages = 100; + + psb->crypto_align_size = 16; + psb->crypto_attached_size = 0; + psb->hash_strlen = 0; + psb->cipher_strlen = 0; + psb->perform_crypto = 0; + psb->crypto_thread_num = 2; + psb->crypto_fail_unsupported = 0; + mutex_init(&psb->crypto_thread_lock); + INIT_LIST_HEAD(&psb->crypto_ready_list); + INIT_LIST_HEAD(&psb->crypto_active_list); + + atomic_set(&psb->trans_gen, 1); + atomic_long_set(&psb->total_inodes, 0); + + mutex_init(&psb->state_lock); + INIT_LIST_HEAD(&psb->state_list); + + err = pohmelfs_parse_options((char *) data, psb, 0); + if (err) + goto err_out_free_bdi; + + err = pohmelfs_copy_crypto(psb); + if (err) + goto err_out_free_bdi; + + err = pohmelfs_state_init(psb); + if (err) + goto err_out_free_strings; + + err = pohmelfs_crypto_init(psb); + if (err) + goto err_out_state_exit; + + err = pohmelfs_root_handshake(psb); + if (err) + goto err_out_crypto_exit; + + str.name = "/"; + str.hash = jhash("/", 1, 0); + str.len = 1; + + npi = pohmelfs_create_entry_local(psb, NULL, &str, 0, 0755|S_IFDIR); + if (IS_ERR(npi)) { + err = PTR_ERR(npi); + goto err_out_crypto_exit; + } + set_bit(NETFS_INODE_REMOTE_SYNCED, &npi->state); + clear_bit(NETFS_INODE_OWNED, &npi->state); + + root = &npi->vfs_inode; + + sb->s_root = d_alloc_root(root); + if (!sb->s_root) + goto err_out_put_root; + + INIT_DELAYED_WORK(&psb->drop_dwork, pohmelfs_drop_scan); + schedule_delayed_work(&psb->drop_dwork, psb->drop_scan_timeout); + + INIT_DELAYED_WORK(&psb->dwork, pohmelfs_trans_scan); + schedule_delayed_work(&psb->dwork, psb->trans_scan_timeout); + + return 0; + +err_out_put_root: + iput(root); +err_out_crypto_exit: + pohmelfs_crypto_exit(psb); +err_out_state_exit: + pohmelfs_state_exit(psb); +err_out_free_strings: + kfree(psb->cipher_string); + kfree(psb->hash_string); +err_out_free_bdi: + bdi_destroy(&psb->bdi); +err_out_free_sb: + kfree(psb); +err_out_exit: + + dprintk("%s: err: %d.\n", __func__, err); + return err; +} + +/* + * Some VFS magic here... + */ +static struct dentry *pohmelfs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) +{ + return mount_nodev(fs_type, flags, data, pohmelfs_fill_super); +} + +/* + * We need this to sync all inodes earlier, since when writeback + * is invoked from the umount/mntput path dcache is already shrunk, + * see generic_shutdown_super(), and no inodes can access the path. + */ +static void pohmelfs_kill_super(struct super_block *sb) +{ + sync_inodes_sb(sb); + kill_anon_super(sb); +} + +static struct file_system_type pohmel_fs_type = { + .owner = THIS_MODULE, + .name = "pohmel", + .mount = pohmelfs_mount, + .kill_sb = pohmelfs_kill_super, +}; + +/* + * Cache and module initializations and freeing routings. + */ +static void pohmelfs_init_once(void *data) +{ + struct pohmelfs_inode *pi = data; + + inode_init_once(&pi->vfs_inode); +} + +static int __init pohmelfs_init_inodecache(void) +{ + pohmelfs_inode_cache = kmem_cache_create("pohmelfs_inode_cache", + sizeof(struct pohmelfs_inode), + 0, (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD), + pohmelfs_init_once); + if (!pohmelfs_inode_cache) + return -ENOMEM; + + return 0; +} + +static void pohmelfs_destroy_inodecache(void) +{ + kmem_cache_destroy(pohmelfs_inode_cache); +} + +static int __init init_pohmel_fs(void) +{ + int err; + + err = pohmelfs_config_init(); + if (err) + goto err_out_exit; + + err = pohmelfs_init_inodecache(); + if (err) + goto err_out_config_exit; + + err = pohmelfs_mcache_init(); + if (err) + goto err_out_destroy; + + err = netfs_trans_init(); + if (err) + goto err_out_mcache_exit; + + err = register_filesystem(&pohmel_fs_type); + if (err) + goto err_out_trans; + + return 0; + +err_out_trans: + netfs_trans_exit(); +err_out_mcache_exit: + pohmelfs_mcache_exit(); +err_out_destroy: + pohmelfs_destroy_inodecache(); +err_out_config_exit: + pohmelfs_config_exit(); +err_out_exit: + return err; +} + +static void __exit exit_pohmel_fs(void) +{ + unregister_filesystem(&pohmel_fs_type); + pohmelfs_destroy_inodecache(); + pohmelfs_mcache_exit(); + pohmelfs_config_exit(); + netfs_trans_exit(); +} + +module_init(init_pohmel_fs); +module_exit(exit_pohmel_fs); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Evgeniy Polyakov "); +MODULE_DESCRIPTION("Pohmel filesystem"); diff --git a/trunk/drivers/staging/pohmelfs/lock.c b/trunk/drivers/staging/pohmelfs/lock.c new file mode 100644 index 000000000000..6710114cd425 --- /dev/null +++ b/trunk/drivers/staging/pohmelfs/lock.c @@ -0,0 +1,182 @@ +/* + * 2007+ Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#include "netfs.h" + +static int pohmelfs_send_lock_trans(struct pohmelfs_inode *pi, + u64 id, u64 start, u32 size, int type) +{ + struct inode *inode = &pi->vfs_inode; + struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); + struct netfs_trans *t; + struct netfs_cmd *cmd; + int path_len, err; + void *data; + struct netfs_lock *l; + int isize = (type & POHMELFS_LOCK_GRAB) ? 0 : sizeof(struct netfs_inode_info); + + err = pohmelfs_path_length(pi); + if (err < 0) + goto err_out_exit; + + path_len = err; + + err = -ENOMEM; + t = netfs_trans_alloc(psb, path_len + sizeof(struct netfs_lock) + isize, + NETFS_TRANS_SINGLE_DST, 0); + if (!t) + goto err_out_exit; + + cmd = netfs_trans_current(t); + data = cmd + 1; + + err = pohmelfs_construct_path_string(pi, data, path_len); + if (err < 0) + goto err_out_free; + path_len = err; + + l = data + path_len; + + l->start = start; + l->size = size; + l->type = type; + l->ino = pi->ino; + + cmd->cmd = NETFS_LOCK; + cmd->start = 0; + cmd->id = id; + cmd->size = sizeof(struct netfs_lock) + path_len + isize; + cmd->ext = path_len; + cmd->csize = 0; + + netfs_convert_cmd(cmd); + netfs_convert_lock(l); + + if (isize) { + struct netfs_inode_info *info = (struct netfs_inode_info *)(l + 1); + + info->mode = inode->i_mode; + info->nlink = inode->i_nlink; + info->uid = inode->i_uid; + info->gid = inode->i_gid; + info->blocks = inode->i_blocks; + info->rdev = inode->i_rdev; + info->size = inode->i_size; + info->version = inode->i_version; + + netfs_convert_inode_info(info); + } + + netfs_trans_update(cmd, t, path_len + sizeof(struct netfs_lock) + isize); + + return netfs_trans_finish(t, psb); + +err_out_free: + netfs_trans_free(t); +err_out_exit: + printk("%s: err: %d.\n", __func__, err); + return err; +} + +int pohmelfs_data_lock(struct pohmelfs_inode *pi, u64 start, u32 size, int type) +{ + struct pohmelfs_sb *psb = POHMELFS_SB(pi->vfs_inode.i_sb); + struct pohmelfs_mcache *m; + int err = -ENOMEM; + struct iattr iattr; + struct inode *inode = &pi->vfs_inode; + + dprintk("%s: %p: ino: %llu, start: %llu, size: %u, " + "type: %d, locked as: %d, owned: %d.\n", + __func__, &pi->vfs_inode, pi->ino, + start, size, type, pi->lock_type, + !!test_bit(NETFS_INODE_OWNED, &pi->state)); + + if (!pohmelfs_need_lock(pi, type)) + return 0; + + m = pohmelfs_mcache_alloc(psb, start, size, NULL); + if (IS_ERR(m)) + return PTR_ERR(m); + + err = pohmelfs_send_lock_trans(pi, m->gen, start, size, + type | POHMELFS_LOCK_GRAB); + if (err) + goto err_out_put; + + err = wait_for_completion_timeout(&m->complete, psb->mcache_timeout); + if (err) + err = m->err; + else + err = -ETIMEDOUT; + + if (err) { + printk("%s: %p: ino: %llu, mgen: %llu, start: %llu, size: %u, err: %d.\n", + __func__, &pi->vfs_inode, pi->ino, m->gen, start, size, err); + } + + if (err && (err != -ENOENT)) + goto err_out_put; + + if (!err) { + netfs_convert_inode_info(&m->info); + + iattr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_SIZE | ATTR_ATIME; + iattr.ia_mode = m->info.mode; + iattr.ia_uid = m->info.uid; + iattr.ia_gid = m->info.gid; + iattr.ia_size = m->info.size; + iattr.ia_atime = CURRENT_TIME; + + dprintk("%s: %p: ino: %llu, mgen: %llu, start: %llu, isize: %llu -> %llu.\n", + __func__, &pi->vfs_inode, pi->ino, m->gen, start, inode->i_size, m->info.size); + + err = pohmelfs_setattr_raw(inode, &iattr); + if (!err) { + struct dentry *dentry = d_find_alias(inode); + if (dentry) { + fsnotify_change(dentry, iattr.ia_valid); + dput(dentry); + } + } + } + + pi->lock_type = type; + set_bit(NETFS_INODE_OWNED, &pi->state); + + pohmelfs_mcache_put(psb, m); + + return 0; + +err_out_put: + pohmelfs_mcache_put(psb, m); + return err; +} + +int pohmelfs_data_unlock(struct pohmelfs_inode *pi, u64 start, u32 size, int type) +{ + dprintk("%s: %p: ino: %llu, start: %llu, size: %u, type: %d.\n", + __func__, &pi->vfs_inode, pi->ino, start, size, type); + pi->lock_type = 0; + clear_bit(NETFS_INODE_REMOTE_DIR_SYNCED, &pi->state); + clear_bit(NETFS_INODE_OWNED, &pi->state); + return pohmelfs_send_lock_trans(pi, pi->ino, start, size, type); +} diff --git a/trunk/drivers/staging/pohmelfs/mcache.c b/trunk/drivers/staging/pohmelfs/mcache.c new file mode 100644 index 000000000000..e22665cdd16c --- /dev/null +++ b/trunk/drivers/staging/pohmelfs/mcache.c @@ -0,0 +1,171 @@ +/* + * 2007+ Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include "netfs.h" + +static struct kmem_cache *pohmelfs_mcache_cache; +static mempool_t *pohmelfs_mcache_pool; + +static inline int pohmelfs_mcache_cmp(u64 gen, u64 new) +{ + if (gen < new) + return 1; + if (gen > new) + return -1; + return 0; +} + +struct pohmelfs_mcache *pohmelfs_mcache_search(struct pohmelfs_sb *psb, u64 gen) +{ + struct rb_root *root = &psb->mcache_root; + struct rb_node *n = root->rb_node; + struct pohmelfs_mcache *tmp, *ret = NULL; + int cmp; + + while (n) { + tmp = rb_entry(n, struct pohmelfs_mcache, mcache_entry); + + cmp = pohmelfs_mcache_cmp(tmp->gen, gen); + if (cmp < 0) + n = n->rb_left; + else if (cmp > 0) + n = n->rb_right; + else { + ret = tmp; + pohmelfs_mcache_get(ret); + break; + } + } + + return ret; +} + +static int pohmelfs_mcache_insert(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m) +{ + struct rb_root *root = &psb->mcache_root; + struct rb_node **n = &root->rb_node, *parent = NULL; + struct pohmelfs_mcache *ret = NULL, *tmp; + int cmp; + + while (*n) { + parent = *n; + + tmp = rb_entry(parent, struct pohmelfs_mcache, mcache_entry); + + cmp = pohmelfs_mcache_cmp(tmp->gen, m->gen); + if (cmp < 0) + n = &parent->rb_left; + else if (cmp > 0) + n = &parent->rb_right; + else { + ret = tmp; + break; + } + } + + if (ret) + return -EEXIST; + + rb_link_node(&m->mcache_entry, parent, n); + rb_insert_color(&m->mcache_entry, root); + + return 0; +} + +static int pohmelfs_mcache_remove(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m) +{ + if (m && m->mcache_entry.rb_parent_color) { + rb_erase(&m->mcache_entry, &psb->mcache_root); + m->mcache_entry.rb_parent_color = 0; + return 1; + } + return 0; +} + +void pohmelfs_mcache_remove_locked(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m) +{ + mutex_lock(&psb->mcache_lock); + pohmelfs_mcache_remove(psb, m); + mutex_unlock(&psb->mcache_lock); +} + +struct pohmelfs_mcache *pohmelfs_mcache_alloc(struct pohmelfs_sb *psb, u64 start, + unsigned int size, void *data) +{ + struct pohmelfs_mcache *m; + int err = -ENOMEM; + + m = mempool_alloc(pohmelfs_mcache_pool, GFP_KERNEL); + if (!m) + goto err_out_exit; + + init_completion(&m->complete); + m->err = 0; + atomic_set(&m->refcnt, 1); + m->data = data; + m->start = start; + m->size = size; + m->gen = atomic_long_inc_return(&psb->mcache_gen); + + mutex_lock(&psb->mcache_lock); + err = pohmelfs_mcache_insert(psb, m); + mutex_unlock(&psb->mcache_lock); + if (err) + goto err_out_free; + + return m; + +err_out_free: + mempool_free(m, pohmelfs_mcache_pool); +err_out_exit: + return ERR_PTR(err); +} + +void pohmelfs_mcache_free(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m) +{ + pohmelfs_mcache_remove_locked(psb, m); + + mempool_free(m, pohmelfs_mcache_pool); +} + +int __init pohmelfs_mcache_init(void) +{ + pohmelfs_mcache_cache = kmem_cache_create("pohmelfs_mcache_cache", + sizeof(struct pohmelfs_mcache), + 0, (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD), NULL); + if (!pohmelfs_mcache_cache) + goto err_out_exit; + + pohmelfs_mcache_pool = mempool_create_slab_pool(256, pohmelfs_mcache_cache); + if (!pohmelfs_mcache_pool) + goto err_out_free; + + return 0; + +err_out_free: + kmem_cache_destroy(pohmelfs_mcache_cache); +err_out_exit: + return -ENOMEM; +} + +void pohmelfs_mcache_exit(void) +{ + mempool_destroy(pohmelfs_mcache_pool); + kmem_cache_destroy(pohmelfs_mcache_cache); +} diff --git a/trunk/drivers/staging/pohmelfs/net.c b/trunk/drivers/staging/pohmelfs/net.c new file mode 100644 index 000000000000..b2e918622088 --- /dev/null +++ b/trunk/drivers/staging/pohmelfs/net.c @@ -0,0 +1,1209 @@ +/* + * 2007+ Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netfs.h" + +/* + * Async machinery lives here. + * All commands being sent to server do _not_ require sync reply, + * instead, if it is really needed, like readdir or readpage, caller + * sleeps waiting for data, which will be placed into provided buffer + * and caller will be awakened. + * + * Every command response can come without some listener. For example + * readdir response will add new objects into cache without appropriate + * request from userspace. This is used in cache coherency. + * + * If object is not found for given data, it is discarded. + * + * All requests are received by dedicated kernel thread. + */ + +/* + * Basic network sending/receiving functions. + * Blocked mode is used. + */ +static int netfs_data_recv(struct netfs_state *st, void *buf, u64 size) +{ + struct msghdr msg; + struct kvec iov; + int err; + + BUG_ON(!size); + + iov.iov_base = buf; + iov.iov_len = size; + + msg.msg_iov = (struct iovec *)&iov; + msg.msg_iovlen = 1; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = MSG_DONTWAIT; + + err = kernel_recvmsg(st->socket, &msg, &iov, 1, iov.iov_len, + msg.msg_flags); + if (err <= 0) { + printk("%s: failed to recv data: size: %llu, err: %d.\n", __func__, size, err); + if (err == 0) + err = -ECONNRESET; + } + + return err; +} + +static int pohmelfs_data_recv(struct netfs_state *st, void *data, unsigned int size) +{ + unsigned int revents = 0; + unsigned int err_mask = POLLERR | POLLHUP | POLLRDHUP; + unsigned int mask = err_mask | POLLIN; + int err = 0; + + while (size && !err) { + revents = netfs_state_poll(st); + + if (!(revents & mask)) { + DEFINE_WAIT(wait); + + for (;;) { + prepare_to_wait(&st->thread_wait, &wait, TASK_INTERRUPTIBLE); + if (kthread_should_stop()) + break; + + revents = netfs_state_poll(st); + + if (revents & mask) + break; + + if (signal_pending(current)) + break; + + schedule(); + continue; + } + finish_wait(&st->thread_wait, &wait); + } + + err = 0; + netfs_state_lock(st); + if (st->socket && (st->read_socket == st->socket) && (revents & POLLIN)) { + err = netfs_data_recv(st, data, size); + if (err > 0) { + data += err; + size -= err; + err = 0; + } else if (err == 0) + err = -ECONNRESET; + } + + if (revents & err_mask) { + printk("%s: revents: %x, socket: %p, size: %u, err: %d.\n", + __func__, revents, st->socket, size, err); + err = -ECONNRESET; + } + netfs_state_unlock(st); + + if (err < 0) { + if (netfs_state_trylock_send(st)) { + netfs_state_exit(st); + err = netfs_state_init(st); + if (!err) + err = -EAGAIN; + netfs_state_unlock_send(st); + } else { + st->need_reset = 1; + } + } + + if (kthread_should_stop()) + err = -ENODEV; + + if (err) + printk("%s: socket: %p, read_socket: %p, revents: %x, rev_error: %d, " + "should_stop: %d, size: %u, err: %d.\n", + __func__, st->socket, st->read_socket, + revents, revents & err_mask, kthread_should_stop(), size, err); + } + + return err; +} + +int pohmelfs_data_recv_and_check(struct netfs_state *st, void *data, unsigned int size) +{ + struct netfs_cmd *cmd = &st->cmd; + int err; + + err = pohmelfs_data_recv(st, data, size); + if (err) + return err; + + return pohmelfs_crypto_process_input_data(&st->eng, cmd->iv, data, NULL, size); +} + +/* + * Polling machinery. + */ + +struct netfs_poll_helper { + poll_table pt; + struct netfs_state *st; +}; + +static int netfs_queue_wake(wait_queue_t *wait, unsigned mode, int sync, void *key) +{ + struct netfs_state *st = container_of(wait, struct netfs_state, wait); + + wake_up(&st->thread_wait); + return 1; +} + +static void netfs_queue_func(struct file *file, wait_queue_head_t *whead, + poll_table *pt) +{ + struct netfs_state *st = container_of(pt, struct netfs_poll_helper, pt)->st; + + st->whead = whead; + init_waitqueue_func_entry(&st->wait, netfs_queue_wake); + add_wait_queue(whead, &st->wait); +} + +static void netfs_poll_exit(struct netfs_state *st) +{ + if (st->whead) { + remove_wait_queue(st->whead, &st->wait); + st->whead = NULL; + } +} + +static int netfs_poll_init(struct netfs_state *st) +{ + struct netfs_poll_helper ph; + + ph.st = st; + init_poll_funcptr(&ph.pt, &netfs_queue_func); + + st->socket->ops->poll(NULL, st->socket, &ph.pt); + return 0; +} + +/* + * Get response for readpage command. We search inode and page in its mapping + * and copy data into. If it was async request, then we queue page into shared + * data and wakeup listener, who will copy it to userspace. + * + * There is a work in progress of allowing to call copy_to_user() directly from + * async receiving kernel thread. + */ +static int pohmelfs_read_page_response(struct netfs_state *st) +{ + struct pohmelfs_sb *psb = st->psb; + struct netfs_cmd *cmd = &st->cmd; + struct inode *inode; + struct page *page; + int err = 0; + + if (cmd->size > PAGE_CACHE_SIZE) { + err = -EINVAL; + goto err_out_exit; + } + + inode = ilookup(st->psb->sb, cmd->id); + if (!inode) { + printk("%s: failed to find inode: id: %llu.\n", __func__, cmd->id); + err = -ENOENT; + goto err_out_exit; + } + + page = find_get_page(inode->i_mapping, cmd->start >> PAGE_CACHE_SHIFT); + if (!page || !PageLocked(page)) { + printk("%s: failed to find/lock page: page: %p, id: %llu, start: %llu, index: %llu.\n", + __func__, page, cmd->id, cmd->start, cmd->start >> PAGE_CACHE_SHIFT); + + while (cmd->size) { + unsigned int sz = min(cmd->size, st->size); + + err = pohmelfs_data_recv(st, st->data, sz); + if (err) + break; + + cmd->size -= sz; + } + + err = -ENODEV; + if (page) + goto err_out_page_put; + goto err_out_put; + } + + if (cmd->size) { + void *addr; + + addr = kmap(page); + err = pohmelfs_data_recv(st, addr, cmd->size); + kunmap(page); + + if (err) + goto err_out_page_unlock; + } + + dprintk("%s: page: %p, start: %llu, size: %u, locked: %d.\n", + __func__, page, cmd->start, cmd->size, PageLocked(page)); + + SetPageChecked(page); + if ((psb->hash_string || psb->cipher_string) && psb->perform_crypto && cmd->size) { + err = pohmelfs_crypto_process_input_page(&st->eng, page, cmd->size, cmd->iv); + if (err < 0) + goto err_out_page_unlock; + } else { + SetPageUptodate(page); + unlock_page(page); + page_cache_release(page); + } + + pohmelfs_put_inode(POHMELFS_I(inode)); + wake_up(&st->psb->wait); + + return 0; + +err_out_page_unlock: + SetPageError(page); + unlock_page(page); +err_out_page_put: + page_cache_release(page); +err_out_put: + pohmelfs_put_inode(POHMELFS_I(inode)); +err_out_exit: + wake_up(&st->psb->wait); + return err; +} + +static int pohmelfs_check_name(struct pohmelfs_inode *parent, struct qstr *str, + struct netfs_inode_info *info) +{ + struct inode *inode; + struct pohmelfs_name *n; + int err = 0; + u64 ino = 0; + + mutex_lock(&parent->offset_lock); + n = pohmelfs_search_hash(parent, str->hash); + if (n) + ino = n->ino; + mutex_unlock(&parent->offset_lock); + + if (!ino) + goto out; + + inode = ilookup(parent->vfs_inode.i_sb, ino); + if (!inode) + goto out; + + dprintk("%s: parent: %llu, inode: %llu.\n", __func__, parent->ino, ino); + + pohmelfs_fill_inode(inode, info); + pohmelfs_put_inode(POHMELFS_I(inode)); + err = -EEXIST; +out: + return err; +} + +/* + * Readdir response from server. If special field is set, we wakeup + * listener (readdir() call), which will copy data to userspace. + */ +static int pohmelfs_readdir_response(struct netfs_state *st) +{ + struct inode *inode; + struct netfs_cmd *cmd = &st->cmd; + struct netfs_inode_info *info; + struct pohmelfs_inode *parent = NULL, *npi; + int err = 0, last = cmd->ext; + struct qstr str; + + if (cmd->size > st->size) + return -EINVAL; + + inode = ilookup(st->psb->sb, cmd->id); + if (!inode) { + printk("%s: failed to find inode: id: %llu.\n", __func__, cmd->id); + return -ENOENT; + } + parent = POHMELFS_I(inode); + + if (!cmd->size && cmd->start) { + err = -cmd->start; + goto out; + } + + if (cmd->size) { + char *name; + + err = pohmelfs_data_recv_and_check(st, st->data, cmd->size); + if (err) + goto err_out_put; + + info = (struct netfs_inode_info *)(st->data); + + name = (char *)(info + 1); + str.len = cmd->size - sizeof(struct netfs_inode_info) - 1 - cmd->cpad; + name[str.len] = 0; + str.name = name; + str.hash = jhash(str.name, str.len, 0); + + netfs_convert_inode_info(info); + + if (parent) { + err = pohmelfs_check_name(parent, &str, info); + if (err) { + if (err == -EEXIST) + err = 0; + goto out; + } + } + + info->ino = cmd->start; + if (!info->ino) + info->ino = pohmelfs_new_ino(st->psb); + + dprintk("%s: parent: %llu, ino: %llu, name: '%s', hash: %x, len: %u, mode: %o.\n", + __func__, parent->ino, info->ino, str.name, str.hash, str.len, + info->mode); + + npi = pohmelfs_new_inode(st->psb, parent, &str, info, 0); + if (IS_ERR(npi)) { + err = PTR_ERR(npi); + + if (err != -EEXIST) + goto err_out_put; + } else { + struct dentry *dentry, *alias, *pd; + + set_bit(NETFS_INODE_REMOTE_SYNCED, &npi->state); + clear_bit(NETFS_INODE_OWNED, &npi->state); + + pd = d_find_alias(&parent->vfs_inode); + if (pd) { + str.hash = full_name_hash(str.name, str.len); + dentry = d_alloc(pd, &str); + if (dentry) { + alias = d_materialise_unique(dentry, &npi->vfs_inode); + if (alias) + dput(alias); + } + + dput(dentry); + dput(pd); + } + } + } +out: + if (last) { + set_bit(NETFS_INODE_REMOTE_DIR_SYNCED, &parent->state); + set_bit(NETFS_INODE_REMOTE_SYNCED, &parent->state); + wake_up(&st->psb->wait); + } + pohmelfs_put_inode(parent); + + return err; + +err_out_put: + clear_bit(NETFS_INODE_REMOTE_DIR_SYNCED, &parent->state); + printk("%s: parent: %llu, ino: %llu, cmd_id: %llu.\n", __func__, parent->ino, cmd->start, cmd->id); + pohmelfs_put_inode(parent); + wake_up(&st->psb->wait); + return err; +} + +/* + * Lookup command response. + * It searches for inode to be looked at (if it exists) and substitutes + * its inode information (size, permission, mode and so on), if inode does + * not exist, new one will be created and inserted into caches. + */ +static int pohmelfs_lookup_response(struct netfs_state *st) +{ + struct inode *inode = NULL; + struct netfs_cmd *cmd = &st->cmd; + struct netfs_inode_info *info; + struct pohmelfs_inode *parent = NULL, *npi; + int err = -EINVAL; + char *name; + + inode = ilookup(st->psb->sb, cmd->id); + if (!inode) { + printk("%s: lookup response: id: %llu, start: %llu, size: %u.\n", + __func__, cmd->id, cmd->start, cmd->size); + err = -ENOENT; + goto err_out_exit; + } + parent = POHMELFS_I(inode); + + if (!cmd->size) { + err = -cmd->start; + goto err_out_put; + } + + if (cmd->size < sizeof(struct netfs_inode_info)) { + printk("%s: broken lookup response: id: %llu, start: %llu, size: %u.\n", + __func__, cmd->id, cmd->start, cmd->size); + err = -EINVAL; + goto err_out_put; + } + + err = pohmelfs_data_recv_and_check(st, st->data, cmd->size); + if (err) + goto err_out_put; + + info = (struct netfs_inode_info *)(st->data); + name = (char *)(info + 1); + + netfs_convert_inode_info(info); + + info->ino = cmd->start; + if (!info->ino) + info->ino = pohmelfs_new_ino(st->psb); + + dprintk("%s: parent: %llu, ino: %llu, name: '%s', start: %llu.\n", + __func__, parent->ino, info->ino, name, cmd->start); + + if (cmd->start) + npi = pohmelfs_new_inode(st->psb, parent, NULL, info, 0); + else { + struct qstr str; + + str.name = name; + str.len = cmd->size - sizeof(struct netfs_inode_info) - 1 - cmd->cpad; + str.hash = jhash(name, str.len, 0); + + npi = pohmelfs_new_inode(st->psb, parent, &str, info, 0); + } + if (IS_ERR(npi)) { + err = PTR_ERR(npi); + + if (err != -EEXIST) + goto err_out_put; + } else { + set_bit(NETFS_INODE_REMOTE_SYNCED, &npi->state); + clear_bit(NETFS_INODE_OWNED, &npi->state); + } + + clear_bit(NETFS_COMMAND_PENDING, &parent->state); + pohmelfs_put_inode(parent); + + wake_up(&st->psb->wait); + + return 0; + +err_out_put: + pohmelfs_put_inode(parent); +err_out_exit: + clear_bit(NETFS_COMMAND_PENDING, &parent->state); + wake_up(&st->psb->wait); + printk("%s: inode: %p, id: %llu, start: %llu, size: %u, err: %d.\n", + __func__, inode, cmd->id, cmd->start, cmd->size, err); + return err; +} + +/* + * Create response, just marks local inode as 'created', so that writeback + * for any of its children (or own) would not try to sync it again. + */ +static int pohmelfs_create_response(struct netfs_state *st) +{ + struct inode *inode; + struct netfs_cmd *cmd = &st->cmd; + struct pohmelfs_inode *pi; + + inode = ilookup(st->psb->sb, cmd->id); + if (!inode) { + printk("%s: failed to find inode: id: %llu, start: %llu.\n", + __func__, cmd->id, cmd->start); + goto err_out_exit; + } + + pi = POHMELFS_I(inode); + + /* + * To lock or not to lock? + * We actually do not care if it races... + */ + if (cmd->start) + make_bad_inode(inode); + set_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state); + + pohmelfs_put_inode(pi); + + wake_up(&st->psb->wait); + return 0; + +err_out_exit: + wake_up(&st->psb->wait); + return -ENOENT; +} + +/* + * Object remove response. Just says that remove request has been received. + * Used in cache coherency protocol. + */ +static int pohmelfs_remove_response(struct netfs_state *st) +{ + struct netfs_cmd *cmd = &st->cmd; + int err; + + err = pohmelfs_data_recv_and_check(st, st->data, cmd->size); + if (err) + return err; + + dprintk("%s: parent: %llu, path: '%s'.\n", __func__, cmd->id, (char *)st->data); + + return 0; +} + +/* + * Transaction reply processing. + * + * Find transaction based on its generation number, bump its reference counter, + * so that none could free it under us, drop from the trees and lists and + * drop reference counter. When it hits zero (when all destinations replied + * and all timeout handled by async scanning code), completion will be called + * and transaction will be freed. + */ +static int pohmelfs_transaction_response(struct netfs_state *st) +{ + struct netfs_trans_dst *dst; + struct netfs_trans *t = NULL; + struct netfs_cmd *cmd = &st->cmd; + short err = (signed)cmd->ext; + + mutex_lock(&st->trans_lock); + dst = netfs_trans_search(st, cmd->start); + if (dst) { + netfs_trans_remove_nolock(dst, st); + t = dst->trans; + } + mutex_unlock(&st->trans_lock); + + if (!t) { + printk("%s: failed to find transaction: start: %llu: id: %llu, size: %u, ext: %u.\n", + __func__, cmd->start, cmd->id, cmd->size, cmd->ext); + err = -EINVAL; + goto out; + } + + t->result = err; + netfs_trans_drop_dst_nostate(dst); + +out: + wake_up(&st->psb->wait); + return err; +} + +/* + * Inode metadata cache coherency message. + */ +static int pohmelfs_page_cache_response(struct netfs_state *st) +{ + struct netfs_cmd *cmd = &st->cmd; + struct inode *inode; + + dprintk("%s: st: %p, id: %llu, start: %llu, size: %u.\n", __func__, st, cmd->id, cmd->start, cmd->size); + + inode = ilookup(st->psb->sb, cmd->id); + if (!inode) { + printk("%s: failed to find inode: id: %llu.\n", __func__, cmd->id); + return -ENOENT; + } + + set_bit(NETFS_INODE_NEED_FLUSH, &POHMELFS_I(inode)->state); + pohmelfs_put_inode(POHMELFS_I(inode)); + + return 0; +} + +/* + * Root capabilities response: export statistics + * like used and available size, number of files and dirs, + * permissions. + */ +static int pohmelfs_root_cap_response(struct netfs_state *st) +{ + struct netfs_cmd *cmd = &st->cmd; + struct netfs_root_capabilities *cap; + struct pohmelfs_sb *psb = st->psb; + + if (cmd->size != sizeof(struct netfs_root_capabilities)) { + psb->flags = EPROTO; + wake_up(&psb->wait); + return -EPROTO; + } + + cap = st->data; + + netfs_convert_root_capabilities(cap); + + if (psb->total_size < cap->used + cap->avail) + psb->total_size = cap->used + cap->avail; + if (cap->avail) + psb->avail_size = cap->avail; + psb->state_flags = cap->flags; + + if (psb->state_flags & POHMELFS_FLAGS_RO) { + psb->sb->s_flags |= MS_RDONLY; + printk(KERN_INFO "Mounting POHMELFS (%d) read-only.\n", psb->idx); + } + + if (psb->state_flags & POHMELFS_FLAGS_XATTR) + printk(KERN_INFO "Mounting POHMELFS (%d) " + "with extended attributes support.\n", psb->idx); + + if (atomic_long_read(&psb->total_inodes) <= 1) + atomic_long_set(&psb->total_inodes, cap->nr_files); + + dprintk("%s: total: %llu, avail: %llu, flags: %llx, inodes: %llu.\n", + __func__, psb->total_size, psb->avail_size, psb->state_flags, cap->nr_files); + + psb->flags = 0; + wake_up(&psb->wait); + return 0; +} + +/* + * Crypto capabilities of the server, where it says that + * it supports or does not requested hash/cipher algorithms. + */ +static int pohmelfs_crypto_cap_response(struct netfs_state *st) +{ + struct netfs_cmd *cmd = &st->cmd; + struct netfs_crypto_capabilities *cap; + struct pohmelfs_sb *psb = st->psb; + int err = 0; + + if (cmd->size != sizeof(struct netfs_crypto_capabilities)) { + psb->flags = EPROTO; + wake_up(&psb->wait); + return -EPROTO; + } + + cap = st->data; + + dprintk("%s: cipher '%s': %s, hash: '%s': %s.\n", + __func__, + psb->cipher_string, (cap->cipher_strlen) ? "SUPPORTED" : "NOT SUPPORTED", + psb->hash_string, (cap->hash_strlen) ? "SUPPORTED" : "NOT SUPPORTED"); + + if (!cap->hash_strlen) { + if (psb->hash_strlen && psb->crypto_fail_unsupported) + err = -ENOTSUPP; + psb->hash_strlen = 0; + kfree(psb->hash_string); + psb->hash_string = NULL; + } + + if (!cap->cipher_strlen) { + if (psb->cipher_strlen && psb->crypto_fail_unsupported) + err = -ENOTSUPP; + psb->cipher_strlen = 0; + kfree(psb->cipher_string); + psb->cipher_string = NULL; + } + + return err; +} + +/* + * Capabilities handshake response. + */ +static int pohmelfs_capabilities_response(struct netfs_state *st) +{ + struct netfs_cmd *cmd = &st->cmd; + int err = 0; + + err = pohmelfs_data_recv(st, st->data, cmd->size); + if (err) + return err; + + switch (cmd->id) { + case POHMELFS_CRYPTO_CAPABILITIES: + return pohmelfs_crypto_cap_response(st); + case POHMELFS_ROOT_CAPABILITIES: + return pohmelfs_root_cap_response(st); + default: + break; + } + return -EINVAL; +} + +/* + * Receiving extended attribute. + * Does not work properly if received size is more than requested one, + * it should not happen with current request/reply model though. + */ +static int pohmelfs_getxattr_response(struct netfs_state *st) +{ + struct pohmelfs_sb *psb = st->psb; + struct netfs_cmd *cmd = &st->cmd; + struct pohmelfs_mcache *m; + short error = (signed short)cmd->ext, err; + unsigned int sz, total_size; + + m = pohmelfs_mcache_search(psb, cmd->id); + + dprintk("%s: id: %llu, gen: %llu, err: %d.\n", + __func__, cmd->id, (m) ? m->gen : 0, error); + + if (!m) { + printk("%s: failed to find getxattr cache entry: id: %llu.\n", __func__, cmd->id); + return -ENOENT; + } + + if (cmd->size) { + sz = min_t(unsigned int, cmd->size, m->size); + err = pohmelfs_data_recv_and_check(st, m->data, sz); + if (err) { + error = err; + goto out; + } + + m->size = sz; + total_size = cmd->size - sz; + + while (total_size) { + sz = min(total_size, st->size); + + err = pohmelfs_data_recv_and_check(st, st->data, sz); + if (err) { + error = err; + break; + } + + total_size -= sz; + } + } + +out: + m->err = error; + complete(&m->complete); + pohmelfs_mcache_put(psb, m); + + return error; +} + +int pohmelfs_data_lock_response(struct netfs_state *st) +{ + struct pohmelfs_sb *psb = st->psb; + struct netfs_cmd *cmd = &st->cmd; + struct pohmelfs_mcache *m; + short err = (signed short)cmd->ext; + u64 id = cmd->id; + + m = pohmelfs_mcache_search(psb, id); + + dprintk("%s: id: %llu, gen: %llu, err: %d.\n", + __func__, cmd->id, (m) ? m->gen : 0, err); + + if (!m) { + pohmelfs_data_recv(st, st->data, cmd->size); + printk("%s: failed to find data lock response: id: %llu.\n", __func__, cmd->id); + return -ENOENT; + } + + if (cmd->size) + err = pohmelfs_data_recv_and_check(st, &m->info, cmd->size); + + m->err = err; + complete(&m->complete); + pohmelfs_mcache_put(psb, m); + + return err; +} + +static void __inline__ netfs_state_reset(struct netfs_state *st) +{ + netfs_state_lock_send(st); + netfs_state_exit(st); + netfs_state_init(st); + netfs_state_unlock_send(st); +} + +/* + * Main receiving function, called from dedicated kernel thread. + */ +static int pohmelfs_recv(void *data) +{ + int err = -EINTR; + struct netfs_state *st = data; + struct netfs_cmd *cmd = &st->cmd; + + while (!kthread_should_stop()) { + /* + * If socket will be reset after this statement, then + * pohmelfs_data_recv() will just fail and loop will + * start again, so it can be done without any locks. + * + * st->read_socket is needed to prevents state machine + * breaking between this data reading and subsequent one + * in protocol specific functions during connection reset. + * In case of reset we have to read next command and do + * not expect data for old command to magically appear in + * new connection. + */ + st->read_socket = st->socket; + err = pohmelfs_data_recv(st, cmd, sizeof(struct netfs_cmd)); + if (err) { + msleep(1000); + continue; + } + + netfs_convert_cmd(cmd); + + dprintk("%s: cmd: %u, id: %llu, start: %llu, size: %u, " + "ext: %u, csize: %u, cpad: %u.\n", + __func__, cmd->cmd, cmd->id, cmd->start, + cmd->size, cmd->ext, cmd->csize, cmd->cpad); + + if (cmd->csize) { + struct pohmelfs_crypto_engine *e = &st->eng; + + if (unlikely(cmd->csize > e->size/2)) { + netfs_state_reset(st); + continue; + } + + if (e->hash && unlikely(cmd->csize != st->psb->crypto_attached_size)) { + dprintk("%s: cmd: cmd: %u, id: %llu, start: %llu, size: %u, " + "csize: %u != digest size %u.\n", + __func__, cmd->cmd, cmd->id, cmd->start, cmd->size, + cmd->csize, st->psb->crypto_attached_size); + netfs_state_reset(st); + continue; + } + + err = pohmelfs_data_recv(st, e->data, cmd->csize); + if (err) { + netfs_state_reset(st); + continue; + } + +#ifdef CONFIG_POHMELFS_DEBUG + { + unsigned int i; + unsigned char *hash = e->data; + + dprintk("%s: received hash: ", __func__); + for (i = 0; i < cmd->csize; ++i) + printk("%02x ", hash[i]); + + printk("\n"); + } +#endif + cmd->size -= cmd->csize; + } + + /* + * This should catch protocol breakage and random garbage instead of commands. + */ + if (unlikely((cmd->size > st->size) && (cmd->cmd != NETFS_XATTR_GET))) { + netfs_state_reset(st); + continue; + } + + switch (cmd->cmd) { + case NETFS_READ_PAGE: + err = pohmelfs_read_page_response(st); + break; + case NETFS_READDIR: + err = pohmelfs_readdir_response(st); + break; + case NETFS_LOOKUP: + err = pohmelfs_lookup_response(st); + break; + case NETFS_CREATE: + err = pohmelfs_create_response(st); + break; + case NETFS_REMOVE: + err = pohmelfs_remove_response(st); + break; + case NETFS_TRANS: + err = pohmelfs_transaction_response(st); + break; + case NETFS_PAGE_CACHE: + err = pohmelfs_page_cache_response(st); + break; + case NETFS_CAPABILITIES: + err = pohmelfs_capabilities_response(st); + break; + case NETFS_LOCK: + err = pohmelfs_data_lock_response(st); + break; + case NETFS_XATTR_GET: + err = pohmelfs_getxattr_response(st); + break; + default: + printk("%s: wrong cmd: %u, id: %llu, start: %llu, size: %u, ext: %u.\n", + __func__, cmd->cmd, cmd->id, cmd->start, cmd->size, cmd->ext); + netfs_state_reset(st); + break; + } + } + + while (!kthread_should_stop()) + schedule_timeout_uninterruptible(msecs_to_jiffies(10)); + + return err; +} + +int netfs_state_init(struct netfs_state *st) +{ + int err; + struct pohmelfs_ctl *ctl = &st->ctl; + + err = sock_create(ctl->addr.sa_family, ctl->type, ctl->proto, &st->socket); + if (err) { + printk("%s: failed to create a socket: family: %d, type: %d, proto: %d, err: %d.\n", + __func__, ctl->addr.sa_family, ctl->type, ctl->proto, err); + goto err_out_exit; + } + + st->socket->sk->sk_allocation = GFP_NOIO; + st->socket->sk->sk_sndtimeo = st->socket->sk->sk_rcvtimeo = msecs_to_jiffies(60000); + + err = kernel_connect(st->socket, (struct sockaddr *)&ctl->addr, ctl->addrlen, 0); + if (err) { + printk("%s: failed to connect to server: idx: %u, err: %d.\n", + __func__, st->psb->idx, err); + goto err_out_release; + } + st->socket->sk->sk_sndtimeo = st->socket->sk->sk_rcvtimeo = msecs_to_jiffies(60000); + + err = netfs_poll_init(st); + if (err) + goto err_out_release; + + if (st->socket->ops->family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)&ctl->addr; + printk(KERN_INFO "%s: (re)connected to peer %pi4:%d.\n", __func__, + &sin->sin_addr.s_addr, ntohs(sin->sin_port)); + } else if (st->socket->ops->family == AF_INET6) { + struct sockaddr_in6 *sin = (struct sockaddr_in6 *)&ctl->addr; + printk(KERN_INFO "%s: (re)connected to peer %pi6:%d", __func__, + &sin->sin6_addr, ntohs(sin->sin6_port)); + } + + return 0; + +err_out_release: + sock_release(st->socket); +err_out_exit: + st->socket = NULL; + return err; +} + +void netfs_state_exit(struct netfs_state *st) +{ + if (st->socket) { + netfs_poll_exit(st); + st->socket->ops->shutdown(st->socket, 2); + + if (st->socket->ops->family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)&st->ctl.addr; + printk(KERN_INFO "%s: disconnected from peer %pi4:%d.\n", __func__, + &sin->sin_addr.s_addr, ntohs(sin->sin_port)); + } else if (st->socket->ops->family == AF_INET6) { + struct sockaddr_in6 *sin = (struct sockaddr_in6 *)&st->ctl.addr; + printk(KERN_INFO "%s: disconnected from peer %pi6:%d", __func__, + &sin->sin6_addr, ntohs(sin->sin6_port)); + } + + sock_release(st->socket); + st->socket = NULL; + st->read_socket = NULL; + st->need_reset = 0; + } +} + +int pohmelfs_state_init_one(struct pohmelfs_sb *psb, struct pohmelfs_config *conf) +{ + struct netfs_state *st = &conf->state; + int err = -ENOMEM; + + mutex_init(&st->__state_lock); + mutex_init(&st->__state_send_lock); + init_waitqueue_head(&st->thread_wait); + + st->psb = psb; + st->trans_root = RB_ROOT; + mutex_init(&st->trans_lock); + + st->size = psb->trans_data_size; + st->data = kmalloc(st->size, GFP_KERNEL); + if (!st->data) + goto err_out_exit; + + if (psb->perform_crypto) { + err = pohmelfs_crypto_engine_init(&st->eng, psb); + if (err) + goto err_out_free_data; + } + + err = netfs_state_init(st); + if (err) + goto err_out_free_engine; + + st->thread = kthread_run(pohmelfs_recv, st, "pohmelfs/%u", psb->idx); + if (IS_ERR(st->thread)) { + err = PTR_ERR(st->thread); + goto err_out_netfs_exit; + } + + if (!psb->active_state) + psb->active_state = conf; + + dprintk("%s: conf: %p, st: %p, socket: %p.\n", + __func__, conf, st, st->socket); + return 0; + +err_out_netfs_exit: + netfs_state_exit(st); +err_out_free_engine: + pohmelfs_crypto_engine_exit(&st->eng); +err_out_free_data: + kfree(st->data); +err_out_exit: + return err; + +} + +void pohmelfs_state_flush_transactions(struct netfs_state *st) +{ + struct rb_node *rb_node; + struct netfs_trans_dst *dst; + + mutex_lock(&st->trans_lock); + for (rb_node = rb_first(&st->trans_root); rb_node; ) { + dst = rb_entry(rb_node, struct netfs_trans_dst, state_entry); + rb_node = rb_next(rb_node); + + dst->trans->result = -EINVAL; + netfs_trans_remove_nolock(dst, st); + netfs_trans_drop_dst_nostate(dst); + } + mutex_unlock(&st->trans_lock); +} + +static void pohmelfs_state_exit_one(struct pohmelfs_config *c) +{ + struct netfs_state *st = &c->state; + + dprintk("%s: exiting, st: %p.\n", __func__, st); + if (st->thread) { + kthread_stop(st->thread); + st->thread = NULL; + } + + netfs_state_lock_send(st); + netfs_state_exit(st); + netfs_state_unlock_send(st); + + pohmelfs_state_flush_transactions(st); + + pohmelfs_crypto_engine_exit(&st->eng); + kfree(st->data); + + kfree(c); +} + +/* + * Initialize network stack. It searches for given ID in global + * configuration table, this contains information of the remote server + * (address (any supported by socket interface) and port, protocol and so on). + */ +int pohmelfs_state_init(struct pohmelfs_sb *psb) +{ + int err = -ENOMEM; + + err = pohmelfs_copy_config(psb); + if (err) { + pohmelfs_state_exit(psb); + return err; + } + + return 0; +} + +void pohmelfs_state_exit(struct pohmelfs_sb *psb) +{ + struct pohmelfs_config *c, *tmp; + + list_for_each_entry_safe(c, tmp, &psb->state_list, config_entry) { + list_del(&c->config_entry); + pohmelfs_state_exit_one(c); + } +} + +void pohmelfs_switch_active(struct pohmelfs_sb *psb) +{ + struct pohmelfs_config *c = psb->active_state; + + if (!list_empty(&psb->state_list)) { + if (c->config_entry.next != &psb->state_list) { + psb->active_state = list_entry(c->config_entry.next, + struct pohmelfs_config, config_entry); + } else { + psb->active_state = list_entry(psb->state_list.next, + struct pohmelfs_config, config_entry); + } + + dprintk("%s: empty: %d, active %p -> %p.\n", + __func__, list_empty(&psb->state_list), c, + psb->active_state); + } else + psb->active_state = NULL; +} + +void pohmelfs_check_states(struct pohmelfs_sb *psb) +{ + struct pohmelfs_config *c, *tmp; + LIST_HEAD(delete_list); + + mutex_lock(&psb->state_lock); + list_for_each_entry_safe(c, tmp, &psb->state_list, config_entry) { + if (pohmelfs_config_check(c, psb->idx)) { + + if (psb->active_state == c) + pohmelfs_switch_active(psb); + list_move(&c->config_entry, &delete_list); + } + } + pohmelfs_copy_config(psb); + mutex_unlock(&psb->state_lock); + + list_for_each_entry_safe(c, tmp, &delete_list, config_entry) { + list_del(&c->config_entry); + pohmelfs_state_exit_one(c); + } +} diff --git a/trunk/drivers/staging/pohmelfs/netfs.h b/trunk/drivers/staging/pohmelfs/netfs.h new file mode 100644 index 000000000000..f26894f2a57f --- /dev/null +++ b/trunk/drivers/staging/pohmelfs/netfs.h @@ -0,0 +1,919 @@ +/* + * 2007+ Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __NETFS_H +#define __NETFS_H + +#include +#include +#include + +#define POHMELFS_CN_IDX 5 +#define POHMELFS_CN_VAL 0 + +#define POHMELFS_CTLINFO_ACK 1 +#define POHMELFS_NOINFO_ACK 2 + +#define POHMELFS_NULL_IDX 65535 + +/* + * Network command structure. + * Will be extended. + */ +struct netfs_cmd { + __u16 cmd; /* Command number */ + __u16 csize; /* Attached crypto information size */ + __u16 cpad; /* Attached padding size */ + __u16 ext; /* External flags */ + __u32 size; /* Size of the attached data */ + __u32 trans; /* Transaction id */ + __u64 id; /* Object ID to operate on. Used for feedback.*/ + __u64 start; /* Start of the object. */ + __u64 iv; /* IV sequence */ + __u8 data[0]; +}; + +static inline void netfs_convert_cmd(struct netfs_cmd *cmd) +{ + cmd->id = __be64_to_cpu(cmd->id); + cmd->start = __be64_to_cpu(cmd->start); + cmd->iv = __be64_to_cpu(cmd->iv); + cmd->cmd = __be16_to_cpu(cmd->cmd); + cmd->ext = __be16_to_cpu(cmd->ext); + cmd->csize = __be16_to_cpu(cmd->csize); + cmd->cpad = __be16_to_cpu(cmd->cpad); + cmd->size = __be32_to_cpu(cmd->size); +} + +#define NETFS_TRANS_SINGLE_DST (1<<0) + +enum { + NETFS_READDIR = 1, /* Read directory for given inode number */ + NETFS_READ_PAGE, /* Read data page from the server */ + NETFS_WRITE_PAGE, /* Write data page to the server */ + NETFS_CREATE, /* Create directory entry */ + NETFS_REMOVE, /* Remove directory entry */ + + NETFS_LOOKUP, /* Lookup single object */ + NETFS_LINK, /* Create a link */ + NETFS_TRANS, /* Transaction */ + NETFS_OPEN, /* Open intent */ + NETFS_INODE_INFO, /* Metadata cache coherency synchronization message */ + + NETFS_PAGE_CACHE, /* Page cache invalidation message */ + NETFS_READ_PAGES, /* Read multiple contiguous pages in one go */ + NETFS_RENAME, /* Rename object */ + NETFS_CAPABILITIES, /* Capabilities of the client, for example supported crypto */ + NETFS_LOCK, /* Distributed lock message */ + + NETFS_XATTR_SET, /* Set extended attribute */ + NETFS_XATTR_GET, /* Get extended attribute */ + NETFS_CMD_MAX +}; + +enum { + POHMELFS_FLAGS_ADD = 0, /* Network state control message for ADD */ + POHMELFS_FLAGS_DEL, /* Network state control message for DEL */ + POHMELFS_FLAGS_SHOW, /* Network state control message for SHOW */ + POHMELFS_FLAGS_CRYPTO, /* Crypto data control message */ + POHMELFS_FLAGS_MODIFY, /* Network state modification message */ + POHMELFS_FLAGS_DUMP, /* Network state control message for SHOW ALL */ + POHMELFS_FLAGS_FLUSH, /* Network state control message for FLUSH */ +}; + +/* + * Always wanted to copy it from socket headers into public one, + * since they are __KERNEL__ protected there. + */ +#define _K_SS_MAXSIZE 128 + +struct saddr { + unsigned short sa_family; + char addr[_K_SS_MAXSIZE]; +}; + +enum { + POHMELFS_CRYPTO_HASH = 0, + POHMELFS_CRYPTO_CIPHER, +}; + +struct pohmelfs_crypto { + unsigned int idx; /* Config index */ + unsigned short strlen; /* Size of the attached crypto string including 0-byte + * "cbc(aes)" for example */ + unsigned short type; /* HMAC, cipher, both */ + unsigned int keysize; /* Key size */ + unsigned char data[0]; /* Algorithm string, key and IV */ +}; + +#define POHMELFS_IO_PERM_READ (1<<0) +#define POHMELFS_IO_PERM_WRITE (1<<1) + +/* + * Configuration command used to create table of different remote servers. + */ +struct pohmelfs_ctl { + __u32 idx; /* Config index */ + __u32 type; /* Socket type */ + __u32 proto; /* Socket protocol */ + __u16 addrlen; /* Size of the address */ + __u16 perm; /* IO permission */ + __u16 prio; /* IO priority */ + struct saddr addr; /* Remote server address */ +}; + +/* + * Ack for userspace about requested command. + */ +struct pohmelfs_cn_ack { + struct cn_msg msg; + int error; + int msg_num; + int unused[3]; + struct pohmelfs_ctl ctl; +}; + +/* + * Inode info structure used to sync with server. + * Check what stat() returns. + */ +struct netfs_inode_info { + unsigned int mode; + unsigned int nlink; + unsigned int uid; + unsigned int gid; + unsigned int blocksize; + unsigned int padding; + __u64 ino; + __u64 blocks; + __u64 rdev; + __u64 size; + __u64 version; +}; + +static inline void netfs_convert_inode_info(struct netfs_inode_info *info) +{ + info->mode = __cpu_to_be32(info->mode); + info->nlink = __cpu_to_be32(info->nlink); + info->uid = __cpu_to_be32(info->uid); + info->gid = __cpu_to_be32(info->gid); + info->blocksize = __cpu_to_be32(info->blocksize); + info->blocks = __cpu_to_be64(info->blocks); + info->rdev = __cpu_to_be64(info->rdev); + info->size = __cpu_to_be64(info->size); + info->version = __cpu_to_be64(info->version); + info->ino = __cpu_to_be64(info->ino); +} + +/* + * Cache state machine. + */ +enum { + NETFS_COMMAND_PENDING = 0, /* Command is being executed */ + NETFS_INODE_REMOTE_SYNCED, /* Inode was synced to server */ + NETFS_INODE_REMOTE_DIR_SYNCED, /* Inode (directory) was synced from the server */ + NETFS_INODE_OWNED, /* Inode is owned by given host */ + NETFS_INODE_NEED_FLUSH, /* Inode has to be flushed to the server */ +}; + +/* + * POHMELFS capabilities: information about supported + * crypto operations (hash/cipher, modes, key sizes and so on), + * root information (used/available size, number of objects, permissions) + */ +enum pohmelfs_capabilities { + POHMELFS_CRYPTO_CAPABILITIES = 0, + POHMELFS_ROOT_CAPABILITIES, +}; + +/* Read-only mount */ +#define POHMELFS_FLAGS_RO (1<<0) +/* Extended attributes support on/off */ +#define POHMELFS_FLAGS_XATTR (1<<1) + +struct netfs_root_capabilities { + __u64 nr_files; + __u64 used, avail; + __u64 flags; +}; + +static inline void netfs_convert_root_capabilities(struct netfs_root_capabilities *cap) +{ + cap->nr_files = __cpu_to_be64(cap->nr_files); + cap->used = __cpu_to_be64(cap->used); + cap->avail = __cpu_to_be64(cap->avail); + cap->flags = __cpu_to_be64(cap->flags); +} + +struct netfs_crypto_capabilities { + unsigned short hash_strlen; /* Hash string length, like "hmac(sha1) including 0 byte "*/ + unsigned short cipher_strlen; /* Cipher string length with the same format */ + unsigned int cipher_keysize; /* Cipher key size */ +}; + +static inline void netfs_convert_crypto_capabilities(struct netfs_crypto_capabilities *cap) +{ + cap->hash_strlen = __cpu_to_be16(cap->hash_strlen); + cap->cipher_strlen = __cpu_to_be16(cap->cipher_strlen); + cap->cipher_keysize = __cpu_to_be32(cap->cipher_keysize); +} + +enum pohmelfs_lock_type { + POHMELFS_LOCK_GRAB = (1<<15), + + POHMELFS_READ_LOCK = 0, + POHMELFS_WRITE_LOCK, +}; + +struct netfs_lock { + __u64 start; + __u64 ino; + __u32 size; + __u32 type; +}; + +static inline void netfs_convert_lock(struct netfs_lock *lock) +{ + lock->start = __cpu_to_be64(lock->start); + lock->ino = __cpu_to_be64(lock->ino); + lock->size = __cpu_to_be32(lock->size); + lock->type = __cpu_to_be32(lock->type); +} + +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include + +/* + * Private POHMELFS cache of objects in directory. + */ +struct pohmelfs_name { + struct rb_node hash_node; + + struct list_head sync_create_entry; + + u64 ino; + + u32 hash; + u32 mode; + u32 len; + + char *data; +}; + +/* + * POHMELFS inode. Main object. + */ +struct pohmelfs_inode { + struct list_head inode_entry; /* Entry in superblock list. + * Objects which are not bound to dentry require to be dropped + * in ->put_super() + */ + struct rb_root hash_root; /* The same, but indexed by name hash and len */ + struct mutex offset_lock; /* Protect both above trees */ + + struct list_head sync_create_list; /* List of created but not yet synced to the server children */ + + unsigned int drop_count; + + int lock_type; /* How this inode is locked: read or write */ + + int error; /* Transaction error for given inode */ + + long state; /* State machine above */ + + u64 ino; /* Inode number */ + u64 total_len; /* Total length of all children names, used to create offsets */ + + struct inode vfs_inode; +}; + +struct netfs_trans; +typedef int (*netfs_trans_complete_t)(struct page **pages, unsigned int page_num, + void *private, int err); + +struct netfs_state; +struct pohmelfs_sb; + +struct netfs_trans { + /* + * Transaction header and attached contiguous data live here. + */ + struct iovec iovec; + + /* + * Pages attached to transaction. + */ + struct page **pages; + + /* + * List and protecting lock for transaction destination + * network states. + */ + spinlock_t dst_lock; + struct list_head dst_list; + + /* + * Number of users for given transaction. + * For example each network state attached to transaction + * via dst_list increases it. + */ + atomic_t refcnt; + + /* + * Number of pages attached to given transaction. + * Some slots in above page array can be NULL, since + * for example page can be under writeback already, + * so we skip it in this transaction. + */ + unsigned int page_num; + + /* + * Transaction flags: single dst or broadcast and so on. + */ + unsigned int flags; + + /* + * Size of the data, which can be placed into + * iovec.iov_base area. + */ + unsigned int total_size; + + /* + * Number of pages to be sent to remote server. + * Usually equal to above page_num, but in case of partial + * writeback it can accumulate only pages already completed + * previous writeback. + */ + unsigned int attached_pages; + + /* + * Attached number of bytes in all above pages. + */ + unsigned int attached_size; + + /* + * Unique transacton generation number. + * Used as identity in the network state tree of transactions. + */ + unsigned int gen; + + /* + * Transaction completion status. + */ + int result; + + /* + * Superblock this transaction belongs to + */ + struct pohmelfs_sb *psb; + + /* + * Crypto engine, which processed this transaction. + * Can be not NULL only if crypto engine holds encrypted pages. + */ + struct pohmelfs_crypto_engine *eng; + + /* Private data */ + void *private; + + /* Completion callback, invoked just before transaction is destroyed */ + netfs_trans_complete_t complete; +}; + +static inline int netfs_trans_cur_len(struct netfs_trans *t) +{ + return (signed)(t->total_size - t->iovec.iov_len); +} + +static inline void *netfs_trans_current(struct netfs_trans *t) +{ + return t->iovec.iov_base + t->iovec.iov_len; +} + +struct netfs_trans *netfs_trans_alloc(struct pohmelfs_sb *psb, unsigned int size, + unsigned int flags, unsigned int nr); +void netfs_trans_free(struct netfs_trans *t); +int netfs_trans_finish(struct netfs_trans *t, struct pohmelfs_sb *psb); +int netfs_trans_finish_send(struct netfs_trans *t, struct pohmelfs_sb *psb); + +static inline void netfs_trans_reset(struct netfs_trans *t) +{ + t->complete = NULL; +} + +struct netfs_trans_dst { + struct list_head trans_entry; + struct rb_node state_entry; + + unsigned long send_time; + + /* + * Times this transaction was resent to its old or new, + * depending on flags, destinations. When it reaches maximum + * allowed number, specified in superblock->trans_retries, + * transaction will be freed with ETIMEDOUT error. + */ + unsigned int retries; + + struct netfs_trans *trans; + struct netfs_state *state; +}; + +struct netfs_trans_dst *netfs_trans_search(struct netfs_state *st, unsigned int gen); +void netfs_trans_drop_dst(struct netfs_trans_dst *dst); +void netfs_trans_drop_dst_nostate(struct netfs_trans_dst *dst); +void netfs_trans_drop_trans(struct netfs_trans *t, struct netfs_state *st); +void netfs_trans_drop_last(struct netfs_trans *t, struct netfs_state *st); +int netfs_trans_resend(struct netfs_trans *t, struct pohmelfs_sb *psb); +int netfs_trans_remove_nolock(struct netfs_trans_dst *dst, struct netfs_state *st); + +int netfs_trans_init(void); +void netfs_trans_exit(void); + +struct pohmelfs_crypto_engine { + u64 iv; /* Crypto IV for current operation */ + unsigned long timeout; /* Crypto waiting timeout */ + unsigned int size; /* Size of crypto scratchpad */ + void *data; /* Temporal crypto scratchpad */ + /* + * Crypto operations performed on objects. + */ + struct crypto_hash *hash; + struct crypto_ablkcipher *cipher; + + struct pohmelfs_crypto_thread *thread; /* Crypto thread which hosts this engine */ + + struct page **pages; + unsigned int page_num; +}; + +struct pohmelfs_crypto_thread { + struct list_head thread_entry; + + struct task_struct *thread; + struct pohmelfs_sb *psb; + + struct pohmelfs_crypto_engine eng; + + struct netfs_trans *trans; + + wait_queue_head_t wait; + int error; + + unsigned int size; + struct page *page; +}; + +void pohmelfs_crypto_thread_make_ready(struct pohmelfs_crypto_thread *th); + +/* + * Network state, attached to one server. + */ +struct netfs_state { + struct mutex __state_lock; /* Can not allow to use the same socket simultaneously */ + struct mutex __state_send_lock; + struct netfs_cmd cmd; /* Cached command */ + struct netfs_inode_info info; /* Cached inode info */ + + void *data; /* Cached some data */ + unsigned int size; /* Size of that data */ + + struct pohmelfs_sb *psb; /* Superblock */ + + struct task_struct *thread; /* Async receiving thread */ + + /* Waiting/polling machinery */ + wait_queue_t wait; + wait_queue_head_t *whead; + wait_queue_head_t thread_wait; + + struct mutex trans_lock; + struct rb_root trans_root; + + struct pohmelfs_ctl ctl; /* Remote peer */ + + struct socket *socket; /* Socket object */ + struct socket *read_socket; /* Cached pointer to socket object. + * Used to determine if between lock drops socket was changed. + * Never used to read data or any kind of access. + */ + /* + * Crypto engines to process incoming data. + */ + struct pohmelfs_crypto_engine eng; + + int need_reset; +}; + +int netfs_state_init(struct netfs_state *st); +void netfs_state_exit(struct netfs_state *st); + +static inline void netfs_state_lock_send(struct netfs_state *st) +{ + mutex_lock(&st->__state_send_lock); +} + +static inline int netfs_state_trylock_send(struct netfs_state *st) +{ + return mutex_trylock(&st->__state_send_lock); +} + +static inline void netfs_state_unlock_send(struct netfs_state *st) +{ + BUG_ON(!mutex_is_locked(&st->__state_send_lock)); + + mutex_unlock(&st->__state_send_lock); +} + +static inline void netfs_state_lock(struct netfs_state *st) +{ + mutex_lock(&st->__state_lock); +} + +static inline void netfs_state_unlock(struct netfs_state *st) +{ + BUG_ON(!mutex_is_locked(&st->__state_lock)); + + mutex_unlock(&st->__state_lock); +} + +static inline unsigned int netfs_state_poll(struct netfs_state *st) +{ + unsigned int revents = POLLHUP | POLLERR; + + netfs_state_lock(st); + if (st->socket) + revents = st->socket->ops->poll(NULL, st->socket, NULL); + netfs_state_unlock(st); + + return revents; +} + +struct pohmelfs_config; + +struct pohmelfs_sb { + struct rb_root mcache_root; + struct mutex mcache_lock; + atomic_long_t mcache_gen; + unsigned long mcache_timeout; + + unsigned int idx; + + unsigned int trans_retries; + + atomic_t trans_gen; + + unsigned int crypto_attached_size; + unsigned int crypto_align_size; + + unsigned int crypto_fail_unsupported; + + unsigned int crypto_thread_num; + struct list_head crypto_active_list, crypto_ready_list; + struct mutex crypto_thread_lock; + + unsigned int trans_max_pages; + unsigned long trans_data_size; + unsigned long trans_timeout; + + unsigned long drop_scan_timeout; + unsigned long trans_scan_timeout; + + unsigned long wait_on_page_timeout; + + struct list_head flush_list; + struct list_head drop_list; + spinlock_t ino_lock; + u64 ino; + + /* + * Remote nodes POHMELFS connected to. + */ + struct list_head state_list; + struct mutex state_lock; + + /* + * Currently active state to request data from. + */ + struct pohmelfs_config *active_state; + + + wait_queue_head_t wait; + + /* + * Timed checks: stale transactions, inodes to be freed and so on. + */ + struct delayed_work dwork; + struct delayed_work drop_dwork; + + struct super_block *sb; + + struct backing_dev_info bdi; + + /* + * Algorithm strings. + */ + char *hash_string; + char *cipher_string; + + u8 *hash_key; + u8 *cipher_key; + + /* + * Algorithm string lengths. + */ + unsigned int hash_strlen; + unsigned int cipher_strlen; + unsigned int hash_keysize; + unsigned int cipher_keysize; + + /* + * Controls whether to perfrom crypto processing or not. + */ + int perform_crypto; + + /* + * POHMELFS statistics. + */ + u64 total_size; + u64 avail_size; + atomic_long_t total_inodes; + + /* + * Xattr support, read-only and so on. + */ + u64 state_flags; + + /* + * Temporary storage to detect changes in the wait queue. + */ + long flags; +}; + +static inline void netfs_trans_update(struct netfs_cmd *cmd, + struct netfs_trans *t, unsigned int size) +{ + unsigned int sz = ALIGN(size, t->psb->crypto_align_size); + + t->iovec.iov_len += sizeof(struct netfs_cmd) + sz; + cmd->cpad = __cpu_to_be16(sz - size); +} + +static inline struct pohmelfs_sb *POHMELFS_SB(struct super_block *sb) +{ + return sb->s_fs_info; +} + +static inline struct pohmelfs_inode *POHMELFS_I(struct inode *inode) +{ + return container_of(inode, struct pohmelfs_inode, vfs_inode); +} + +static inline u64 pohmelfs_new_ino(struct pohmelfs_sb *psb) +{ + u64 ino; + + spin_lock(&psb->ino_lock); + ino = psb->ino++; + spin_unlock(&psb->ino_lock); + + return ino; +} + +static inline void pohmelfs_put_inode(struct pohmelfs_inode *pi) +{ + struct pohmelfs_sb *psb = POHMELFS_SB(pi->vfs_inode.i_sb); + + spin_lock(&psb->ino_lock); + list_move_tail(&pi->inode_entry, &psb->drop_list); + pi->drop_count++; + spin_unlock(&psb->ino_lock); +} + +struct pohmelfs_config { + struct list_head config_entry; + + struct netfs_state state; +}; + +struct pohmelfs_config_group { + /* + * Entry in the global config group list. + */ + struct list_head group_entry; + + /* + * Index of the current group. + */ + unsigned int idx; + /* + * Number of config_list entries in this group entry. + */ + unsigned int num_entry; + /* + * Algorithm strings. + */ + char *hash_string; + char *cipher_string; + + /* + * Algorithm string lengths. + */ + unsigned int hash_strlen; + unsigned int cipher_strlen; + + /* + * Key and its size. + */ + unsigned int hash_keysize; + unsigned int cipher_keysize; + u8 *hash_key; + u8 *cipher_key; + + /* + * List of config entries (network state info) for given idx. + */ + struct list_head config_list; +}; + +int __init pohmelfs_config_init(void); +void pohmelfs_config_exit(void); +int pohmelfs_copy_config(struct pohmelfs_sb *psb); +int pohmelfs_copy_crypto(struct pohmelfs_sb *psb); +int pohmelfs_config_check(struct pohmelfs_config *config, int idx); +int pohmelfs_state_init_one(struct pohmelfs_sb *psb, struct pohmelfs_config *conf); + +extern const struct file_operations pohmelfs_dir_fops; +extern const struct inode_operations pohmelfs_dir_inode_ops; + +int pohmelfs_state_init(struct pohmelfs_sb *psb); +void pohmelfs_state_exit(struct pohmelfs_sb *psb); +void pohmelfs_state_flush_transactions(struct netfs_state *st); + +void pohmelfs_fill_inode(struct inode *inode, struct netfs_inode_info *info); + +void pohmelfs_name_del(struct pohmelfs_inode *parent, struct pohmelfs_name *n); +void pohmelfs_free_names(struct pohmelfs_inode *parent); +struct pohmelfs_name *pohmelfs_search_hash(struct pohmelfs_inode *pi, u32 hash); + +void pohmelfs_inode_del_inode(struct pohmelfs_sb *psb, struct pohmelfs_inode *pi); + +struct pohmelfs_inode *pohmelfs_create_entry_local(struct pohmelfs_sb *psb, + struct pohmelfs_inode *parent, struct qstr *str, u64 start, umode_t mode); + +int pohmelfs_write_create_inode(struct pohmelfs_inode *pi); + +int pohmelfs_write_inode_create(struct inode *inode, struct netfs_trans *trans); +int pohmelfs_remove_child(struct pohmelfs_inode *parent, struct pohmelfs_name *n); + +struct pohmelfs_inode *pohmelfs_new_inode(struct pohmelfs_sb *psb, + struct pohmelfs_inode *parent, struct qstr *str, + struct netfs_inode_info *info, int link); + +int pohmelfs_setattr(struct dentry *dentry, struct iattr *attr); +int pohmelfs_setattr_raw(struct inode *inode, struct iattr *attr); + +int pohmelfs_meta_command(struct pohmelfs_inode *pi, unsigned int cmd_op, unsigned int flags, + netfs_trans_complete_t complete, void *priv, u64 start); +int pohmelfs_meta_command_data(struct pohmelfs_inode *pi, u64 id, unsigned int cmd_op, char *addon, + unsigned int flags, netfs_trans_complete_t complete, void *priv, u64 start); + +void pohmelfs_check_states(struct pohmelfs_sb *psb); +void pohmelfs_switch_active(struct pohmelfs_sb *psb); + +int pohmelfs_construct_path_string(struct pohmelfs_inode *pi, void *data, int len); +int pohmelfs_path_length(struct pohmelfs_inode *pi); + +struct pohmelfs_crypto_completion { + struct completion complete; + int error; +}; + +int pohmelfs_trans_crypt(struct netfs_trans *t, struct pohmelfs_sb *psb); +void pohmelfs_crypto_exit(struct pohmelfs_sb *psb); +int pohmelfs_crypto_init(struct pohmelfs_sb *psb); + +int pohmelfs_crypto_engine_init(struct pohmelfs_crypto_engine *e, struct pohmelfs_sb *psb); +void pohmelfs_crypto_engine_exit(struct pohmelfs_crypto_engine *e); + +int pohmelfs_crypto_process_input_data(struct pohmelfs_crypto_engine *e, u64 iv, + void *data, struct page *page, unsigned int size); +int pohmelfs_crypto_process_input_page(struct pohmelfs_crypto_engine *e, + struct page *page, unsigned int size, u64 iv); + +static inline u64 pohmelfs_gen_iv(struct netfs_trans *t) +{ + u64 iv = t->gen; + + iv <<= 32; + iv |= ((unsigned long)t) & 0xffffffff; + + return iv; +} + +int pohmelfs_data_lock(struct pohmelfs_inode *pi, u64 start, u32 size, int type); +int pohmelfs_data_unlock(struct pohmelfs_inode *pi, u64 start, u32 size, int type); +int pohmelfs_data_lock_response(struct netfs_state *st); + +static inline int pohmelfs_need_lock(struct pohmelfs_inode *pi, int type) +{ + if (test_bit(NETFS_INODE_OWNED, &pi->state)) { + if (type == pi->lock_type) + return 0; + if ((type == POHMELFS_READ_LOCK) && (pi->lock_type == POHMELFS_WRITE_LOCK)) + return 0; + } + + if (!test_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state)) + return 0; + + return 1; +} + +int __init pohmelfs_mcache_init(void); +void pohmelfs_mcache_exit(void); + +/* #define CONFIG_POHMELFS_DEBUG */ + +#ifdef CONFIG_POHMELFS_DEBUG +#define dprintka(f, a...) printk(f, ##a) +#define dprintk(f, a...) printk("%d: " f, task_pid_vnr(current), ##a) +#else +#define dprintka(f, a...) do {} while (0) +#define dprintk(f, a...) do {} while (0) +#endif + +static inline void netfs_trans_get(struct netfs_trans *t) +{ + atomic_inc(&t->refcnt); +} + +static inline void netfs_trans_put(struct netfs_trans *t) +{ + if (atomic_dec_and_test(&t->refcnt)) { + dprintk("%s: t: %p, gen: %u, err: %d.\n", + __func__, t, t->gen, t->result); + if (t->complete) + t->complete(t->pages, t->page_num, + t->private, t->result); + netfs_trans_free(t); + } +} + +struct pohmelfs_mcache { + struct rb_node mcache_entry; + struct completion complete; + + atomic_t refcnt; + + u64 gen; + + void *data; + u64 start; + u32 size; + int err; + + struct netfs_inode_info info; +}; + +struct pohmelfs_mcache *pohmelfs_mcache_alloc(struct pohmelfs_sb *psb, u64 start, + unsigned int size, void *data); +void pohmelfs_mcache_free(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m); +struct pohmelfs_mcache *pohmelfs_mcache_search(struct pohmelfs_sb *psb, u64 gen); +void pohmelfs_mcache_remove_locked(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m); + +static inline void pohmelfs_mcache_get(struct pohmelfs_mcache *m) +{ + atomic_inc(&m->refcnt); +} + +static inline void pohmelfs_mcache_put(struct pohmelfs_sb *psb, + struct pohmelfs_mcache *m) +{ + if (atomic_dec_and_test(&m->refcnt)) + pohmelfs_mcache_free(psb, m); +} + +/*#define POHMELFS_TRUNCATE_ON_INODE_FLUSH + */ + +#endif /* __KERNEL__*/ + +#endif /* __NETFS_H */ diff --git a/trunk/drivers/staging/pohmelfs/path_entry.c b/trunk/drivers/staging/pohmelfs/path_entry.c new file mode 100644 index 000000000000..400a9fc386ad --- /dev/null +++ b/trunk/drivers/staging/pohmelfs/path_entry.c @@ -0,0 +1,120 @@ +/* + * 2007+ Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netfs.h" + +#define UNHASHED_OBSCURE_STRING_SIZE sizeof(" (deleted)") + +/* + * Create path from root for given inode. + * Path is formed as set of stuctures, containing name of the object + * and its inode data (mode, permissions and so on). + */ +int pohmelfs_construct_path_string(struct pohmelfs_inode *pi, void *data, int len) +{ + struct path path; + struct dentry *d; + char *ptr; + int err = 0, strlen, reduce = 0; + + d = d_find_alias(&pi->vfs_inode); + if (!d) { + printk("%s: no alias, list_empty: %d.\n", __func__, list_empty(&pi->vfs_inode.i_dentry)); + return -ENOENT; + } + + spin_lock(¤t->fs->lock); + path.mnt = mntget(current->fs->root.mnt); + spin_unlock(¤t->fs->lock); + + path.dentry = d; + + if (!IS_ROOT(d) && d_unhashed(d)) + reduce = 1; + + ptr = d_path(&path, data, len); + if (IS_ERR(ptr)) { + err = PTR_ERR(ptr); + goto out; + } + + if (reduce && len >= UNHASHED_OBSCURE_STRING_SIZE) { + char *end = data + len - UNHASHED_OBSCURE_STRING_SIZE; + *end = '\0'; + } + + strlen = len - (ptr - (char *)data); + memmove(data, ptr, strlen); + ptr = data; + + err = strlen; + + dprintk("%s: dname: '%s', len: %u, maxlen: %u, name: '%s', strlen: %d.\n", + __func__, d->d_name.name, d->d_name.len, len, ptr, strlen); + +out: + dput(d); + mntput(path.mnt); + + return err; +} + +int pohmelfs_path_length(struct pohmelfs_inode *pi) +{ + struct dentry *d, *root, *first; + int len; + unsigned seq; + + first = d_find_alias(&pi->vfs_inode); + if (!first) { + dprintk("%s: ino: %llu, mode: %o.\n", __func__, pi->ino, pi->vfs_inode.i_mode); + return -ENOENT; + } + + spin_lock(¤t->fs->lock); + root = dget(current->fs->root.dentry); + spin_unlock(¤t->fs->lock); + +rename_retry: + len = 1; /* Root slash */ + d = first; + seq = read_seqbegin(&rename_lock); + rcu_read_lock(); + + if (!IS_ROOT(d) && d_unhashed(d)) + len += UNHASHED_OBSCURE_STRING_SIZE; /* Obscure " (deleted)" string */ + + while (d && d != root && !IS_ROOT(d)) { + len += d->d_name.len + 1; /* Plus slash */ + d = d->d_parent; + } + rcu_read_unlock(); + if (read_seqretry(&rename_lock, seq)) + goto rename_retry; + + dput(root); + dput(first); + + return len + 1; /* Including zero-byte */ +} diff --git a/trunk/drivers/staging/pohmelfs/trans.c b/trunk/drivers/staging/pohmelfs/trans.c new file mode 100644 index 000000000000..06c1a7451b1b --- /dev/null +++ b/trunk/drivers/staging/pohmelfs/trans.c @@ -0,0 +1,706 @@ +/* + * 2007+ Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netfs.h" + +static struct kmem_cache *netfs_trans_dst; +static mempool_t *netfs_trans_dst_pool; + +static void netfs_trans_init_static(struct netfs_trans *t, int num, int size) +{ + t->page_num = num; + t->total_size = size; + atomic_set(&t->refcnt, 1); + + spin_lock_init(&t->dst_lock); + INIT_LIST_HEAD(&t->dst_list); +} + +static int netfs_trans_send_pages(struct netfs_trans *t, struct netfs_state *st) +{ + int err = 0; + unsigned int i, attached_pages = t->attached_pages, ci; + struct msghdr msg; + struct page **pages = (t->eng) ? t->eng->pages : t->pages; + struct page *p; + unsigned int size; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = MSG_WAITALL | MSG_MORE; + + ci = 0; + for (i = 0; i < t->page_num; ++i) { + struct page *page = pages[ci]; + struct netfs_cmd cmd; + struct iovec io; + + p = t->pages[i]; + + if (!p) + continue; + + size = page_private(p); + + io.iov_base = &cmd; + io.iov_len = sizeof(struct netfs_cmd); + + cmd.cmd = NETFS_WRITE_PAGE; + cmd.ext = 0; + cmd.id = 0; + cmd.size = size; + cmd.start = p->index; + cmd.start <<= PAGE_CACHE_SHIFT; + cmd.csize = 0; + cmd.cpad = 0; + cmd.iv = pohmelfs_gen_iv(t); + + netfs_convert_cmd(&cmd); + + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_flags = MSG_WAITALL | MSG_MORE; + + err = kernel_sendmsg(st->socket, &msg, (struct kvec *)msg.msg_iov, 1, sizeof(struct netfs_cmd)); + if (err <= 0) { + printk("%s: %d/%d failed to send transaction header: t: %p, gen: %u, err: %d.\n", + __func__, i, t->page_num, t, t->gen, err); + if (err == 0) + err = -ECONNRESET; + goto err_out; + } + + msg.msg_flags = MSG_WAITALL | (attached_pages == 1 ? 0 : + MSG_MORE); + + err = kernel_sendpage(st->socket, page, 0, size, msg.msg_flags); + if (err <= 0) { + printk("%s: %d/%d failed to send transaction page: t: %p, gen: %u, size: %u, err: %d.\n", + __func__, i, t->page_num, t, t->gen, size, err); + if (err == 0) + err = -ECONNRESET; + goto err_out; + } + + dprintk("%s: %d/%d sent t: %p, gen: %u, page: %p/%p, size: %u.\n", + __func__, i, t->page_num, t, t->gen, page, p, size); + + err = 0; + attached_pages--; + if (!attached_pages) + break; + ci++; + + continue; + +err_out: + printk("%s: t: %p, gen: %u, err: %d.\n", __func__, t, t->gen, err); + netfs_state_exit(st); + break; + } + + return err; +} + +int netfs_trans_send(struct netfs_trans *t, struct netfs_state *st) +{ + int err; + struct msghdr msg; + + BUG_ON(!t->iovec.iov_len); + BUG_ON(t->iovec.iov_len > 1024*1024*1024); + + netfs_state_lock_send(st); + if (!st->socket) { + err = netfs_state_init(st); + if (err) + goto err_out_unlock_return; + } + + msg.msg_iov = &t->iovec; + msg.msg_iovlen = 1; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = MSG_WAITALL; + + if (t->attached_pages) + msg.msg_flags |= MSG_MORE; + + err = kernel_sendmsg(st->socket, &msg, (struct kvec *)msg.msg_iov, 1, t->iovec.iov_len); + if (err <= 0) { + printk("%s: failed to send contig transaction: t: %p, gen: %u, size: %zu, err: %d.\n", + __func__, t, t->gen, t->iovec.iov_len, err); + if (err == 0) + err = -ECONNRESET; + goto err_out_unlock_return; + } + + dprintk("%s: sent %s transaction: t: %p, gen: %u, size: %zu, page_num: %u.\n", + __func__, (t->page_num) ? "partial" : "full", + t, t->gen, t->iovec.iov_len, t->page_num); + + err = 0; + if (t->attached_pages) + err = netfs_trans_send_pages(t, st); + +err_out_unlock_return: + + if (st->need_reset) + netfs_state_exit(st); + + netfs_state_unlock_send(st); + + dprintk("%s: t: %p, gen: %u, err: %d.\n", + __func__, t, t->gen, err); + + t->result = err; + return err; +} + +static inline int netfs_trans_cmp(unsigned int gen, unsigned int new) +{ + if (gen < new) + return 1; + if (gen > new) + return -1; + return 0; +} + +struct netfs_trans_dst *netfs_trans_search(struct netfs_state *st, unsigned int gen) +{ + struct rb_root *root = &st->trans_root; + struct rb_node *n = root->rb_node; + struct netfs_trans_dst *tmp, *ret = NULL; + struct netfs_trans *t; + int cmp; + + while (n) { + tmp = rb_entry(n, struct netfs_trans_dst, state_entry); + t = tmp->trans; + + cmp = netfs_trans_cmp(t->gen, gen); + if (cmp < 0) + n = n->rb_left; + else if (cmp > 0) + n = n->rb_right; + else { + ret = tmp; + break; + } + } + + return ret; +} + +static int netfs_trans_insert(struct netfs_trans_dst *ndst, struct netfs_state *st) +{ + struct rb_root *root = &st->trans_root; + struct rb_node **n = &root->rb_node, *parent = NULL; + struct netfs_trans_dst *ret = NULL, *tmp; + struct netfs_trans *t = NULL, *new = ndst->trans; + int cmp; + + while (*n) { + parent = *n; + + tmp = rb_entry(parent, struct netfs_trans_dst, state_entry); + t = tmp->trans; + + cmp = netfs_trans_cmp(t->gen, new->gen); + if (cmp < 0) + n = &parent->rb_left; + else if (cmp > 0) + n = &parent->rb_right; + else { + ret = tmp; + break; + } + } + + if (ret) { + printk("%s: exist: old: gen: %u, flags: %x, send_time: %lu, " + "new: gen: %u, flags: %x, send_time: %lu.\n", + __func__, t->gen, t->flags, ret->send_time, + new->gen, new->flags, ndst->send_time); + return -EEXIST; + } + + rb_link_node(&ndst->state_entry, parent, n); + rb_insert_color(&ndst->state_entry, root); + ndst->send_time = jiffies; + + return 0; +} + +int netfs_trans_remove_nolock(struct netfs_trans_dst *dst, struct netfs_state *st) +{ + if (dst && dst->state_entry.rb_parent_color) { + rb_erase(&dst->state_entry, &st->trans_root); + dst->state_entry.rb_parent_color = 0; + return 1; + } + return 0; +} + +static int netfs_trans_remove_state(struct netfs_trans_dst *dst) +{ + int ret; + struct netfs_state *st = dst->state; + + mutex_lock(&st->trans_lock); + ret = netfs_trans_remove_nolock(dst, st); + mutex_unlock(&st->trans_lock); + + return ret; +} + +/* + * Create new destination for given transaction associated with given network state. + * Transaction's reference counter is bumped and will be dropped when either + * reply is received or when async timeout detection task will fail resending + * and drop transaction. + */ +static int netfs_trans_push_dst(struct netfs_trans *t, struct netfs_state *st) +{ + struct netfs_trans_dst *dst; + int err; + + dst = mempool_alloc(netfs_trans_dst_pool, GFP_KERNEL); + if (!dst) + return -ENOMEM; + + dst->retries = 0; + dst->send_time = 0; + dst->state = st; + dst->trans = t; + netfs_trans_get(t); + + mutex_lock(&st->trans_lock); + err = netfs_trans_insert(dst, st); + mutex_unlock(&st->trans_lock); + + if (err) + goto err_out_free; + + spin_lock(&t->dst_lock); + list_add_tail(&dst->trans_entry, &t->dst_list); + spin_unlock(&t->dst_lock); + + return 0; + +err_out_free: + t->result = err; + netfs_trans_put(t); + mempool_free(dst, netfs_trans_dst_pool); + return err; +} + +static void netfs_trans_free_dst(struct netfs_trans_dst *dst) +{ + netfs_trans_put(dst->trans); + mempool_free(dst, netfs_trans_dst_pool); +} + +static void netfs_trans_remove_dst(struct netfs_trans_dst *dst) +{ + if (netfs_trans_remove_state(dst)) + netfs_trans_free_dst(dst); +} + +/* + * Drop destination transaction entry when we know it. + */ +void netfs_trans_drop_dst(struct netfs_trans_dst *dst) +{ + struct netfs_trans *t = dst->trans; + + spin_lock(&t->dst_lock); + list_del_init(&dst->trans_entry); + spin_unlock(&t->dst_lock); + + netfs_trans_remove_dst(dst); +} + +/* + * Drop destination transaction entry when we know it and when we + * already removed dst from state tree. + */ +void netfs_trans_drop_dst_nostate(struct netfs_trans_dst *dst) +{ + struct netfs_trans *t = dst->trans; + + spin_lock(&t->dst_lock); + list_del_init(&dst->trans_entry); + spin_unlock(&t->dst_lock); + + netfs_trans_free_dst(dst); +} + +/* + * This drops destination transaction entry from appropriate network state + * tree and drops related reference counter. It is possible that transaction + * will be freed here if its reference counter hits zero. + * Destination transaction entry will be freed. + */ +void netfs_trans_drop_trans(struct netfs_trans *t, struct netfs_state *st) +{ + struct netfs_trans_dst *dst, *tmp, *ret = NULL; + + spin_lock(&t->dst_lock); + list_for_each_entry_safe(dst, tmp, &t->dst_list, trans_entry) { + if (dst->state == st) { + ret = dst; + list_del(&dst->trans_entry); + break; + } + } + spin_unlock(&t->dst_lock); + + if (ret) + netfs_trans_remove_dst(ret); +} + +/* + * This drops destination transaction entry from appropriate network state + * tree and drops related reference counter. It is possible that transaction + * will be freed here if its reference counter hits zero. + * Destination transaction entry will be freed. + */ +void netfs_trans_drop_last(struct netfs_trans *t, struct netfs_state *st) +{ + struct netfs_trans_dst *dst, *tmp, *ret; + + spin_lock(&t->dst_lock); + ret = list_entry(t->dst_list.prev, struct netfs_trans_dst, trans_entry); + if (ret->state != st) { + ret = NULL; + list_for_each_entry_safe(dst, tmp, &t->dst_list, trans_entry) { + if (dst->state == st) { + ret = dst; + list_del_init(&dst->trans_entry); + break; + } + } + } else { + list_del(&ret->trans_entry); + } + spin_unlock(&t->dst_lock); + + if (ret) + netfs_trans_remove_dst(ret); +} + +static int netfs_trans_push(struct netfs_trans *t, struct netfs_state *st) +{ + int err; + + err = netfs_trans_push_dst(t, st); + if (err) + return err; + + err = netfs_trans_send(t, st); + if (err) + goto err_out_free; + + if (t->flags & NETFS_TRANS_SINGLE_DST) + pohmelfs_switch_active(st->psb); + + return 0; + +err_out_free: + t->result = err; + netfs_trans_drop_last(t, st); + + return err; +} + +int netfs_trans_finish_send(struct netfs_trans *t, struct pohmelfs_sb *psb) +{ + struct pohmelfs_config *c; + int err = -ENODEV; + struct netfs_state *st; +#if 0 + dprintk("%s: t: %p, gen: %u, size: %u, page_num: %u, active: %p.\n", + __func__, t, t->gen, t->iovec.iov_len, t->page_num, psb->active_state); +#endif + mutex_lock(&psb->state_lock); + list_for_each_entry(c, &psb->state_list, config_entry) { + st = &c->state; + + if (t->flags & NETFS_TRANS_SINGLE_DST) { + if (!(st->ctl.perm & POHMELFS_IO_PERM_READ)) + continue; + } else { + if (!(st->ctl.perm & POHMELFS_IO_PERM_WRITE)) + continue; + } + + if (psb->active_state && (psb->active_state->state.ctl.prio >= st->ctl.prio) && + (t->flags & NETFS_TRANS_SINGLE_DST)) + st = &psb->active_state->state; + + err = netfs_trans_push(t, st); + if (!err && (t->flags & NETFS_TRANS_SINGLE_DST)) + break; + } + + mutex_unlock(&psb->state_lock); +#if 0 + dprintk("%s: fully sent t: %p, gen: %u, size: %u, page_num: %u, err: %d.\n", + __func__, t, t->gen, t->iovec.iov_len, t->page_num, err); +#endif + if (err) + t->result = err; + return err; +} + +int netfs_trans_finish(struct netfs_trans *t, struct pohmelfs_sb *psb) +{ + int err; + struct netfs_cmd *cmd = t->iovec.iov_base; + + t->gen = atomic_inc_return(&psb->trans_gen); + + cmd->size = t->iovec.iov_len - sizeof(struct netfs_cmd) + + t->attached_size + t->attached_pages * sizeof(struct netfs_cmd); + cmd->cmd = NETFS_TRANS; + cmd->start = t->gen; + cmd->id = 0; + + if (psb->perform_crypto) { + cmd->ext = psb->crypto_attached_size; + cmd->csize = psb->crypto_attached_size; + } + + dprintk("%s: t: %u, size: %u, iov_len: %zu, attached_size: %u, attached_pages: %u.\n", + __func__, t->gen, cmd->size, t->iovec.iov_len, t->attached_size, t->attached_pages); + err = pohmelfs_trans_crypt(t, psb); + if (err) { + t->result = err; + netfs_convert_cmd(cmd); + dprintk("%s: trans: %llu, crypto_attached_size: %u, attached_size: %u, attached_pages: %d, trans_size: %u, err: %d.\n", + __func__, cmd->start, psb->crypto_attached_size, t->attached_size, t->attached_pages, cmd->size, err); + } + netfs_trans_put(t); + return err; +} + +/* + * Resend transaction to remote server(s). + * If new servers were added into superblock, we can try to send data + * to them too. + * + * It is called under superblock's state_lock, so we can safely + * dereference psb->state_list. Also, transaction's reference counter is + * bumped, so it can not go away under us, thus we can safely access all + * its members. State is locked. + * + * This function returns 0 if transaction was successfully sent to at + * least one destination target. + */ +int netfs_trans_resend(struct netfs_trans *t, struct pohmelfs_sb *psb) +{ + struct netfs_trans_dst *dst; + struct netfs_state *st; + struct pohmelfs_config *c; + int err, exist, error = -ENODEV; + + list_for_each_entry(c, &psb->state_list, config_entry) { + st = &c->state; + + exist = 0; + spin_lock(&t->dst_lock); + list_for_each_entry(dst, &t->dst_list, trans_entry) { + if (st == dst->state) { + exist = 1; + break; + } + } + spin_unlock(&t->dst_lock); + + if (exist) { + if (!(t->flags & NETFS_TRANS_SINGLE_DST) || + (c->config_entry.next == &psb->state_list)) { + dprintk("%s: resending st: %p, t: %p, gen: %u.\n", + __func__, st, t, t->gen); + err = netfs_trans_send(t, st); + if (!err) + error = 0; + } + continue; + } + + dprintk("%s: pushing/resending st: %p, t: %p, gen: %u.\n", + __func__, st, t, t->gen); + err = netfs_trans_push(t, st); + if (err) + continue; + error = 0; + if (t->flags & NETFS_TRANS_SINGLE_DST) + break; + } + + t->result = error; + return error; +} + +void *netfs_trans_add(struct netfs_trans *t, unsigned int size) +{ + struct iovec *io = &t->iovec; + void *ptr; + + if (size > t->total_size) { + ptr = ERR_PTR(-EINVAL); + goto out; + } + + if (io->iov_len + size > t->total_size) { + dprintk("%s: too big size t: %p, gen: %u, iov_len: %zu, size: %u, total: %u.\n", + __func__, t, t->gen, io->iov_len, size, t->total_size); + ptr = ERR_PTR(-E2BIG); + goto out; + } + + ptr = io->iov_base + io->iov_len; + io->iov_len += size; + +out: + dprintk("%s: t: %p, gen: %u, size: %u, total: %zu.\n", + __func__, t, t->gen, size, io->iov_len); + return ptr; +} + +void netfs_trans_free(struct netfs_trans *t) +{ + if (t->eng) + pohmelfs_crypto_thread_make_ready(t->eng->thread); + kfree(t); +} + +struct netfs_trans *netfs_trans_alloc(struct pohmelfs_sb *psb, unsigned int size, + unsigned int flags, unsigned int nr) +{ + struct netfs_trans *t; + unsigned int num, cont, pad, size_no_trans; + unsigned int crypto_added = 0; + struct netfs_cmd *cmd; + + if (psb->perform_crypto) + crypto_added = psb->crypto_attached_size; + + /* + * |sizeof(struct netfs_trans)| + * |sizeof(struct netfs_cmd)| - transaction header + * |size| - buffer with requested size + * |padding| - crypto padding, zero bytes + * |nr * sizeof(struct page *)| - array of page pointers + * + * Overall size should be less than PAGE_SIZE for guaranteed allocation. + */ + + cont = size; + size = ALIGN(size, psb->crypto_align_size); + pad = size - cont; + + size_no_trans = size + sizeof(struct netfs_cmd) * 2 + crypto_added; + + cont = sizeof(struct netfs_trans) + size_no_trans; + + num = (PAGE_SIZE - cont)/sizeof(struct page *); + + if (nr > num) + nr = num; + + t = kzalloc(cont + nr*sizeof(struct page *), GFP_NOIO); + if (!t) + goto err_out_exit; + + t->iovec.iov_base = (void *)(t + 1); + t->pages = (struct page **)(t->iovec.iov_base + size_no_trans); + + /* + * Reserving space for transaction header. + */ + t->iovec.iov_len = sizeof(struct netfs_cmd) + crypto_added; + + netfs_trans_init_static(t, nr, size_no_trans); + + t->flags = flags; + t->psb = psb; + + cmd = (struct netfs_cmd *)t->iovec.iov_base; + + cmd->size = size; + cmd->cpad = pad; + cmd->csize = crypto_added; + + dprintk("%s: t: %p, gen: %u, size: %u, padding: %u, align_size: %u, flags: %x, " + "page_num: %u, base: %p, pages: %p.\n", + __func__, t, t->gen, size, pad, psb->crypto_align_size, flags, nr, + t->iovec.iov_base, t->pages); + + return t; + +err_out_exit: + return NULL; +} + +int netfs_trans_init(void) +{ + int err = -ENOMEM; + + netfs_trans_dst = kmem_cache_create("netfs_trans_dst", sizeof(struct netfs_trans_dst), + 0, 0, NULL); + if (!netfs_trans_dst) + goto err_out_exit; + + netfs_trans_dst_pool = mempool_create_slab_pool(256, netfs_trans_dst); + if (!netfs_trans_dst_pool) + goto err_out_free; + + return 0; + +err_out_free: + kmem_cache_destroy(netfs_trans_dst); +err_out_exit: + return err; +} + +void netfs_trans_exit(void) +{ + mempool_destroy(netfs_trans_dst_pool); + kmem_cache_destroy(netfs_trans_dst); +} diff --git a/trunk/drivers/staging/rtl8712/drv_types.h b/trunk/drivers/staging/rtl8712/drv_types.h index ed85b4415207..9b5d771e650c 100644 --- a/trunk/drivers/staging/rtl8712/drv_types.h +++ b/trunk/drivers/staging/rtl8712/drv_types.h @@ -37,8 +37,6 @@ struct _adapter; #include "wlan_bssdef.h" #include "rtl8712_spec.h" #include "rtl8712_hal.h" -#include -#include enum _NIC_VERSION { RTL8711_NIC, @@ -170,7 +168,6 @@ struct _adapter { s32 bSurpriseRemoved; u32 IsrContent; u32 ImrContent; - bool fw_found; u8 EepromAddressSize; u8 hw_init_completed; struct task_struct *cmdThread; @@ -187,10 +184,6 @@ struct _adapter { _workitem wkFilterRxFF0; u8 blnEnableRxFF0Filter; spinlock_t lockRxFF0Filter; - const struct firmware *fw; - struct usb_interface *pusb_intf; - struct mutex mutex_start; - struct completion rtl8712_fw_ready; }; static inline u8 *myid(struct eeprom_priv *peepriv) diff --git a/trunk/drivers/staging/rtl8712/hal_init.c b/trunk/drivers/staging/rtl8712/hal_init.c index cc893c0f5ad3..d0029aa4cd3c 100644 --- a/trunk/drivers/staging/rtl8712/hal_init.c +++ b/trunk/drivers/staging/rtl8712/hal_init.c @@ -42,56 +42,29 @@ #define FWBUFF_ALIGN_SZ 512 #define MAX_DUMP_FWSZ 49152 /*default = 49152 (48k)*/ -static void rtl871x_load_fw_cb(const struct firmware *firmware, void *context) +static u32 rtl871x_open_fw(struct _adapter *padapter, void **pphfwfile_hdl, + const u8 **ppmappedfw) { - struct _adapter *padapter = context; - - complete(&padapter->rtl8712_fw_ready); - if (!firmware) { - struct usb_device *udev = padapter->dvobjpriv.pusbdev; - struct usb_interface *pusb_intf = padapter->pusb_intf; - printk(KERN_ERR "r8712u: Firmware request failed\n"); - padapter->fw_found = false; - usb_put_dev(udev); - usb_set_intfdata(pusb_intf, NULL); - return; - } - padapter->fw = firmware; - padapter->fw_found = true; - /* firmware available - start netdev */ - register_netdev(padapter->pnetdev); -} - -static const char firmware_file[] = "rtlwifi/rtl8712u.bin"; - -int rtl871x_load_fw(struct _adapter *padapter) -{ - struct device *dev = &padapter->dvobjpriv.pusbdev->dev; int rc; + const char firmware_file[] = "rtlwifi/rtl8712u.bin"; + const struct firmware **praw = (const struct firmware **) + (pphfwfile_hdl); + struct dvobj_priv *pdvobjpriv = (struct dvobj_priv *) + (&padapter->dvobjpriv); + struct usb_device *pusbdev = pdvobjpriv->pusbdev; - init_completion(&padapter->rtl8712_fw_ready); printk(KERN_INFO "r8712u: Loading firmware from \"%s\"\n", firmware_file); - rc = request_firmware_nowait(THIS_MODULE, 1, firmware_file, dev, - GFP_KERNEL, padapter, rtl871x_load_fw_cb); - if (rc) - printk(KERN_ERR "r8712u: Firmware request error %d\n", rc); - return rc; -} -MODULE_FIRMWARE("rtlwifi/rtl8712u.bin"); - -static u32 rtl871x_open_fw(struct _adapter *padapter, const u8 **ppmappedfw) -{ - const struct firmware **praw = &padapter->fw; - - if (padapter->fw->size > 200000) { - printk(KERN_ERR "r8172u: Badfw->size of %d\n", - (int)padapter->fw->size); + rc = request_firmware(praw, firmware_file, &pusbdev->dev); + if (rc < 0) { + printk(KERN_ERR "r8712u: Unable to load firmware\n"); + printk(KERN_ERR "r8712u: Install latest linux-firmware\n"); return 0; } *ppmappedfw = (u8 *)((*praw)->data); return (*praw)->size; } +MODULE_FIRMWARE("rtlwifi/rtl8712u.bin"); static void fill_fwpriv(struct _adapter *padapter, struct fw_priv *pfwpriv) { @@ -169,17 +142,18 @@ static u8 rtl8712_dl_fw(struct _adapter *padapter) uint dump_imem_sz, imem_sz, dump_emem_sz, emem_sz; /* max = 49152; */ struct fw_hdr fwhdr; u32 ulfilelength; /* FW file size */ + void *phfwfile_hdl = NULL; const u8 *pmappedfw = NULL; u8 *ptmpchar = NULL, *ppayload, *ptr; struct tx_desc *ptx_desc; u32 txdscp_sz = sizeof(struct tx_desc); u8 ret = _FAIL; - ulfilelength = rtl871x_open_fw(padapter, &pmappedfw); + ulfilelength = rtl871x_open_fw(padapter, &phfwfile_hdl, &pmappedfw); if (pmappedfw && (ulfilelength > 0)) { update_fwhdr(&fwhdr, pmappedfw); if (chk_fwhdr(&fwhdr, ulfilelength) == _FAIL) - return ret; + goto firmware_rel; fill_fwpriv(padapter, &fwhdr.fwpriv); /* firmware check ok */ maxlen = (fwhdr.img_IMEM_size > fwhdr.img_SRAM_size) ? @@ -187,7 +161,7 @@ static u8 rtl8712_dl_fw(struct _adapter *padapter) maxlen += txdscp_sz; ptmpchar = _malloc(maxlen + FWBUFF_ALIGN_SZ); if (ptmpchar == NULL) - return ret; + goto firmware_rel; ptx_desc = (struct tx_desc *)(ptmpchar + FWBUFF_ALIGN_SZ - ((addr_t)(ptmpchar) & (FWBUFF_ALIGN_SZ - 1))); @@ -323,6 +297,8 @@ static u8 rtl8712_dl_fw(struct _adapter *padapter) exit_fail: kfree(ptmpchar); +firmware_rel: + release_firmware((struct firmware *)phfwfile_hdl); return ret; } diff --git a/trunk/drivers/staging/rtl8712/os_intfs.c b/trunk/drivers/staging/rtl8712/os_intfs.c index 98a3d684f9b2..9a75c6dbe505 100644 --- a/trunk/drivers/staging/rtl8712/os_intfs.c +++ b/trunk/drivers/staging/rtl8712/os_intfs.c @@ -31,7 +31,6 @@ #include #include #include -#include #include "osdep_service.h" #include "drv_types.h" #include "xmit_osdep.h" @@ -265,12 +264,12 @@ static void start_drv_timers(struct _adapter *padapter) void r8712_stop_drv_timers(struct _adapter *padapter) { _cancel_timer_ex(&padapter->mlmepriv.assoc_timer); + _cancel_timer_ex(&padapter->mlmepriv.sitesurveyctrl. + sitesurvey_ctrl_timer); _cancel_timer_ex(&padapter->securitypriv.tkip_timer); _cancel_timer_ex(&padapter->mlmepriv.scan_to_timer); _cancel_timer_ex(&padapter->mlmepriv.dhcp_timer); _cancel_timer_ex(&padapter->mlmepriv.wdg_timer); - _cancel_timer_ex(&padapter->mlmepriv.sitesurveyctrl. - sitesurvey_ctrl_timer); } static u8 init_default_value(struct _adapter *padapter) @@ -348,8 +347,7 @@ u8 r8712_free_drv_sw(struct _adapter *padapter) r8712_free_mlme_priv(&padapter->mlmepriv); r8712_free_io_queue(padapter); _free_xmit_priv(&padapter->xmitpriv); - if (padapter->fw_found) - _r8712_free_sta_priv(&padapter->stapriv); + _r8712_free_sta_priv(&padapter->stapriv); _r8712_free_recv_priv(&padapter->recvpriv); mp871xdeinit(padapter); if (pnetdev) @@ -390,7 +388,6 @@ static int netdev_open(struct net_device *pnetdev) { struct _adapter *padapter = (struct _adapter *)netdev_priv(pnetdev); - mutex_lock(&padapter->mutex_start); if (padapter->bup == false) { padapter->bDriverStopped = false; padapter->bSurpriseRemoved = false; @@ -438,13 +435,11 @@ static int netdev_open(struct net_device *pnetdev) /* start driver mlme relation timer */ start_drv_timers(padapter); padapter->ledpriv.LedControlHandler(padapter, LED_CTL_NO_LINK); - mutex_unlock(&padapter->mutex_start); return 0; netdev_open_error: padapter->bup = false; netif_carrier_off(pnetdev); netif_stop_queue(pnetdev); - mutex_unlock(&padapter->mutex_start); return -1; } @@ -478,9 +473,6 @@ static int netdev_close(struct net_device *pnetdev) r8712_free_network_queue(padapter); /* The interface is no longer Up: */ padapter->bup = false; - release_firmware(padapter->fw); - /* never exit with a firmware callback pending */ - wait_for_completion(&padapter->rtl8712_fw_ready); return 0; } diff --git a/trunk/drivers/staging/rtl8712/rtl8712_hal.h b/trunk/drivers/staging/rtl8712/rtl8712_hal.h index d19865a5a50c..665e71838172 100644 --- a/trunk/drivers/staging/rtl8712/rtl8712_hal.h +++ b/trunk/drivers/staging/rtl8712/rtl8712_hal.h @@ -145,6 +145,5 @@ struct hal_priv { }; uint rtl8712_hal_init(struct _adapter *padapter); -int rtl871x_load_fw(struct _adapter *padapter); #endif diff --git a/trunk/drivers/staging/rtl8712/rtl871x_sta_mgt.c b/trunk/drivers/staging/rtl8712/rtl871x_sta_mgt.c index 81bde803c59f..64f569618839 100644 --- a/trunk/drivers/staging/rtl8712/rtl871x_sta_mgt.c +++ b/trunk/drivers/staging/rtl8712/rtl871x_sta_mgt.c @@ -43,7 +43,6 @@ static void _init_stainfo(struct sta_info *psta) _r8712_init_sta_xmit_priv(&psta->sta_xmitpriv); _r8712_init_sta_recv_priv(&psta->sta_recvpriv); #ifdef CONFIG_R8712_AP - _init_listhead(&psta->asoc_list); _init_listhead(&psta->auth_list); #endif } diff --git a/trunk/drivers/staging/rtl8712/usb_intf.c b/trunk/drivers/staging/rtl8712/usb_intf.c index 9bade184883b..5385da2e9cdb 100644 --- a/trunk/drivers/staging/rtl8712/usb_intf.c +++ b/trunk/drivers/staging/rtl8712/usb_intf.c @@ -89,7 +89,6 @@ static struct usb_device_id rtl871x_usb_id_tbl[] = { {USB_DEVICE(0x0DF6, 0x0045)}, {USB_DEVICE(0x0DF6, 0x0059)}, /* 11n mode disable */ {USB_DEVICE(0x0DF6, 0x004B)}, - {USB_DEVICE(0x0DF6, 0x005B)}, {USB_DEVICE(0x0DF6, 0x005D)}, {USB_DEVICE(0x0DF6, 0x0063)}, /* Sweex */ @@ -390,7 +389,6 @@ static int r871xu_drv_init(struct usb_interface *pusb_intf, pdvobjpriv = &padapter->dvobjpriv; pdvobjpriv->padapter = padapter; padapter->dvobjpriv.pusbdev = udev; - padapter->pusb_intf = pusb_intf; usb_set_intfdata(pusb_intf, pnetdev); SET_NETDEV_DEV(pnetdev, &pusb_intf->dev); /* step 2. */ @@ -597,11 +595,10 @@ static int r871xu_drv_init(struct usb_interface *pusb_intf, "%pM\n", mac); memcpy(pnetdev->dev_addr, mac, ETH_ALEN); } - /* step 6. Load the firmware asynchronously */ - if (rtl871x_load_fw(padapter)) + /* step 6. Tell the network stack we exist */ + if (register_netdev(pnetdev) != 0) goto error; spin_lock_init(&padapter->lockRxFF0Filter); - mutex_init(&padapter->mutex_start); return 0; error: usb_put_dev(udev); @@ -632,8 +629,7 @@ static void r871xu_dev_remove(struct usb_interface *pusb_intf) flush_scheduled_work(); udelay(1); /*Stop driver mlme relation timer */ - if (padapter->fw_found) - r8712_stop_drv_timers(padapter); + r8712_stop_drv_timers(padapter); r871x_dev_unload(padapter); r8712_free_drv_sw(padapter); } diff --git a/trunk/drivers/staging/tidspbridge/core/tiomap3430.c b/trunk/drivers/staging/tidspbridge/core/tiomap3430.c index dde559d06c43..e1c4492a7105 100644 --- a/trunk/drivers/staging/tidspbridge/core/tiomap3430.c +++ b/trunk/drivers/staging/tidspbridge/core/tiomap3430.c @@ -1046,6 +1046,8 @@ static int bridge_dev_destroy(struct bridge_dev_context *dev_ctxt) /* Free the driver's device context: */ kfree(drv_datap->base_img); + kfree(drv_datap); + dev_set_drvdata(bridge, NULL); kfree((void *)dev_ctxt); return status; } diff --git a/trunk/drivers/staging/tidspbridge/rmgr/drv_interface.c b/trunk/drivers/staging/tidspbridge/rmgr/drv_interface.c index 385740bad0de..76cfc6edecd9 100644 --- a/trunk/drivers/staging/tidspbridge/rmgr/drv_interface.c +++ b/trunk/drivers/staging/tidspbridge/rmgr/drv_interface.c @@ -410,9 +410,6 @@ static int __devexit omap34_xx_bridge_remove(struct platform_device *pdev) DBC_ASSERT(ret == true); } - kfree(drv_datap); - dev_set_drvdata(bridge, NULL); - func_cont: mem_ext_phys_pool_release(); @@ -503,42 +500,35 @@ static int bridge_open(struct inode *ip, struct file *filp) } #endif pr_ctxt = kzalloc(sizeof(struct process_context), GFP_KERNEL); - if (!pr_ctxt) - return -ENOMEM; - - pr_ctxt->res_state = PROC_RES_ALLOCATED; - spin_lock_init(&pr_ctxt->dmm_map_lock); - INIT_LIST_HEAD(&pr_ctxt->dmm_map_list); - spin_lock_init(&pr_ctxt->dmm_rsv_lock); - INIT_LIST_HEAD(&pr_ctxt->dmm_rsv_list); - - pr_ctxt->node_id = kzalloc(sizeof(struct idr), GFP_KERNEL); - if (!pr_ctxt->node_id) { - status = -ENOMEM; - goto err1; - } - - idr_init(pr_ctxt->node_id); + if (pr_ctxt) { + pr_ctxt->res_state = PROC_RES_ALLOCATED; + spin_lock_init(&pr_ctxt->dmm_map_lock); + INIT_LIST_HEAD(&pr_ctxt->dmm_map_list); + spin_lock_init(&pr_ctxt->dmm_rsv_lock); + INIT_LIST_HEAD(&pr_ctxt->dmm_rsv_list); + + pr_ctxt->node_id = kzalloc(sizeof(struct idr), GFP_KERNEL); + if (pr_ctxt->node_id) { + idr_init(pr_ctxt->node_id); + } else { + status = -ENOMEM; + goto err; + } - pr_ctxt->stream_id = kzalloc(sizeof(struct idr), GFP_KERNEL); - if (!pr_ctxt->stream_id) { + pr_ctxt->stream_id = kzalloc(sizeof(struct idr), GFP_KERNEL); + if (pr_ctxt->stream_id) + idr_init(pr_ctxt->stream_id); + else + status = -ENOMEM; + } else { status = -ENOMEM; - goto err2; } - - idr_init(pr_ctxt->stream_id); - +err: filp->private_data = pr_ctxt; - #ifdef CONFIG_TIDSPBRIDGE_RECOVERY - atomic_inc(&bridge_cref); + if (!status) + atomic_inc(&bridge_cref); #endif - return 0; - -err2: - kfree(pr_ctxt->node_id); -err1: - kfree(pr_ctxt); return status; } @@ -560,8 +550,6 @@ static int bridge_release(struct inode *ip, struct file *filp) flush_signals(current); drv_remove_all_resources(pr_ctxt); proc_detach(pr_ctxt); - kfree(pr_ctxt->node_id); - kfree(pr_ctxt->stream_id); kfree(pr_ctxt); filp->private_data = NULL; diff --git a/trunk/drivers/staging/usbip/stub_main.c b/trunk/drivers/staging/usbip/stub_main.c index 705a9e530a19..2d6317850064 100644 --- a/trunk/drivers/staging/usbip/stub_main.c +++ b/trunk/drivers/staging/usbip/stub_main.c @@ -246,9 +246,8 @@ static int __init usbip_host_init(void) { int ret; - init_busid_table(); - stub_priv_cache = KMEM_CACHE(stub_priv, SLAB_HWCACHE_ALIGN); + if (!stub_priv_cache) { pr_err("kmem_cache_create failed\n"); return -ENOMEM; @@ -267,6 +266,7 @@ static int __init usbip_host_init(void) goto err_create_file; } + init_busid_table(); pr_info(DRIVER_DESC " v" USBIP_VERSION "\n"); return ret; diff --git a/trunk/drivers/staging/zcache/zcache-main.c b/trunk/drivers/staging/zcache/zcache-main.c index ef7c52bb1df9..642840c612ac 100644 --- a/trunk/drivers/staging/zcache/zcache-main.c +++ b/trunk/drivers/staging/zcache/zcache-main.c @@ -358,8 +358,8 @@ static struct zbud_hdr *zbud_create(uint16_t client_id, uint16_t pool_id, if (unlikely(zbpg == NULL)) goto out; /* ok, have a page, now compress the data before taking locks */ - spin_lock(&zbud_budlists_spinlock); spin_lock(&zbpg->lock); + spin_lock(&zbud_budlists_spinlock); list_add_tail(&zbpg->bud_list, &zbud_unbuddied[nchunks].list); zbud_unbuddied[nchunks].count++; zh = &zbpg->buddy[0]; @@ -389,11 +389,12 @@ static struct zbud_hdr *zbud_create(uint16_t client_id, uint16_t pool_id, zh->oid = *oid; zh->pool_id = pool_id; zh->client_id = client_id; + /* can wait to copy the data until the list locks are dropped */ + spin_unlock(&zbud_budlists_spinlock); + to = zbud_data(zh, size); memcpy(to, cdata, size); spin_unlock(&zbpg->lock); - spin_unlock(&zbud_budlists_spinlock); - zbud_cumul_chunk_counts[nchunks]++; atomic_inc(&zcache_zbud_curr_zpages); zcache_zbud_cumul_zpages++; @@ -654,8 +655,8 @@ static unsigned int zv_max_zsize = (PAGE_SIZE / 8) * 7; */ static unsigned int zv_max_mean_zsize = (PAGE_SIZE / 8) * 5; -static atomic_t zv_curr_dist_counts[NCHUNKS]; -static atomic_t zv_cumul_dist_counts[NCHUNKS]; +static unsigned long zv_curr_dist_counts[NCHUNKS]; +static unsigned long zv_cumul_dist_counts[NCHUNKS]; static struct zv_hdr *zv_create(struct xv_pool *xvpool, uint32_t pool_id, struct tmem_oid *oid, uint32_t index, @@ -674,8 +675,8 @@ static struct zv_hdr *zv_create(struct xv_pool *xvpool, uint32_t pool_id, &page, &offset, ZCACHE_GFP_MASK); if (unlikely(ret)) goto out; - atomic_inc(&zv_curr_dist_counts[chunks]); - atomic_inc(&zv_cumul_dist_counts[chunks]); + zv_curr_dist_counts[chunks]++; + zv_cumul_dist_counts[chunks]++; zv = kmap_atomic(page, KM_USER0) + offset; zv->index = index; zv->oid = *oid; @@ -697,7 +698,7 @@ static void zv_free(struct xv_pool *xvpool, struct zv_hdr *zv) ASSERT_SENTINEL(zv, ZVH); BUG_ON(chunks >= NCHUNKS); - atomic_dec(&zv_curr_dist_counts[chunks]); + zv_curr_dist_counts[chunks]--; size -= sizeof(*zv); BUG_ON(size == 0); INVERT_SENTINEL(zv, ZVH); @@ -737,7 +738,7 @@ static int zv_curr_dist_counts_show(char *buf) char *p = buf; for (i = 0; i < NCHUNKS; i++) { - n = atomic_read(&zv_curr_dist_counts[i]); + n = zv_curr_dist_counts[i]; p += sprintf(p, "%lu ", n); chunks += n; sum_total_chunks += i * n; @@ -753,7 +754,7 @@ static int zv_cumul_dist_counts_show(char *buf) char *p = buf; for (i = 0; i < NCHUNKS; i++) { - n = atomic_read(&zv_cumul_dist_counts[i]); + n = zv_cumul_dist_counts[i]; p += sprintf(p, "%lu ", n); chunks += n; sum_total_chunks += i * n; @@ -1781,9 +1782,9 @@ static int zcache_frontswap_poolid = -1; * Swizzling increases objects per swaptype, increasing tmem concurrency * for heavy swaploads. Later, larger nr_cpus -> larger SWIZ_BITS * Setting SWIZ_BITS to 27 basically reconstructs the swap entry from - * frontswap_get_page(), but has side-effects. Hence using 8. + * frontswap_get_page() */ -#define SWIZ_BITS 8 +#define SWIZ_BITS 27 #define SWIZ_MASK ((1 << SWIZ_BITS) - 1) #define _oswiz(_type, _ind) ((_type << SWIZ_BITS) | (_ind & SWIZ_MASK)) #define iswiz(_ind) (_ind >> SWIZ_BITS) diff --git a/trunk/drivers/tty/serial/8250/8250.c b/trunk/drivers/tty/serial/8250/8250.c index 9b7336fcfbb3..9f50c4e3c2be 100644 --- a/trunk/drivers/tty/serial/8250/8250.c +++ b/trunk/drivers/tty/serial/8250/8250.c @@ -45,7 +45,7 @@ #include "8250.h" #ifdef CONFIG_SPARC -#include "../suncore.h" +#include "suncore.h" #endif /* diff --git a/trunk/drivers/tty/serial/m32r_sio.c b/trunk/drivers/tty/serial/8250/m32r_sio.c similarity index 100% rename from trunk/drivers/tty/serial/m32r_sio.c rename to trunk/drivers/tty/serial/8250/m32r_sio.c diff --git a/trunk/drivers/tty/serial/m32r_sio.h b/trunk/drivers/tty/serial/8250/m32r_sio.h similarity index 100% rename from trunk/drivers/tty/serial/m32r_sio.h rename to trunk/drivers/tty/serial/8250/m32r_sio.h diff --git a/trunk/drivers/tty/serial/m32r_sio_reg.h b/trunk/drivers/tty/serial/8250/m32r_sio_reg.h similarity index 100% rename from trunk/drivers/tty/serial/m32r_sio_reg.h rename to trunk/drivers/tty/serial/8250/m32r_sio_reg.h diff --git a/trunk/drivers/tty/serial/omap-serial.c b/trunk/drivers/tty/serial/omap-serial.c index f80904145fd4..1c2426931484 100644 --- a/trunk/drivers/tty/serial/omap-serial.c +++ b/trunk/drivers/tty/serial/omap-serial.c @@ -46,13 +46,6 @@ #define DEFAULT_CLK_SPEED 48000000 /* 48Mhz*/ -/* SCR register bitmasks */ -#define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK (1 << 7) - -/* FCR register bitmasks */ -#define OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT 6 -#define OMAP_UART_FCR_RX_FIFO_TRIG_MASK (0x3 << 6) - static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS]; /* Forward declaration of functions */ @@ -136,7 +129,6 @@ static void serial_omap_enable_ms(struct uart_port *port) static void serial_omap_stop_tx(struct uart_port *port) { struct uart_omap_port *up = (struct uart_omap_port *)port; - struct omap_uart_port_info *pdata = up->pdev->dev.platform_data; if (up->use_dma && up->uart_dma.tx_dma_channel != OMAP_UART_DMA_CH_FREE) { @@ -159,9 +151,6 @@ static void serial_omap_stop_tx(struct uart_port *port) serial_out(up, UART_IER, up->ier); } - if (!up->use_dma && pdata->set_forceidle) - pdata->set_forceidle(up->pdev); - pm_runtime_mark_last_busy(&up->pdev->dev); pm_runtime_put_autosuspend(&up->pdev->dev); } @@ -290,7 +279,6 @@ static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up) static void serial_omap_start_tx(struct uart_port *port) { struct uart_omap_port *up = (struct uart_omap_port *)port; - struct omap_uart_port_info *pdata = up->pdev->dev.platform_data; struct circ_buf *xmit; unsigned int start; int ret = 0; @@ -298,8 +286,6 @@ static void serial_omap_start_tx(struct uart_port *port) if (!up->use_dma) { pm_runtime_get_sync(&up->pdev->dev); serial_omap_enable_ier_thri(up); - if (pdata->set_noidle) - pdata->set_noidle(up->pdev); pm_runtime_mark_last_busy(&up->pdev->dev); pm_runtime_put_autosuspend(&up->pdev->dev); return; @@ -740,7 +726,8 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, quot = serial_omap_get_divisor(port, baud); /* calculate wakeup latency constraint */ - up->calc_latency = (USEC_PER_SEC * up->port.fifosize) / (baud / 8); + up->calc_latency = (1000000 * up->port.fifosize) / + (1000 * baud / 8); up->latency = up->calc_latency; schedule_work(&up->qos_work); @@ -824,21 +811,14 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, up->mcr = serial_in(up, UART_MCR); serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); /* FIFO ENABLE, DMA MODE */ - - up->scr |= OMAP_UART_SCR_RX_TRIG_GRANU1_MASK; + serial_out(up, UART_FCR, up->fcr); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); if (up->use_dma) { serial_out(up, UART_TI752_TLR, 0); - up->scr |= UART_FCR_TRIGGER_4; - } else { - /* Set receive FIFO threshold to 1 byte */ - up->fcr &= ~OMAP_UART_FCR_RX_FIFO_TRIG_MASK; - up->fcr |= (0x1 << OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT); + up->scr |= (UART_FCR_TRIGGER_4 | UART_FCR_TRIGGER_8); } - serial_out(up, UART_FCR, up->fcr); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - serial_out(up, UART_OMAP_SCR, up->scr); serial_out(up, UART_EFR, up->efr); diff --git a/trunk/drivers/tty/serial/samsung.c b/trunk/drivers/tty/serial/samsung.c index c55e5fb16fa3..f96f37b5fec6 100644 --- a/trunk/drivers/tty/serial/samsung.c +++ b/trunk/drivers/tty/serial/samsung.c @@ -1593,8 +1593,7 @@ static struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = { #define S5PV210_SERIAL_DRV_DATA (kernel_ulong_t)NULL #endif -#if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212) || \ - defined(CONFIG_SOC_EXYNOS4412) || defined(CONFIG_SOC_EXYNOS5250) +#ifdef CONFIG_CPU_EXYNOS4210 static struct s3c24xx_serial_drv_data exynos4210_serial_drv_data = { .info = &(struct s3c24xx_uart_info) { .name = "Samsung Exynos4 UART", diff --git a/trunk/drivers/usb/gadget/f_loopback.c b/trunk/drivers/usb/gadget/f_loopback.c index 2c0cd824c667..6d87f288df4e 100644 --- a/trunk/drivers/usb/gadget/f_loopback.c +++ b/trunk/drivers/usb/gadget/f_loopback.c @@ -418,7 +418,7 @@ int __init loopback_add(struct usb_composite_dev *cdev, bool autoresume) /* support autoresume for remote wakeup testing */ if (autoresume) - loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; /* support OTG systems */ if (gadget_is_otg(cdev->gadget)) { diff --git a/trunk/drivers/usb/host/Kconfig b/trunk/drivers/usb/host/Kconfig index 353cdd488b93..91413cac97be 100644 --- a/trunk/drivers/usb/host/Kconfig +++ b/trunk/drivers/usb/host/Kconfig @@ -130,7 +130,7 @@ config USB_FSL_MPH_DR_OF tristate config USB_EHCI_FSL - bool "Support for Freescale PPC on-chip EHCI USB controller" + bool "Support for Freescale on-chip EHCI USB controller" depends on USB_EHCI_HCD && FSL_SOC select USB_EHCI_ROOT_HUB_TT select USB_FSL_MPH_DR_OF if OF @@ -138,7 +138,7 @@ config USB_EHCI_FSL Variation of ARC USB block used in some Freescale chips. config USB_EHCI_MXC - bool "Support for Freescale i.MX on-chip EHCI USB controller" + bool "Support for Freescale on-chip EHCI USB controller" depends on USB_EHCI_HCD && ARCH_MXC select USB_EHCI_ROOT_HUB_TT ---help--- @@ -546,7 +546,7 @@ config USB_RENESAS_USBHS_HCD config USB_WHCI_HCD tristate "Wireless USB Host Controller Interface (WHCI) driver (EXPERIMENTAL)" depends on EXPERIMENTAL - depends on PCI && USB && UWB + depends on PCI && USB select USB_WUSB select UWB_WHCI help @@ -559,7 +559,7 @@ config USB_WHCI_HCD config USB_HWA_HCD tristate "Host Wire Adapter (HWA) driver (EXPERIMENTAL)" depends on EXPERIMENTAL - depends on USB && UWB + depends on USB select USB_WUSB select UWB_HWA help diff --git a/trunk/drivers/usb/host/ehci-fsl.c b/trunk/drivers/usb/host/ehci-fsl.c index c26a82e83f6e..b556a72264d1 100644 --- a/trunk/drivers/usb/host/ehci-fsl.c +++ b/trunk/drivers/usb/host/ehci-fsl.c @@ -239,7 +239,7 @@ static void ehci_fsl_setup_phy(struct ehci_hcd *ehci, ehci_writel(ehci, portsc, &ehci->regs->port_status[port_offset]); } -static int ehci_fsl_usb_setup(struct ehci_hcd *ehci) +static void ehci_fsl_usb_setup(struct ehci_hcd *ehci) { struct usb_hcd *hcd = ehci_to_hcd(ehci); struct fsl_usb2_platform_data *pdata; @@ -299,19 +299,12 @@ static int ehci_fsl_usb_setup(struct ehci_hcd *ehci) #endif out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001); } - - if (!(in_be32(non_ehci + FSL_SOC_USB_CTRL) & CTRL_PHY_CLK_VALID)) { - printk(KERN_WARNING "fsl-ehci: USB PHY clock invalid\n"); - return -ENODEV; - } - return 0; } /* called after powerup, by probe or system-pm "wakeup" */ static int ehci_fsl_reinit(struct ehci_hcd *ehci) { - if (ehci_fsl_usb_setup(ehci)) - return -ENODEV; + ehci_fsl_usb_setup(ehci); ehci_port_power(ehci, 0); return 0; diff --git a/trunk/drivers/usb/host/ehci-fsl.h b/trunk/drivers/usb/host/ehci-fsl.h index bdf43e2adc51..491806221165 100644 --- a/trunk/drivers/usb/host/ehci-fsl.h +++ b/trunk/drivers/usb/host/ehci-fsl.h @@ -45,6 +45,5 @@ #define FSL_SOC_USB_PRICTRL 0x40c /* NOTE: big-endian */ #define FSL_SOC_USB_SICTRL 0x410 /* NOTE: big-endian */ #define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */ -#define CTRL_PHY_CLK_VALID (1 << 17) #define SNOOP_SIZE_2GB 0x1e #endif /* _EHCI_FSL_H */ diff --git a/trunk/drivers/usb/host/pci-quirks.c b/trunk/drivers/usb/host/pci-quirks.c index ac53a662a6a3..caf87428ca43 100644 --- a/trunk/drivers/usb/host/pci-quirks.c +++ b/trunk/drivers/usb/host/pci-quirks.c @@ -867,12 +867,6 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev) static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev) { - /* Skip Netlogic mips SoC's internal PCI USB controller. - * This device does not need/support EHCI/OHCI handoff - */ - if (pdev->vendor == 0x184e) /* vendor Netlogic */ - return; - if (pdev->class == PCI_CLASS_SERIAL_USB_UHCI) quirk_usb_handoff_uhci(pdev); else if (pdev->class == PCI_CLASS_SERIAL_USB_OHCI) diff --git a/trunk/drivers/usb/musb/musb_io.h b/trunk/drivers/usb/musb/musb_io.h index 1d5eda26fbd1..e61aa95f2d2a 100644 --- a/trunk/drivers/usb/musb/musb_io.h +++ b/trunk/drivers/usb/musb/musb_io.h @@ -39,8 +39,7 @@ #if !defined(CONFIG_ARM) && !defined(CONFIG_SUPERH) \ && !defined(CONFIG_AVR32) && !defined(CONFIG_PPC32) \ - && !defined(CONFIG_PPC64) && !defined(CONFIG_BLACKFIN) \ - && !defined(CONFIG_MIPS) + && !defined(CONFIG_PPC64) && !defined(CONFIG_BLACKFIN) static inline void readsl(const void __iomem *addr, void *buf, int len) { insl((unsigned long)addr, buf, len); } static inline void readsw(const void __iomem *addr, void *buf, int len) diff --git a/trunk/drivers/usb/otg/Kconfig b/trunk/drivers/usb/otg/Kconfig index 735ef4c2339a..76d629345418 100644 --- a/trunk/drivers/usb/otg/Kconfig +++ b/trunk/drivers/usb/otg/Kconfig @@ -118,7 +118,7 @@ config FSL_USB2_OTG config USB_MV_OTG tristate "Marvell USB OTG support" - depends on USB_EHCI_MV && USB_MV_UDC && USB_SUSPEND + depends on USB_MV_UDC && USB_SUSPEND select USB_OTG select USB_OTG_UTILS help diff --git a/trunk/drivers/usb/serial/ftdi_sio.c b/trunk/drivers/usb/serial/ftdi_sio.c index f770415305f8..ad654f8208ef 100644 --- a/trunk/drivers/usb/serial/ftdi_sio.c +++ b/trunk/drivers/usb/serial/ftdi_sio.c @@ -839,7 +839,6 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_LOGBOOKML_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_LS_LOGBOOK_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_HS_LOGBOOK_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CINTERION_MC55I_PID) }, { USB_DEVICE(FTDI_VID, FTDI_DOTEC_PID) }, { USB_DEVICE(QIHARDWARE_VID, MILKYMISTONE_JTAGSERIAL_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, diff --git a/trunk/drivers/usb/serial/ftdi_sio_ids.h b/trunk/drivers/usb/serial/ftdi_sio_ids.h index 6f6058f0db1b..f994503df2dd 100644 --- a/trunk/drivers/usb/serial/ftdi_sio_ids.h +++ b/trunk/drivers/usb/serial/ftdi_sio_ids.h @@ -1187,10 +1187,3 @@ */ /* ZigBee controller */ #define FTDI_RF_R106 0x8A28 - -/* - * Product: HCP HIT GPRS modem - * Manufacturer: HCP d.o.o. - * ATI command output: Cinterion MC55i - */ -#define FTDI_CINTERION_MC55I_PID 0xA951 diff --git a/trunk/drivers/usb/serial/option.c b/trunk/drivers/usb/serial/option.c index 39ed1f46cec0..ea126a4490cd 100644 --- a/trunk/drivers/usb/serial/option.c +++ b/trunk/drivers/usb/serial/option.c @@ -855,18 +855,6 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0083, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0086, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0087, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0088, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0089, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0090, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0091, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0092, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0093, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0094, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0095, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0096, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0097, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0098, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0099, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0104, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0105, 0xff, 0xff, 0xff) }, @@ -895,6 +883,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0151, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0152, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0153, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0154, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0155, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0156, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0157, 0xff, 0xff, 0xff) }, @@ -903,12 +892,6 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0160, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0161, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0162, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0164, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0165, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0168, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0170, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0176, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0178, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1012, 0xff, 0xff, 0xff) }, @@ -1083,116 +1066,6 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1298, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1299, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1300, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1401, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1402, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1403, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1404, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1405, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1406, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1407, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1408, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1409, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1410, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1411, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1412, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1413, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1414, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1415, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1416, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1417, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1418, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1419, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1420, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1421, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1422, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1423, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1424, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1425, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1426, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1427, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1428, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1429, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1430, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1431, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1432, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1433, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1434, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1435, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1436, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1437, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1438, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1439, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1440, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1441, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1442, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1443, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1444, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1445, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1446, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1447, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1448, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1449, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1450, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1451, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1452, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1453, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1454, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1455, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1456, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1457, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1458, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1459, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1460, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1461, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1462, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1463, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1464, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1465, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1466, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1467, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1468, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1469, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1470, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1471, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1472, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1473, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1474, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1475, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1476, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1477, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1478, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1479, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1480, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1481, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1482, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1483, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1484, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1485, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1486, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1487, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1488, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1489, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1490, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1491, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1492, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1493, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1494, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1495, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1496, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1497, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1498, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1499, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1500, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1501, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1502, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1503, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1504, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1505, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1506, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1507, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1508, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1509, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1510, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0014, 0xff, 0xff, 0xff) }, /* ZTE CDMA products */ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0027, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0059, 0xff, 0xff, 0xff) }, diff --git a/trunk/drivers/usb/serial/qcserial.c b/trunk/drivers/usb/serial/qcserial.c index f98800f2324c..1d5deee3be52 100644 --- a/trunk/drivers/usb/serial/qcserial.c +++ b/trunk/drivers/usb/serial/qcserial.c @@ -36,11 +36,6 @@ static const struct usb_device_id id_table[] = { {USB_DEVICE(0x413c, 0x8171)}, /* Dell Gobi QDL device */ {USB_DEVICE(0x1410, 0xa001)}, /* Novatel Gobi Modem device */ {USB_DEVICE(0x1410, 0xa008)}, /* Novatel Gobi QDL device */ - {USB_DEVICE(0x1410, 0xa010)}, /* Novatel Gobi QDL device */ - {USB_DEVICE(0x1410, 0xa011)}, /* Novatel Gobi QDL device */ - {USB_DEVICE(0x1410, 0xa012)}, /* Novatel Gobi QDL device */ - {USB_DEVICE(0x1410, 0xa013)}, /* Novatel Gobi QDL device */ - {USB_DEVICE(0x1410, 0xa014)}, /* Novatel Gobi QDL device */ {USB_DEVICE(0x0b05, 0x1776)}, /* Asus Gobi Modem device */ {USB_DEVICE(0x0b05, 0x1774)}, /* Asus Gobi QDL device */ {USB_DEVICE(0x19d2, 0xfff3)}, /* ONDA Gobi Modem device */ @@ -91,16 +86,7 @@ static const struct usb_device_id id_table[] = { {USB_DEVICE(0x16d8, 0x8002)}, /* CMDTech Gobi 2000 Modem device (VU922) */ {USB_DEVICE(0x05c6, 0x9204)}, /* Gobi 2000 QDL device */ {USB_DEVICE(0x05c6, 0x9205)}, /* Gobi 2000 Modem device */ - - {USB_DEVICE(0x05c6, 0x920c)}, /* Gobi 3000 QDL */ - {USB_DEVICE(0x05c6, 0x920d)}, /* Gobi 3000 Composite */ - {USB_DEVICE(0x1410, 0xa020)}, /* Novatel Gobi 3000 QDL */ - {USB_DEVICE(0x1410, 0xa021)}, /* Novatel Gobi 3000 Composite */ - {USB_DEVICE(0x413c, 0x8193)}, /* Dell Gobi 3000 QDL */ - {USB_DEVICE(0x413c, 0x8194)}, /* Dell Gobi 3000 Composite */ {USB_DEVICE(0x1199, 0x9013)}, /* Sierra Wireless Gobi 3000 Modem device (MC8355) */ - {USB_DEVICE(0x12D1, 0x14F0)}, /* Sony Gobi 3000 QDL */ - {USB_DEVICE(0x12D1, 0x14F1)}, /* Sony Gobi 3000 Composite */ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, id_table); @@ -137,6 +123,8 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) spin_lock_init(&data->susp_lock); + usb_enable_autosuspend(serial->dev); + switch (nintf) { case 1: /* QDL mode */ diff --git a/trunk/drivers/video/omap2/dss/dpi.c b/trunk/drivers/video/omap2/dss/dpi.c index faaf305fda27..395d658a94fc 100644 --- a/trunk/drivers/video/omap2/dss/dpi.c +++ b/trunk/drivers/video/omap2/dss/dpi.c @@ -180,11 +180,6 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev) { int r; - if (cpu_is_omap34xx() && !dpi.vdds_dsi_reg) { - DSSERR("no VDSS_DSI regulator\n"); - return -ENODEV; - } - if (dssdev->manager == NULL) { DSSERR("failed to enable display: no manager\n"); return -ENODEV; diff --git a/trunk/drivers/xen/cpu_hotplug.c b/trunk/drivers/xen/cpu_hotplug.c index 4dcfced107f5..14e2d995e958 100644 --- a/trunk/drivers/xen/cpu_hotplug.c +++ b/trunk/drivers/xen/cpu_hotplug.c @@ -30,8 +30,7 @@ static int vcpu_online(unsigned int cpu) sprintf(dir, "cpu/%u", cpu); err = xenbus_scanf(XBT_NIL, dir, "availability", "%s", state); if (err != 1) { - if (!xen_initial_domain()) - printk(KERN_ERR "XENBUS: Unable to read cpu state\n"); + printk(KERN_ERR "XENBUS: Unable to read cpu state\n"); return err; } diff --git a/trunk/drivers/xen/xen-pciback/pci_stub.c b/trunk/drivers/xen/xen-pciback/pci_stub.c index 19834d1c7c36..7944a17f5cbf 100644 --- a/trunk/drivers/xen/xen-pciback/pci_stub.c +++ b/trunk/drivers/xen/xen-pciback/pci_stub.c @@ -884,7 +884,7 @@ static inline int str_to_quirk(const char *buf, int *domain, int *bus, int int err; err = - sscanf(buf, " %04x:%02x:%02x.%d-%08x:%1x:%08x", domain, bus, slot, + sscanf(buf, " %04x:%02x:%02x.%1x-%08x:%1x:%08x", domain, bus, slot, func, reg, size, mask); if (err == 7) return 0; @@ -904,7 +904,7 @@ static int pcistub_device_id_add(int domain, int bus, int slot, int func) pci_dev_id->bus = bus; pci_dev_id->devfn = PCI_DEVFN(slot, func); - pr_debug(DRV_NAME ": wants to seize %04x:%02x:%02x.%d\n", + pr_debug(DRV_NAME ": wants to seize %04x:%02x:%02x.%01x\n", domain, bus, slot, func); spin_lock_irqsave(&device_ids_lock, flags); @@ -934,7 +934,7 @@ static int pcistub_device_id_remove(int domain, int bus, int slot, int func) err = 0; - pr_debug(DRV_NAME ": removed %04x:%02x:%02x.%d from " + pr_debug(DRV_NAME ": removed %04x:%02x:%02x.%01x from " "seize list\n", domain, bus, slot, func); } } @@ -1029,7 +1029,7 @@ static ssize_t pcistub_slot_show(struct device_driver *drv, char *buf) break; count += scnprintf(buf + count, PAGE_SIZE - count, - "%04x:%02x:%02x.%d\n", + "%04x:%02x:%02x.%01x\n", pci_dev_id->domain, pci_dev_id->bus, PCI_SLOT(pci_dev_id->devfn), PCI_FUNC(pci_dev_id->devfn)); diff --git a/trunk/drivers/xen/xen-pciback/xenbus.c b/trunk/drivers/xen/xen-pciback/xenbus.c index 64b11f99eacc..d5dcf8d5d3d9 100644 --- a/trunk/drivers/xen/xen-pciback/xenbus.c +++ b/trunk/drivers/xen/xen-pciback/xenbus.c @@ -206,7 +206,6 @@ static int xen_pcibk_publish_pci_dev(struct xen_pcibk_device *pdev, goto out; } - /* Note: The PV protocol uses %02x, don't change it */ err = xenbus_printf(XBT_NIL, pdev->xdev->nodename, str, "%04x:%02x:%02x.%02x", domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); @@ -230,7 +229,7 @@ static int xen_pcibk_export_device(struct xen_pcibk_device *pdev, err = -EINVAL; xenbus_dev_fatal(pdev->xdev, err, "Couldn't locate PCI device " - "(%04x:%02x:%02x.%d)! " + "(%04x:%02x:%02x.%01x)! " "perhaps already in-use?", domain, bus, slot, func); goto out; @@ -275,7 +274,7 @@ static int xen_pcibk_remove_device(struct xen_pcibk_device *pdev, if (!dev) { err = -EINVAL; dev_dbg(&pdev->xdev->dev, "Couldn't locate PCI device " - "(%04x:%02x:%02x.%d)! not owned by this domain\n", + "(%04x:%02x:%02x.%01x)! not owned by this domain\n", domain, bus, slot, func); goto out; } diff --git a/trunk/drivers/xen/xenbus/xenbus_dev_frontend.c b/trunk/drivers/xen/xenbus/xenbus_dev_frontend.c index 89f76252a16f..527dc2a3b89f 100644 --- a/trunk/drivers/xen/xenbus/xenbus_dev_frontend.c +++ b/trunk/drivers/xen/xenbus/xenbus_dev_frontend.c @@ -369,10 +369,6 @@ static int xenbus_write_watch(unsigned msg_type, struct xenbus_file_priv *u) goto out; } token++; - if (memchr(token, 0, u->u.msg.len - (token - path)) == NULL) { - rc = -EILSEQ; - goto out; - } if (msg_type == XS_WATCH) { watch = alloc_watch_adapter(path, token); diff --git a/trunk/fs/bio.c b/trunk/fs/bio.c index b980ecde026a..b1fe82cf88cf 100644 --- a/trunk/fs/bio.c +++ b/trunk/fs/bio.c @@ -505,9 +505,13 @@ EXPORT_SYMBOL(bio_clone); int bio_get_nr_vecs(struct block_device *bdev) { struct request_queue *q = bdev_get_queue(bdev); - return min_t(unsigned, - queue_max_segments(q), - queue_max_sectors(q) / (PAGE_SIZE >> 9) + 1); + int nr_pages; + + nr_pages = ((queue_max_sectors(q) << 9) + PAGE_SIZE - 1) >> PAGE_SHIFT; + if (nr_pages > queue_max_segments(q)) + nr_pages = queue_max_segments(q); + + return nr_pages; } EXPORT_SYMBOL(bio_get_nr_vecs); diff --git a/trunk/fs/cifs/connect.c b/trunk/fs/cifs/connect.c index 602f77c304c9..9c288653e6d6 100644 --- a/trunk/fs/cifs/connect.c +++ b/trunk/fs/cifs/connect.c @@ -773,11 +773,10 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) cifs_dump_mem("Bad SMB: ", buf, min_t(unsigned int, server->total_read, 48)); - if (!mid) - return length; + if (mid) + handle_mid(mid, server, smb_buffer, length); - handle_mid(mid, server, smb_buffer, length); - return 0; + return length; } static int @@ -2126,7 +2125,7 @@ cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses) down_read(&key->sem); upayload = key->payload.data; if (IS_ERR_OR_NULL(upayload)) { - rc = upayload ? PTR_ERR(upayload) : -EINVAL; + rc = PTR_ERR(key); goto out_key_put; } diff --git a/trunk/fs/cifs/dir.c b/trunk/fs/cifs/dir.c index 63a196b97d50..df8fecb5b993 100644 --- a/trunk/fs/cifs/dir.c +++ b/trunk/fs/cifs/dir.c @@ -492,7 +492,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, { int xid; int rc = 0; /* to get around spurious gcc warning, set to zero here */ - __u32 oplock = enable_oplocks ? REQ_OPLOCK : 0; + __u32 oplock = 0; __u16 fileHandle = 0; bool posix_open = false; struct cifs_sb_info *cifs_sb; diff --git a/trunk/fs/fs-writeback.c b/trunk/fs/fs-writeback.c index 5b4a9362d5aa..f855916657ba 100644 --- a/trunk/fs/fs-writeback.c +++ b/trunk/fs/fs-writeback.c @@ -52,6 +52,14 @@ struct wb_writeback_work { struct completion *done; /* set if the caller waits */ }; +/* + * Include the creation of the trace points after defining the + * wb_writeback_work structure so that the definition remains local to this + * file. + */ +#define CREATE_TRACE_POINTS +#include + /* * We don't actually have pdflush, but this one is exported though /proc... */ @@ -84,14 +92,6 @@ static inline struct inode *wb_inode(struct list_head *head) return list_entry(head, struct inode, i_wb_list); } -/* - * Include the creation of the trace points after defining the - * wb_writeback_work structure and inline functions so that the definition - * remains local to this file. - */ -#define CREATE_TRACE_POINTS -#include - /* Wakeup flusher thread or forker thread to fork it. Requires bdi->wb_lock. */ static void bdi_wakeup_flusher(struct backing_dev_info *bdi) { diff --git a/trunk/fs/ioprio.c b/trunk/fs/ioprio.c index 0f1b9515213b..f84b380d65e5 100644 --- a/trunk/fs/ioprio.c +++ b/trunk/fs/ioprio.c @@ -51,7 +51,7 @@ int set_task_ioprio(struct task_struct *task, int ioprio) ioc = get_task_io_context(task, GFP_ATOMIC, NUMA_NO_NODE); if (ioc) { ioc_ioprio_changed(ioc, ioprio); - put_io_context(ioc); + put_io_context(ioc, NULL); } return err; diff --git a/trunk/fs/xfs/kmem.h b/trunk/fs/xfs/kmem.h index ab7c53fe346e..292eff198030 100644 --- a/trunk/fs/xfs/kmem.h +++ b/trunk/fs/xfs/kmem.h @@ -110,4 +110,10 @@ kmem_zone_destroy(kmem_zone_t *zone) extern void *kmem_zone_alloc(kmem_zone_t *, unsigned int __nocast); extern void *kmem_zone_zalloc(kmem_zone_t *, unsigned int __nocast); +static inline int +kmem_shake_allow(gfp_t gfp_mask) +{ + return ((gfp_mask & __GFP_WAIT) && (gfp_mask & __GFP_FS)); +} + #endif /* __XFS_SUPPORT_KMEM_H__ */ diff --git a/trunk/fs/xfs/xfs_dquot.c b/trunk/fs/xfs/xfs_dquot.c index cbcb7bea38e2..b4ff40b5f918 100644 --- a/trunk/fs/xfs/xfs_dquot.c +++ b/trunk/fs/xfs/xfs_dquot.c @@ -62,6 +62,82 @@ int xfs_dqerror_mod = 33; static struct lock_class_key xfs_dquot_other_class; +/* + * Allocate and initialize a dquot. We don't always allocate fresh memory; + * we try to reclaim a free dquot if the number of incore dquots are above + * a threshold. + * The only field inside the core that gets initialized at this point + * is the d_id field. The idea is to fill in the entire q_core + * when we read in the on disk dquot. + */ +STATIC xfs_dquot_t * +xfs_qm_dqinit( + xfs_mount_t *mp, + xfs_dqid_t id, + uint type) +{ + xfs_dquot_t *dqp; + boolean_t brandnewdquot; + + brandnewdquot = xfs_qm_dqalloc_incore(&dqp); + dqp->dq_flags = type; + dqp->q_core.d_id = cpu_to_be32(id); + dqp->q_mount = mp; + + /* + * No need to re-initialize these if this is a reclaimed dquot. + */ + if (brandnewdquot) { + INIT_LIST_HEAD(&dqp->q_freelist); + mutex_init(&dqp->q_qlock); + init_waitqueue_head(&dqp->q_pinwait); + + /* + * Because we want to use a counting completion, complete + * the flush completion once to allow a single access to + * the flush completion without blocking. + */ + init_completion(&dqp->q_flush); + complete(&dqp->q_flush); + + trace_xfs_dqinit(dqp); + } else { + /* + * Only the q_core portion was zeroed in dqreclaim_one(). + * So, we need to reset others. + */ + dqp->q_nrefs = 0; + dqp->q_blkno = 0; + INIT_LIST_HEAD(&dqp->q_mplist); + INIT_LIST_HEAD(&dqp->q_hashlist); + dqp->q_bufoffset = 0; + dqp->q_fileoffset = 0; + dqp->q_transp = NULL; + dqp->q_gdquot = NULL; + dqp->q_res_bcount = 0; + dqp->q_res_icount = 0; + dqp->q_res_rtbcount = 0; + atomic_set(&dqp->q_pincount, 0); + dqp->q_hash = NULL; + ASSERT(list_empty(&dqp->q_freelist)); + + trace_xfs_dqreuse(dqp); + } + + /* + * In either case we need to make sure group quotas have a different + * lock class than user quotas, to make sure lockdep knows we can + * locks of one of each at the same time. + */ + if (!(type & XFS_DQ_USER)) + lockdep_set_class(&dqp->q_qlock, &xfs_dquot_other_class); + + /* + * log item gets initialized later + */ + return (dqp); +} + /* * This is called to free all the memory associated with a dquot */ @@ -491,32 +567,7 @@ xfs_qm_dqread( int error; int cancelflags = 0; - - dqp = kmem_zone_zalloc(xfs_Gqm->qm_dqzone, KM_SLEEP); - - dqp->dq_flags = type; - dqp->q_core.d_id = cpu_to_be32(id); - dqp->q_mount = mp; - INIT_LIST_HEAD(&dqp->q_freelist); - mutex_init(&dqp->q_qlock); - init_waitqueue_head(&dqp->q_pinwait); - - /* - * Because we want to use a counting completion, complete - * the flush completion once to allow a single access to - * the flush completion without blocking. - */ - init_completion(&dqp->q_flush); - complete(&dqp->q_flush); - - /* - * Make sure group quotas have a different lock class than user - * quotas. - */ - if (!(type & XFS_DQ_USER)) - lockdep_set_class(&dqp->q_qlock, &xfs_dquot_other_class); - - atomic_inc(&xfs_Gqm->qm_totaldquots); + dqp = xfs_qm_dqinit(mp, id, type); trace_xfs_dqread(dqp); diff --git a/trunk/fs/xfs/xfs_log_recover.c b/trunk/fs/xfs/xfs_log_recover.c index 15ff5392fb65..541a508adea1 100644 --- a/trunk/fs/xfs/xfs_log_recover.c +++ b/trunk/fs/xfs/xfs_log_recover.c @@ -1489,7 +1489,7 @@ xlog_recover_add_to_cont_trans( old_ptr = item->ri_buf[item->ri_cnt-1].i_addr; old_len = item->ri_buf[item->ri_cnt-1].i_len; - ptr = kmem_realloc(old_ptr, len+old_len, old_len, KM_SLEEP); + ptr = kmem_realloc(old_ptr, len+old_len, old_len, 0u); memcpy(&ptr[old_len], dp, len); /* d, s, l */ item->ri_buf[item->ri_cnt-1].i_len += len; item->ri_buf[item->ri_cnt-1].i_addr = ptr; diff --git a/trunk/fs/xfs/xfs_qm.c b/trunk/fs/xfs/xfs_qm.c index c436def733bf..671f37eae1c7 100644 --- a/trunk/fs/xfs/xfs_qm.c +++ b/trunk/fs/xfs/xfs_qm.c @@ -50,6 +50,7 @@ */ struct mutex xfs_Gqm_lock; struct xfs_qm *xfs_Gqm; +uint ndquot; kmem_zone_t *qm_dqzone; kmem_zone_t *qm_dqtrxzone; @@ -92,6 +93,7 @@ xfs_Gqm_init(void) goto out_free_udqhash; hsize /= sizeof(xfs_dqhash_t); + ndquot = hsize << 8; xqm = kmem_zalloc(sizeof(xfs_qm_t), KM_SLEEP); xqm->qm_dqhashmask = hsize - 1; @@ -135,6 +137,7 @@ xfs_Gqm_init(void) xqm->qm_dqtrxzone = qm_dqtrxzone; atomic_set(&xqm->qm_totaldquots, 0); + xqm->qm_dqfree_ratio = XFS_QM_DQFREE_RATIO; xqm->qm_nrefs = 0; return xqm; @@ -1597,150 +1600,216 @@ xfs_qm_init_quotainos( return 0; } -STATIC void -xfs_qm_dqfree_one( - struct xfs_dquot *dqp) -{ - struct xfs_mount *mp = dqp->q_mount; - struct xfs_quotainfo *qi = mp->m_quotainfo; - mutex_lock(&dqp->q_hash->qh_lock); - list_del_init(&dqp->q_hashlist); - dqp->q_hash->qh_version++; - mutex_unlock(&dqp->q_hash->qh_lock); - mutex_lock(&qi->qi_dqlist_lock); - list_del_init(&dqp->q_mplist); - qi->qi_dquots--; - qi->qi_dqreclaims++; - mutex_unlock(&qi->qi_dqlist_lock); - - xfs_qm_dqdestroy(dqp); -} - -STATIC void -xfs_qm_dqreclaim_one( - struct xfs_dquot *dqp, - struct list_head *dispose_list) +/* + * Pop the least recently used dquot off the freelist and recycle it. + */ +STATIC struct xfs_dquot * +xfs_qm_dqreclaim_one(void) { - struct xfs_mount *mp = dqp->q_mount; - int error; - - if (!xfs_dqlock_nowait(dqp)) - goto out_busy; - - /* - * This dquot has acquired a reference in the meantime remove it from - * the freelist and try again. - */ - if (dqp->q_nrefs) { - xfs_dqunlock(dqp); + struct xfs_dquot *dqp; + int restarts = 0; - trace_xfs_dqreclaim_want(dqp); - XQM_STATS_INC(xqmstats.xs_qm_dqwants); + mutex_lock(&xfs_Gqm->qm_dqfrlist_lock); +restart: + list_for_each_entry(dqp, &xfs_Gqm->qm_dqfrlist, q_freelist) { + struct xfs_mount *mp = dqp->q_mount; - list_del_init(&dqp->q_freelist); - xfs_Gqm->qm_dqfrlist_cnt--; - return; - } + if (!xfs_dqlock_nowait(dqp)) + continue; - ASSERT(dqp->q_hash); - ASSERT(!list_empty(&dqp->q_mplist)); + /* + * This dquot has already been grabbed by dqlookup. + * Remove it from the freelist and try again. + */ + if (dqp->q_nrefs) { + trace_xfs_dqreclaim_want(dqp); + XQM_STATS_INC(xqmstats.xs_qm_dqwants); + + list_del_init(&dqp->q_freelist); + xfs_Gqm->qm_dqfrlist_cnt--; + restarts++; + goto dqunlock; + } - /* - * Try to grab the flush lock. If this dquot is in the process of - * getting flushed to disk, we don't want to reclaim it. - */ - if (!xfs_dqflock_nowait(dqp)) - goto out_busy; + ASSERT(dqp->q_hash); + ASSERT(!list_empty(&dqp->q_mplist)); - /* - * We have the flush lock so we know that this is not in the - * process of being flushed. So, if this is dirty, flush it - * DELWRI so that we don't get a freelist infested with - * dirty dquots. - */ - if (XFS_DQ_IS_DIRTY(dqp)) { - trace_xfs_dqreclaim_dirty(dqp); + /* + * Try to grab the flush lock. If this dquot is in the process + * of getting flushed to disk, we don't want to reclaim it. + */ + if (!xfs_dqflock_nowait(dqp)) + goto dqunlock; /* - * We flush it delayed write, so don't bother releasing the - * freelist lock. + * We have the flush lock so we know that this is not in the + * process of being flushed. So, if this is dirty, flush it + * DELWRI so that we don't get a freelist infested with + * dirty dquots. */ - error = xfs_qm_dqflush(dqp, 0); - if (error) { - xfs_warn(mp, "%s: dquot %p flush failed", - __func__, dqp); + if (XFS_DQ_IS_DIRTY(dqp)) { + int error; + + trace_xfs_dqreclaim_dirty(dqp); + + /* + * We flush it delayed write, so don't bother + * releasing the freelist lock. + */ + error = xfs_qm_dqflush(dqp, SYNC_TRYLOCK); + if (error) { + xfs_warn(mp, "%s: dquot %p flush failed", + __func__, dqp); + } + goto dqunlock; } + xfs_dqfunlock(dqp); /* - * Give the dquot another try on the freelist, as the - * flushing will take some time. + * Prevent lookup now that we are going to reclaim the dquot. + * Once XFS_DQ_FREEING is set lookup won't touch the dquot, + * thus we can drop the lock now. */ - goto out_busy; - } - xfs_dqfunlock(dqp); + dqp->dq_flags |= XFS_DQ_FREEING; + xfs_dqunlock(dqp); - /* - * Prevent lookups now that we are past the point of no return. - */ - dqp->dq_flags |= XFS_DQ_FREEING; - xfs_dqunlock(dqp); + mutex_lock(&dqp->q_hash->qh_lock); + list_del_init(&dqp->q_hashlist); + dqp->q_hash->qh_version++; + mutex_unlock(&dqp->q_hash->qh_lock); - ASSERT(dqp->q_nrefs == 0); - list_move_tail(&dqp->q_freelist, dispose_list); - xfs_Gqm->qm_dqfrlist_cnt--; + mutex_lock(&mp->m_quotainfo->qi_dqlist_lock); + list_del_init(&dqp->q_mplist); + mp->m_quotainfo->qi_dquots--; + mp->m_quotainfo->qi_dqreclaims++; + mutex_unlock(&mp->m_quotainfo->qi_dqlist_lock); - trace_xfs_dqreclaim_done(dqp); - XQM_STATS_INC(xqmstats.xs_qm_dqreclaims); - return; + ASSERT(dqp->q_nrefs == 0); + list_del_init(&dqp->q_freelist); + xfs_Gqm->qm_dqfrlist_cnt--; -out_busy: - xfs_dqunlock(dqp); + mutex_unlock(&xfs_Gqm->qm_dqfrlist_lock); + return dqp; +dqunlock: + xfs_dqunlock(dqp); + if (restarts >= XFS_QM_RECLAIM_MAX_RESTARTS) + break; + goto restart; + } - /* - * Move the dquot to the tail of the list so that we don't spin on it. - */ - list_move_tail(&dqp->q_freelist, &xfs_Gqm->qm_dqfrlist); + mutex_unlock(&xfs_Gqm->qm_dqfrlist_lock); + return NULL; +} - trace_xfs_dqreclaim_busy(dqp); - XQM_STATS_INC(xqmstats.xs_qm_dqreclaim_misses); +/* + * Traverse the freelist of dquots and attempt to reclaim a maximum of + * 'howmany' dquots. This operation races with dqlookup(), and attempts to + * favor the lookup function ... + */ +STATIC int +xfs_qm_shake_freelist( + int howmany) +{ + int nreclaimed = 0; + xfs_dquot_t *dqp; + + if (howmany <= 0) + return 0; + + while (nreclaimed < howmany) { + dqp = xfs_qm_dqreclaim_one(); + if (!dqp) + return nreclaimed; + xfs_qm_dqdestroy(dqp); + nreclaimed++; + } + return nreclaimed; } +/* + * The kmem_shake interface is invoked when memory is running low. + */ +/* ARGSUSED */ STATIC int xfs_qm_shake( - struct shrinker *shrink, - struct shrink_control *sc) + struct shrinker *shrink, + struct shrink_control *sc) { - int nr_to_scan = sc->nr_to_scan; - LIST_HEAD (dispose_list); - struct xfs_dquot *dqp; + int ndqused, nfree, n; + gfp_t gfp_mask = sc->gfp_mask; - if ((sc->gfp_mask & (__GFP_FS|__GFP_WAIT)) != (__GFP_FS|__GFP_WAIT)) + if (!kmem_shake_allow(gfp_mask)) + return 0; + if (!xfs_Gqm) return 0; - if (!nr_to_scan) - goto out; - mutex_lock(&xfs_Gqm->qm_dqfrlist_lock); - while (!list_empty(&xfs_Gqm->qm_dqfrlist)) { - if (nr_to_scan-- <= 0) - break; - dqp = list_first_entry(&xfs_Gqm->qm_dqfrlist, struct xfs_dquot, - q_freelist); - xfs_qm_dqreclaim_one(dqp, &dispose_list); - } - mutex_unlock(&xfs_Gqm->qm_dqfrlist_lock); + nfree = xfs_Gqm->qm_dqfrlist_cnt; /* free dquots */ + /* incore dquots in all f/s's */ + ndqused = atomic_read(&xfs_Gqm->qm_totaldquots) - nfree; - while (!list_empty(&dispose_list)) { - dqp = list_first_entry(&dispose_list, struct xfs_dquot, - q_freelist); - list_del_init(&dqp->q_freelist); - xfs_qm_dqfree_one(dqp); + ASSERT(ndqused >= 0); + + if (nfree <= ndqused && nfree < ndquot) + return 0; + + ndqused *= xfs_Gqm->qm_dqfree_ratio; /* target # of free dquots */ + n = nfree - ndqused - ndquot; /* # over target */ + + return xfs_qm_shake_freelist(MAX(nfree, n)); +} + + +/*------------------------------------------------------------------*/ + +/* + * Return a new incore dquot. Depending on the number of + * dquots in the system, we either allocate a new one on the kernel heap, + * or reclaim a free one. + * Return value is B_TRUE if we allocated a new dquot, B_FALSE if we managed + * to reclaim an existing one from the freelist. + */ +boolean_t +xfs_qm_dqalloc_incore( + xfs_dquot_t **O_dqpp) +{ + xfs_dquot_t *dqp; + + /* + * Check against high water mark to see if we want to pop + * a nincompoop dquot off the freelist. + */ + if (atomic_read(&xfs_Gqm->qm_totaldquots) >= ndquot) { + /* + * Try to recycle a dquot from the freelist. + */ + if ((dqp = xfs_qm_dqreclaim_one())) { + XQM_STATS_INC(xqmstats.xs_qm_dqreclaims); + /* + * Just zero the core here. The rest will get + * reinitialized by caller. XXX we shouldn't even + * do this zero ... + */ + memset(&dqp->q_core, 0, sizeof(dqp->q_core)); + *O_dqpp = dqp; + return B_FALSE; + } + XQM_STATS_INC(xqmstats.xs_qm_dqreclaim_misses); } -out: - return (xfs_Gqm->qm_dqfrlist_cnt / 100) * sysctl_vfs_cache_pressure; + + /* + * Allocate a brand new dquot on the kernel heap and return it + * to the caller to initialize. + */ + ASSERT(xfs_Gqm->qm_dqzone != NULL); + *O_dqpp = kmem_zone_zalloc(xfs_Gqm->qm_dqzone, KM_SLEEP); + atomic_inc(&xfs_Gqm->qm_totaldquots); + + return B_TRUE; } + /* * Start a transaction and write the incore superblock changes to * disk. flags parameter indicates which fields have changed. diff --git a/trunk/fs/xfs/xfs_qm.h b/trunk/fs/xfs/xfs_qm.h index 9a9b997e1a0a..9b4f3adefbc5 100644 --- a/trunk/fs/xfs/xfs_qm.h +++ b/trunk/fs/xfs/xfs_qm.h @@ -26,11 +26,23 @@ struct xfs_qm; struct xfs_inode; +extern uint ndquot; extern struct mutex xfs_Gqm_lock; extern struct xfs_qm *xfs_Gqm; extern kmem_zone_t *qm_dqzone; extern kmem_zone_t *qm_dqtrxzone; +/* + * Ditto, for xfs_qm_dqreclaim_one. + */ +#define XFS_QM_RECLAIM_MAX_RESTARTS 4 + +/* + * Ideal ratio of free to in use dquots. Quota manager makes an attempt + * to keep this balance. + */ +#define XFS_QM_DQFREE_RATIO 2 + /* * Dquot hashtable constants/threshold values. */ @@ -62,6 +74,7 @@ typedef struct xfs_qm { int qm_dqfrlist_cnt; atomic_t qm_totaldquots; /* total incore dquots */ uint qm_nrefs; /* file systems with quota on */ + int qm_dqfree_ratio;/* ratio of free to inuse dquots */ kmem_zone_t *qm_dqzone; /* dquot mem-alloc zone */ kmem_zone_t *qm_dqtrxzone; /* t_dqinfo of transactions */ } xfs_qm_t; @@ -130,6 +143,7 @@ extern int xfs_qm_quotacheck(xfs_mount_t *); extern int xfs_qm_write_sb_changes(xfs_mount_t *, __int64_t); /* dquot stuff */ +extern boolean_t xfs_qm_dqalloc_incore(xfs_dquot_t **); extern int xfs_qm_dqpurge_all(xfs_mount_t *, uint); extern void xfs_qm_dqrele_all_inodes(xfs_mount_t *, uint); diff --git a/trunk/fs/xfs/xfs_qm_stats.c b/trunk/fs/xfs/xfs_qm_stats.c index 5729ba570877..8671a0b32644 100644 --- a/trunk/fs/xfs/xfs_qm_stats.c +++ b/trunk/fs/xfs/xfs_qm_stats.c @@ -42,9 +42,9 @@ static int xqm_proc_show(struct seq_file *m, void *v) { /* maximum; incore; ratio free to inuse; freelist */ seq_printf(m, "%d\t%d\t%d\t%u\n", - 0, + ndquot, xfs_Gqm? atomic_read(&xfs_Gqm->qm_totaldquots) : 0, - 0, + xfs_Gqm? xfs_Gqm->qm_dqfree_ratio : 0, xfs_Gqm? xfs_Gqm->qm_dqfrlist_cnt : 0); return 0; } diff --git a/trunk/fs/xfs/xfs_trace.h b/trunk/fs/xfs/xfs_trace.h index bb134a819930..6b6df5802e95 100644 --- a/trunk/fs/xfs/xfs_trace.h +++ b/trunk/fs/xfs/xfs_trace.h @@ -733,10 +733,11 @@ DEFINE_EVENT(xfs_dquot_class, name, \ DEFINE_DQUOT_EVENT(xfs_dqadjust); DEFINE_DQUOT_EVENT(xfs_dqreclaim_want); DEFINE_DQUOT_EVENT(xfs_dqreclaim_dirty); -DEFINE_DQUOT_EVENT(xfs_dqreclaim_busy); -DEFINE_DQUOT_EVENT(xfs_dqreclaim_done); +DEFINE_DQUOT_EVENT(xfs_dqreclaim_unlink); DEFINE_DQUOT_EVENT(xfs_dqattach_found); DEFINE_DQUOT_EVENT(xfs_dqattach_get); +DEFINE_DQUOT_EVENT(xfs_dqinit); +DEFINE_DQUOT_EVENT(xfs_dqreuse); DEFINE_DQUOT_EVENT(xfs_dqalloc); DEFINE_DQUOT_EVENT(xfs_dqtobp_read); DEFINE_DQUOT_EVENT(xfs_dqread); diff --git a/trunk/include/linux/blkdev.h b/trunk/include/linux/blkdev.h index 606cf339bb56..6c6a1f008065 100644 --- a/trunk/include/linux/blkdev.h +++ b/trunk/include/linux/blkdev.h @@ -399,6 +399,9 @@ struct request_queue { /* Throttle data */ struct throtl_data *td; #endif +#ifdef CONFIG_LOCKDEP + int ioc_release_depth; +#endif }; #define QUEUE_FLAG_QUEUED 1 /* uses generic tag queueing */ diff --git a/trunk/include/linux/cdrom.h b/trunk/include/linux/cdrom.h index 7c48029dffe6..35eae4b67503 100644 --- a/trunk/include/linux/cdrom.h +++ b/trunk/include/linux/cdrom.h @@ -952,8 +952,7 @@ struct cdrom_device_info { char name[20]; /* name of the device type */ /* per-device flags */ __u8 sanyo_slot : 2; /* Sanyo 3 CD changer support */ - __u8 keeplocked : 1; /* CDROM_LOCKDOOR status */ - __u8 reserved : 5; /* not used yet */ + __u8 reserved : 6; /* not used yet */ int cdda_method; /* see flags */ __u8 last_sense; __u8 media_written; /* dirty flag, DVD+RW bookkeeping */ diff --git a/trunk/include/linux/elevator.h b/trunk/include/linux/elevator.h index 7d4e0356f329..c24f3d7fbf1e 100644 --- a/trunk/include/linux/elevator.h +++ b/trunk/include/linux/elevator.h @@ -42,6 +42,12 @@ struct elevator_ops elevator_merged_fn *elevator_merged_fn; elevator_merge_req_fn *elevator_merge_req_fn; elevator_allow_merge_fn *elevator_allow_merge_fn; + + /* + * Used for both plugged list and elevator merging and in the + * former case called without queue_lock. Read comment on top of + * attempt_plug_merge() for details. + */ elevator_bio_merged_fn *elevator_bio_merged_fn; elevator_dispatch_fn *elevator_dispatch_fn; @@ -116,6 +122,7 @@ extern void elv_dispatch_add_tail(struct request_queue *, struct request *); extern void elv_add_request(struct request_queue *, struct request *, int); extern void __elv_add_request(struct request_queue *, struct request *, int); extern int elv_merge(struct request_queue *, struct request **, struct bio *); +extern int elv_try_merge(struct request *, struct bio *); extern void elv_merge_requests(struct request_queue *, struct request *, struct request *); extern void elv_merged_request(struct request_queue *, struct request *, int); @@ -148,7 +155,7 @@ extern ssize_t elv_iosched_store(struct request_queue *, const char *, size_t); extern int elevator_init(struct request_queue *, char *); extern void elevator_exit(struct elevator_queue *); extern int elevator_change(struct request_queue *, const char *); -extern bool elv_rq_merge_ok(struct request *, struct bio *); +extern int elv_rq_merge_ok(struct request *, struct bio *); /* * Helper functions. diff --git a/trunk/include/linux/hyperv.h b/trunk/include/linux/hyperv.h index 0ae065a5fcb2..62b908e0e591 100644 --- a/trunk/include/linux/hyperv.h +++ b/trunk/include/linux/hyperv.h @@ -35,7 +35,7 @@ #include -#define MAX_PAGE_BUFFER_COUNT 19 +#define MAX_PAGE_BUFFER_COUNT 18 #define MAX_MULTIPAGE_BUFFER_COUNT 32 /* 128K */ #pragma pack(push, 1) diff --git a/trunk/include/linux/iocontext.h b/trunk/include/linux/iocontext.h index 119773eebe31..7e1371c4bccf 100644 --- a/trunk/include/linux/iocontext.h +++ b/trunk/include/linux/iocontext.h @@ -133,7 +133,7 @@ static inline struct io_context *ioc_task_link(struct io_context *ioc) struct task_struct; #ifdef CONFIG_BLOCK -void put_io_context(struct io_context *ioc); +void put_io_context(struct io_context *ioc, struct request_queue *locked_q); void exit_io_context(struct task_struct *task); struct io_context *get_task_io_context(struct task_struct *task, gfp_t gfp_flags, int node); @@ -141,7 +141,8 @@ void ioc_ioprio_changed(struct io_context *ioc, int ioprio); void ioc_cgroup_changed(struct io_context *ioc); #else struct io_context; -static inline void put_io_context(struct io_context *ioc) { } +static inline void put_io_context(struct io_context *ioc, + struct request_queue *locked_q) { } static inline void exit_io_context(struct task_struct *task) { } #endif diff --git a/trunk/include/linux/mmc/card.h b/trunk/include/linux/mmc/card.h index 19a41d1737af..9f22ba572de0 100644 --- a/trunk/include/linux/mmc/card.h +++ b/trunk/include/linux/mmc/card.h @@ -217,7 +217,6 @@ struct mmc_card { #define MMC_CARD_SDXC (1<<6) /* card is SDXC */ #define MMC_CARD_REMOVED (1<<7) /* card has been removed */ #define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in HS200 mode */ -#define MMC_STATE_SLEEP (1<<9) /* card is in sleep state */ unsigned int quirks; /* card quirks */ #define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */ #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */ @@ -383,7 +382,6 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED) #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC) #define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED)) -#define mmc_card_is_sleep(c) ((c)->state & MMC_STATE_SLEEP) #define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT) #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) @@ -395,9 +393,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED) #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC) #define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED) -#define mmc_card_set_sleep(c) ((c)->state |= MMC_STATE_SLEEP) -#define mmc_card_clr_sleep(c) ((c)->state &= ~MMC_STATE_SLEEP) /* * Quirk add/remove for MMC products. */ diff --git a/trunk/include/linux/mmc/dw_mmc.h b/trunk/include/linux/mmc/dw_mmc.h index aae5d1f1bb39..e8779c6d1759 100644 --- a/trunk/include/linux/mmc/dw_mmc.h +++ b/trunk/include/linux/mmc/dw_mmc.h @@ -14,8 +14,6 @@ #ifndef LINUX_MMC_DW_MMC_H #define LINUX_MMC_DW_MMC_H -#include - #define MAX_MCI_SLOTS 2 enum dw_mci_state { @@ -42,7 +40,7 @@ struct mmc_data; * @lock: Spinlock protecting the queue and associated data. * @regs: Pointer to MMIO registers. * @sg: Scatterlist entry currently being processed by PIO code, if any. - * @sg_miter: PIO mapping scatterlist iterator. + * @pio_offset: Offset into the current scatterlist entry. * @cur_slot: The slot which is currently using the controller. * @mrq: The request currently being processed on @cur_slot, * or NULL if the controller is idle. @@ -117,7 +115,7 @@ struct dw_mci { void __iomem *regs; struct scatterlist *sg; - struct sg_mapping_iter sg_miter; + unsigned int pio_offset; struct dw_mci_slot *cur_slot; struct mmc_request *mrq; diff --git a/trunk/include/linux/mmc/host.h b/trunk/include/linux/mmc/host.h index ee2b0363c040..0beba1e5e1ed 100644 --- a/trunk/include/linux/mmc/host.h +++ b/trunk/include/linux/mmc/host.h @@ -257,7 +257,6 @@ struct mmc_host { #define MMC_CAP2_HS200_1_2V_SDR (1 << 6) /* can support */ #define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | \ MMC_CAP2_HS200_1_2V_SDR) -#define MMC_CAP2_BROKEN_VOLTAGE (1 << 7) /* Use the broken voltage */ mmc_pm_flag_t pm_caps; /* supported pm features */ unsigned int power_notify_type; @@ -445,23 +444,4 @@ static inline int mmc_boot_partition_access(struct mmc_host *host) return !(host->caps2 & MMC_CAP2_BOOTPART_NOACC); } -#ifdef CONFIG_MMC_CLKGATE -void mmc_host_clk_hold(struct mmc_host *host); -void mmc_host_clk_release(struct mmc_host *host); -unsigned int mmc_host_clk_rate(struct mmc_host *host); - -#else -static inline void mmc_host_clk_hold(struct mmc_host *host) -{ -} - -static inline void mmc_host_clk_release(struct mmc_host *host) -{ -} - -static inline unsigned int mmc_host_clk_rate(struct mmc_host *host) -{ - return host->ios.clock; -} -#endif #endif /* LINUX_MMC_HOST_H */ diff --git a/trunk/include/linux/proportions.h b/trunk/include/linux/proportions.h index 26a8a4ed9b07..ef35bb73f69b 100644 --- a/trunk/include/linux/proportions.h +++ b/trunk/include/linux/proportions.h @@ -81,11 +81,7 @@ void prop_inc_percpu(struct prop_descriptor *pd, struct prop_local_percpu *pl) * Limit the time part in order to ensure there are some bits left for the * cycle counter and fraction multiply. */ -#if BITS_PER_LONG == 32 #define PROP_MAX_SHIFT (3*BITS_PER_LONG/4) -#else -#define PROP_MAX_SHIFT (BITS_PER_LONG/2) -#endif #define PROP_FRAC_SHIFT (BITS_PER_LONG - PROP_MAX_SHIFT - 1) #define PROP_FRAC_BASE (1UL << PROP_FRAC_SHIFT) diff --git a/trunk/include/linux/usb/ch9.h b/trunk/include/linux/usb/ch9.h index 3b6f628880f8..61b29057b054 100644 --- a/trunk/include/linux/usb/ch9.h +++ b/trunk/include/linux/usb/ch9.h @@ -589,7 +589,7 @@ static inline int usb_endpoint_is_isoc_out( */ static inline int usb_endpoint_maxp(const struct usb_endpoint_descriptor *epd) { - return __le16_to_cpu(epd->wMaxPacketSize); + return le16_to_cpu(epd->wMaxPacketSize); } /*-------------------------------------------------------------------------*/ diff --git a/trunk/include/net/flow.h b/trunk/include/net/flow.h index 6c469dbdb917..9b582437fbea 100644 --- a/trunk/include/net/flow.h +++ b/trunk/include/net/flow.h @@ -93,16 +93,6 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif, fl4->fl4_dport = dport; fl4->fl4_sport = sport; } - -/* Reset some input parameters after previous lookup */ -static inline void flowi4_update_output(struct flowi4 *fl4, int oif, __u8 tos, - __be32 daddr, __be32 saddr) -{ - fl4->flowi4_oif = oif; - fl4->flowi4_tos = tos; - fl4->daddr = daddr; - fl4->saddr = saddr; -} struct flowi6 { diff --git a/trunk/include/net/netprio_cgroup.h b/trunk/include/net/netprio_cgroup.h index d58fdec47597..7b2d43139c8e 100644 --- a/trunk/include/net/netprio_cgroup.h +++ b/trunk/include/net/netprio_cgroup.h @@ -37,51 +37,19 @@ extern int net_prio_subsys_id; extern void sock_update_netprioidx(struct sock *sk); -#if IS_BUILTIN(CONFIG_NETPRIO_CGROUP) - -static inline u32 task_netprioidx(struct task_struct *p) +static inline struct cgroup_netprio_state + *task_netprio_state(struct task_struct *p) { - struct cgroup_netprio_state *state; - u32 idx; - - rcu_read_lock(); - state = container_of(task_subsys_state(p, net_prio_subsys_id), - struct cgroup_netprio_state, css); - idx = state->prioidx; - rcu_read_unlock(); - return idx; -} - -#elif IS_MODULE(CONFIG_NETPRIO_CGROUP) - -static inline u32 task_netprioidx(struct task_struct *p) -{ - struct cgroup_netprio_state *state; - int subsys_id; - u32 idx = 0; - - rcu_read_lock(); - subsys_id = rcu_dereference_index_check(net_prio_subsys_id, - rcu_read_lock_held()); - if (subsys_id >= 0) { - state = container_of(task_subsys_state(p, subsys_id), - struct cgroup_netprio_state, css); - idx = state->prioidx; - } - rcu_read_unlock(); - return idx; -} - +#if IS_ENABLED(CONFIG_NETPRIO_CGROUP) + return container_of(task_subsys_state(p, net_prio_subsys_id), + struct cgroup_netprio_state, css); #else - -static inline u32 task_netprioidx(struct task_struct *p) -{ - return 0; + return NULL; +#endif } -#endif /* CONFIG_NETPRIO_CGROUP */ - #else + #define sock_update_netprioidx(sk) #endif diff --git a/trunk/include/net/route.h b/trunk/include/net/route.h index b1c0d5b564c2..91855d185b53 100644 --- a/trunk/include/net/route.h +++ b/trunk/include/net/route.h @@ -270,7 +270,6 @@ static inline struct rtable *ip_route_connect(struct flowi4 *fl4, if (IS_ERR(rt)) return rt; ip_rt_put(rt); - flowi4_update_output(fl4, oif, tos, fl4->daddr, fl4->saddr); } security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); return ip_route_output_flow(net, fl4, sk); @@ -285,9 +284,6 @@ static inline struct rtable *ip_route_newports(struct flowi4 *fl4, struct rtable fl4->fl4_dport = dport; fl4->fl4_sport = sport; ip_rt_put(rt); - flowi4_update_output(fl4, sk->sk_bound_dev_if, - RT_CONN_FLAGS(sk), fl4->daddr, - fl4->saddr); security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); return ip_route_output_flow(sock_net(sk), fl4, sk); } diff --git a/trunk/include/net/sch_generic.h b/trunk/include/net/sch_generic.h index 55ce96b53b09..f6bb08b73ca4 100644 --- a/trunk/include/net/sch_generic.h +++ b/trunk/include/net/sch_generic.h @@ -220,16 +220,9 @@ struct tcf_proto { struct qdisc_skb_cb { unsigned int pkt_len; - unsigned char data[24]; + long data[]; }; -static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz) -{ - struct qdisc_skb_cb *qcb; - BUILD_BUG_ON(sizeof(skb->cb) < sizeof(unsigned int) + sz); - BUILD_BUG_ON(sizeof(qcb->data) < sz); -} - static inline int qdisc_qlen(const struct Qdisc *q) { return q->q.qlen; diff --git a/trunk/include/net/tcp.h b/trunk/include/net/tcp.h index 42c29bfbcee3..d49db0113a06 100644 --- a/trunk/include/net/tcp.h +++ b/trunk/include/net/tcp.h @@ -273,14 +273,6 @@ static inline int between(__u32 seq1, __u32 seq2, __u32 seq3) return seq3 - seq2 >= seq1 - seq2; } -static inline bool tcp_out_of_memory(struct sock *sk) -{ - if (sk->sk_wmem_queued > SOCK_MIN_SNDBUF && - sk_memory_allocated(sk) > sk_prot_mem_limits(sk, 2)) - return true; - return false; -} - static inline bool tcp_too_many_orphans(struct sock *sk, int shift) { struct percpu_counter *ocp = sk->sk_prot->orphan_count; @@ -291,11 +283,13 @@ static inline bool tcp_too_many_orphans(struct sock *sk, int shift) if (orphans << shift > sysctl_tcp_max_orphans) return true; } + + if (sk->sk_wmem_queued > SOCK_MIN_SNDBUF && + sk_memory_allocated(sk) > sk_prot_mem_limits(sk, 2)) + return true; return false; } -extern bool tcp_check_oom(struct sock *sk, int shift); - /* syncookies: remember time of last synqueue overflow */ static inline void tcp_synq_overflow(struct sock *sk) { diff --git a/trunk/include/trace/events/writeback.h b/trunk/include/trace/events/writeback.h index 5973410e8f8c..8588a8918023 100644 --- a/trunk/include/trace/events/writeback.h +++ b/trunk/include/trace/events/writeback.h @@ -47,10 +47,7 @@ DECLARE_EVENT_CLASS(writeback_work_class, __field(int, reason) ), TP_fast_assign( - struct device *dev = bdi->dev; - if (!dev) - dev = default_backing_dev_info.dev; - strncpy(__entry->name, dev_name(dev), 32); + strncpy(__entry->name, dev_name(bdi->dev), 32); __entry->nr_pages = work->nr_pages; __entry->sb_dev = work->sb ? work->sb->s_dev : 0; __entry->sync_mode = work->sync_mode; @@ -429,7 +426,7 @@ DECLARE_EVENT_CLASS(writeback_single_inode_template, TP_fast_assign( strncpy(__entry->name, - dev_name(inode_to_bdi(inode)->dev), 32); + dev_name(inode->i_mapping->backing_dev_info->dev), 32); __entry->ino = inode->i_ino; __entry->state = inode->i_state; __entry->dirtied_when = inode->dirtied_when; diff --git a/trunk/kernel/events/core.c b/trunk/kernel/events/core.c index 1b5c081d8b9f..ba36013cfb21 100644 --- a/trunk/kernel/events/core.c +++ b/trunk/kernel/events/core.c @@ -2303,7 +2303,7 @@ do { \ static DEFINE_PER_CPU(int, perf_throttled_count); static DEFINE_PER_CPU(u64, perf_throttled_seq); -static void perf_adjust_period(struct perf_event *event, u64 nsec, u64 count, bool disable) +static void perf_adjust_period(struct perf_event *event, u64 nsec, u64 count) { struct hw_perf_event *hwc = &event->hw; s64 period, sample_period; @@ -2322,13 +2322,9 @@ static void perf_adjust_period(struct perf_event *event, u64 nsec, u64 count, bo hwc->sample_period = sample_period; if (local64_read(&hwc->period_left) > 8*sample_period) { - if (disable) - event->pmu->stop(event, PERF_EF_UPDATE); - + event->pmu->stop(event, PERF_EF_UPDATE); local64_set(&hwc->period_left, 0); - - if (disable) - event->pmu->start(event, PERF_EF_RELOAD); + event->pmu->start(event, PERF_EF_RELOAD); } } @@ -2354,7 +2350,6 @@ static void perf_adjust_freq_unthr_context(struct perf_event_context *ctx, return; raw_spin_lock(&ctx->lock); - perf_pmu_disable(ctx->pmu); list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { if (event->state != PERF_EVENT_STATE_ACTIVE) @@ -2386,17 +2381,13 @@ static void perf_adjust_freq_unthr_context(struct perf_event_context *ctx, /* * restart the event * reload only if value has changed - * we have stopped the event so tell that - * to perf_adjust_period() to avoid stopping it - * twice. */ if (delta > 0) - perf_adjust_period(event, period, delta, false); + perf_adjust_period(event, period, delta); event->pmu->start(event, delta > 0 ? PERF_EF_RELOAD : 0); } - perf_pmu_enable(ctx->pmu); raw_spin_unlock(&ctx->lock); } @@ -4571,7 +4562,7 @@ static int __perf_event_overflow(struct perf_event *event, hwc->freq_time_stamp = now; if (delta > 0 && delta < 2*TICK_NSEC) - perf_adjust_period(event, delta, hwc->last_period, true); + perf_adjust_period(event, delta, hwc->last_period); } /* diff --git a/trunk/kernel/fork.c b/trunk/kernel/fork.c index b77fd559c78e..1b2ef3c23ae4 100644 --- a/trunk/kernel/fork.c +++ b/trunk/kernel/fork.c @@ -910,7 +910,7 @@ static int copy_io(unsigned long clone_flags, struct task_struct *tsk) return -ENOMEM; new_ioc->ioprio = ioc->ioprio; - put_io_context(new_ioc); + put_io_context(new_ioc, NULL); } #endif return 0; diff --git a/trunk/kernel/params.c b/trunk/kernel/params.c index 4bc965d8a1fe..32ee04308285 100644 --- a/trunk/kernel/params.c +++ b/trunk/kernel/params.c @@ -97,8 +97,7 @@ static int parse_one(char *param, for (i = 0; i < num_params; i++) { if (parameq(param, params[i].name)) { /* No one handled NULL, so do it here. */ - if (!val && params[i].ops->set != param_set_bool - && params[i].ops->set != param_set_bint) + if (!val && params[i].ops->set != param_set_bool) return -EINVAL; pr_debug("They are equal! Calling %p\n", params[i].ops->set); diff --git a/trunk/kernel/relay.c b/trunk/kernel/relay.c index ab56a1764d4d..4335e1d7ee2d 100644 --- a/trunk/kernel/relay.c +++ b/trunk/kernel/relay.c @@ -164,14 +164,10 @@ static void *relay_alloc_buf(struct rchan_buf *buf, size_t *size) */ static struct rchan_buf *relay_create_buf(struct rchan *chan) { - struct rchan_buf *buf; - - if (chan->n_subbufs > UINT_MAX / sizeof(size_t *)) - return NULL; - - buf = kzalloc(sizeof(struct rchan_buf), GFP_KERNEL); + struct rchan_buf *buf = kzalloc(sizeof(struct rchan_buf), GFP_KERNEL); if (!buf) return NULL; + buf->padding = kmalloc(chan->n_subbufs * sizeof(size_t *), GFP_KERNEL); if (!buf->padding) goto free_buf; @@ -578,8 +574,6 @@ struct rchan *relay_open(const char *base_filename, if (!(subbuf_size && n_subbufs)) return NULL; - if (subbuf_size > UINT_MAX / n_subbufs) - return NULL; chan = kzalloc(sizeof(struct rchan), GFP_KERNEL); if (!chan) diff --git a/trunk/lib/kstrtox.c b/trunk/lib/kstrtox.c index b1dd3e7d88cb..7a94c8f14e29 100644 --- a/trunk/lib/kstrtox.c +++ b/trunk/lib/kstrtox.c @@ -44,13 +44,12 @@ const char *_parse_integer_fixup_radix(const char *s, unsigned int *base) * * Don't you dare use this function. */ -unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *p) +unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *res) { - unsigned long long res; unsigned int rv; int overflow; - res = 0; + *res = 0; rv = 0; overflow = 0; while (*s) { @@ -65,19 +64,12 @@ unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long if (val >= base) break; - /* - * Check for overflow only if we are within range of - * it in the max base we support (16) - */ - if (unlikely(res & (~0ull << 60))) { - if (res > div_u64(ULLONG_MAX - val, base)) - overflow = 1; - } - res = res * base + val; + if (*res > div_u64(ULLONG_MAX - val, base)) + overflow = 1; + *res = *res * base + val; rv++; s++; } - *p = res; if (overflow) rv |= KSTRTOX_OVERFLOW; return rv; diff --git a/trunk/mm/backing-dev.c b/trunk/mm/backing-dev.c index dd8e2aafb07e..7ba8feae11b8 100644 --- a/trunk/mm/backing-dev.c +++ b/trunk/mm/backing-dev.c @@ -318,7 +318,7 @@ static void wakeup_timer_fn(unsigned long data) if (bdi->wb.task) { trace_writeback_wake_thread(bdi); wake_up_process(bdi->wb.task); - } else if (bdi->dev) { + } else { /* * When bdi tasks are inactive for long time, they are killed. * In this case we have to wake-up the forker thread which @@ -584,8 +584,6 @@ EXPORT_SYMBOL(bdi_register_dev); */ static void bdi_wb_shutdown(struct backing_dev_info *bdi) { - struct task_struct *task; - if (!bdi_cap_writeback_dirty(bdi)) return; @@ -604,13 +602,8 @@ static void bdi_wb_shutdown(struct backing_dev_info *bdi) * Finally, kill the kernel thread. We don't need to be RCU * safe anymore, since the bdi is gone from visibility. */ - spin_lock_bh(&bdi->wb_lock); - task = bdi->wb.task; - bdi->wb.task = NULL; - spin_unlock_bh(&bdi->wb_lock); - - if (task) - kthread_stop(task); + if (bdi->wb.task) + kthread_stop(bdi->wb.task); } /* @@ -630,9 +623,7 @@ static void bdi_prune_sb(struct backing_dev_info *bdi) void bdi_unregister(struct backing_dev_info *bdi) { - struct device *dev = bdi->dev; - - if (dev) { + if (bdi->dev) { bdi_set_min_ratio(bdi, 0); trace_writeback_bdi_unregister(bdi); bdi_prune_sb(bdi); @@ -641,12 +632,8 @@ void bdi_unregister(struct backing_dev_info *bdi) if (!bdi_cap_flush_forker(bdi)) bdi_wb_shutdown(bdi); bdi_debug_unregister(bdi); - - spin_lock_bh(&bdi->wb_lock); + device_unregister(bdi->dev); bdi->dev = NULL; - spin_unlock_bh(&bdi->wb_lock); - - device_unregister(dev); } } EXPORT_SYMBOL(bdi_unregister); diff --git a/trunk/net/caif/caif_socket.c b/trunk/net/caif/caif_socket.c index a97d97a3a512..a98628086452 100644 --- a/trunk/net/caif/caif_socket.c +++ b/trunk/net/caif/caif_socket.c @@ -539,10 +539,8 @@ static int transmit_skb(struct sk_buff *skb, struct caifsock *cf_sk, pkt = cfpkt_fromnative(CAIF_DIR_OUT, skb); memset(skb->cb, 0, sizeof(struct caif_payload_info)); - if (cf_sk->layer.dn == NULL) { - kfree_skb(skb); + if (cf_sk->layer.dn == NULL) return -EINVAL; - } return cf_sk->layer.dn->transmit(cf_sk->layer.dn, pkt); } @@ -685,10 +683,10 @@ static int caif_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, } err = transmit_skb(skb, cf_sk, msg->msg_flags&MSG_DONTWAIT, timeo); - if (err < 0) - /* skb is already freed */ + if (err < 0) { + kfree_skb(skb); goto pipe_err; - + } sent += size; } diff --git a/trunk/net/caif/cfmuxl.c b/trunk/net/caif/cfmuxl.c index 94b08612a4d8..b36f24a4c8e7 100644 --- a/trunk/net/caif/cfmuxl.c +++ b/trunk/net/caif/cfmuxl.c @@ -248,6 +248,7 @@ static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, { struct cfmuxl *muxl = container_obj(layr); struct cflayer *layer; + int idx; rcu_read_lock(); list_for_each_entry_rcu(layer, &muxl->srvl_list, node) { @@ -256,9 +257,14 @@ static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, if ((ctrl == _CAIF_CTRLCMD_PHYIF_DOWN_IND || ctrl == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND) && - layer->id != 0) - cfmuxl_remove_uplayer(layr, layer->id); - + layer->id != 0) { + + idx = layer->id % UP_CACHE_SIZE; + spin_lock_bh(&muxl->receive_lock); + RCU_INIT_POINTER(muxl->up_cache[idx], NULL); + list_del_rcu(&layer->node); + spin_unlock_bh(&muxl->receive_lock); + } /* NOTE: ctrlcmd is not allowed to block */ layer->ctrlcmd(layer, ctrl, phyid); } diff --git a/trunk/net/core/dev.c b/trunk/net/core/dev.c index 6ca32f6b3105..115dee1d985d 100644 --- a/trunk/net/core/dev.c +++ b/trunk/net/core/dev.c @@ -3500,20 +3500,14 @@ static inline gro_result_t __napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb) { struct sk_buff *p; - unsigned int maclen = skb->dev->hard_header_len; for (p = napi->gro_list; p; p = p->next) { unsigned long diffs; diffs = (unsigned long)p->dev ^ (unsigned long)skb->dev; diffs |= p->vlan_tci ^ skb->vlan_tci; - if (maclen == ETH_HLEN) - diffs |= compare_ether_header(skb_mac_header(p), - skb_gro_mac_header(skb)); - else if (!diffs) - diffs = memcmp(skb_mac_header(p), - skb_gro_mac_header(skb), - maclen); + diffs |= compare_ether_header(skb_mac_header(p), + skb_gro_mac_header(skb)); NAPI_GRO_CB(p)->same_flow = !diffs; NAPI_GRO_CB(p)->flush = 0; } diff --git a/trunk/net/core/ethtool.c b/trunk/net/core/ethtool.c index 3f79db1b612a..369b41894527 100644 --- a/trunk/net/core/ethtool.c +++ b/trunk/net/core/ethtool.c @@ -1190,8 +1190,6 @@ static noinline_for_stack int ethtool_flash_device(struct net_device *dev, if (!dev->ethtool_ops->flash_device) return -EOPNOTSUPP; - efl.data[ETHTOOL_FLASH_MAX_FILENAME - 1] = 0; - return dev->ethtool_ops->flash_device(dev, &efl); } diff --git a/trunk/net/core/netprio_cgroup.c b/trunk/net/core/netprio_cgroup.c index 4dacc44637ef..3a9fd4826b75 100644 --- a/trunk/net/core/netprio_cgroup.c +++ b/trunk/net/core/netprio_cgroup.c @@ -58,12 +58,11 @@ static int get_prioidx(u32 *prio) spin_lock_irqsave(&prioidx_map_lock, flags); prioidx = find_first_zero_bit(prioidx_map, sizeof(unsigned long) * PRIOIDX_SZ); - if (prioidx == sizeof(unsigned long) * PRIOIDX_SZ) { - spin_unlock_irqrestore(&prioidx_map_lock, flags); - return -ENOSPC; - } set_bit(prioidx, prioidx_map); spin_unlock_irqrestore(&prioidx_map_lock, flags); + if (prioidx == sizeof(unsigned long) * PRIOIDX_SZ) + return -ENOSPC; + atomic_set(&max_prioidx, prioidx); *prio = prioidx; return 0; @@ -108,7 +107,7 @@ static void extend_netdev_table(struct net_device *dev, u32 new_len) static void update_netdev_tables(void) { struct net_device *dev; - u32 max_len = atomic_read(&max_prioidx) + 1; + u32 max_len = atomic_read(&max_prioidx); struct netprio_map *map; rtnl_lock(); @@ -271,6 +270,7 @@ static int netprio_device_event(struct notifier_block *unused, { struct net_device *dev = ptr; struct netprio_map *old; + u32 max_len = atomic_read(&max_prioidx); /* * Note this is called with rtnl_lock held so we have update side @@ -278,6 +278,11 @@ static int netprio_device_event(struct notifier_block *unused, */ switch (event) { + + case NETDEV_REGISTER: + if (max_len) + extend_netdev_table(dev, max_len); + break; case NETDEV_UNREGISTER: old = rtnl_dereference(dev->priomap); RCU_INIT_POINTER(dev->priomap, NULL); diff --git a/trunk/net/core/sock.c b/trunk/net/core/sock.c index 02f8dfe320b7..3e81fd2e3c75 100644 --- a/trunk/net/core/sock.c +++ b/trunk/net/core/sock.c @@ -1171,10 +1171,13 @@ EXPORT_SYMBOL(sock_update_classid); void sock_update_netprioidx(struct sock *sk) { + struct cgroup_netprio_state *state; if (in_interrupt()) return; - - sk->sk_cgrp_prioidx = task_netprioidx(current); + rcu_read_lock(); + state = task_netprio_state(current); + sk->sk_cgrp_prioidx = state ? state->prioidx : 0; + rcu_read_unlock(); } EXPORT_SYMBOL_GPL(sock_update_netprioidx); #endif diff --git a/trunk/net/ipv4/Kconfig b/trunk/net/ipv4/Kconfig index d183262943d9..aa2a2c79776f 100644 --- a/trunk/net/ipv4/Kconfig +++ b/trunk/net/ipv4/Kconfig @@ -409,7 +409,7 @@ config INET_TCP_DIAG config INET_UDP_DIAG tristate "UDP: socket monitoring interface" - depends on INET_DIAG && (IPV6 || IPV6=n) + depends on INET_DIAG default n ---help--- Support for UDP socket monitoring interface used by the ss tool. diff --git a/trunk/net/ipv4/arp.c b/trunk/net/ipv4/arp.c index 63e49890ad31..59402be133f0 100644 --- a/trunk/net/ipv4/arp.c +++ b/trunk/net/ipv4/arp.c @@ -863,8 +863,7 @@ static int arp_process(struct sk_buff *skb) if (addr_type == RTN_UNICAST && (arp_fwd_proxy(in_dev, dev, rt) || arp_fwd_pvlan(in_dev, dev, rt, sip, tip) || - (rt->dst.dev != dev && - pneigh_lookup(&arp_tbl, net, &tip, dev, 0)))) { + pneigh_lookup(&arp_tbl, net, &tip, dev, 0))) { n = neigh_event_ns(&arp_tbl, sha, &sip, dev); if (n) neigh_release(n); diff --git a/trunk/net/ipv4/ip_options.c b/trunk/net/ipv4/ip_options.c index 42dd1a90edea..1e60f7679075 100644 --- a/trunk/net/ipv4/ip_options.c +++ b/trunk/net/ipv4/ip_options.c @@ -573,8 +573,8 @@ void ip_forward_options(struct sk_buff *skb) } if (srrptr + 3 <= srrspace) { opt->is_changed = 1; - ip_hdr(skb)->daddr = opt->nexthop; ip_rt_get_source(&optptr[srrptr-1], skb, rt); + ip_hdr(skb)->daddr = opt->nexthop; optptr[2] = srrptr+4; } else if (net_ratelimit()) printk(KERN_CRIT "ip_forward(): Argh! Destination lost!\n"); diff --git a/trunk/net/ipv4/sysctl_net_ipv4.c b/trunk/net/ipv4/sysctl_net_ipv4.c index 7a7724da9bff..4cb9cd2f2c39 100644 --- a/trunk/net/ipv4/sysctl_net_ipv4.c +++ b/trunk/net/ipv4/sysctl_net_ipv4.c @@ -778,6 +778,7 @@ EXPORT_SYMBOL_GPL(net_ipv4_ctl_path); static __net_init int ipv4_sysctl_init_net(struct net *net) { struct ctl_table *table; + unsigned long limit; table = ipv4_net_table; if (!net_eq(net, &init_net)) { @@ -814,6 +815,11 @@ static __net_init int ipv4_sysctl_init_net(struct net *net) net->ipv4.sysctl_rt_cache_rebuild_count = 4; tcp_init_mem(net); + limit = nr_free_buffer_pages() / 8; + limit = max(limit, 128UL); + net->ipv4.sysctl_tcp_mem[0] = limit / 4 * 3; + net->ipv4.sysctl_tcp_mem[1] = limit; + net->ipv4.sysctl_tcp_mem[2] = net->ipv4.sysctl_tcp_mem[0] * 2; net->ipv4.ipv4_hdr = register_net_sysctl_table(net, net_ipv4_ctl_path, table); diff --git a/trunk/net/ipv4/tcp.c b/trunk/net/ipv4/tcp.c index 37755ccc0e96..06373b4a449a 100644 --- a/trunk/net/ipv4/tcp.c +++ b/trunk/net/ipv4/tcp.c @@ -1876,20 +1876,6 @@ void tcp_shutdown(struct sock *sk, int how) } EXPORT_SYMBOL(tcp_shutdown); -bool tcp_check_oom(struct sock *sk, int shift) -{ - bool too_many_orphans, out_of_socket_memory; - - too_many_orphans = tcp_too_many_orphans(sk, shift); - out_of_socket_memory = tcp_out_of_memory(sk); - - if (too_many_orphans && net_ratelimit()) - pr_info("TCP: too many orphaned sockets\n"); - if (out_of_socket_memory && net_ratelimit()) - pr_info("TCP: out of memory -- consider tuning tcp_mem\n"); - return too_many_orphans || out_of_socket_memory; -} - void tcp_close(struct sock *sk, long timeout) { struct sk_buff *skb; @@ -2029,7 +2015,10 @@ void tcp_close(struct sock *sk, long timeout) } if (sk->sk_state != TCP_CLOSE) { sk_mem_reclaim(sk); - if (tcp_check_oom(sk, 0)) { + if (tcp_too_many_orphans(sk, 0)) { + if (net_ratelimit()) + printk(KERN_INFO "TCP: too many of orphaned " + "sockets\n"); tcp_set_state(sk, TCP_CLOSE); tcp_send_active_reset(sk, GFP_ATOMIC); NET_INC_STATS_BH(sock_net(sk), @@ -3229,6 +3218,7 @@ __setup("thash_entries=", set_thash_entries); void tcp_init_mem(struct net *net) { + /* Set per-socket limits to no more than 1/128 the pressure threshold */ unsigned long limit = nr_free_buffer_pages() / 8; limit = max(limit, 128UL); net->ipv4.sysctl_tcp_mem[0] = limit / 4 * 3; @@ -3297,8 +3287,7 @@ void __init tcp_init(void) sysctl_max_syn_backlog = max(128, cnt / 256); tcp_init_mem(&init_net); - /* Set per-socket limits to no more than 1/128 the pressure threshold */ - limit = nr_free_buffer_pages() << (PAGE_SHIFT - 10); + limit = nr_free_buffer_pages() / 8; limit = max(limit, 128UL); max_share = min(4UL*1024*1024, limit); diff --git a/trunk/net/ipv4/tcp_ipv4.c b/trunk/net/ipv4/tcp_ipv4.c index 94d683a61cba..337ba4cca052 100644 --- a/trunk/net/ipv4/tcp_ipv4.c +++ b/trunk/net/ipv4/tcp_ipv4.c @@ -651,11 +651,6 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) arg.iov[0].iov_len, IPPROTO_TCP, 0); arg.csumoffset = offsetof(struct tcphdr, check) / 2; arg.flags = (sk && inet_sk(sk)->transparent) ? IP_REPLY_ARG_NOSRCCHECK : 0; - /* When socket is gone, all binding information is lost. - * routing might fail in this case. using iif for oif to - * make sure we can deliver it - */ - arg.bound_dev_if = sk ? sk->sk_bound_dev_if : inet_iif(skb); net = dev_net(skb_dst(skb)->dev); arg.tos = ip_hdr(skb)->tos; diff --git a/trunk/net/ipv4/tcp_timer.c b/trunk/net/ipv4/tcp_timer.c index cd2e0723266d..a516d1e399df 100644 --- a/trunk/net/ipv4/tcp_timer.c +++ b/trunk/net/ipv4/tcp_timer.c @@ -77,7 +77,10 @@ static int tcp_out_of_resources(struct sock *sk, int do_reset) if (sk->sk_err_soft) shift++; - if (tcp_check_oom(sk, shift)) { + if (tcp_too_many_orphans(sk, shift)) { + if (net_ratelimit()) + printk(KERN_INFO "Out of socket memory\n"); + /* Catch exceptional cases, when connection requires reset. * 1. Last segment was sent recently. */ if ((s32)(tcp_time_stamp - tp->lsndtime) <= TCP_TIMEWAIT_LEN || diff --git a/trunk/net/mac80211/rx.c b/trunk/net/mac80211/rx.c index 5a5e504a8ffb..751409120769 100644 --- a/trunk/net/mac80211/rx.c +++ b/trunk/net/mac80211/rx.c @@ -611,7 +611,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_hw *hw, index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % tid_agg_rx->buf_size; if (!tid_agg_rx->reorder_buf[index] && - tid_agg_rx->stored_mpdu_num) { + tid_agg_rx->stored_mpdu_num > 1) { /* * No buffers ready to be released, but check whether any * frames in the reorder buffer have timed out. diff --git a/trunk/net/sched/sch_choke.c b/trunk/net/sched/sch_choke.c index 7e267d7b9c75..e465064d39a3 100644 --- a/trunk/net/sched/sch_choke.c +++ b/trunk/net/sched/sch_choke.c @@ -148,7 +148,8 @@ struct choke_skb_cb { static inline struct choke_skb_cb *choke_skb_cb(const struct sk_buff *skb) { - qdisc_cb_private_validate(skb, sizeof(struct choke_skb_cb)); + BUILD_BUG_ON(sizeof(skb->cb) < + sizeof(struct qdisc_skb_cb) + sizeof(struct choke_skb_cb)); return (struct choke_skb_cb *)qdisc_skb_cb(skb)->data; } diff --git a/trunk/net/sched/sch_netem.c b/trunk/net/sched/sch_netem.c index e83d61ca78ca..2776012132ea 100644 --- a/trunk/net/sched/sch_netem.c +++ b/trunk/net/sched/sch_netem.c @@ -130,7 +130,8 @@ struct netem_skb_cb { static inline struct netem_skb_cb *netem_skb_cb(struct sk_buff *skb) { - qdisc_cb_private_validate(skb, sizeof(struct netem_skb_cb)); + BUILD_BUG_ON(sizeof(skb->cb) < + sizeof(struct qdisc_skb_cb) + sizeof(struct netem_skb_cb)); return (struct netem_skb_cb *)qdisc_skb_cb(skb)->data; } diff --git a/trunk/net/sched/sch_sfb.c b/trunk/net/sched/sch_sfb.c index d7eea99333e9..96e42cae4c7a 100644 --- a/trunk/net/sched/sch_sfb.c +++ b/trunk/net/sched/sch_sfb.c @@ -94,7 +94,8 @@ struct sfb_skb_cb { static inline struct sfb_skb_cb *sfb_skb_cb(const struct sk_buff *skb) { - qdisc_cb_private_validate(skb, sizeof(struct sfb_skb_cb)); + BUILD_BUG_ON(sizeof(skb->cb) < + sizeof(struct qdisc_skb_cb) + sizeof(struct sfb_skb_cb)); return (struct sfb_skb_cb *)qdisc_skb_cb(skb)->data; } diff --git a/trunk/net/sched/sch_sfq.c b/trunk/net/sched/sch_sfq.c index 60d47180f043..67494aef9acf 100644 --- a/trunk/net/sched/sch_sfq.c +++ b/trunk/net/sched/sch_sfq.c @@ -166,8 +166,9 @@ struct sfq_skb_cb { static inline struct sfq_skb_cb *sfq_skb_cb(const struct sk_buff *skb) { - qdisc_cb_private_validate(skb, sizeof(struct sfq_skb_cb)); - return (struct sfq_skb_cb *)qdisc_skb_cb(skb)->data; + BUILD_BUG_ON(sizeof(skb->cb) < + sizeof(struct qdisc_skb_cb) + sizeof(struct sfq_skb_cb)); + return (struct sfq_skb_cb *)qdisc_skb_cb(skb)->data; } static unsigned int sfq_hash(const struct sfq_sched_data *q, diff --git a/trunk/scripts/mod/file2alias.c b/trunk/scripts/mod/file2alias.c index d0de2a2c3a2d..e8c969577768 100644 --- a/trunk/scripts/mod/file2alias.c +++ b/trunk/scripts/mod/file2alias.c @@ -932,7 +932,7 @@ static int do_isapnp_entry(const char *filename, (id->function >> 12) & 0x0f, (id->function >> 8) & 0x0f); return 1; } -ADD_TO_DEVTABLE("isapnp", struct isapnp_device_id, do_isapnp_entry); +ADD_TO_DEVTABLE("isa", struct isapnp_device_id, do_isapnp_entry); /* * Append a match expression for a single masked hex digit. diff --git a/trunk/sound/pci/hda/patch_realtek.c b/trunk/sound/pci/hda/patch_realtek.c index 1358987c49d8..9350f3c3bdf8 100644 --- a/trunk/sound/pci/hda/patch_realtek.c +++ b/trunk/sound/pci/hda/patch_realtek.c @@ -4374,7 +4374,6 @@ enum { ALC882_FIXUP_ACER_ASPIRE_8930G, ALC882_FIXUP_ASPIRE_8930G_VERBS, ALC885_FIXUP_MACPRO_GPIO, - ALC889_FIXUP_DAC_ROUTE, }; static void alc889_fixup_coef(struct hda_codec *codec, @@ -4428,23 +4427,6 @@ static void alc885_fixup_macpro_gpio(struct hda_codec *codec, alc882_gpio_mute(codec, 1, 0); } -/* Fix the connection of some pins for ALC889: - * At least, Acer Aspire 5935 shows the connections to DAC3/4 don't - * work correctly (bko#42740) - */ -static void alc889_fixup_dac_route(struct hda_codec *codec, - const struct alc_fixup *fix, int action) -{ - if (action == ALC_FIXUP_ACT_PRE_PROBE) { - hda_nid_t conn1[2] = { 0x0c, 0x0d }; - hda_nid_t conn2[2] = { 0x0e, 0x0f }; - snd_hda_override_conn_list(codec, 0x14, 2, conn1); - snd_hda_override_conn_list(codec, 0x15, 2, conn1); - snd_hda_override_conn_list(codec, 0x18, 2, conn2); - snd_hda_override_conn_list(codec, 0x1a, 2, conn2); - } -} - static const struct alc_fixup alc882_fixups[] = { [ALC882_FIXUP_ABIT_AW9D_MAX] = { .type = ALC_FIXUP_PINS, @@ -4592,10 +4574,6 @@ static const struct alc_fixup alc882_fixups[] = { .type = ALC_FIXUP_FUNC, .v.func = alc885_fixup_macpro_gpio, }, - [ALC889_FIXUP_DAC_ROUTE] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc889_fixup_dac_route, - }, }; static const struct snd_pci_quirk alc882_fixup_tbl[] = { @@ -4620,7 +4598,6 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0142, "Acer Aspire 7730G", ALC882_FIXUP_ACER_ASPIRE_4930G), SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", ALC882_FIXUP_PB_M5210), - SND_PCI_QUIRK(0x1025, 0x0259, "Acer Aspire 5935", ALC889_FIXUP_DAC_ROUTE), SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", ALC882_FIXUP_ACER_ASPIRE_7736), SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_FIXUP_EAPD), SND_PCI_QUIRK(0x1043, 0x1873, "ASUS W90V", ALC882_FIXUP_ASUS_W90V), diff --git a/trunk/sound/pci/hda/patch_sigmatel.c b/trunk/sound/pci/hda/patch_sigmatel.c index 6345df131a00..948f0be2f4f3 100644 --- a/trunk/sound/pci/hda/patch_sigmatel.c +++ b/trunk/sound/pci/hda/patch_sigmatel.c @@ -5078,9 +5078,9 @@ static int stac92xx_update_led_status(struct hda_codec *codec) spec->gpio_dir, spec->gpio_data); } else { notmtd_lvl = spec->gpio_led_polarity ? - AC_PINCTL_VREF_50 : AC_PINCTL_VREF_GRD; + AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_GRD; muted_lvl = spec->gpio_led_polarity ? - AC_PINCTL_VREF_GRD : AC_PINCTL_VREF_50; + AC_PINCTL_VREF_GRD : AC_PINCTL_VREF_HIZ; spec->vref_led = muted ? muted_lvl : notmtd_lvl; stac_vrefout_set(codec, spec->vref_mute_led_nid, spec->vref_led); diff --git a/trunk/sound/pci/hda/patch_via.c b/trunk/sound/pci/hda/patch_via.c index dff9a00ee8fb..284e311040fe 100644 --- a/trunk/sound/pci/hda/patch_via.c +++ b/trunk/sound/pci/hda/patch_via.c @@ -666,9 +666,6 @@ static void via_auto_init_analog_input(struct hda_codec *codec) /* init input-src */ for (i = 0; i < spec->num_adc_nids; i++) { int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx; - /* secondary ADCs must have the unique MUX */ - if (i > 0 && !spec->mux_nids[i]) - break; if (spec->mux_nids[adc_idx]) { int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx; snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, diff --git a/trunk/sound/pci/intel8x0.c b/trunk/sound/pci/intel8x0.c index e0a4263baa20..9f3b01bb72c8 100644 --- a/trunk/sound/pci/intel8x0.c +++ b/trunk/sound/pci/intel8x0.c @@ -2100,12 +2100,6 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = { .name = "MSI P4 ATX 645 Ultra", .type = AC97_TUNE_HP_ONLY }, - { - .subvendor = 0x161f, - .subdevice = 0x202f, - .name = "Gateway M520", - .type = AC97_TUNE_INV_EAPD - }, { .subvendor = 0x161f, .subdevice = 0x203a, diff --git a/trunk/sound/soc/sh/fsi.c b/trunk/sound/soc/sh/fsi.c index ea4a82d01160..db6c89a28bda 100644 --- a/trunk/sound/soc/sh/fsi.c +++ b/trunk/sound/soc/sh/fsi.c @@ -1152,8 +1152,12 @@ static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream) { struct fsi_priv *fsi = fsi_get_priv(substream); struct fsi_stream *io = fsi_get_stream(fsi, fsi_is_play(substream)); + int samples_pos = io->buff_sample_pos - 1; - return fsi_sample2frame(fsi, io->buff_sample_pos); + if (samples_pos < 0) + samples_pos = 0; + + return fsi_sample2frame(fsi, samples_pos); } static struct snd_pcm_ops fsi_pcm_ops = { diff --git a/trunk/tools/perf/bench/mem-memcpy-x86-64-asm.S b/trunk/tools/perf/bench/mem-memcpy-x86-64-asm.S index 185a96d66dd1..a57b66e853c2 100644 --- a/trunk/tools/perf/bench/mem-memcpy-x86-64-asm.S +++ b/trunk/tools/perf/bench/mem-memcpy-x86-64-asm.S @@ -1,8 +1,2 @@ #include "../../../arch/x86/lib/memcpy_64.S" -/* - * We need to provide note.GNU-stack section, saying that we want - * NOT executable stack. Otherwise the final linking will assume that - * the ELF stack should not be restricted at all and set it RWX. - */ -.section .note.GNU-stack,"",@progbits diff --git a/trunk/tools/perf/util/event.c b/trunk/tools/perf/util/event.c index 2044324b755a..73ddaf06b8e7 100644 --- a/trunk/tools/perf/util/event.c +++ b/trunk/tools/perf/util/event.c @@ -554,7 +554,7 @@ static int perf_event__process_kernel_mmap(struct perf_tool *tool __used, is_kernel_mmap = memcmp(event->mmap.filename, kmmap_prefix, - strlen(kmmap_prefix) - 1) == 0; + strlen(kmmap_prefix)) == 0; if (event->mmap.filename[0] == '/' || (!is_kernel_mmap && event->mmap.filename[0] == '[')) { diff --git a/trunk/tools/perf/util/evsel.c b/trunk/tools/perf/util/evsel.c index 7132ee834e0e..667f3b78bb2c 100644 --- a/trunk/tools/perf/util/evsel.c +++ b/trunk/tools/perf/util/evsel.c @@ -463,7 +463,6 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, memset(data, 0, sizeof(*data)); data->cpu = data->pid = data->tid = -1; data->stream_id = data->id = data->time = -1ULL; - data->period = 1; if (event->header.type != PERF_RECORD_SAMPLE) { if (!sample_id_all)