From a35190db379715d951c008830df3779aa83fbf04 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Thu, 9 Feb 2012 19:59:43 +0530 Subject: [PATCH] --- yaml --- r: 287698 b: refs/heads/master c: b57e6b560fc2a2742910ac5ca0eb2c46e45aeac2 h: refs/heads/master v: v3 --- [refs] | 2 +- .../Documentation/DocBook/device-drivers.tmpl | 12 +- trunk/Documentation/driver-model/devres.txt | 4 - .../feature-removal-schedule.txt | 14 - trunk/Documentation/input/event-codes.txt | 72 +- trunk/Documentation/pinctrl.txt | 17 +- trunk/Documentation/stable_kernel_rules.txt | 3 +- trunk/Documentation/sysctl/kernel.txt | 2 - trunk/Documentation/thermal/sysfs-api.txt | 2 +- trunk/Documentation/virtual/00-INDEX | 2 + trunk/MAINTAINERS | 72 +- trunk/Makefile | 2 +- trunk/arch/arm/Kconfig | 4 +- trunk/arch/arm/common/gic.c | 7 +- trunk/arch/arm/include/asm/assembler.h | 4 +- trunk/arch/arm/include/asm/domain.h | 8 +- trunk/arch/arm/include/asm/futex.h | 8 +- trunk/arch/arm/include/asm/smp.h | 6 + trunk/arch/arm/include/asm/smp_plat.h | 6 - trunk/arch/arm/include/asm/tlb.h | 10 +- trunk/arch/arm/include/asm/uaccess.h | 16 +- trunk/arch/arm/kernel/entry-armv.S | 2 +- trunk/arch/arm/kernel/entry-common.S | 15 +- trunk/arch/arm/kernel/perf_event_v7.c | 28 - trunk/arch/arm/kernel/ptrace.c | 8 +- trunk/arch/arm/kernel/setup.c | 17 +- trunk/arch/arm/kernel/signal.c | 5 +- trunk/arch/arm/kernel/smp.c | 24 +- trunk/arch/arm/kernel/smp_twd.c | 2 - trunk/arch/arm/kernel/vmlinux.lds.S | 9 +- trunk/arch/arm/lib/getuser.S | 12 +- trunk/arch/arm/lib/putuser.S | 28 +- trunk/arch/arm/lib/uaccess.S | 82 +- trunk/arch/arm/mach-at91/Kconfig | 14 - trunk/arch/arm/mach-at91/Makefile | 14 +- trunk/arch/arm/mach-at91/at91cap9.c | 9 +- trunk/arch/arm/mach-at91/at91sam9260.c | 1 - trunk/arch/arm/mach-at91/at91sam9261.c | 1 - trunk/arch/arm/mach-at91/at91sam9263.c | 1 - trunk/arch/arm/mach-at91/at91sam9_alt_reset.S | 7 +- trunk/arch/arm/mach-at91/at91sam9g45.c | 7 +- trunk/arch/arm/mach-at91/at91sam9g45_reset.S | 40 - trunk/arch/arm/mach-at91/at91sam9rl.c | 1 - trunk/arch/arm/mach-at91/generic.h | 2 - .../arm/mach-at91/include/mach/at91_rstc.h | 18 +- .../arm/mach-at91/include/mach/at91cap9.h | 2 +- .../mach-at91/include/mach/at91cap9_ddrsdr.h | 108 + .../arm/mach-at91/include/mach/at91sam9260.h | 2 +- .../arm/mach-at91/include/mach/at91sam9261.h | 2 +- .../arm/mach-at91/include/mach/at91sam9263.h | 2 +- .../mach-at91/include/mach/at91sam9_ddrsdr.h | 30 +- .../arm/mach-at91/include/mach/at91sam9g45.h | 2 +- .../arm/mach-at91/include/mach/at91sam9rl.h | 2 +- trunk/arch/arm/mach-at91/include/mach/board.h | 2 +- trunk/arch/arm/mach-at91/pm.c | 9 +- trunk/arch/arm/mach-at91/pm.h | 8 +- trunk/arch/arm/mach-at91/pm_slowclock.S | 5 +- trunk/arch/arm/mach-at91/setup.c | 16 +- trunk/arch/arm/mach-bcmring/arch.c | 2 +- trunk/arch/arm/mach-bcmring/dma.c | 812 ++++++ .../arch/arm/mach-bcmring/include/mach/dma.h | 196 ++ trunk/arch/arm/mach-davinci/board-da850-evm.c | 2 +- trunk/arch/arm/mach-davinci/board-dm365-evm.c | 2 +- .../arch/arm/mach-davinci/board-dm644x-evm.c | 2 +- .../arch/arm/mach-davinci/board-dm646x-evm.c | 2 +- .../arch/arm/mach-davinci/board-neuros-osd2.c | 2 +- .../arm/mach-davinci/board-omapl138-hawk.c | 2 +- trunk/arch/arm/mach-davinci/board-sffsdr.c | 2 +- trunk/arch/arm/mach-davinci/da850.c | 32 + trunk/arch/arm/mach-exynos/hotplug.c | 1 - trunk/arch/arm/mach-exynos/platsmp.c | 1 - trunk/arch/arm/mach-highbank/highbank.c | 3 +- trunk/arch/arm/mach-imx/Kconfig | 1 + trunk/arch/arm/mach-imx/clock-imx6q.c | 11 - trunk/arch/arm/mach-imx/mach-mx53_ard.c | 4 +- trunk/arch/arm/mach-imx/src.c | 5 +- trunk/arch/arm/mach-msm/hotplug.c | 1 - trunk/arch/arm/mach-msm/platsmp.c | 1 - trunk/arch/arm/mach-omap2/Kconfig | 12 +- trunk/arch/arm/mach-omap2/board-4430sdp.c | 18 +- trunk/arch/arm/mach-omap2/board-omap4panda.c | 18 +- trunk/arch/arm/mach-omap2/devices.c | 1 - trunk/arch/arm/mach-omap2/display.c | 4 + trunk/arch/arm/mach-omap2/gpmc.c | 6 - trunk/arch/arm/mach-omap2/hsmmc.c | 16 +- trunk/arch/arm/mach-omap2/io.c | 4 +- .../omap_hwmod_2xxx_3xxx_ipblock_data.c | 21 + .../mach-omap2/omap_hwmod_2xxx_ipblock_data.c | 22 - .../arm/mach-omap2/omap_hwmod_3xxx_data.c | 54 +- .../arm/mach-omap2/omap_hwmod_44xx_data.c | 2 - trunk/arch/arm/mach-omap2/prm2xxx_3xxx.c | 1 - trunk/arch/arm/mach-omap2/serial.c | 8 +- trunk/arch/arm/mach-omap2/smartreflex.c | 2 +- trunk/arch/arm/mach-omap2/timer.c | 2 +- trunk/arch/arm/mach-pxa/devices.c | 20 + trunk/arch/arm/mach-pxa/pxa25x.c | 2 + trunk/arch/arm/mach-pxa/pxa27x.c | 2 + trunk/arch/arm/mach-pxa/pxa300.c | 1 + trunk/arch/arm/mach-pxa/pxa320.c | 1 + trunk/arch/arm/mach-pxa/pxa3xx.c | 1 + trunk/arch/arm/mach-pxa/pxa95x.c | 1 + trunk/arch/arm/mach-realview/hotplug.c | 1 - .../arm/mach-realview/include/mach/board-eb.h | 18 +- .../mach-realview/include/mach/board-pb11mp.h | 2 - trunk/arch/arm/mach-realview/realview_eb.c | 11 +- .../arch/arm/mach-realview/realview_pb11mp.c | 13 +- trunk/arch/arm/mach-sa1100/assabet.c | 12 + trunk/arch/arm/mach-sa1100/cerf.c | 11 + trunk/arch/arm/mach-sa1100/clock.c | 91 +- trunk/arch/arm/mach-sa1100/collie.c | 23 +- trunk/arch/arm/mach-sa1100/cpu-sa1100.c | 2 +- trunk/arch/arm/mach-sa1100/generic.c | 27 +- trunk/arch/arm/mach-sa1100/include/mach/mcp.h | 2 + trunk/arch/arm/mach-sa1100/jornada720_ssp.c | 2 - trunk/arch/arm/mach-sa1100/lart.c | 10 + trunk/arch/arm/mach-sa1100/shannon.c | 11 + trunk/arch/arm/mach-sa1100/simpad.c | 18 +- trunk/arch/arm/mach-shmobile/setup-sh7372.c | 2 - trunk/arch/arm/mach-shmobile/smp-r8a7779.c | 1 - trunk/arch/arm/mach-shmobile/smp-sh73a0.c | 1 - trunk/arch/arm/mach-ux500/Kconfig | 1 - trunk/arch/arm/mach-ux500/board-mop500-sdi.c | 2 - trunk/arch/arm/mach-ux500/cache-l2x0.c | 48 +- trunk/arch/arm/mach-ux500/hotplug.c | 1 - trunk/arch/arm/mach-ux500/platsmp.c | 1 - trunk/arch/arm/mach-ux500/usb.c | 6 + trunk/arch/arm/mach-vexpress/ct-ca9x4.c | 4 +- trunk/arch/arm/mach-vexpress/hotplug.c | 1 - trunk/arch/arm/mm/Kconfig | 1 - trunk/arch/arm/mm/init.c | 2 +- trunk/arch/arm/mm/proc-v7.S | 26 +- .../arch/arm/plat-mxc/include/mach/iomux-v1.h | 2 +- trunk/arch/arm/plat-versatile/platsmp.c | 1 - trunk/arch/avr32/Kconfig | 1 - trunk/arch/m68k/atari/config.c | 8 +- trunk/arch/m68k/include/asm/irq.h | 13 + trunk/arch/m68k/kernel/process_mm.c | 2 +- trunk/arch/m68k/kernel/process_no.c | 4 +- trunk/arch/m68k/kernel/traps.c | 36 +- trunk/arch/m68k/mm/cache.c | 6 +- trunk/arch/microblaze/Kconfig | 1 - trunk/arch/microblaze/include/asm/atomic.h | 1 - trunk/arch/microblaze/kernel/setup.c | 21 +- trunk/arch/mips/Kconfig | 1 - trunk/arch/mips/lib/iomap-pci.c | 4 +- .../powerpc/boot/dts/fsl/mpc8536si-post.dtsi | 4 - .../powerpc/boot/dts/fsl/p1010si-post.dtsi | 3 +- .../powerpc/boot/dts/fsl/p1020si-post.dtsi | 4 - .../powerpc/boot/dts/fsl/p1022si-post.dtsi | 3 +- .../powerpc/boot/dts/fsl/p2020si-post.dtsi | 4 - trunk/arch/powerpc/boot/dts/p1020rdb.dtsi | 13 +- trunk/arch/powerpc/boot/dts/p1021mds.dts | 3 +- trunk/arch/powerpc/boot/dts/p2020ds.dtsi | 3 +- trunk/arch/powerpc/boot/dts/p2020rdb.dts | 3 +- trunk/arch/powerpc/kernel/crash.c | 2 +- trunk/arch/powerpc/kernel/legacy_serial.c | 2 - trunk/arch/powerpc/platforms/85xx/p1022_ds.c | 1 - .../arch/powerpc/platforms/powernv/pci-ioda.c | 43 +- trunk/arch/powerpc/platforms/pseries/Kconfig | 2 +- trunk/arch/powerpc/sysdev/fsl_pci.c | 5 +- trunk/arch/s390/Makefile | 1 + trunk/arch/s390/kernel/vmlinux.lds.S | 4 +- trunk/arch/sh/Kconfig | 1 - trunk/arch/sh/drivers/pci/pci.c | 4 +- trunk/arch/sparc/Kconfig | 1 - trunk/arch/sparc/kernel/sun4m_irq.c | 3 - trunk/arch/sparc/lib/divdi3.S | 16 +- trunk/arch/x86/Kconfig | 1 + trunk/arch/x86/boot/compressed/misc.c | 2 - trunk/arch/x86/include/asm/cmpxchg.h | 6 +- trunk/arch/x86/include/asm/cpufeature.h | 1 - trunk/arch/x86/include/asm/kvm_emulate.h | 16 - trunk/arch/x86/include/asm/uv/uv_hub.h | 4 +- .../arch/x86/kernel/cpu/perf_event_intel_ds.c | 1 + .../x86/kernel/cpu/perf_event_intel_lbr.c | 2 + trunk/arch/x86/kernel/dumpstack.c | 3 +- trunk/arch/x86/kernel/dumpstack_64.c | 8 +- trunk/arch/x86/kernel/microcode_amd.c | 24 +- trunk/arch/x86/kernel/reboot.c | 36 +- trunk/arch/x86/kvm/emulate.c | 51 - trunk/arch/x86/kvm/x86.c | 45 - trunk/arch/x86/mm/fault.c | 4 +- trunk/arch/x86/platform/uv/tlb_uv.c | 2 - trunk/arch/x86/platform/uv/uv_irq.c | 2 +- trunk/arch/x86/xen/spinlock.c | 27 +- trunk/arch/xtensa/include/asm/string.h | 3 + trunk/crypto/sha512_generic.c | 62 +- trunk/drivers/acpi/Makefile | 1 + trunk/drivers/acpi/apei/apei-base.c | 35 +- trunk/drivers/acpi/apei/einj.c | 95 +- trunk/drivers/acpi/atomicio.c | 422 +++ trunk/drivers/acpi/osl.c | 152 +- trunk/drivers/acpi/processor_driver.c | 149 +- trunk/drivers/acpi/sleep.c | 8 - trunk/drivers/base/Makefile | 2 +- trunk/drivers/base/core.c | 17 +- trunk/drivers/base/cpu.c | 21 - trunk/drivers/base/memory.c | 31 +- trunk/drivers/base/node.c | 8 - trunk/drivers/base/regmap/regmap.c | 3 - trunk/drivers/base/sys.c | 383 +++ trunk/drivers/block/rbd.c | 7 +- trunk/drivers/char/agp/backend.c | 12 +- trunk/drivers/dma/at_hdmac.c | 4 +- trunk/drivers/dma/at_hdmac_regs.h | 17 +- trunk/drivers/dma/dmatest.c | 2 +- trunk/drivers/dma/imx-sdma.c | 6 +- trunk/drivers/dma/shdma.c | 3 +- trunk/drivers/firewire/ohci.c | 6 +- trunk/drivers/gpio/gpio-lpc32xx.c | 2 +- trunk/drivers/gpio/gpio-ml-ioh.c | 1 - trunk/drivers/gpio/gpio-pch.c | 1 - trunk/drivers/gpio/gpio-samsung.c | 23 +- trunk/drivers/gpu/drm/drm_auth.c | 6 +- trunk/drivers/gpu/drm/drm_fops.c | 5 - trunk/drivers/gpu/drm/drm_gem.c | 2 +- trunk/drivers/gpu/drm/drm_ioc32.c | 3 +- trunk/drivers/gpu/drm/exynos/Kconfig | 4 +- .../drivers/gpu/drm/exynos/exynos_drm_fimd.c | 109 +- trunk/drivers/gpu/drm/exynos/exynos_hdmi.c | 8 +- trunk/drivers/gpu/drm/gma500/framebuffer.c | 8 +- trunk/drivers/gpu/drm/i810/i810_dma.c | 17 +- trunk/drivers/gpu/drm/i810/i810_drv.c | 1 - trunk/drivers/gpu/drm/i810/i810_drv.h | 6 +- trunk/drivers/gpu/drm/i915/i915_debugfs.c | 31 +- trunk/drivers/gpu/drm/i915/i915_dma.c | 1 - trunk/drivers/gpu/drm/i915/i915_drv.c | 56 +- trunk/drivers/gpu/drm/i915/i915_drv.h | 10 +- trunk/drivers/gpu/drm/i915/i915_irq.c | 3 +- trunk/drivers/gpu/drm/i915/i915_suspend.c | 11 +- trunk/drivers/gpu/drm/i915/intel_bios.h | 6 +- trunk/drivers/gpu/drm/i915/intel_crt.c | 23 - trunk/drivers/gpu/drm/i915/intel_display.c | 30 +- trunk/drivers/gpu/drm/i915/intel_dp.c | 20 +- trunk/drivers/gpu/drm/i915/intel_lvds.c | 16 - trunk/drivers/gpu/drm/i915/intel_ringbuffer.c | 41 +- trunk/drivers/gpu/drm/i915/intel_sdvo.c | 8 +- trunk/drivers/gpu/drm/i915/intel_sprite.c | 8 +- trunk/drivers/gpu/drm/i915/intel_tv.c | 138 +- trunk/drivers/gpu/drm/nouveau/nouveau_bios.h | 5 +- trunk/drivers/gpu/drm/nouveau/nouveau_bo.c | 4 - .../drivers/gpu/drm/nouveau/nouveau_display.c | 10 - trunk/drivers/gpu/drm/nouveau/nouveau_drv.c | 2 +- trunk/drivers/gpu/drm/nouveau/nouveau_gem.c | 23 +- trunk/drivers/gpu/drm/nouveau/nouveau_mxm.c | 9 - trunk/drivers/gpu/drm/nouveau/nv50_pm.c | 4 +- trunk/drivers/gpu/drm/radeon/atombios_crtc.c | 64 +- trunk/drivers/gpu/drm/radeon/atombios_dp.c | 40 +- .../gpu/drm/radeon/atombios_encoders.c | 89 +- trunk/drivers/gpu/drm/radeon/evergreen.c | 2 - trunk/drivers/gpu/drm/radeon/evergreend.h | 1 - trunk/drivers/gpu/drm/radeon/ni.c | 1 - trunk/drivers/gpu/drm/radeon/nid.h | 1 - trunk/drivers/gpu/drm/radeon/r600_blit_kms.c | 35 +- trunk/drivers/gpu/drm/radeon/radeon.h | 84 +- .../gpu/drm/radeon/radeon_atpx_handler.c | 3 +- trunk/drivers/gpu/drm/radeon/radeon_bios.c | 2 +- trunk/drivers/gpu/drm/radeon/radeon_device.c | 12 +- trunk/drivers/gpu/drm/radeon/radeon_display.c | 6 +- .../drivers/gpu/drm/radeon/radeon_encoders.c | 77 - trunk/drivers/gpu/drm/radeon/radeon_i2c.c | 2 - trunk/drivers/gpu/drm/radeon/radeon_irq_kms.c | 6 - trunk/drivers/gpu/drm/radeon/radeon_mode.h | 8 - trunk/drivers/gpu/drm/radeon/radeon_ring.c | 37 +- trunk/drivers/gpu/drm/sis/sis_drv.c | 3 +- trunk/drivers/gpu/drm/ttm/ttm_bo.c | 17 +- trunk/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 2 +- trunk/drivers/hid/hid-hyperv.c | 1 - trunk/drivers/hid/hid-wacom.c | 7 +- trunk/drivers/hid/hid-wiimote-core.c | 4 +- trunk/drivers/hid/usbhid/hiddev.c | 4 +- trunk/drivers/hwmon/f71805f.c | 10 +- trunk/drivers/hwmon/f75375s.c | 11 +- trunk/drivers/hwmon/sht15.c | 3 +- trunk/drivers/hwmon/w83627ehf.c | 34 +- trunk/drivers/i2c/busses/i2c-omap.c | 2 +- trunk/drivers/idle/intel_idle.c | 2 +- trunk/drivers/infiniband/core/ucma.c | 5 +- trunk/drivers/infiniband/core/uverbs_cmd.c | 1 - trunk/drivers/infiniband/core/verbs.c | 2 +- trunk/drivers/infiniband/hw/ipath/ipath_fs.c | 2 +- trunk/drivers/infiniband/hw/mlx4/mad.c | 7 +- trunk/drivers/infiniband/hw/nes/nes.c | 2 +- trunk/drivers/infiniband/hw/nes/nes.h | 2 +- trunk/drivers/infiniband/hw/nes/nes_cm.c | 10 +- trunk/drivers/infiniband/hw/nes/nes_cm.h | 2 +- trunk/drivers/infiniband/hw/nes/nes_context.h | 2 +- trunk/drivers/infiniband/hw/nes/nes_hw.c | 2 +- trunk/drivers/infiniband/hw/nes/nes_hw.h | 2 +- trunk/drivers/infiniband/hw/nes/nes_mgt.c | 2 +- trunk/drivers/infiniband/hw/nes/nes_mgt.h | 2 +- trunk/drivers/infiniband/hw/nes/nes_nic.c | 2 +- trunk/drivers/infiniband/hw/nes/nes_user.h | 2 +- trunk/drivers/infiniband/hw/nes/nes_utils.c | 2 +- trunk/drivers/infiniband/hw/nes/nes_verbs.c | 6 +- trunk/drivers/infiniband/hw/nes/nes_verbs.h | 2 +- trunk/drivers/infiniband/hw/qib/qib_iba6120.c | 2 +- trunk/drivers/infiniband/hw/qib/qib_pcie.c | 2 +- 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/infiniband/ulp/srpt/ib_srpt.c | 17 +- trunk/drivers/infiniband/ulp/srpt/ib_srpt.h | 1 + trunk/drivers/input/evdev.c | 2 +- trunk/drivers/input/keyboard/twl4030_keypad.c | 4 +- trunk/drivers/input/serio/i8042-x86ia64io.h | 7 - trunk/drivers/input/serio/serio_raw.c | 15 +- trunk/drivers/iommu/amd_iommu.c | 3 - trunk/drivers/iommu/msm_iommu.c | 7 +- trunk/drivers/isdn/i4l/isdn_net.c | 2 +- trunk/drivers/leds/leds-lm3530.c | 4 +- trunk/drivers/md/dm-raid.c | 12 +- trunk/drivers/md/md.c | 5 +- trunk/drivers/media/dvb/dvb-usb/anysee.c | 11 +- .../drivers/media/dvb/dvb-usb/cinergyT2-fe.c | 7 +- trunk/drivers/media/dvb/frontends/cxd2820r.h | 6 +- .../media/dvb/frontends/cxd2820r_core.c | 20 +- trunk/drivers/media/video/atmel-isi.c | 14 - trunk/drivers/media/video/em28xx/em28xx-dvb.c | 3 +- trunk/drivers/mfd/mcp-core.c | 61 +- trunk/drivers/mfd/mcp-sa11x0.c | 169 +- trunk/drivers/mfd/twl6040-core.c | 128 +- trunk/drivers/mfd/ucb1x00-core.c | 67 +- trunk/drivers/mfd/ucb1x00-ts.c | 34 +- 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/lkdtm.c | 6 +- trunk/drivers/misc/vmw_balloon.c | 14 +- trunk/drivers/mmc/host/Kconfig | 1 + trunk/drivers/mtd/mtdcore.c | 2 +- trunk/drivers/mtd/nand/atmel_nand.c | 45 +- trunk/drivers/mtd/nand/gpmi-nand/gpmi-lib.c | 18 +- trunk/drivers/mtd/nand/nand_base.c | 2 +- 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 + trunk/drivers/net/ethernet/3com/3c59x.c | 2 +- .../net/ethernet/broadcom/bcm63xx_enet.c | 2 +- .../net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 3 +- .../net/ethernet/broadcom/bnx2x/bnx2x_main.c | 17 +- .../net/ethernet/broadcom/bnx2x/bnx2x_stats.c | 8 +- .../net/ethernet/brocade/bna/bnad_ethtool.c | 10 +- .../net/ethernet/emulex/benet/be_ethtool.c | 6 +- trunk/drivers/net/ethernet/freescale/fec.c | 4 +- .../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 +- trunk/drivers/net/ethernet/mellanox/mlx4/eq.c | 5 +- .../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/micrel/ks8851.c | 2 +- .../drivers/net/ethernet/octeon/octeon_mgmt.c | 4 +- trunk/drivers/net/ethernet/renesas/sh_eth.c | 4 +- trunk/drivers/net/ethernet/renesas/sh_eth.h | 3 + trunk/drivers/net/ethernet/ti/cpmac.c | 4 +- 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 + .../drivers/net/ethernet/xscale/ixp4xx_eth.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/pcmcia/ds.c | 4 +- trunk/drivers/pcmcia/sa1111_generic.c | 3 +- trunk/drivers/pinctrl/core.c | 50 +- trunk/drivers/pinctrl/core.h | 3 - trunk/drivers/pinctrl/pinconf.c | 6 +- trunk/drivers/pinctrl/pinconf.h | 4 +- trunk/drivers/pinctrl/pinmux.c | 81 +- trunk/drivers/pinctrl/pinmux.h | 4 +- trunk/drivers/regulator/of_regulator.c | 2 +- trunk/drivers/rtc/Kconfig | 2 +- trunk/drivers/rtc/rtc-sa1100.c | 295 ++- trunk/drivers/s390/block/dasd.c | 6 - trunk/drivers/s390/block/dasd_alias.c | 64 +- trunk/drivers/s390/block/dasd_eckd.c | 63 +- trunk/drivers/s390/block/dasd_int.h | 2 - trunk/drivers/scsi/mac_esp.c | 3 +- trunk/drivers/scsi/mac_scsi.c | 6 + trunk/drivers/spi/Kconfig | 2 +- trunk/drivers/spi/spi-topcliff-pch.c | 6 +- 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 ++++ .../drivers/staging/media/go7007/go7007-usb.c | 1 - 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/target/iscsi/iscsi_target.c | 39 +- .../target/iscsi/iscsi_target_configfs.c | 1 - .../drivers/target/iscsi/iscsi_target_core.h | 6 +- .../drivers/target/iscsi/iscsi_target_erl1.c | 4 +- .../drivers/target/iscsi/iscsi_target_login.c | 39 +- .../drivers/target/iscsi/iscsi_target_util.c | 11 - trunk/drivers/target/target_core_alua.c | 8 +- trunk/drivers/target/target_core_cdb.c | 51 +- trunk/drivers/target/target_core_configfs.c | 12 +- trunk/drivers/target/target_core_device.c | 28 +- .../target/target_core_fabric_configfs.c | 4 +- trunk/drivers/target/target_core_iblock.c | 11 +- trunk/drivers/target/target_core_internal.h | 2 +- trunk/drivers/target/target_core_pr.c | 43 +- trunk/drivers/target/target_core_pscsi.c | 4 +- trunk/drivers/target/target_core_tpg.c | 3 +- trunk/drivers/target/target_core_transport.c | 124 +- trunk/drivers/target/tcm_fc/tfc_cmd.c | 9 +- trunk/drivers/thermal/thermal_sys.c | 4 +- trunk/drivers/tty/serial/{8250 => }/8250.c | 2 +- trunk/drivers/tty/serial/{8250 => }/8250.h | 0 trunk/drivers/tty/serial/8250/Kconfig | 280 -- trunk/drivers/tty/serial/8250/Makefile | 20 - .../tty/serial/{8250 => }/8250_accent.c | 0 .../tty/serial/{8250 => }/8250_acorn.c | 0 .../drivers/tty/serial/{8250 => }/8250_boca.c | 0 trunk/drivers/tty/serial/{8250 => }/8250_dw.c | 0 .../tty/serial/{8250 => }/8250_early.c | 0 .../serial/{8250 => }/8250_exar_st16c554.c | 0 .../tty/serial/{8250 => }/8250_fourport.c | 0 .../drivers/tty/serial/{8250 => }/8250_fsl.c | 0 .../drivers/tty/serial/{8250 => }/8250_gsc.c | 0 .../tty/serial/{8250 => }/8250_hp300.c | 0 .../drivers/tty/serial/{8250 => }/8250_hub6.c | 0 .../drivers/tty/serial/{8250 => }/8250_mca.c | 0 .../drivers/tty/serial/{8250 => }/8250_pci.c | 0 .../drivers/tty/serial/{8250 => }/8250_pnp.c | 0 trunk/drivers/tty/serial/Kconfig | 283 +- trunk/drivers/tty/serial/Makefile | 20 +- trunk/drivers/tty/serial/amba-pl011.c | 30 +- trunk/drivers/tty/serial/jsm/jsm_driver.c | 1 - trunk/drivers/tty/serial/max3107-aava.c | 344 +++ trunk/drivers/tty/serial/omap-serial.c | 34 +- trunk/drivers/tty/serial/samsung.c | 3 +- trunk/drivers/tty/serial/serial_core.c | 6 +- .../drivers/tty/serial/{8250 => }/serial_cs.c | 0 trunk/drivers/tty/tty_port.c | 12 +- trunk/drivers/tty/vt/vt_ioctl.c | 1 + trunk/drivers/usb/class/cdc-wdm.c | 59 +- trunk/drivers/usb/dwc3/ep0.c | 15 +- trunk/drivers/usb/dwc3/gadget.c | 2 +- trunk/drivers/usb/gadget/composite.c | 7 +- trunk/drivers/usb/gadget/epautoconf.c | 2 +- trunk/drivers/usb/gadget/f_loopback.c | 2 +- trunk/drivers/usb/gadget/f_mass_storage.c | 10 +- trunk/drivers/usb/gadget/fsl_udc_core.c | 4 +- trunk/drivers/usb/gadget/langwell_udc.c | 107 +- trunk/drivers/usb/gadget/langwell_udc.h | 1 + trunk/drivers/usb/gadget/storage_common.c | 6 +- trunk/drivers/usb/host/Kconfig | 8 +- trunk/drivers/usb/host/ehci-fsl.c | 13 +- trunk/drivers/usb/host/ehci-fsl.h | 1 - trunk/drivers/usb/host/ehci-pci.c | 6 - trunk/drivers/usb/host/ohci-at91.c | 24 +- trunk/drivers/usb/host/ohci-dbg.c | 12 +- trunk/drivers/usb/host/ohci-pci.c | 4 - trunk/drivers/usb/host/pci-quirks.c | 6 - trunk/drivers/usb/host/xhci-ring.c | 6 +- trunk/drivers/usb/misc/emi26.c | 3 + trunk/drivers/usb/misc/emi62.c | 3 + trunk/drivers/usb/misc/usbsevseg.c | 2 +- trunk/drivers/usb/musb/davinci.c | 3 + trunk/drivers/usb/musb/musb_core.c | 5 +- trunk/drivers/usb/musb/musb_io.h | 3 +- trunk/drivers/usb/musb/omap2430.c | 4 +- trunk/drivers/usb/otg/Kconfig | 18 +- trunk/drivers/usb/otg/Makefile | 1 + trunk/drivers/usb/otg/langwell_otg.c | 2347 +++++++++++++++++ trunk/drivers/usb/otg/mv_otg.c | 2 - trunk/drivers/usb/renesas_usbhs/mod_gadget.c | 2 +- trunk/drivers/usb/serial/cp210x.c | 105 +- trunk/drivers/usb/serial/ftdi_sio.c | 15 +- trunk/drivers/usb/serial/ftdi_sio_ids.h | 26 - trunk/drivers/usb/serial/io_ti.c | 10 +- trunk/drivers/usb/serial/kobil_sct.c | 2 +- trunk/drivers/usb/serial/option.c | 134 +- trunk/drivers/usb/serial/qcaux.c | 7 +- trunk/drivers/usb/serial/qcserial.c | 16 +- trunk/drivers/usb/storage/realtek_cr.c | 2 +- trunk/drivers/usb/usb-skeleton.c | 18 +- trunk/drivers/usb/wusbcore/Kconfig | 2 +- trunk/drivers/video/atmel_lcdfb.c | 2 +- trunk/drivers/video/fsl-diu-fb.c | 4 +- trunk/drivers/video/intelfb/intelfbdrv.c | 1 + trunk/drivers/video/macfb.c | 60 +- trunk/drivers/video/omap2/dss/dispc.c | 2 +- trunk/drivers/video/omap2/dss/dsi.c | 2 +- trunk/drivers/video/omap2/dss/dss.c | 2 +- trunk/drivers/video/omap2/dss/hdmi.c | 5 +- trunk/drivers/video/omap2/dss/rfbi.c | 2 +- trunk/drivers/video/omap2/dss/ti_hdmi.h | 4 - .../drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 68 +- trunk/drivers/video/omap2/dss/venc.c | 2 +- trunk/drivers/virtio/virtio_ring.c | 8 +- trunk/drivers/watchdog/dw_wdt.c | 6 +- trunk/drivers/watchdog/iTCO_wdt.c | 35 - trunk/drivers/watchdog/imx2_wdt.c | 11 +- trunk/drivers/watchdog/nuc900_wdt.c | 5 +- trunk/drivers/watchdog/omap_wdt.c | 2 - trunk/drivers/watchdog/pnx4008_wdt.c | 2 +- trunk/drivers/watchdog/stmp3xxx_wdt.c | 2 +- trunk/drivers/watchdog/via_wdt.c | 6 +- trunk/drivers/watchdog/wafer5823wdt.c | 4 +- trunk/drivers/watchdog/wm8350_wdt.c | 4 +- trunk/drivers/xen/grant-table.c | 7 +- trunk/fs/btrfs/backref.c | 4 +- trunk/fs/btrfs/check-integrity.c | 11 +- trunk/fs/btrfs/disk-io.c | 7 - trunk/fs/btrfs/extent-tree.c | 49 +- trunk/fs/btrfs/extent_io.c | 2 - trunk/fs/btrfs/free-space-cache.c | 5 +- trunk/fs/btrfs/inode.c | 10 +- trunk/fs/btrfs/ioctl.c | 2 +- trunk/fs/btrfs/tree-log.c | 6 +- trunk/fs/ceph/caps.c | 4 +- trunk/fs/ceph/dir.c | 4 +- trunk/fs/ceph/mds_client.c | 10 +- trunk/fs/ceph/mds_client.h | 7 +- trunk/fs/ceph/xattr.c | 4 +- trunk/fs/cifs/Kconfig | 4 +- trunk/fs/cifs/connect.c | 14 +- trunk/fs/cifs/sess.c | 11 +- trunk/fs/ecryptfs/crypto.c | 54 +- trunk/fs/ecryptfs/ecryptfs_kernel.h | 5 - trunk/fs/ecryptfs/inode.c | 48 +- trunk/fs/ecryptfs/keystore.c | 5 +- trunk/fs/ecryptfs/miscdev.c | 140 +- trunk/fs/ecryptfs/mmap.c | 8 +- trunk/fs/ecryptfs/read_write.c | 96 +- trunk/fs/exec.c | 33 +- trunk/fs/fs-writeback.c | 16 +- trunk/fs/jffs2/erase.c | 2 +- trunk/fs/logfs/dev_mtd.c | 6 + trunk/fs/logfs/dir.c | 2 +- trunk/fs/logfs/file.c | 2 - trunk/fs/logfs/gc.c | 2 +- trunk/fs/logfs/inode.c | 4 +- trunk/fs/logfs/journal.c | 1 + trunk/fs/logfs/logfs.h | 5 +- trunk/fs/logfs/readwrite.c | 51 +- trunk/fs/logfs/segment.c | 51 +- trunk/fs/logfs/super.c | 3 +- trunk/fs/nilfs2/ioctl.c | 2 - trunk/fs/proc/base.c | 126 +- trunk/fs/sysfs/file.c | 6 - trunk/fs/sysfs/inode.c | 5 +- trunk/fs/xfs/xfs_vnodeops.c | 3 +- trunk/include/acpi/acpiosxf.h | 4 - trunk/include/acpi/atomicio.h | 10 + trunk/include/acpi/processor.h | 1 - trunk/include/asm-generic/pci_iomap.h | 10 - trunk/include/drm/drmP.h | 1 - trunk/include/linux/binfmts.h | 3 +- trunk/include/linux/freezer.h | 2 - trunk/include/linux/gpio_keys.h | 2 - trunk/include/linux/hyperv.h | 2 +- trunk/include/linux/lp8727.h | 0 trunk/include/linux/mfd/mcp.h | 12 +- trunk/include/linux/mfd/twl6040.h | 2 - trunk/include/linux/mfd/ucb1x00.h | 5 +- trunk/include/linux/mod_devicetable.h | 11 + trunk/include/linux/mpi.h | 2 + trunk/include/linux/mtd/mtd.h | 6 +- trunk/include/linux/perf_event.h | 1 - trunk/include/linux/pm_qos.h | 14 +- trunk/include/linux/proportions.h | 4 - trunk/include/linux/sched.h | 8 +- trunk/include/linux/sh_dma.h | 1 - trunk/include/linux/sysdev.h | 164 ++ trunk/include/linux/thermal.h | 4 +- trunk/include/linux/usb/ch9.h | 2 +- trunk/include/linux/usb/langwell_otg.h | 139 + 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/sound/core.h | 2 - trunk/include/target/target_core_backend.h | 4 +- trunk/include/target/target_core_base.h | 1 - trunk/include/target/target_core_fabric.h | 2 +- trunk/include/trace/events/writeback.h | 7 +- trunk/include/video/omapdss.h | 5 - trunk/kernel/events/callchain.c | 2 + trunk/kernel/events/core.c | 125 +- trunk/kernel/exit.c | 16 - trunk/kernel/fork.c | 20 - trunk/kernel/kprobes.c | 6 +- trunk/kernel/power/power.h | 24 +- trunk/kernel/power/process.c | 26 +- trunk/kernel/power/user.c | 15 +- trunk/kernel/rcutorture.c | 8 +- trunk/kernel/sched/core.c | 19 +- trunk/kernel/sched/fair.c | 34 +- trunk/kernel/sched/rt.c | 5 - trunk/kernel/watchdog.c | 2 +- trunk/lib/Kconfig | 7 - trunk/lib/Makefile | 2 - trunk/lib/bug.c | 2 +- trunk/lib/clz_tab.c | 18 - trunk/lib/digsig.c | 52 +- trunk/lib/kstrtox.c | 18 +- trunk/lib/mpi/longlong.h | 44 +- trunk/lib/mpi/mpi-bit.c | 19 + trunk/lib/mpi/mpi-div.c | 5 - trunk/lib/mpi/mpi-pow.c | 2 +- trunk/lib/mpi/mpicoder.c | 91 +- trunk/lib/mpi/mpih-div.c | 4 - trunk/lib/mpi/mpiutil.c | 5 +- trunk/lib/pci_iomap.c | 2 +- trunk/mm/backing-dev.c | 23 +- trunk/mm/compaction.c | 24 +- trunk/mm/filemap.c | 8 +- trunk/mm/filemap_xip.c | 7 +- trunk/mm/huge_memory.c | 4 +- trunk/mm/kmemleak.c | 3 +- trunk/mm/memblock.c | 7 +- trunk/mm/memcontrol.c | 3 +- trunk/mm/migrate.c | 2 +- trunk/mm/process_vm_access.c | 23 +- trunk/mm/swap.c | 2 +- trunk/net/caif/caif_socket.c | 10 +- trunk/net/caif/cfmuxl.c | 12 +- trunk/net/ceph/ceph_common.c | 2 + trunk/net/ceph/mon_client.c | 13 +- trunk/net/core/dev.c | 10 +- trunk/net/core/ethtool.c | 2 - trunk/net/core/netpoll.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_input.c | 45 +- trunk/net/ipv4/tcp_ipv4.c | 5 - trunk/net/ipv4/tcp_timer.c | 5 +- trunk/net/mac80211/main.c | 4 +- trunk/net/rxrpc/ar-key.c | 4 +- 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/net/sunrpc/auth_generic.c | 17 +- trunk/scripts/checkpatch.pl | 6 - trunk/scripts/mod/file2alias.c | 10 + trunk/sound/core/compress_offload.c | 13 +- trunk/sound/isa/sb/emu8000_patch.c | 1 + trunk/sound/pci/hda/hda_codec.c | 2 +- trunk/sound/pci/hda/hda_jack.c | 24 +- trunk/sound/pci/hda/patch_ca0132.c | 33 +- trunk/sound/pci/hda/patch_cirrus.c | 6 +- trunk/sound/pci/hda/patch_realtek.c | 61 +- trunk/sound/pci/hda/patch_sigmatel.c | 9 +- trunk/sound/pci/hda/patch_via.c | 284 +- trunk/sound/pci/oxygen/oxygen_mixer.c | 25 +- trunk/sound/pci/ymfpci/ymfpci.c | 21 +- trunk/sound/pci/ymfpci/ymfpci_main.c | 21 +- trunk/sound/soc/codecs/cs42l73.c | 2 +- trunk/sound/soc/codecs/sgtl5000.c | 17 +- trunk/sound/soc/codecs/tlv320aic32x4.c | 110 +- trunk/sound/soc/codecs/wm2000.c | 31 +- trunk/sound/soc/codecs/wm5100.c | 15 +- trunk/sound/soc/codecs/wm8958-dsp2.c | 2 +- trunk/sound/soc/codecs/wm8962.c | 8 +- trunk/sound/soc/codecs/wm8994.c | 16 +- trunk/sound/soc/codecs/wm8996.c | 9 +- trunk/sound/soc/codecs/wm8996.h | 4 - trunk/sound/soc/codecs/wm_hubs.c | 18 +- trunk/sound/soc/mxs/mxs-saif.c | 5 - trunk/sound/soc/samsung/neo1973_wm8753.c | 65 +- trunk/sound/soc/soc-core.c | 11 - trunk/sound/usb/quirks-table.h | 8 - trunk/tools/perf/Makefile | 7 +- .../tools/perf/bench/mem-memcpy-x86-64-asm.S | 6 - trunk/tools/perf/builtin-probe.c | 2 + trunk/tools/perf/builtin-top.c | 13 +- trunk/tools/perf/util/event.c | 2 +- trunk/tools/perf/util/evsel.c | 1 - trunk/tools/perf/util/header.c | 2 +- trunk/tools/perf/util/probe-event.c | 2 + trunk/tools/perf/util/symbol.c | 1 + trunk/tools/perf/util/trace-event-parse.c | 3 +- trunk/tools/perf/util/ui/browsers/hists.c | 2 + trunk/tools/perf/util/ui/helpline.c | 1 + trunk/tools/perf/util/util.h | 1 + trunk/virt/kvm/kvm_main.c | 2 +- 852 files changed, 50078 insertions(+), 6775 deletions(-) delete mode 100644 trunk/arch/arm/mach-at91/at91sam9g45_reset.S create mode 100644 trunk/arch/arm/mach-at91/include/mach/at91cap9_ddrsdr.h create mode 100644 trunk/drivers/acpi/atomicio.c create mode 100644 trunk/drivers/base/sys.c 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 => }/8250.c (99%) rename trunk/drivers/tty/serial/{8250 => }/8250.h (100%) delete mode 100644 trunk/drivers/tty/serial/8250/Kconfig delete mode 100644 trunk/drivers/tty/serial/8250/Makefile rename trunk/drivers/tty/serial/{8250 => }/8250_accent.c (100%) rename trunk/drivers/tty/serial/{8250 => }/8250_acorn.c (100%) rename trunk/drivers/tty/serial/{8250 => }/8250_boca.c (100%) rename trunk/drivers/tty/serial/{8250 => }/8250_dw.c (100%) rename trunk/drivers/tty/serial/{8250 => }/8250_early.c (100%) rename trunk/drivers/tty/serial/{8250 => }/8250_exar_st16c554.c (100%) rename trunk/drivers/tty/serial/{8250 => }/8250_fourport.c (100%) rename trunk/drivers/tty/serial/{8250 => }/8250_fsl.c (100%) rename trunk/drivers/tty/serial/{8250 => }/8250_gsc.c (100%) rename trunk/drivers/tty/serial/{8250 => }/8250_hp300.c (100%) rename trunk/drivers/tty/serial/{8250 => }/8250_hub6.c (100%) rename trunk/drivers/tty/serial/{8250 => }/8250_mca.c (100%) rename trunk/drivers/tty/serial/{8250 => }/8250_pci.c (100%) rename trunk/drivers/tty/serial/{8250 => }/8250_pnp.c (100%) create mode 100644 trunk/drivers/tty/serial/max3107-aava.c rename trunk/drivers/tty/serial/{8250 => }/serial_cs.c (100%) create mode 100644 trunk/drivers/usb/otg/langwell_otg.c create mode 100644 trunk/include/acpi/atomicio.h mode change 100644 => 100755 trunk/include/linux/lp8727.h create mode 100644 trunk/include/linux/sysdev.h create mode 100644 trunk/include/linux/usb/langwell_otg.h delete mode 100644 trunk/lib/clz_tab.c diff --git a/[refs] b/[refs] index bd8221e11fdf..75f0e1562d89 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 3013dc0cceb9baaf25d5624034eeaa259bf99004 +refs/heads/master: b57e6b560fc2a2742910ac5ca0eb2c46e45aeac2 diff --git a/trunk/Documentation/DocBook/device-drivers.tmpl b/trunk/Documentation/DocBook/device-drivers.tmpl index 9c27e5125dd2..2f7fd4360848 100644 --- a/trunk/Documentation/DocBook/device-drivers.tmpl +++ b/trunk/Documentation/DocBook/device-drivers.tmpl @@ -102,12 +102,9 @@ X!Iinclude/linux/kobject.h !Iinclude/linux/device.h Device Drivers Base -!Idrivers/base/init.c !Edrivers/base/driver.c !Edrivers/base/core.c -!Edrivers/base/syscore.c !Edrivers/base/class.c -!Idrivers/base/node.c !Edrivers/base/firmware_class.c !Edrivers/base/transport_class.c -!Edrivers/base/dd.c +!Edrivers/base/sys.c !Iinclude/linux/platform_device.h !Edrivers/base/platform.c !Edrivers/base/bus.c - - Device Drivers DMA Management -!Edrivers/base/dma-buf.c -!Edrivers/base/dma-coherent.c -!Edrivers/base/dma-mapping.c Device Drivers Power Management !Edrivers/base/power/main.c @@ -227,7 +219,7 @@ X!Isound/sound_firmware.c 16x50 UART Driver !Edrivers/tty/serial/serial_core.c -!Edrivers/tty/serial/8250/8250.c +!Edrivers/tty/serial/8250.c diff --git a/trunk/Documentation/driver-model/devres.txt b/trunk/Documentation/driver-model/devres.txt index 41c0c5d1ba14..10c64c8a13d4 100644 --- a/trunk/Documentation/driver-model/devres.txt +++ b/trunk/Documentation/driver-model/devres.txt @@ -233,10 +233,6 @@ certainly invest a bit more effort into libata core layer). 6. List of managed interfaces ----------------------------- -MEM - devm_kzalloc() - devm_kfree() - IO region devm_request_region() devm_request_mem_region() diff --git a/trunk/Documentation/feature-removal-schedule.txt b/trunk/Documentation/feature-removal-schedule.txt index a0ffac029a0d..1bea46a54b1c 100644 --- a/trunk/Documentation/feature-removal-schedule.txt +++ b/trunk/Documentation/feature-removal-schedule.txt @@ -510,17 +510,3 @@ Why: The pci_scan_bus_parented() interface creates a new root bus. The convert to using pci_scan_root_bus() so they can supply a list of bus resources when the bus is created. Who: Bjorn Helgaas - ----------------------------- - -What: The CAP9 SoC family will be removed -When: 3.4 -Files: arch/arm/mach-at91/at91cap9.c - arch/arm/mach-at91/at91cap9_devices.c - arch/arm/mach-at91/include/mach/at91cap9.h - arch/arm/mach-at91/include/mach/at91cap9_matrix.h - arch/arm/mach-at91/include/mach/at91cap9_ddrsdr.h - arch/arm/mach-at91/board-cap9adk.c -Why: The code is not actively maintained and platforms are now hard to find. -Who: Nicolas Ferre - Jean-Christophe PLAGNIOL-VILLARD diff --git a/trunk/Documentation/input/event-codes.txt b/trunk/Documentation/input/event-codes.txt index 53305bd08182..23fcb05175be 100644 --- a/trunk/Documentation/input/event-codes.txt +++ b/trunk/Documentation/input/event-codes.txt @@ -17,11 +17,11 @@ reports supported by a device are also provided by sysfs in class/input/event*/device/capabilities/, and the properties of a device are provided in class/input/event*/device/properties. -Event types: -=========== -Event types are groupings of codes under a logical input construct. Each -type has a set of applicable codes to be used in generating events. See the -Codes section for details on valid codes for each type. +Types: +========== +Types are groupings of codes under a logical input construct. Each type has a +set of applicable codes to be used in generating events. See the Codes section +for details on valid codes for each type. * EV_SYN: - Used as markers to separate events. Events may be separated in time or in @@ -63,9 +63,9 @@ Codes section for details on valid codes for each type. * EV_FF_STATUS: - Used to receive force feedback device status. -Event codes: -=========== -Event codes define the precise type of event. +Codes: +========== +Codes define the precise type of event. EV_SYN: ---------- @@ -220,56 +220,6 @@ EV_PWR: EV_PWR events are a special type of event used specifically for power mangement. Its usage is not well defined. To be addressed later. -Device properties: -================= -Normally, userspace sets up an input device based on the data it emits, -i.e., the event types. In the case of two devices emitting the same event -types, additional information can be provided in the form of device -properties. - -INPUT_PROP_DIRECT + INPUT_PROP_POINTER: --------------------------------------- -The INPUT_PROP_DIRECT property indicates that device coordinates should be -directly mapped to screen coordinates (not taking into account trivial -transformations, such as scaling, flipping and rotating). Non-direct input -devices require non-trivial transformation, such as absolute to relative -transformation for touchpads. Typical direct input devices: touchscreens, -drawing tablets; non-direct devices: touchpads, mice. - -The INPUT_PROP_POINTER property indicates that the device is not transposed -on the screen and thus requires use of an on-screen pointer to trace user's -movements. Typical pointer devices: touchpads, tablets, mice; non-pointer -device: touchscreen. - -If neither INPUT_PROP_DIRECT or INPUT_PROP_POINTER are set, the property is -considered undefined and the device type should be deduced in the -traditional way, using emitted event types. - -INPUT_PROP_BUTTONPAD: --------------------- -For touchpads where the button is placed beneath the surface, such that -pressing down on the pad causes a button click, this property should be -set. Common in clickpad notebooks and macbooks from 2009 and onwards. - -Originally, the buttonpad property was coded into the bcm5974 driver -version field under the name integrated button. For backwards -compatibility, both methods need to be checked in userspace. - -INPUT_PROP_SEMI_MT: ------------------- -Some touchpads, most common between 2008 and 2011, can detect the presence -of multiple contacts without resolving the individual positions; only the -number of contacts and a rectangular shape is known. For such -touchpads, the semi-mt property should be set. - -Depending on the device, the rectangle may enclose all touches, like a -bounding box, or just some of them, for instance the two most recent -touches. The diversity makes the rectangle of limited use, but some -gestures can normally be extracted from it. - -If INPUT_PROP_SEMI_MT is not set, the device is assumed to be a true MT -device. - Guidelines: ========== The guidelines below ensure proper single-touch and multi-finger functionality. @@ -290,8 +240,6 @@ used to report when a touch is active on the screen. BTN_{MOUSE,LEFT,MIDDLE,RIGHT} must not be reported as the result of touch contact. BTN_TOOL_ events should be reported where possible. -For new hardware, INPUT_PROP_DIRECT should be set. - Trackpads: ---------- Legacy trackpads that only provide relative position information must report @@ -302,8 +250,6 @@ location of the touch. BTN_TOUCH should be used to report when a touch is active on the trackpad. Where multi-finger support is available, BTN_TOOL_ should be used to report the number of touches active on the trackpad. -For new hardware, INPUT_PROP_POINTER should be set. - Tablets: ---------- BTN_TOOL_ events must be reported when a stylus or other tool is active on @@ -314,5 +260,3 @@ button may be used for buttons on the tablet except BTN_{MOUSE,LEFT}. BTN_{0,1,2,etc} are good generic codes for unlabeled buttons. Do not use meaningful buttons, like BTN_FORWARD, unless the button is labeled for that purpose on the device. - -For new hardware, both INPUT_PROP_DIRECT and INPUT_PROP_POINTER should be set. diff --git a/trunk/Documentation/pinctrl.txt b/trunk/Documentation/pinctrl.txt index 150fd3833d0b..6727b92bc2fb 100644 --- a/trunk/Documentation/pinctrl.txt +++ b/trunk/Documentation/pinctrl.txt @@ -857,41 +857,42 @@ case), we define a mapping like this: ... { - .name = "2bit" + .name "2bit" .ctrl_dev_name = "pinctrl-foo", .function = "mmc0", .group = "mmc0_1_grp", .dev_name = "foo-mmc.0", }, { - .name = "4bit" + .name "4bit" .ctrl_dev_name = "pinctrl-foo", .function = "mmc0", .group = "mmc0_1_grp", .dev_name = "foo-mmc.0", }, { - .name = "4bit" + .name "4bit" .ctrl_dev_name = "pinctrl-foo", .function = "mmc0", .group = "mmc0_2_grp", .dev_name = "foo-mmc.0", }, { - .name = "8bit" + .name "8bit" .ctrl_dev_name = "pinctrl-foo", + .function = "mmc0", .group = "mmc0_1_grp", .dev_name = "foo-mmc.0", }, { - .name = "8bit" + .name "8bit" .ctrl_dev_name = "pinctrl-foo", .function = "mmc0", .group = "mmc0_2_grp", .dev_name = "foo-mmc.0", }, { - .name = "8bit" + .name "8bit" .ctrl_dev_name = "pinctrl-foo", .function = "mmc0", .group = "mmc0_3_grp", @@ -994,7 +995,7 @@ This is enabled by simply setting the .hog_on_boot field in the map to true, like this: { - .name = "POWERMAP" + .name "POWERMAP" .ctrl_dev_name = "pinctrl-foo", .function = "power_func", .hog_on_boot = true, @@ -1024,7 +1025,7 @@ it, disables and releases it, and muxes it in on the pins defined by group B: foo_switch() { - struct pinmux *pmx; + struct pinmux pmx; /* Enable on position A */ pmx = pinmux_get(&device, "spi0-pos-A"); diff --git a/trunk/Documentation/stable_kernel_rules.txt b/trunk/Documentation/stable_kernel_rules.txt index f0ab5cf28fca..21fd05c28e73 100644 --- a/trunk/Documentation/stable_kernel_rules.txt +++ b/trunk/Documentation/stable_kernel_rules.txt @@ -25,8 +25,7 @@ Procedure for submitting patches to the -stable tree: - Send the patch, after verifying that it follows the above rules, to stable@vger.kernel.org. You must note the upstream commit ID in the - changelog of your submission, as well as the kernel version you wish - it to be applied to. + changelog of your submission. - To have the patch automatically included in the stable tree, add the tag Cc: stable@vger.kernel.org in the sign-off area. Once the patch is merged it will be applied to diff --git a/trunk/Documentation/sysctl/kernel.txt b/trunk/Documentation/sysctl/kernel.txt index 6d78841fd416..8c20fbd8b42d 100644 --- a/trunk/Documentation/sysctl/kernel.txt +++ b/trunk/Documentation/sysctl/kernel.txt @@ -601,8 +601,6 @@ can be ORed together: instead of using the one provided by the hardware. 512 - A kernel warning has occurred. 1024 - A module from drivers/staging was loaded. -2048 - The system is working around a severe firmware bug. -4096 - An out-of-tree module has been loaded. ============================================================== diff --git a/trunk/Documentation/thermal/sysfs-api.txt b/trunk/Documentation/thermal/sysfs-api.txt index 1733ab947a95..b61e46f449aa 100644 --- a/trunk/Documentation/thermal/sysfs-api.txt +++ b/trunk/Documentation/thermal/sysfs-api.txt @@ -284,7 +284,7 @@ method, the sys I/F structure will be built like this: The framework includes a simple notification mechanism, in the form of a netlink event. Netlink socket initialization is done during the _init_ of the framework. Drivers which intend to use the notification mechanism -just need to call thermal_generate_netlink_event() with two arguments viz +just need to call generate_netlink_event() with two arguments viz (originator, event). Typically the originator will be an integer assigned to a thermal_zone_device when it registers itself with the framework. The event will be one of:{THERMAL_AUX0, THERMAL_AUX1, THERMAL_CRITICAL, diff --git a/trunk/Documentation/virtual/00-INDEX b/trunk/Documentation/virtual/00-INDEX index 924bd462675e..8e601991d91c 100644 --- a/trunk/Documentation/virtual/00-INDEX +++ b/trunk/Documentation/virtual/00-INDEX @@ -4,6 +4,8 @@ Virtualization support in the Linux kernel. - this file. kvm/ - Kernel Virtual Machine. See also http://linux-kvm.org +lguest/ + - Extremely simple hypervisor for experimental/educational use. uml/ - User Mode Linux, builds/runs Linux kernel as a userspace program. virtio.txt diff --git a/trunk/MAINTAINERS b/trunk/MAINTAINERS index 9a648eb8e213..93c68d5f1cf4 100644 --- a/trunk/MAINTAINERS +++ b/trunk/MAINTAINERS @@ -159,7 +159,7 @@ S: Maintained F: drivers/net/ethernet/realtek/r8169.c 8250/16?50 (AND CLONE UARTS) SERIAL DRIVER -M: Greg Kroah-Hartman +M: Greg Kroah-Hartman L: linux-serial@vger.kernel.org W: http://serial.sourceforge.net S: Maintained @@ -789,6 +789,12 @@ F: arch/arm/mach-mx*/ F: arch/arm/mach-imx/ F: arch/arm/plat-mxc/ +ARM/FREESCALE IMX51 +M: Amit Kucheria +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: arch/arm/mach-mx5/ + ARM/FREESCALE IMX6 M: Shawn Guo L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) @@ -1777,9 +1783,9 @@ X: net/wireless/wext* CHAR and MISC DRIVERS M: Arnd Bergmann -M: Greg Kroah-Hartman +M: Greg Kroah-Hartman T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git -S: Supported +S: Maintained F: drivers/char/* F: drivers/misc/* @@ -2281,7 +2287,7 @@ F: drivers/acpi/dock.c DOCUMENTATION M: Randy Dunlap L: linux-doc@vger.kernel.org -T: quilt http://xenotime.net/kernel-doc-patches/current/ +T: quilt http://userweb.kernel.org/~rdunlap/kernel-doc-patches/current/ S: Maintained F: Documentation/ @@ -2314,7 +2320,7 @@ F: lib/lru_cache.c F: Documentation/blockdev/drbd/ DRIVER CORE, KOBJECTS, DEBUGFS AND SYSFS -M: Greg Kroah-Hartman +M: Greg Kroah-Hartman T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6.git S: Supported F: Documentation/kobject.txt @@ -2344,9 +2350,6 @@ F: include/drm/i915* DRM DRIVERS FOR EXYNOS M: Inki Dae -M: Joonyoung Shim -M: Seung-Woo Kim -M: Kyungmin Park L: dri-devel@lists.freedesktop.org S: Supported F: drivers/gpu/drm/exynos @@ -2399,7 +2402,7 @@ F: net/bridge/netfilter/ebt*.c ECRYPT FILE SYSTEM M: Tyler Hicks -M: Dustin Kirkland +M: Dustin Kirkland L: ecryptfs@vger.kernel.org W: https://launchpad.net/ecryptfs S: Supported @@ -3318,12 +3321,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 @@ -3992,11 +3989,11 @@ M: Rusty Russell L: lguest@lists.ozlabs.org W: http://lguest.ozlabs.org/ S: Odd Fixes -F: arch/x86/include/asm/lguest*.h +F: Documentation/virtual/lguest/ F: arch/x86/lguest/ F: drivers/lguest/ F: include/linux/lguest*.h -F: tools/lguest/ +F: arch/x86/include/asm/lguest*.h LINUX FOR IBM pSERIES (RS/6000) M: Paul Mackerras @@ -4136,11 +4133,10 @@ L: linux-ntfs-dev@lists.sourceforge.net W: http://www.linux-ntfs.org/content/view/19/37/ S: Maintained F: Documentation/ldm.txt -F: block/partitions/ldm.* +F: fs/partitions/ldm.* LogFS M: Joern Engel -M: Prasad Joshi L: logfs@logfs.org W: logfs.org S: Maintained @@ -4282,6 +4278,13 @@ S: Orphan F: drivers/video/matrox/matroxfb_* F: include/linux/matroxfb.h +MAX1668 TEMPERATURE SENSOR DRIVER +M: "David George" +L: lm-sensors@lm-sensors.org +S: Maintained +F: Documentation/hwmon/max1668 +F: drivers/hwmon/max1668.c + MAX6650 HARDWARE MONITOR AND FAN CONTROLLER DRIVER M: "Hans J. Koch" L: lm-sensors@lm-sensors.org @@ -5633,7 +5636,7 @@ W: http://www.ibm.com/developerworks/linux/linux390/ S: Supported F: arch/s390/ F: drivers/s390/ -F: block/partitions/ibm.c +F: fs/partitions/ibm.c F: Documentation/s390/ F: Documentation/DocBook/s390* @@ -6276,15 +6279,15 @@ S: Maintained F: arch/alpha/kernel/srm_env.c STABLE BRANCH -M: Greg Kroah-Hartman +M: Greg Kroah-Hartman L: stable@vger.kernel.org -S: Supported +S: Maintained STAGING SUBSYSTEM -M: Greg Kroah-Hartman +M: Greg Kroah-Hartman T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git L: devel@driverdev.osuosl.org -S: Supported +S: Maintained F: drivers/staging/ STAGING - AGERE HERMES II and II.5 WIRELESS DRIVERS @@ -6396,6 +6399,11 @@ M: Omar Ramirez Luna S: Odd Fixes F: drivers/staging/tidspbridge/ +STAGING - TRIDENT TVMASTER TMxxxx USB VIDEO CAPTURE DRIVERS +L: linux-media@vger.kernel.org +S: Odd Fixes +F: drivers/staging/tm6000/ + STAGING - USB ENE SM/MS CARD READER DRIVER M: Al Cho S: Odd Fixes @@ -6664,10 +6672,10 @@ S: Maintained K: ^Subject:.*(?i)trivial TTY LAYER -M: Greg Kroah-Hartman -S: Supported +M: Greg Kroah-Hartman +S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty-2.6.git -F: drivers/tty/ +F: drivers/tty/* F: drivers/tty/serial/serial_core.c F: include/linux/serial_core.h F: include/linux/serial.h @@ -6953,7 +6961,7 @@ S: Maintained F: drivers/usb/serial/digi_acceleport.c USB SERIAL DRIVER -M: Greg Kroah-Hartman +M: Greg Kroah-Hartman L: linux-usb@vger.kernel.org S: Supported F: Documentation/usb/usb-serial.txt @@ -6968,8 +6976,9 @@ S: Maintained F: drivers/usb/serial/empeg.c USB SERIAL KEYSPAN DRIVER -M: Greg Kroah-Hartman +M: Greg Kroah-Hartman L: linux-usb@vger.kernel.org +W: http://www.kroah.com/linux/ S: Maintained F: drivers/usb/serial/*keyspan* @@ -6997,7 +7006,7 @@ F: Documentation/video4linux/sn9c102.txt F: drivers/media/video/sn9c102/ USB SUBSYSTEM -M: Greg Kroah-Hartman +M: Greg Kroah-Hartman L: linux-usb@vger.kernel.org W: http://www.linux-usb.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6.git @@ -7084,7 +7093,7 @@ F: fs/hppfs/ USERSPACE I/O (UIO) M: "Hans J. Koch" -M: Greg Kroah-Hartman +M: Greg Kroah-Hartman S: Maintained F: Documentation/DocBook/uio-howto.tmpl F: drivers/uio/ @@ -7359,7 +7368,6 @@ S: Supported F: Documentation/hwmon/wm83?? F: arch/arm/mach-s3c64xx/mach-crag6410* F: drivers/leds/leds-wm83*.c -F: drivers/hwmon/wm83??-hwmon.c F: drivers/input/misc/wm831x-on.c F: drivers/input/touchscreen/wm831x-ts.c F: drivers/input/touchscreen/wm97*.c diff --git a/trunk/Makefile b/trunk/Makefile index 7c44b676b77a..71e6ed21dd15 100644 --- a/trunk/Makefile +++ b/trunk/Makefile @@ -1,7 +1,7 @@ VERSION = 3 PATCHLEVEL = 3 SUBLEVEL = 0 -EXTRAVERSION = -rc3 +EXTRAVERSION = -rc1 NAME = Saber-toothed Squirrel # *DOCUMENTATION* diff --git a/trunk/arch/arm/Kconfig b/trunk/arch/arm/Kconfig index a48aecc17eac..24626b0419ee 100644 --- a/trunk/arch/arm/Kconfig +++ b/trunk/arch/arm/Kconfig @@ -754,7 +754,7 @@ config ARCH_SA1100 select ARCH_HAS_CPUFREQ select CPU_FREQ select GENERIC_CLOCKEVENTS - select HAVE_CLK + select CLKDEV_LOOKUP select HAVE_SCHED_CLOCK select TICK_ONESHOT select ARCH_REQUIRE_GPIOLIB @@ -825,6 +825,7 @@ config ARCH_S5PC100 select HAVE_CLK select CLKDEV_LOOKUP select CPU_V7 + select ARM_L1_CACHE_SHIFT_6 select ARCH_USES_GETTIMEOFFSET select HAVE_S3C2410_I2C if I2C select HAVE_S3C_RTC if RTC_CLASS @@ -841,6 +842,7 @@ config ARCH_S5PV210 select HAVE_CLK select CLKDEV_LOOKUP select CLKSRC_MMIO + select ARM_L1_CACHE_SHIFT_6 select ARCH_HAS_CPUFREQ select GENERIC_CLOCKEVENTS select HAVE_SCHED_CLOCK diff --git a/trunk/arch/arm/common/gic.c b/trunk/arch/arm/common/gic.c index c47d6199b784..b2dc2dd7f1df 100644 --- a/trunk/arch/arm/common/gic.c +++ b/trunk/arch/arm/common/gic.c @@ -41,7 +41,6 @@ #include #include -#include #include #include @@ -353,7 +352,11 @@ static void __init gic_dist_init(struct gic_chip_data *gic) unsigned int gic_irqs = gic->gic_irqs; struct irq_domain *domain = &gic->domain; void __iomem *base = gic_data_dist_base(gic); - u32 cpu = cpu_logical_map(smp_processor_id()); + u32 cpu = 0; + +#ifdef CONFIG_SMP + cpu = cpu_logical_map(smp_processor_id()); +#endif cpumask = 1 << cpu; cpumask |= cpumask << 8; diff --git a/trunk/arch/arm/include/asm/assembler.h b/trunk/arch/arm/include/asm/assembler.h index 62f8095d46de..b6e65dedfd71 100644 --- a/trunk/arch/arm/include/asm/assembler.h +++ b/trunk/arch/arm/include/asm/assembler.h @@ -237,7 +237,7 @@ */ #ifdef CONFIG_THUMB2_KERNEL - .macro usraccoff, instr, reg, ptr, inc, off, cond, abort, t=TUSER() + .macro usraccoff, instr, reg, ptr, inc, off, cond, abort, t=T() 9999: .if \inc == 1 \instr\cond\()b\()\t\().w \reg, [\ptr, #\off] @@ -277,7 +277,7 @@ #else /* !CONFIG_THUMB2_KERNEL */ - .macro usracc, instr, reg, ptr, inc, cond, rept, abort, t=TUSER() + .macro usracc, instr, reg, ptr, inc, cond, rept, abort, t=T() .rept \rept 9999: .if \inc == 1 diff --git a/trunk/arch/arm/include/asm/domain.h b/trunk/arch/arm/include/asm/domain.h index b5dc173d336f..af18ceaacf5d 100644 --- a/trunk/arch/arm/include/asm/domain.h +++ b/trunk/arch/arm/include/asm/domain.h @@ -83,9 +83,9 @@ * instructions (inline assembly) */ #ifdef CONFIG_CPU_USE_DOMAINS -#define TUSER(instr) #instr "t" +#define T(instr) #instr "t" #else -#define TUSER(instr) #instr +#define T(instr) #instr #endif #else /* __ASSEMBLY__ */ @@ -95,9 +95,9 @@ * instructions */ #ifdef CONFIG_CPU_USE_DOMAINS -#define TUSER(instr) instr ## t +#define T(instr) instr ## t #else -#define TUSER(instr) instr +#define T(instr) instr #endif #endif /* __ASSEMBLY__ */ diff --git a/trunk/arch/arm/include/asm/futex.h b/trunk/arch/arm/include/asm/futex.h index 7be54690aeec..253cc86318bf 100644 --- a/trunk/arch/arm/include/asm/futex.h +++ b/trunk/arch/arm/include/asm/futex.h @@ -75,9 +75,9 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, #define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg) \ __asm__ __volatile__( \ - "1: " TUSER(ldr) " %1, [%3]\n" \ + "1: " T(ldr) " %1, [%3]\n" \ " " insn "\n" \ - "2: " TUSER(str) " %0, [%3]\n" \ + "2: " T(str) " %0, [%3]\n" \ " mov %0, #0\n" \ __futex_atomic_ex_table("%5") \ : "=&r" (ret), "=&r" (oldval), "=&r" (tmp) \ @@ -95,10 +95,10 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, return -EFAULT; __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n" - "1: " TUSER(ldr) " %1, [%4]\n" + "1: " T(ldr) " %1, [%4]\n" " teq %1, %2\n" " it eq @ explicit IT needed for the 2b label\n" - "2: " TUSER(streq) " %3, [%4]\n" + "2: " T(streq) " %3, [%4]\n" __futex_atomic_ex_table("%5") : "+r" (ret), "=&r" (val) : "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT) diff --git a/trunk/arch/arm/include/asm/smp.h b/trunk/arch/arm/include/asm/smp.h index ae29293270a3..1e5717afc4ac 100644 --- a/trunk/arch/arm/include/asm/smp.h +++ b/trunk/arch/arm/include/asm/smp.h @@ -70,6 +70,12 @@ extern void platform_secondary_init(unsigned int cpu); */ extern void platform_smp_prepare_cpus(unsigned int); +/* + * Logical CPU mapping. + */ +extern int __cpu_logical_map[NR_CPUS]; +#define cpu_logical_map(cpu) __cpu_logical_map[cpu] + /* * Initial data for bringing up a secondary CPU. */ diff --git a/trunk/arch/arm/include/asm/smp_plat.h b/trunk/arch/arm/include/asm/smp_plat.h index 558d6c80aca9..f24c1b9e211d 100644 --- a/trunk/arch/arm/include/asm/smp_plat.h +++ b/trunk/arch/arm/include/asm/smp_plat.h @@ -43,10 +43,4 @@ static inline int cache_ops_need_broadcast(void) } #endif -/* - * Logical CPU mapping. - */ -extern int __cpu_logical_map[]; -#define cpu_logical_map(cpu) __cpu_logical_map[cpu] - #endif diff --git a/trunk/arch/arm/include/asm/tlb.h b/trunk/arch/arm/include/asm/tlb.h index 314d4664eae7..5d3ed7e38561 100644 --- a/trunk/arch/arm/include/asm/tlb.h +++ b/trunk/arch/arm/include/asm/tlb.h @@ -198,15 +198,7 @@ static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte, unsigned long addr) { pgtable_page_dtor(pte); - - /* - * With the classic ARM MMU, a pte page has two corresponding pmd - * entries, each covering 1MB. - */ - addr &= PMD_MASK; - tlb_add_flush(tlb, addr + SZ_1M - PAGE_SIZE); - tlb_add_flush(tlb, addr + SZ_1M); - + tlb_add_flush(tlb, addr); tlb_remove_page(tlb, pte); } diff --git a/trunk/arch/arm/include/asm/uaccess.h b/trunk/arch/arm/include/asm/uaccess.h index 2958976d867b..b293616a1a1a 100644 --- a/trunk/arch/arm/include/asm/uaccess.h +++ b/trunk/arch/arm/include/asm/uaccess.h @@ -227,7 +227,7 @@ do { \ #define __get_user_asm_byte(x,addr,err) \ __asm__ __volatile__( \ - "1: " TUSER(ldrb) " %1,[%2],#0\n" \ + "1: " T(ldrb) " %1,[%2],#0\n" \ "2:\n" \ " .pushsection .fixup,\"ax\"\n" \ " .align 2\n" \ @@ -263,7 +263,7 @@ do { \ #define __get_user_asm_word(x,addr,err) \ __asm__ __volatile__( \ - "1: " TUSER(ldr) " %1,[%2],#0\n" \ + "1: " T(ldr) " %1,[%2],#0\n" \ "2:\n" \ " .pushsection .fixup,\"ax\"\n" \ " .align 2\n" \ @@ -308,7 +308,7 @@ do { \ #define __put_user_asm_byte(x,__pu_addr,err) \ __asm__ __volatile__( \ - "1: " TUSER(strb) " %1,[%2],#0\n" \ + "1: " T(strb) " %1,[%2],#0\n" \ "2:\n" \ " .pushsection .fixup,\"ax\"\n" \ " .align 2\n" \ @@ -341,7 +341,7 @@ do { \ #define __put_user_asm_word(x,__pu_addr,err) \ __asm__ __volatile__( \ - "1: " TUSER(str) " %1,[%2],#0\n" \ + "1: " T(str) " %1,[%2],#0\n" \ "2:\n" \ " .pushsection .fixup,\"ax\"\n" \ " .align 2\n" \ @@ -366,10 +366,10 @@ do { \ #define __put_user_asm_dword(x,__pu_addr,err) \ __asm__ __volatile__( \ - ARM( "1: " TUSER(str) " " __reg_oper1 ", [%1], #4\n" ) \ - ARM( "2: " TUSER(str) " " __reg_oper0 ", [%1]\n" ) \ - THUMB( "1: " TUSER(str) " " __reg_oper1 ", [%1]\n" ) \ - THUMB( "2: " TUSER(str) " " __reg_oper0 ", [%1, #4]\n" ) \ + ARM( "1: " T(str) " " __reg_oper1 ", [%1], #4\n" ) \ + ARM( "2: " T(str) " " __reg_oper0 ", [%1]\n" ) \ + THUMB( "1: " T(str) " " __reg_oper1 ", [%1]\n" ) \ + THUMB( "2: " T(str) " " __reg_oper0 ", [%1, #4]\n" ) \ "3:\n" \ " .pushsection .fixup,\"ax\"\n" \ " .align 2\n" \ diff --git a/trunk/arch/arm/kernel/entry-armv.S b/trunk/arch/arm/kernel/entry-armv.S index be16a48007b4..3a456c6c7005 100644 --- a/trunk/arch/arm/kernel/entry-armv.S +++ b/trunk/arch/arm/kernel/entry-armv.S @@ -790,7 +790,7 @@ __kuser_cmpxchg64: @ 0xffff0f60 smp_dmb arm rsbs r0, r3, #0 @ set returned val and C flag ldmfd sp!, {r4, r5, r6, r7} - usr_ret lr + bx lr #elif !defined(CONFIG_SMP) diff --git a/trunk/arch/arm/kernel/entry-common.S b/trunk/arch/arm/kernel/entry-common.S index 9fd0ba90c1d2..520889cf1b5b 100644 --- a/trunk/arch/arm/kernel/entry-common.S +++ b/trunk/arch/arm/kernel/entry-common.S @@ -149,11 +149,6 @@ ENDPROC(ret_from_fork) #endif #endif -.macro mcount_adjust_addr rd, rn - bic \rd, \rn, #1 @ clear the Thumb bit if present - sub \rd, \rd, #MCOUNT_INSN_SIZE -.endm - .macro __mcount suffix mcount_enter ldr r0, =ftrace_trace_function @@ -178,7 +173,8 @@ ENDPROC(ret_from_fork) mcount_exit 1: mcount_get_lr r1 @ lr of instrumented func - mcount_adjust_addr r0, lr @ instrumented function + mov r0, lr @ instrumented function + sub r0, r0, #MCOUNT_INSN_SIZE adr lr, BSYM(2f) mov pc, r2 2: mcount_exit @@ -188,7 +184,8 @@ ENDPROC(ret_from_fork) mcount_enter mcount_get_lr r1 @ lr of instrumented func - mcount_adjust_addr r0, lr @ instrumented function + mov r0, lr @ instrumented function + sub r0, r0, #MCOUNT_INSN_SIZE .globl ftrace_call\suffix ftrace_call\suffix: @@ -208,11 +205,11 @@ ftrace_graph_call\suffix: #ifdef CONFIG_DYNAMIC_FTRACE @ called from __ftrace_caller, saved in mcount_enter ldr r1, [sp, #16] @ instrumented routine (func) - mcount_adjust_addr r1, r1 #else @ called from __mcount, untouched in lr - mcount_adjust_addr r1, lr @ instrumented routine (func) + mov r1, lr @ instrumented routine (func) #endif + sub r1, r1, #MCOUNT_INSN_SIZE mov r2, fp @ frame pointer bl prepare_ftrace_return mcount_exit diff --git a/trunk/arch/arm/kernel/perf_event_v7.c b/trunk/arch/arm/kernel/perf_event_v7.c index 6933244c68f9..460bbbb6b885 100644 --- a/trunk/arch/arm/kernel/perf_event_v7.c +++ b/trunk/arch/arm/kernel/perf_event_v7.c @@ -469,20 +469,6 @@ static const unsigned armv7_a5_perf_cache_map[PERF_COUNT_HW_CACHE_MAX] [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, }, }, - [C(NODE)] = { - [C(OP_READ)] = { - [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - [C(OP_WRITE)] = { - [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - [C(OP_PREFETCH)] = { - [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - }, }; /* @@ -593,20 +579,6 @@ static const unsigned armv7_a15_perf_cache_map[PERF_COUNT_HW_CACHE_MAX] [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, }, }, - [C(NODE)] = { - [C(OP_READ)] = { - [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - [C(OP_WRITE)] = { - [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - [C(OP_PREFETCH)] = { - [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, - [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, - }, - }, }; /* diff --git a/trunk/arch/arm/kernel/ptrace.c b/trunk/arch/arm/kernel/ptrace.c index e33870ff0ac0..e1d5e1929fbd 100644 --- a/trunk/arch/arm/kernel/ptrace.c +++ b/trunk/arch/arm/kernel/ptrace.c @@ -699,13 +699,10 @@ static int vfp_set(struct task_struct *target, { int ret; struct thread_info *thread = task_thread_info(target); - struct vfp_hard_struct new_vfp; + struct vfp_hard_struct new_vfp = thread->vfpstate.hard; const size_t user_fpregs_offset = offsetof(struct user_vfp, fpregs); const size_t user_fpscr_offset = offsetof(struct user_vfp, fpscr); - vfp_sync_hwstate(thread); - new_vfp = thread->vfpstate.hard; - ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &new_vfp.fpregs, user_fpregs_offset, @@ -726,8 +723,9 @@ static int vfp_set(struct task_struct *target, if (ret) return ret; - vfp_flush_hwstate(thread); + vfp_sync_hwstate(thread); thread->vfpstate.hard = new_vfp; + vfp_flush_hwstate(thread); return 0; } diff --git a/trunk/arch/arm/kernel/setup.c b/trunk/arch/arm/kernel/setup.c index a255c39612ca..129fbd55bde8 100644 --- a/trunk/arch/arm/kernel/setup.c +++ b/trunk/arch/arm/kernel/setup.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -159,7 +160,7 @@ static struct resource mem_res[] = { .flags = IORESOURCE_MEM }, { - .name = "Kernel code", + .name = "Kernel text", .start = 0, .end = 0, .flags = IORESOURCE_MEM @@ -426,20 +427,6 @@ void cpu_init(void) : "r14"); } -int __cpu_logical_map[NR_CPUS]; - -void __init smp_setup_processor_id(void) -{ - int i; - u32 cpu = is_smp() ? read_cpuid_mpidr() & 0xff : 0; - - cpu_logical_map(0) = cpu; - for (i = 1; i < NR_CPUS; ++i) - cpu_logical_map(i) = i == cpu ? 0 : i; - - printk(KERN_INFO "Booting Linux on physical CPU %d\n", cpu); -} - static void __init setup_processor(void) { struct proc_info_list *list; diff --git a/trunk/arch/arm/kernel/signal.c b/trunk/arch/arm/kernel/signal.c index 9e617bd4a146..0340224cf73c 100644 --- a/trunk/arch/arm/kernel/signal.c +++ b/trunk/arch/arm/kernel/signal.c @@ -227,8 +227,6 @@ static int restore_vfp_context(struct vfp_sigframe __user *frame) if (magic != VFP_MAGIC || size != VFP_STORAGE_SIZE) return -EINVAL; - vfp_flush_hwstate(thread); - /* * Copy the floating point registers. There can be unused * registers see asm/hwcap.h for details. @@ -253,6 +251,9 @@ static int restore_vfp_context(struct vfp_sigframe __user *frame) __get_user_error(h->fpinst, &frame->ufp_exc.fpinst, err); __get_user_error(h->fpinst2, &frame->ufp_exc.fpinst2, err); + if (!err) + vfp_flush_hwstate(thread); + return err ? -EFAULT : 0; } diff --git a/trunk/arch/arm/kernel/smp.c b/trunk/arch/arm/kernel/smp.c index cdeb727527d3..57db122a4f62 100644 --- a/trunk/arch/arm/kernel/smp.c +++ b/trunk/arch/arm/kernel/smp.c @@ -233,6 +233,20 @@ void __ref cpu_die(void) } #endif /* CONFIG_HOTPLUG_CPU */ +int __cpu_logical_map[NR_CPUS]; + +void __init smp_setup_processor_id(void) +{ + int i; + u32 cpu = is_smp() ? read_cpuid_mpidr() & 0xff : 0; + + cpu_logical_map(0) = cpu; + for (i = 1; i < NR_CPUS; ++i) + cpu_logical_map(i) = i == cpu ? 0 : i; + + printk(KERN_INFO "Booting Linux on physical CPU %d\n", cpu); +} + /* * Called by both boot and secondaries to move global data into * per-processor storage. @@ -429,7 +443,9 @@ static DEFINE_PER_CPU(struct clock_event_device, percpu_clockevent); static void ipi_timer(void) { struct clock_event_device *evt = &__get_cpu_var(percpu_clockevent); + irq_enter(); evt->event_handler(evt); + irq_exit(); } #ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST @@ -532,9 +548,7 @@ void handle_IPI(int ipinr, struct pt_regs *regs) switch (ipinr) { case IPI_TIMER: - irq_enter(); ipi_timer(); - irq_exit(); break; case IPI_RESCHEDULE: @@ -542,21 +556,15 @@ void handle_IPI(int ipinr, struct pt_regs *regs) break; case IPI_CALL_FUNC: - irq_enter(); generic_smp_call_function_interrupt(); - irq_exit(); break; case IPI_CALL_FUNC_SINGLE: - irq_enter(); generic_smp_call_function_single_interrupt(); - irq_exit(); break; case IPI_CPU_STOP: - irq_enter(); ipi_cpu_stop(cpu); - irq_exit(); break; default: diff --git a/trunk/arch/arm/kernel/smp_twd.c b/trunk/arch/arm/kernel/smp_twd.c index 4285daa077b0..c8e938553d47 100644 --- a/trunk/arch/arm/kernel/smp_twd.c +++ b/trunk/arch/arm/kernel/smp_twd.c @@ -252,8 +252,6 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk) else twd_calibrate_rate(); - __raw_writel(0, twd_base + TWD_TIMER_CONTROL); - clk->name = "local_timer"; clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP; diff --git a/trunk/arch/arm/kernel/vmlinux.lds.S b/trunk/arch/arm/kernel/vmlinux.lds.S index 1e19691e0406..f76e75548670 100644 --- a/trunk/arch/arm/kernel/vmlinux.lds.S +++ b/trunk/arch/arm/kernel/vmlinux.lds.S @@ -4,7 +4,6 @@ */ #include -#include #include #include #include @@ -182,7 +181,7 @@ SECTIONS } #endif - PERCPU_SECTION(L1_CACHE_BYTES) + PERCPU_SECTION(32) #ifdef CONFIG_XIP_KERNEL __data_loc = ALIGN(4); /* location in binary */ @@ -213,13 +212,13 @@ SECTIONS #endif NOSAVE_DATA - CACHELINE_ALIGNED_DATA(L1_CACHE_BYTES) - READ_MOSTLY_DATA(L1_CACHE_BYTES) + CACHELINE_ALIGNED_DATA(32) + READ_MOSTLY_DATA(32) /* * The exception fixup table (might need resorting at runtime) */ - . = ALIGN(4); + . = ALIGN(32); __start___ex_table = .; #ifdef CONFIG_MMU *(__ex_table) diff --git a/trunk/arch/arm/lib/getuser.S b/trunk/arch/arm/lib/getuser.S index 11093a7c3e32..1b049cd7a49a 100644 --- a/trunk/arch/arm/lib/getuser.S +++ b/trunk/arch/arm/lib/getuser.S @@ -31,18 +31,18 @@ #include ENTRY(__get_user_1) -1: TUSER(ldrb) r2, [r0] +1: T(ldrb) r2, [r0] mov r0, #0 mov pc, lr ENDPROC(__get_user_1) ENTRY(__get_user_2) #ifdef CONFIG_THUMB2_KERNEL -2: TUSER(ldrb) r2, [r0] -3: TUSER(ldrb) r3, [r0, #1] +2: T(ldrb) r2, [r0] +3: T(ldrb) r3, [r0, #1] #else -2: TUSER(ldrb) r2, [r0], #1 -3: TUSER(ldrb) r3, [r0] +2: T(ldrb) r2, [r0], #1 +3: T(ldrb) r3, [r0] #endif #ifndef __ARMEB__ orr r2, r2, r3, lsl #8 @@ -54,7 +54,7 @@ ENTRY(__get_user_2) ENDPROC(__get_user_2) ENTRY(__get_user_4) -4: TUSER(ldr) r2, [r0] +4: T(ldr) r2, [r0] mov r0, #0 mov pc, lr ENDPROC(__get_user_4) diff --git a/trunk/arch/arm/lib/putuser.S b/trunk/arch/arm/lib/putuser.S index 7db25990c589..c023fc11e86c 100644 --- a/trunk/arch/arm/lib/putuser.S +++ b/trunk/arch/arm/lib/putuser.S @@ -31,7 +31,7 @@ #include ENTRY(__put_user_1) -1: TUSER(strb) r2, [r0] +1: T(strb) r2, [r0] mov r0, #0 mov pc, lr ENDPROC(__put_user_1) @@ -40,19 +40,19 @@ ENTRY(__put_user_2) mov ip, r2, lsr #8 #ifdef CONFIG_THUMB2_KERNEL #ifndef __ARMEB__ -2: TUSER(strb) r2, [r0] -3: TUSER(strb) ip, [r0, #1] +2: T(strb) r2, [r0] +3: T(strb) ip, [r0, #1] #else -2: TUSER(strb) ip, [r0] -3: TUSER(strb) r2, [r0, #1] +2: T(strb) ip, [r0] +3: T(strb) r2, [r0, #1] #endif #else /* !CONFIG_THUMB2_KERNEL */ #ifndef __ARMEB__ -2: TUSER(strb) r2, [r0], #1 -3: TUSER(strb) ip, [r0] +2: T(strb) r2, [r0], #1 +3: T(strb) ip, [r0] #else -2: TUSER(strb) ip, [r0], #1 -3: TUSER(strb) r2, [r0] +2: T(strb) ip, [r0], #1 +3: T(strb) r2, [r0] #endif #endif /* CONFIG_THUMB2_KERNEL */ mov r0, #0 @@ -60,18 +60,18 @@ ENTRY(__put_user_2) ENDPROC(__put_user_2) ENTRY(__put_user_4) -4: TUSER(str) r2, [r0] +4: T(str) r2, [r0] mov r0, #0 mov pc, lr ENDPROC(__put_user_4) ENTRY(__put_user_8) #ifdef CONFIG_THUMB2_KERNEL -5: TUSER(str) r2, [r0] -6: TUSER(str) r3, [r0, #4] +5: T(str) r2, [r0] +6: T(str) r3, [r0, #4] #else -5: TUSER(str) r2, [r0], #4 -6: TUSER(str) r3, [r0] +5: T(str) r2, [r0], #4 +6: T(str) r3, [r0] #endif mov r0, #0 mov pc, lr diff --git a/trunk/arch/arm/lib/uaccess.S b/trunk/arch/arm/lib/uaccess.S index 5c908b1cb8ed..d0ece2aeb70d 100644 --- a/trunk/arch/arm/lib/uaccess.S +++ b/trunk/arch/arm/lib/uaccess.S @@ -32,11 +32,11 @@ rsb ip, ip, #4 cmp ip, #2 ldrb r3, [r1], #1 -USER( TUSER( strb) r3, [r0], #1) @ May fault +USER( T(strb) r3, [r0], #1) @ May fault ldrgeb r3, [r1], #1 -USER( TUSER( strgeb) r3, [r0], #1) @ May fault +USER( T(strgeb) r3, [r0], #1) @ May fault ldrgtb r3, [r1], #1 -USER( TUSER( strgtb) r3, [r0], #1) @ May fault +USER( T(strgtb) r3, [r0], #1) @ May fault sub r2, r2, ip b .Lc2u_dest_aligned @@ -59,7 +59,7 @@ ENTRY(__copy_to_user) addmi ip, r2, #4 bmi .Lc2u_0nowords ldr r3, [r1], #4 -USER( TUSER( str) r3, [r0], #4) @ May fault +USER( T(str) r3, [r0], #4) @ May fault mov ip, r0, lsl #32 - PAGE_SHIFT @ On each page, use a ld/st??t instruction rsb ip, ip, #0 movs ip, ip, lsr #32 - PAGE_SHIFT @@ -88,18 +88,18 @@ USER( TUSER( str) r3, [r0], #4) @ May fault stmneia r0!, {r3 - r4} @ Shouldnt fault tst ip, #4 ldrne r3, [r1], #4 - TUSER( strne) r3, [r0], #4 @ Shouldnt fault + T(strne) r3, [r0], #4 @ Shouldnt fault ands ip, ip, #3 beq .Lc2u_0fupi .Lc2u_0nowords: teq ip, #0 beq .Lc2u_finished .Lc2u_nowords: cmp ip, #2 ldrb r3, [r1], #1 -USER( TUSER( strb) r3, [r0], #1) @ May fault +USER( T(strb) r3, [r0], #1) @ May fault ldrgeb r3, [r1], #1 -USER( TUSER( strgeb) r3, [r0], #1) @ May fault +USER( T(strgeb) r3, [r0], #1) @ May fault ldrgtb r3, [r1], #1 -USER( TUSER( strgtb) r3, [r0], #1) @ May fault +USER( T(strgtb) r3, [r0], #1) @ May fault b .Lc2u_finished .Lc2u_not_enough: @@ -120,7 +120,7 @@ USER( TUSER( strgtb) r3, [r0], #1) @ May fault mov r3, r7, pull #8 ldr r7, [r1], #4 orr r3, r3, r7, push #24 -USER( TUSER( str) r3, [r0], #4) @ May fault +USER( T(str) r3, [r0], #4) @ May fault mov ip, r0, lsl #32 - PAGE_SHIFT rsb ip, ip, #0 movs ip, ip, lsr #32 - PAGE_SHIFT @@ -155,18 +155,18 @@ USER( TUSER( str) r3, [r0], #4) @ May fault movne r3, r7, pull #8 ldrne r7, [r1], #4 orrne r3, r3, r7, push #24 - TUSER( strne) r3, [r0], #4 @ Shouldnt fault + T(strne) r3, [r0], #4 @ Shouldnt fault ands ip, ip, #3 beq .Lc2u_1fupi .Lc2u_1nowords: mov r3, r7, get_byte_1 teq ip, #0 beq .Lc2u_finished cmp ip, #2 -USER( TUSER( strb) r3, [r0], #1) @ May fault +USER( T(strb) r3, [r0], #1) @ May fault movge r3, r7, get_byte_2 -USER( TUSER( strgeb) r3, [r0], #1) @ May fault +USER( T(strgeb) r3, [r0], #1) @ May fault movgt r3, r7, get_byte_3 -USER( TUSER( strgtb) r3, [r0], #1) @ May fault +USER( T(strgtb) r3, [r0], #1) @ May fault b .Lc2u_finished .Lc2u_2fupi: subs r2, r2, #4 @@ -175,7 +175,7 @@ USER( TUSER( strgtb) r3, [r0], #1) @ May fault mov r3, r7, pull #16 ldr r7, [r1], #4 orr r3, r3, r7, push #16 -USER( TUSER( str) r3, [r0], #4) @ May fault +USER( T(str) r3, [r0], #4) @ May fault mov ip, r0, lsl #32 - PAGE_SHIFT rsb ip, ip, #0 movs ip, ip, lsr #32 - PAGE_SHIFT @@ -210,18 +210,18 @@ USER( TUSER( str) r3, [r0], #4) @ May fault movne r3, r7, pull #16 ldrne r7, [r1], #4 orrne r3, r3, r7, push #16 - TUSER( strne) r3, [r0], #4 @ Shouldnt fault + T(strne) r3, [r0], #4 @ Shouldnt fault ands ip, ip, #3 beq .Lc2u_2fupi .Lc2u_2nowords: mov r3, r7, get_byte_2 teq ip, #0 beq .Lc2u_finished cmp ip, #2 -USER( TUSER( strb) r3, [r0], #1) @ May fault +USER( T(strb) r3, [r0], #1) @ May fault movge r3, r7, get_byte_3 -USER( TUSER( strgeb) r3, [r0], #1) @ May fault +USER( T(strgeb) r3, [r0], #1) @ May fault ldrgtb r3, [r1], #0 -USER( TUSER( strgtb) r3, [r0], #1) @ May fault +USER( T(strgtb) r3, [r0], #1) @ May fault b .Lc2u_finished .Lc2u_3fupi: subs r2, r2, #4 @@ -230,7 +230,7 @@ USER( TUSER( strgtb) r3, [r0], #1) @ May fault mov r3, r7, pull #24 ldr r7, [r1], #4 orr r3, r3, r7, push #8 -USER( TUSER( str) r3, [r0], #4) @ May fault +USER( T(str) r3, [r0], #4) @ May fault mov ip, r0, lsl #32 - PAGE_SHIFT rsb ip, ip, #0 movs ip, ip, lsr #32 - PAGE_SHIFT @@ -265,18 +265,18 @@ USER( TUSER( str) r3, [r0], #4) @ May fault movne r3, r7, pull #24 ldrne r7, [r1], #4 orrne r3, r3, r7, push #8 - TUSER( strne) r3, [r0], #4 @ Shouldnt fault + T(strne) r3, [r0], #4 @ Shouldnt fault ands ip, ip, #3 beq .Lc2u_3fupi .Lc2u_3nowords: mov r3, r7, get_byte_3 teq ip, #0 beq .Lc2u_finished cmp ip, #2 -USER( TUSER( strb) r3, [r0], #1) @ May fault +USER( T(strb) r3, [r0], #1) @ May fault ldrgeb r3, [r1], #1 -USER( TUSER( strgeb) r3, [r0], #1) @ May fault +USER( T(strgeb) r3, [r0], #1) @ May fault ldrgtb r3, [r1], #0 -USER( TUSER( strgtb) r3, [r0], #1) @ May fault +USER( T(strgtb) r3, [r0], #1) @ May fault b .Lc2u_finished ENDPROC(__copy_to_user) @@ -295,11 +295,11 @@ ENDPROC(__copy_to_user) .Lcfu_dest_not_aligned: rsb ip, ip, #4 cmp ip, #2 -USER( TUSER( ldrb) r3, [r1], #1) @ May fault +USER( T(ldrb) r3, [r1], #1) @ May fault strb r3, [r0], #1 -USER( TUSER( ldrgeb) r3, [r1], #1) @ May fault +USER( T(ldrgeb) r3, [r1], #1) @ May fault strgeb r3, [r0], #1 -USER( TUSER( ldrgtb) r3, [r1], #1) @ May fault +USER( T(ldrgtb) r3, [r1], #1) @ May fault strgtb r3, [r0], #1 sub r2, r2, ip b .Lcfu_dest_aligned @@ -322,7 +322,7 @@ ENTRY(__copy_from_user) .Lcfu_0fupi: subs r2, r2, #4 addmi ip, r2, #4 bmi .Lcfu_0nowords -USER( TUSER( ldr) r3, [r1], #4) +USER( T(ldr) r3, [r1], #4) str r3, [r0], #4 mov ip, r1, lsl #32 - PAGE_SHIFT @ On each page, use a ld/st??t instruction rsb ip, ip, #0 @@ -351,18 +351,18 @@ USER( TUSER( ldr) r3, [r1], #4) ldmneia r1!, {r3 - r4} @ Shouldnt fault stmneia r0!, {r3 - r4} tst ip, #4 - TUSER( ldrne) r3, [r1], #4 @ Shouldnt fault + T(ldrne) r3, [r1], #4 @ Shouldnt fault strne r3, [r0], #4 ands ip, ip, #3 beq .Lcfu_0fupi .Lcfu_0nowords: teq ip, #0 beq .Lcfu_finished .Lcfu_nowords: cmp ip, #2 -USER( TUSER( ldrb) r3, [r1], #1) @ May fault +USER( T(ldrb) r3, [r1], #1) @ May fault strb r3, [r0], #1 -USER( TUSER( ldrgeb) r3, [r1], #1) @ May fault +USER( T(ldrgeb) r3, [r1], #1) @ May fault strgeb r3, [r0], #1 -USER( TUSER( ldrgtb) r3, [r1], #1) @ May fault +USER( T(ldrgtb) r3, [r1], #1) @ May fault strgtb r3, [r0], #1 b .Lcfu_finished @@ -375,7 +375,7 @@ USER( TUSER( ldrgtb) r3, [r1], #1) @ May fault .Lcfu_src_not_aligned: bic r1, r1, #3 -USER( TUSER( ldr) r7, [r1], #4) @ May fault +USER( T(ldr) r7, [r1], #4) @ May fault cmp ip, #2 bgt .Lcfu_3fupi beq .Lcfu_2fupi @@ -383,7 +383,7 @@ USER( TUSER( ldr) r7, [r1], #4) @ May fault addmi ip, r2, #4 bmi .Lcfu_1nowords mov r3, r7, pull #8 -USER( TUSER( ldr) r7, [r1], #4) @ May fault +USER( T(ldr) r7, [r1], #4) @ May fault orr r3, r3, r7, push #24 str r3, [r0], #4 mov ip, r1, lsl #32 - PAGE_SHIFT @@ -418,7 +418,7 @@ USER( TUSER( ldr) r7, [r1], #4) @ May fault stmneia r0!, {r3 - r4} tst ip, #4 movne r3, r7, pull #8 -USER( TUSER( ldrne) r7, [r1], #4) @ May fault +USER( T(ldrne) r7, [r1], #4) @ May fault orrne r3, r3, r7, push #24 strne r3, [r0], #4 ands ip, ip, #3 @@ -438,7 +438,7 @@ USER( TUSER( ldrne) r7, [r1], #4) @ May fault addmi ip, r2, #4 bmi .Lcfu_2nowords mov r3, r7, pull #16 -USER( TUSER( ldr) r7, [r1], #4) @ May fault +USER( T(ldr) r7, [r1], #4) @ May fault orr r3, r3, r7, push #16 str r3, [r0], #4 mov ip, r1, lsl #32 - PAGE_SHIFT @@ -474,7 +474,7 @@ USER( TUSER( ldr) r7, [r1], #4) @ May fault stmneia r0!, {r3 - r4} tst ip, #4 movne r3, r7, pull #16 -USER( TUSER( ldrne) r7, [r1], #4) @ May fault +USER( T(ldrne) r7, [r1], #4) @ May fault orrne r3, r3, r7, push #16 strne r3, [r0], #4 ands ip, ip, #3 @@ -486,7 +486,7 @@ USER( TUSER( ldrne) r7, [r1], #4) @ May fault strb r3, [r0], #1 movge r3, r7, get_byte_3 strgeb r3, [r0], #1 -USER( TUSER( ldrgtb) r3, [r1], #0) @ May fault +USER( T(ldrgtb) r3, [r1], #0) @ May fault strgtb r3, [r0], #1 b .Lcfu_finished @@ -494,7 +494,7 @@ USER( TUSER( ldrgtb) r3, [r1], #0) @ May fault addmi ip, r2, #4 bmi .Lcfu_3nowords mov r3, r7, pull #24 -USER( TUSER( ldr) r7, [r1], #4) @ May fault +USER( T(ldr) r7, [r1], #4) @ May fault orr r3, r3, r7, push #8 str r3, [r0], #4 mov ip, r1, lsl #32 - PAGE_SHIFT @@ -529,7 +529,7 @@ USER( TUSER( ldr) r7, [r1], #4) @ May fault stmneia r0!, {r3 - r4} tst ip, #4 movne r3, r7, pull #24 -USER( TUSER( ldrne) r7, [r1], #4) @ May fault +USER( T(ldrne) r7, [r1], #4) @ May fault orrne r3, r3, r7, push #8 strne r3, [r0], #4 ands ip, ip, #3 @@ -539,9 +539,9 @@ USER( TUSER( ldrne) r7, [r1], #4) @ May fault beq .Lcfu_finished cmp ip, #2 strb r3, [r0], #1 -USER( TUSER( ldrgeb) r3, [r1], #1) @ May fault +USER( T(ldrgeb) r3, [r1], #1) @ May fault strgeb r3, [r0], #1 -USER( TUSER( ldrgtb) r3, [r1], #1) @ May fault +USER( T(ldrgtb) r3, [r1], #1) @ May fault strgtb r3, [r0], #1 b .Lcfu_finished ENDPROC(__copy_from_user) diff --git a/trunk/arch/arm/mach-at91/Kconfig b/trunk/arch/arm/mach-at91/Kconfig index 71feb00a1e99..4f991f295284 100644 --- a/trunk/arch/arm/mach-at91/Kconfig +++ b/trunk/arch/arm/mach-at91/Kconfig @@ -18,12 +18,6 @@ config HAVE_AT91_USART4 config HAVE_AT91_USART5 bool -config AT91_SAM9_ALT_RESET - bool - -config AT91_SAM9G45_RESET - bool - menu "Atmel AT91 System-on-Chip" choice @@ -45,7 +39,6 @@ config ARCH_AT91SAM9260 select HAVE_AT91_USART4 select HAVE_AT91_USART5 select HAVE_NET_MACB - select AT91_SAM9_ALT_RESET config ARCH_AT91SAM9261 bool "AT91SAM9261" @@ -53,7 +46,6 @@ config ARCH_AT91SAM9261 select GENERIC_CLOCKEVENTS select HAVE_FB_ATMEL select HAVE_AT91_DBGU0 - select AT91_SAM9_ALT_RESET config ARCH_AT91SAM9G10 bool "AT91SAM9G10" @@ -61,7 +53,6 @@ config ARCH_AT91SAM9G10 select GENERIC_CLOCKEVENTS select HAVE_AT91_DBGU0 select HAVE_FB_ATMEL - select AT91_SAM9_ALT_RESET config ARCH_AT91SAM9263 bool "AT91SAM9263" @@ -70,7 +61,6 @@ config ARCH_AT91SAM9263 select HAVE_FB_ATMEL select HAVE_NET_MACB select HAVE_AT91_DBGU1 - select AT91_SAM9_ALT_RESET config ARCH_AT91SAM9RL bool "AT91SAM9RL" @@ -79,7 +69,6 @@ config ARCH_AT91SAM9RL select HAVE_AT91_USART3 select HAVE_FB_ATMEL select HAVE_AT91_DBGU0 - select AT91_SAM9_ALT_RESET config ARCH_AT91SAM9G20 bool "AT91SAM9G20" @@ -90,7 +79,6 @@ config ARCH_AT91SAM9G20 select HAVE_AT91_USART4 select HAVE_AT91_USART5 select HAVE_NET_MACB - select AT91_SAM9_ALT_RESET config ARCH_AT91SAM9G45 bool "AT91SAM9G45" @@ -100,7 +88,6 @@ config ARCH_AT91SAM9G45 select HAVE_FB_ATMEL select HAVE_NET_MACB select HAVE_AT91_DBGU1 - select AT91_SAM9G45_RESET config ARCH_AT91CAP9 bool "AT91CAP9" @@ -109,7 +96,6 @@ config ARCH_AT91CAP9 select HAVE_FB_ATMEL select HAVE_NET_MACB select HAVE_AT91_DBGU1 - select AT91_SAM9G45_RESET config ARCH_AT91X40 bool "AT91x40" diff --git a/trunk/arch/arm/mach-at91/Makefile b/trunk/arch/arm/mach-at91/Makefile index 705e1fbded39..242174f9f355 100644 --- a/trunk/arch/arm/mach-at91/Makefile +++ b/trunk/arch/arm/mach-at91/Makefile @@ -8,17 +8,15 @@ obj-n := obj- := obj-$(CONFIG_AT91_PMC_UNIT) += clock.o -obj-$(CONFIG_AT91_SAM9_ALT_RESET) += at91sam9_alt_reset.o -obj-$(CONFIG_AT91_SAM9G45_RESET) += at91sam9g45_reset.o # CPU-specific support obj-$(CONFIG_ARCH_AT91RM9200) += at91rm9200.o at91rm9200_time.o at91rm9200_devices.o -obj-$(CONFIG_ARCH_AT91SAM9260) += at91sam9260.o at91sam926x_time.o at91sam9260_devices.o sam9_smc.o -obj-$(CONFIG_ARCH_AT91SAM9261) += at91sam9261.o at91sam926x_time.o at91sam9261_devices.o sam9_smc.o -obj-$(CONFIG_ARCH_AT91SAM9G10) += at91sam9261.o at91sam926x_time.o at91sam9261_devices.o sam9_smc.o -obj-$(CONFIG_ARCH_AT91SAM9263) += at91sam9263.o at91sam926x_time.o at91sam9263_devices.o sam9_smc.o -obj-$(CONFIG_ARCH_AT91SAM9RL) += at91sam9rl.o at91sam926x_time.o at91sam9rl_devices.o sam9_smc.o -obj-$(CONFIG_ARCH_AT91SAM9G20) += at91sam9260.o at91sam926x_time.o at91sam9260_devices.o sam9_smc.o +obj-$(CONFIG_ARCH_AT91SAM9260) += at91sam9260.o at91sam926x_time.o at91sam9260_devices.o sam9_smc.o at91sam9_alt_reset.o +obj-$(CONFIG_ARCH_AT91SAM9261) += at91sam9261.o at91sam926x_time.o at91sam9261_devices.o sam9_smc.o at91sam9_alt_reset.o +obj-$(CONFIG_ARCH_AT91SAM9G10) += at91sam9261.o at91sam926x_time.o at91sam9261_devices.o sam9_smc.o at91sam9_alt_reset.o +obj-$(CONFIG_ARCH_AT91SAM9263) += at91sam9263.o at91sam926x_time.o at91sam9263_devices.o sam9_smc.o at91sam9_alt_reset.o +obj-$(CONFIG_ARCH_AT91SAM9RL) += at91sam9rl.o at91sam926x_time.o at91sam9rl_devices.o sam9_smc.o at91sam9_alt_reset.o +obj-$(CONFIG_ARCH_AT91SAM9G20) += at91sam9260.o at91sam926x_time.o at91sam9260_devices.o sam9_smc.o at91sam9_alt_reset.o obj-$(CONFIG_ARCH_AT91SAM9G45) += at91sam9g45.o at91sam926x_time.o at91sam9g45_devices.o sam9_smc.o obj-$(CONFIG_ARCH_AT91CAP9) += at91cap9.o at91sam926x_time.o at91cap9_devices.o sam9_smc.o obj-$(CONFIG_ARCH_AT91X40) += at91x40.o at91x40_time.o diff --git a/trunk/arch/arm/mach-at91/at91cap9.c b/trunk/arch/arm/mach-at91/at91cap9.c index a42edc25a87e..edb879ac04c8 100644 --- a/trunk/arch/arm/mach-at91/at91cap9.c +++ b/trunk/arch/arm/mach-at91/at91cap9.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "soc.h" #include "generic.h" @@ -313,6 +314,11 @@ static struct at91_gpio_bank at91cap9_gpio[] __initdata = { } }; +static void at91cap9_restart(char mode, const char *cmd) +{ + at91_sys_write(AT91_RSTC_CR, AT91_RSTC_KEY | AT91_RSTC_PROCRST | AT91_RSTC_PERRST); +} + /* -------------------------------------------------------------------- * AT91CAP9 processor initialization * -------------------------------------------------------------------- */ @@ -325,14 +331,13 @@ static void __init at91cap9_map_io(void) static void __init at91cap9_ioremap_registers(void) { at91_ioremap_shdwc(AT91CAP9_BASE_SHDWC); - at91_ioremap_rstc(AT91CAP9_BASE_RSTC); at91sam926x_ioremap_pit(AT91CAP9_BASE_PIT); at91sam9_ioremap_smc(0, AT91CAP9_BASE_SMC); } static void __init at91cap9_initialize(void) { - arm_pm_restart = at91sam9g45_restart; + arm_pm_restart = at91cap9_restart; at91_extern_irq = (1 << AT91CAP9_ID_IRQ0) | (1 << AT91CAP9_ID_IRQ1); /* Register GPIO subsystem */ diff --git a/trunk/arch/arm/mach-at91/at91sam9260.c b/trunk/arch/arm/mach-at91/at91sam9260.c index d4036ba43612..5e46e4a96430 100644 --- a/trunk/arch/arm/mach-at91/at91sam9260.c +++ b/trunk/arch/arm/mach-at91/at91sam9260.c @@ -323,7 +323,6 @@ static void __init at91sam9260_map_io(void) static void __init at91sam9260_ioremap_registers(void) { at91_ioremap_shdwc(AT91SAM9260_BASE_SHDWC); - at91_ioremap_rstc(AT91SAM9260_BASE_RSTC); at91sam926x_ioremap_pit(AT91SAM9260_BASE_PIT); at91sam9_ioremap_smc(0, AT91SAM9260_BASE_SMC); } diff --git a/trunk/arch/arm/mach-at91/at91sam9261.c b/trunk/arch/arm/mach-at91/at91sam9261.c index 023c2ff138df..b85b9ea60170 100644 --- a/trunk/arch/arm/mach-at91/at91sam9261.c +++ b/trunk/arch/arm/mach-at91/at91sam9261.c @@ -281,7 +281,6 @@ static void __init at91sam9261_map_io(void) static void __init at91sam9261_ioremap_registers(void) { at91_ioremap_shdwc(AT91SAM9261_BASE_SHDWC); - at91_ioremap_rstc(AT91SAM9261_BASE_RSTC); at91sam926x_ioremap_pit(AT91SAM9261_BASE_PIT); at91sam9_ioremap_smc(0, AT91SAM9261_BASE_SMC); } diff --git a/trunk/arch/arm/mach-at91/at91sam9263.c b/trunk/arch/arm/mach-at91/at91sam9263.c index 75e876c258af..79e3669b1117 100644 --- a/trunk/arch/arm/mach-at91/at91sam9263.c +++ b/trunk/arch/arm/mach-at91/at91sam9263.c @@ -301,7 +301,6 @@ static void __init at91sam9263_map_io(void) static void __init at91sam9263_ioremap_registers(void) { at91_ioremap_shdwc(AT91SAM9263_BASE_SHDWC); - at91_ioremap_rstc(AT91SAM9263_BASE_RSTC); at91sam926x_ioremap_pit(AT91SAM9263_BASE_PIT); at91sam9_ioremap_smc(0, AT91SAM9263_BASE_SMC0); at91sam9_ioremap_smc(1, AT91SAM9263_BASE_SMC1); diff --git a/trunk/arch/arm/mach-at91/at91sam9_alt_reset.S b/trunk/arch/arm/mach-at91/at91sam9_alt_reset.S index 518e42377171..d3f931c5942e 100644 --- a/trunk/arch/arm/mach-at91/at91sam9_alt_reset.S +++ b/trunk/arch/arm/mach-at91/at91sam9_alt_reset.S @@ -23,8 +23,7 @@ .globl at91sam9_alt_restart at91sam9_alt_restart: ldr r0, .at91_va_base_sdramc @ preload constants - ldr r1, =at91_rstc_base - ldr r1, [r1] + ldr r1, .at91_va_base_rstc_cr mov r2, #1 mov r3, #AT91_SDRAMC_LPCB_POWER_DOWN @@ -34,9 +33,11 @@ at91sam9_alt_restart: ldr r0, .at91_va_base_sdramc @ preload constants str r2, [r0, #AT91_SDRAMC_TR] @ disable SDRAM access str r3, [r0, #AT91_SDRAMC_LPR] @ power down SDRAM - str r4, [r1, #AT91_RSTC_CR] @ reset processor + str r4, [r1] @ reset processor b . .at91_va_base_sdramc: .word AT91_VA_BASE_SYS + AT91_SDRAMC0 +.at91_va_base_rstc_cr: + .word AT91_VA_BASE_SYS + AT91_RSTC_CR diff --git a/trunk/arch/arm/mach-at91/at91sam9g45.c b/trunk/arch/arm/mach-at91/at91sam9g45.c index 1cb6a96b1c1e..7032dd32cdf0 100644 --- a/trunk/arch/arm/mach-at91/at91sam9g45.c +++ b/trunk/arch/arm/mach-at91/at91sam9g45.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "soc.h" @@ -317,6 +318,11 @@ static struct at91_gpio_bank at91sam9g45_gpio[] __initdata = { } }; +static void at91sam9g45_restart(char mode, const char *cmd) +{ + at91_sys_write(AT91_RSTC_CR, AT91_RSTC_KEY | AT91_RSTC_PROCRST | AT91_RSTC_PERRST); +} + /* -------------------------------------------------------------------- * AT91SAM9G45 processor initialization * -------------------------------------------------------------------- */ @@ -330,7 +336,6 @@ static void __init at91sam9g45_map_io(void) static void __init at91sam9g45_ioremap_registers(void) { at91_ioremap_shdwc(AT91SAM9G45_BASE_SHDWC); - at91_ioremap_rstc(AT91SAM9G45_BASE_RSTC); at91sam926x_ioremap_pit(AT91SAM9G45_BASE_PIT); at91sam9_ioremap_smc(0, AT91SAM9G45_BASE_SMC); } diff --git a/trunk/arch/arm/mach-at91/at91sam9g45_reset.S b/trunk/arch/arm/mach-at91/at91sam9g45_reset.S deleted file mode 100644 index 0468be10980b..000000000000 --- a/trunk/arch/arm/mach-at91/at91sam9g45_reset.S +++ /dev/null @@ -1,40 +0,0 @@ -/* - * reset AT91SAM9G45 as per errata - * - * Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD - * - * unless the SDRAM is cleanly shutdown before we hit the - * reset register it can be left driving the data bus and - * killing the chance of a subsequent boot from NAND - * - * GPLv2 Only - */ - -#include -#include -#include -#include - - .arm - - .globl at91sam9g45_restart - -at91sam9g45_restart: - ldr r0, .at91_va_base_sdramc0 @ preload constants - ldr r1, =at91_rstc_base - ldr r1, [r1] - - mov r2, #1 - mov r3, #AT91_DDRSDRC_LPCB_POWER_DOWN - ldr r4, =AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST - - .balign 32 @ align to cache line - - str r2, [r0, #AT91_DDRSDRC_RTR] @ disable DDR0 access - str r3, [r0, #AT91_DDRSDRC_LPR] @ power down DDR0 - str r4, [r1, #AT91_RSTC_CR] @ reset processor - - b . - -.at91_va_base_sdramc0: - .word AT91_VA_BASE_SYS + AT91_DDRSDRC0 diff --git a/trunk/arch/arm/mach-at91/at91sam9rl.c b/trunk/arch/arm/mach-at91/at91sam9rl.c index d2c91a841cb8..d6bcb1da11df 100644 --- a/trunk/arch/arm/mach-at91/at91sam9rl.c +++ b/trunk/arch/arm/mach-at91/at91sam9rl.c @@ -286,7 +286,6 @@ static void __init at91sam9rl_map_io(void) static void __init at91sam9rl_ioremap_registers(void) { at91_ioremap_shdwc(AT91SAM9RL_BASE_SHDWC); - at91_ioremap_rstc(AT91SAM9RL_BASE_RSTC); at91sam926x_ioremap_pit(AT91SAM9RL_BASE_PIT); at91sam9_ioremap_smc(0, AT91SAM9RL_BASE_SMC); } diff --git a/trunk/arch/arm/mach-at91/generic.h b/trunk/arch/arm/mach-at91/generic.h index 594133451c0c..4866b8180d66 100644 --- a/trunk/arch/arm/mach-at91/generic.h +++ b/trunk/arch/arm/mach-at91/generic.h @@ -58,9 +58,7 @@ extern void at91_irq_suspend(void); extern void at91_irq_resume(void); /* reset */ -extern void at91_ioremap_rstc(u32 base_addr); extern void at91sam9_alt_restart(char, const char *); -extern void at91sam9g45_restart(char, const char *); /* shutdown */ extern void at91_ioremap_shdwc(u32 base_addr); diff --git a/trunk/arch/arm/mach-at91/include/mach/at91_rstc.h b/trunk/arch/arm/mach-at91/include/mach/at91_rstc.h index 875fa336800b..cbd2bf052c1f 100644 --- a/trunk/arch/arm/mach-at91/include/mach/at91_rstc.h +++ b/trunk/arch/arm/mach-at91/include/mach/at91_rstc.h @@ -16,25 +16,13 @@ #ifndef AT91_RSTC_H #define AT91_RSTC_H -#ifndef __ASSEMBLY__ -extern void __iomem *at91_rstc_base; - -#define at91_rstc_read(field) \ - __raw_readl(at91_rstc_base + field) - -#define at91_rstc_write(field, value) \ - __raw_writel(value, at91_rstc_base + field); -#else -.extern at91_rstc_base -#endif - -#define AT91_RSTC_CR 0x00 /* Reset Controller Control Register */ +#define AT91_RSTC_CR (AT91_RSTC + 0x00) /* Reset Controller Control Register */ #define AT91_RSTC_PROCRST (1 << 0) /* Processor Reset */ #define AT91_RSTC_PERRST (1 << 2) /* Peripheral Reset */ #define AT91_RSTC_EXTRST (1 << 3) /* External Reset */ #define AT91_RSTC_KEY (0xa5 << 24) /* KEY Password */ -#define AT91_RSTC_SR 0x04 /* Reset Controller Status Register */ +#define AT91_RSTC_SR (AT91_RSTC + 0x04) /* Reset Controller Status Register */ #define AT91_RSTC_URSTS (1 << 0) /* User Reset Status */ #define AT91_RSTC_RSTTYP (7 << 8) /* Reset Type */ #define AT91_RSTC_RSTTYP_GENERAL (0 << 8) @@ -45,7 +33,7 @@ extern void __iomem *at91_rstc_base; #define AT91_RSTC_NRSTL (1 << 16) /* NRST Pin Level */ #define AT91_RSTC_SRCMP (1 << 17) /* Software Reset Command in Progress */ -#define AT91_RSTC_MR 0x08 /* Reset Controller Mode Register */ +#define AT91_RSTC_MR (AT91_RSTC + 0x08) /* Reset Controller Mode Register */ #define AT91_RSTC_URSTEN (1 << 0) /* User Reset Enable */ #define AT91_RSTC_URSTIEN (1 << 4) /* User Reset Interrupt Enable */ #define AT91_RSTC_ERSTL (0xf << 8) /* External Reset Length */ diff --git a/trunk/arch/arm/mach-at91/include/mach/at91cap9.h b/trunk/arch/arm/mach-at91/include/mach/at91cap9.h index 61d952902f2b..4c0e2f6011d7 100644 --- a/trunk/arch/arm/mach-at91/include/mach/at91cap9.h +++ b/trunk/arch/arm/mach-at91/include/mach/at91cap9.h @@ -83,6 +83,7 @@ #define AT91_DDRSDRC0 (0xffffe600 - AT91_BASE_SYS) #define AT91_MATRIX (0xffffea00 - AT91_BASE_SYS) #define AT91_PMC (0xfffffc00 - AT91_BASE_SYS) +#define AT91_RSTC (0xfffffd00 - AT91_BASE_SYS) #define AT91_GPBR (cpu_is_at91cap9_revB() ? \ (0xfffffd50 - AT91_BASE_SYS) : \ (0xfffffd60 - AT91_BASE_SYS)) @@ -95,7 +96,6 @@ #define AT91CAP9_BASE_PIOB 0xfffff400 #define AT91CAP9_BASE_PIOC 0xfffff600 #define AT91CAP9_BASE_PIOD 0xfffff800 -#define AT91CAP9_BASE_RSTC 0xfffffd00 #define AT91CAP9_BASE_SHDWC 0xfffffd10 #define AT91CAP9_BASE_RTT 0xfffffd20 #define AT91CAP9_BASE_PIT 0xfffffd30 diff --git a/trunk/arch/arm/mach-at91/include/mach/at91cap9_ddrsdr.h b/trunk/arch/arm/mach-at91/include/mach/at91cap9_ddrsdr.h new file mode 100644 index 000000000000..976f4a6c3353 --- /dev/null +++ b/trunk/arch/arm/mach-at91/include/mach/at91cap9_ddrsdr.h @@ -0,0 +1,108 @@ +/* + * arch/arm/mach-at91/include/mach/at91cap9_ddrsdr.h + * + * (C) 2008 Andrew Victor + * + * DDR/SDR Controller (DDRSDRC) - System peripherals registers. + * Based on AT91CAP9 datasheet revision B. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef AT91CAP9_DDRSDR_H +#define AT91CAP9_DDRSDR_H + +#define AT91_DDRSDRC_MR 0x00 /* Mode Register */ +#define AT91_DDRSDRC_MODE (0xf << 0) /* Command Mode */ +#define AT91_DDRSDRC_MODE_NORMAL 0 +#define AT91_DDRSDRC_MODE_NOP 1 +#define AT91_DDRSDRC_MODE_PRECHARGE 2 +#define AT91_DDRSDRC_MODE_LMR 3 +#define AT91_DDRSDRC_MODE_REFRESH 4 +#define AT91_DDRSDRC_MODE_EXT_LMR 5 +#define AT91_DDRSDRC_MODE_DEEP 6 + +#define AT91_DDRSDRC_RTR 0x04 /* Refresh Timer Register */ +#define AT91_DDRSDRC_COUNT (0xfff << 0) /* Refresh Timer Counter */ + +#define AT91_DDRSDRC_CR 0x08 /* Configuration Register */ +#define AT91_DDRSDRC_NC (3 << 0) /* Number of Column Bits */ +#define AT91_DDRSDRC_NC_SDR8 (0 << 0) +#define AT91_DDRSDRC_NC_SDR9 (1 << 0) +#define AT91_DDRSDRC_NC_SDR10 (2 << 0) +#define AT91_DDRSDRC_NC_SDR11 (3 << 0) +#define AT91_DDRSDRC_NC_DDR9 (0 << 0) +#define AT91_DDRSDRC_NC_DDR10 (1 << 0) +#define AT91_DDRSDRC_NC_DDR11 (2 << 0) +#define AT91_DDRSDRC_NC_DDR12 (3 << 0) +#define AT91_DDRSDRC_NR (3 << 2) /* Number of Row Bits */ +#define AT91_DDRSDRC_NR_11 (0 << 2) +#define AT91_DDRSDRC_NR_12 (1 << 2) +#define AT91_DDRSDRC_NR_13 (2 << 2) +#define AT91_DDRSDRC_CAS (7 << 4) /* CAS Latency */ +#define AT91_DDRSDRC_CAS_2 (2 << 4) +#define AT91_DDRSDRC_CAS_3 (3 << 4) +#define AT91_DDRSDRC_CAS_25 (6 << 4) +#define AT91_DDRSDRC_DLL (1 << 7) /* Reset DLL */ +#define AT91_DDRSDRC_DICDS (1 << 8) /* Output impedance control */ + +#define AT91_DDRSDRC_T0PR 0x0C /* Timing 0 Register */ +#define AT91_DDRSDRC_TRAS (0xf << 0) /* Active to Precharge delay */ +#define AT91_DDRSDRC_TRCD (0xf << 4) /* Row to Column delay */ +#define AT91_DDRSDRC_TWR (0xf << 8) /* Write recovery delay */ +#define AT91_DDRSDRC_TRC (0xf << 12) /* Row cycle delay */ +#define AT91_DDRSDRC_TRP (0xf << 16) /* Row precharge delay */ +#define AT91_DDRSDRC_TRRD (0xf << 20) /* Active BankA to BankB */ +#define AT91_DDRSDRC_TWTR (1 << 24) /* Internal Write to Read delay */ +#define AT91_DDRSDRC_TMRD (0xf << 28) /* Load mode to active/refresh delay */ + +#define AT91_DDRSDRC_T1PR 0x10 /* Timing 1 Register */ +#define AT91_DDRSDRC_TRFC (0x1f << 0) /* Row Cycle Delay */ +#define AT91_DDRSDRC_TXSNR (0xff << 8) /* Exit self-refresh to non-read */ +#define AT91_DDRSDRC_TXSRD (0xff << 16) /* Exit self-refresh to read */ +#define AT91_DDRSDRC_TXP (0xf << 24) /* Exit power-down delay */ + +#define AT91_DDRSDRC_LPR 0x18 /* Low Power Register */ +#define AT91_DDRSDRC_LPCB (3 << 0) /* Low-power Configurations */ +#define AT91_DDRSDRC_LPCB_DISABLE 0 +#define AT91_DDRSDRC_LPCB_SELF_REFRESH 1 +#define AT91_DDRSDRC_LPCB_POWER_DOWN 2 +#define AT91_DDRSDRC_LPCB_DEEP_POWER_DOWN 3 +#define AT91_DDRSDRC_CLKFR (1 << 2) /* Clock Frozen */ +#define AT91_DDRSDRC_PASR (7 << 4) /* Partial Array Self Refresh */ +#define AT91_DDRSDRC_TCSR (3 << 8) /* Temperature Compensated Self Refresh */ +#define AT91_DDRSDRC_DS (3 << 10) /* Drive Strength */ +#define AT91_DDRSDRC_TIMEOUT (3 << 12) /* Time to define when Low Power Mode is enabled */ +#define AT91_DDRSDRC_TIMEOUT_0_CLK_CYCLES (0 << 12) +#define AT91_DDRSDRC_TIMEOUT_64_CLK_CYCLES (1 << 12) +#define AT91_DDRSDRC_TIMEOUT_128_CLK_CYCLES (2 << 12) + +#define AT91_DDRSDRC_MDR 0x1C /* Memory Device Register */ +#define AT91_DDRSDRC_MD (3 << 0) /* Memory Device Type */ +#define AT91_DDRSDRC_MD_SDR 0 +#define AT91_DDRSDRC_MD_LOW_POWER_SDR 1 +#define AT91_DDRSDRC_MD_DDR 2 +#define AT91_DDRSDRC_MD_LOW_POWER_DDR 3 + +#define AT91_DDRSDRC_DLLR 0x20 /* DLL Information Register */ +#define AT91_DDRSDRC_MDINC (1 << 0) /* Master Delay increment */ +#define AT91_DDRSDRC_MDDEC (1 << 1) /* Master Delay decrement */ +#define AT91_DDRSDRC_MDOVF (1 << 2) /* Master Delay Overflow */ +#define AT91_DDRSDRC_SDCOVF (1 << 3) /* Slave Delay Correction Overflow */ +#define AT91_DDRSDRC_SDCUDF (1 << 4) /* Slave Delay Correction Underflow */ +#define AT91_DDRSDRC_SDERF (1 << 5) /* Slave Delay Correction error */ +#define AT91_DDRSDRC_MDVAL (0xff << 8) /* Master Delay value */ +#define AT91_DDRSDRC_SDVAL (0xff << 16) /* Slave Delay value */ +#define AT91_DDRSDRC_SDCVAL (0xff << 24) /* Slave Delay Correction value */ + +/* Register access macros */ +#define at91_ramc_read(num, reg) \ + at91_sys_read(AT91_DDRSDRC##num + reg) +#define at91_ramc_write(num, reg, value) \ + at91_sys_write(AT91_DDRSDRC##num + reg, value) + + +#endif diff --git a/trunk/arch/arm/mach-at91/include/mach/at91sam9260.h b/trunk/arch/arm/mach-at91/include/mach/at91sam9260.h index fa5ca278adeb..f937c476bb67 100644 --- a/trunk/arch/arm/mach-at91/include/mach/at91sam9260.h +++ b/trunk/arch/arm/mach-at91/include/mach/at91sam9260.h @@ -83,6 +83,7 @@ #define AT91_SDRAMC0 (0xffffea00 - AT91_BASE_SYS) #define AT91_MATRIX (0xffffee00 - AT91_BASE_SYS) #define AT91_PMC (0xfffffc00 - AT91_BASE_SYS) +#define AT91_RSTC (0xfffffd00 - AT91_BASE_SYS) #define AT91_GPBR (0xfffffd50 - AT91_BASE_SYS) #define AT91SAM9260_BASE_ECC 0xffffe800 @@ -91,7 +92,6 @@ #define AT91SAM9260_BASE_PIOA 0xfffff400 #define AT91SAM9260_BASE_PIOB 0xfffff600 #define AT91SAM9260_BASE_PIOC 0xfffff800 -#define AT91SAM9260_BASE_RSTC 0xfffffd00 #define AT91SAM9260_BASE_SHDWC 0xfffffd10 #define AT91SAM9260_BASE_RTT 0xfffffd20 #define AT91SAM9260_BASE_PIT 0xfffffd30 diff --git a/trunk/arch/arm/mach-at91/include/mach/at91sam9261.h b/trunk/arch/arm/mach-at91/include/mach/at91sam9261.h index 7cde2d36570e..175604e261be 100644 --- a/trunk/arch/arm/mach-at91/include/mach/at91sam9261.h +++ b/trunk/arch/arm/mach-at91/include/mach/at91sam9261.h @@ -68,6 +68,7 @@ #define AT91_SDRAMC0 (0xffffea00 - AT91_BASE_SYS) #define AT91_MATRIX (0xffffee00 - AT91_BASE_SYS) #define AT91_PMC (0xfffffc00 - AT91_BASE_SYS) +#define AT91_RSTC (0xfffffd00 - AT91_BASE_SYS) #define AT91_GPBR (0xfffffd50 - AT91_BASE_SYS) #define AT91SAM9261_BASE_SMC 0xffffec00 @@ -75,7 +76,6 @@ #define AT91SAM9261_BASE_PIOA 0xfffff400 #define AT91SAM9261_BASE_PIOB 0xfffff600 #define AT91SAM9261_BASE_PIOC 0xfffff800 -#define AT91SAM9261_BASE_RSTC 0xfffffd00 #define AT91SAM9261_BASE_SHDWC 0xfffffd10 #define AT91SAM9261_BASE_RTT 0xfffffd20 #define AT91SAM9261_BASE_PIT 0xfffffd30 diff --git a/trunk/arch/arm/mach-at91/include/mach/at91sam9263.h b/trunk/arch/arm/mach-at91/include/mach/at91sam9263.h index 5949abda962b..80c915002d83 100644 --- a/trunk/arch/arm/mach-at91/include/mach/at91sam9263.h +++ b/trunk/arch/arm/mach-at91/include/mach/at91sam9263.h @@ -78,6 +78,7 @@ #define AT91_SDRAMC1 (0xffffe800 - AT91_BASE_SYS) #define AT91_MATRIX (0xffffec00 - AT91_BASE_SYS) #define AT91_PMC (0xfffffc00 - AT91_BASE_SYS) +#define AT91_RSTC (0xfffffd00 - AT91_BASE_SYS) #define AT91_GPBR (0xfffffd60 - AT91_BASE_SYS) #define AT91SAM9263_BASE_ECC0 0xffffe000 @@ -90,7 +91,6 @@ #define AT91SAM9263_BASE_PIOC 0xfffff600 #define AT91SAM9263_BASE_PIOD 0xfffff800 #define AT91SAM9263_BASE_PIOE 0xfffffa00 -#define AT91SAM9263_BASE_RSTC 0xfffffd00 #define AT91SAM9263_BASE_SHDWC 0xfffffd10 #define AT91SAM9263_BASE_RTT0 0xfffffd20 #define AT91SAM9263_BASE_PIT 0xfffffd30 diff --git a/trunk/arch/arm/mach-at91/include/mach/at91sam9_ddrsdr.h b/trunk/arch/arm/mach-at91/include/mach/at91sam9_ddrsdr.h index e2f8da8ce5bc..d27b15ba8ebf 100644 --- a/trunk/arch/arm/mach-at91/include/mach/at91sam9_ddrsdr.h +++ b/trunk/arch/arm/mach-at91/include/mach/at91sam9_ddrsdr.h @@ -46,10 +46,10 @@ #define AT91_DDRSDRC_CAS_25 (6 << 4) #define AT91_DDRSDRC_RST_DLL (1 << 7) /* Reset DLL */ #define AT91_DDRSDRC_DICDS (1 << 8) /* Output impedance control */ -#define AT91_DDRSDRC_DIS_DLL (1 << 9) /* Disable DLL [SAM9 Only] */ -#define AT91_DDRSDRC_OCD (1 << 12) /* Off-Chip Driver [SAM9 Only] */ -#define AT91_DDRSDRC_DQMS (1 << 16) /* Mask Data is Shared [SAM9 Only] */ -#define AT91_DDRSDRC_ACTBST (1 << 18) /* Active Bank X to Burst Stop Read Access Bank Y [SAM9 Only] */ +#define AT91_DDRSDRC_DIS_DLL (1 << 9) /* Disable DLL */ +#define AT91_DDRSDRC_OCD (1 << 12) /* Off-Chip Driver */ +#define AT91_DDRSDRC_DQMS (1 << 16) /* Mask Data is Shared */ +#define AT91_DDRSDRC_ACTBST (1 << 18) /* Active Bank X to Burst Stop Read Access Bank Y */ #define AT91_DDRSDRC_T0PR 0x0C /* Timing 0 Register */ #define AT91_DDRSDRC_TRAS (0xf << 0) /* Active to Precharge delay */ @@ -59,8 +59,7 @@ #define AT91_DDRSDRC_TRP (0xf << 16) /* Row precharge delay */ #define AT91_DDRSDRC_TRRD (0xf << 20) /* Active BankA to BankB */ #define AT91_DDRSDRC_TWTR (0x7 << 24) /* Internal Write to Read delay */ -#define AT91CAP9_DDRSDRC_TWTR (1 << 24) /* Internal Write to Read delay */ -#define AT91_DDRSDRC_RED_WRRD (0x1 << 27) /* Reduce Write to Read Delay [SAM9 Only] */ +#define AT91_DDRSDRC_RED_WRRD (0x1 << 27) /* Reduce Write to Read Delay */ #define AT91_DDRSDRC_TMRD (0xf << 28) /* Load mode to active/refresh delay */ #define AT91_DDRSDRC_T1PR 0x10 /* Timing 1 Register */ @@ -69,14 +68,13 @@ #define AT91_DDRSDRC_TXSRD (0xff << 16) /* Exit self-refresh to read */ #define AT91_DDRSDRC_TXP (0xf << 24) /* Exit power-down delay */ -#define AT91_DDRSDRC_T2PR 0x14 /* Timing 2 Register [SAM9 Only] */ +#define AT91_DDRSDRC_T2PR 0x14 /* Timing 2 Register */ #define AT91_DDRSDRC_TXARD (0xf << 0) /* Exit active power down delay to read command in mode "Fast Exit" */ #define AT91_DDRSDRC_TXARDS (0xf << 4) /* Exit active power down delay to read command in mode "Slow Exit" */ #define AT91_DDRSDRC_TRPA (0xf << 8) /* Row Precharge All delay */ #define AT91_DDRSDRC_TRTP (0x7 << 12) /* Read to Precharge delay */ #define AT91_DDRSDRC_LPR 0x1C /* Low Power Register */ -#define AT91CAP9_DDRSDRC_LPR 0x18 /* Low Power Register */ #define AT91_DDRSDRC_LPCB (3 << 0) /* Low-power Configurations */ #define AT91_DDRSDRC_LPCB_DISABLE 0 #define AT91_DDRSDRC_LPCB_SELF_REFRESH 1 @@ -94,40 +92,32 @@ #define AT91_DDRSDRC_UPD_MR (3 << 20) /* Update load mode register and extended mode register */ #define AT91_DDRSDRC_MDR 0x20 /* Memory Device Register */ -#define AT91CAP9_DDRSDRC_MDR 0x1C /* Memory Device Register */ #define AT91_DDRSDRC_MD (3 << 0) /* Memory Device Type */ #define AT91_DDRSDRC_MD_SDR 0 #define AT91_DDRSDRC_MD_LOW_POWER_SDR 1 -#define AT91CAP9_DDRSDRC_MD_DDR 2 #define AT91_DDRSDRC_MD_LOW_POWER_DDR 3 -#define AT91_DDRSDRC_MD_DDR2 6 /* [SAM9 Only] */ +#define AT91_DDRSDRC_MD_DDR2 6 #define AT91_DDRSDRC_DBW (1 << 4) /* Data Bus Width */ #define AT91_DDRSDRC_DBW_32BITS (0 << 4) #define AT91_DDRSDRC_DBW_16BITS (1 << 4) #define AT91_DDRSDRC_DLL 0x24 /* DLL Information Register */ -#define AT91CAP9_DDRSDRC_DLL 0x20 /* DLL Information Register */ #define AT91_DDRSDRC_MDINC (1 << 0) /* Master Delay increment */ #define AT91_DDRSDRC_MDDEC (1 << 1) /* Master Delay decrement */ #define AT91_DDRSDRC_MDOVF (1 << 2) /* Master Delay Overflow */ -#define AT91CAP9_DDRSDRC_SDCOVF (1 << 3) /* Slave Delay Correction Overflow */ -#define AT91CAP9_DDRSDRC_SDCUDF (1 << 4) /* Slave Delay Correction Underflow */ -#define AT91CAP9_DDRSDRC_SDERF (1 << 5) /* Slave Delay Correction error */ #define AT91_DDRSDRC_MDVAL (0xff << 8) /* Master Delay value */ -#define AT91CAP9_DDRSDRC_SDVAL (0xff << 16) /* Slave Delay value */ -#define AT91CAP9_DDRSDRC_SDCVAL (0xff << 24) /* Slave Delay Correction value */ -#define AT91_DDRSDRC_HS 0x2C /* High Speed Register [SAM9 Only] */ +#define AT91_DDRSDRC_HS 0x2C /* High Speed Register */ #define AT91_DDRSDRC_DIS_ATCP_RD (1 << 2) /* Anticip read access is disabled */ #define AT91_DDRSDRC_DELAY(n) (0x30 + (0x4 * (n))) /* Delay I/O Register n */ -#define AT91_DDRSDRC_WPMR 0xE4 /* Write Protect Mode Register [SAM9 Only] */ +#define AT91_DDRSDRC_WPMR 0xE4 /* Write Protect Mode Register */ #define AT91_DDRSDRC_WP (1 << 0) /* Write protect enable */ #define AT91_DDRSDRC_WPKEY (0xffffff << 8) /* Write protect key */ #define AT91_DDRSDRC_KEY (0x444452 << 8) /* Write protect key = "DDR" */ -#define AT91_DDRSDRC_WPSR 0xE8 /* Write Protect Status Register [SAM9 Only] */ +#define AT91_DDRSDRC_WPSR 0xE8 /* Write Protect Status Register */ #define AT91_DDRSDRC_WPVS (1 << 0) /* Write protect violation status */ #define AT91_DDRSDRC_WPVSRC (0xffff << 8) /* Write protect violation source */ diff --git a/trunk/arch/arm/mach-at91/include/mach/at91sam9g45.h b/trunk/arch/arm/mach-at91/include/mach/at91sam9g45.h index dd9c95ea0862..f0c23c960dec 100644 --- a/trunk/arch/arm/mach-at91/include/mach/at91sam9g45.h +++ b/trunk/arch/arm/mach-at91/include/mach/at91sam9g45.h @@ -90,6 +90,7 @@ #define AT91_DDRSDRC0 (0xffffe600 - AT91_BASE_SYS) #define AT91_MATRIX (0xffffea00 - AT91_BASE_SYS) #define AT91_PMC (0xfffffc00 - AT91_BASE_SYS) +#define AT91_RSTC (0xfffffd00 - AT91_BASE_SYS) #define AT91_GPBR (0xfffffd60 - AT91_BASE_SYS) #define AT91SAM9G45_BASE_ECC 0xffffe200 @@ -101,7 +102,6 @@ #define AT91SAM9G45_BASE_PIOC 0xfffff600 #define AT91SAM9G45_BASE_PIOD 0xfffff800 #define AT91SAM9G45_BASE_PIOE 0xfffffa00 -#define AT91SAM9G45_BASE_RSTC 0xfffffd00 #define AT91SAM9G45_BASE_SHDWC 0xfffffd10 #define AT91SAM9G45_BASE_RTT 0xfffffd20 #define AT91SAM9G45_BASE_PIT 0xfffffd30 diff --git a/trunk/arch/arm/mach-at91/include/mach/at91sam9rl.h b/trunk/arch/arm/mach-at91/include/mach/at91sam9rl.h index d7bead7118da..2bb359e60b97 100644 --- a/trunk/arch/arm/mach-at91/include/mach/at91sam9rl.h +++ b/trunk/arch/arm/mach-at91/include/mach/at91sam9rl.h @@ -72,6 +72,7 @@ #define AT91_SDRAMC0 (0xffffea00 - AT91_BASE_SYS) #define AT91_MATRIX (0xffffee00 - AT91_BASE_SYS) #define AT91_PMC (0xfffffc00 - AT91_BASE_SYS) +#define AT91_RSTC (0xfffffd00 - AT91_BASE_SYS) #define AT91_SCKCR (0xfffffd50 - AT91_BASE_SYS) #define AT91_GPBR (0xfffffd60 - AT91_BASE_SYS) @@ -83,7 +84,6 @@ #define AT91SAM9RL_BASE_PIOB 0xfffff600 #define AT91SAM9RL_BASE_PIOC 0xfffff800 #define AT91SAM9RL_BASE_PIOD 0xfffffa00 -#define AT91SAM9RL_BASE_RSTC 0xfffffd00 #define AT91SAM9RL_BASE_SHDWC 0xfffffd10 #define AT91SAM9RL_BASE_RTT 0xfffffd20 #define AT91SAM9RL_BASE_PIT 0xfffffd30 diff --git a/trunk/arch/arm/mach-at91/include/mach/board.h b/trunk/arch/arm/mach-at91/include/mach/board.h index 3b33f07b1e11..d0b377b21bd7 100644 --- a/trunk/arch/arm/mach-at91/include/mach/board.h +++ b/trunk/arch/arm/mach-at91/include/mach/board.h @@ -88,7 +88,7 @@ extern void __init at91_add_device_eth(struct macb_platform_data *data); struct at91_usbh_data { u8 ports; /* number of ports on root hub */ int vbus_pin[2]; /* port power-control pin */ - u8 vbus_pin_active_low[2]; + u8 vbus_pin_inverted; u8 overcurrent_supported; int overcurrent_pin[2]; u8 overcurrent_status[2]; diff --git a/trunk/arch/arm/mach-at91/pm.c b/trunk/arch/arm/mach-at91/pm.c index 1606379ac284..62ad95556c36 100644 --- a/trunk/arch/arm/mach-at91/pm.c +++ b/trunk/arch/arm/mach-at91/pm.c @@ -34,6 +34,7 @@ /* * Show the reason for the previous system reset. */ +#if defined(AT91_RSTC) #include #include @@ -57,10 +58,10 @@ static void __init show_reset_status(void) char *reason, *r2 = reset; u32 reset_type, wake_type; - if (!at91_shdwc_base || !at91_rstc_base) + if (!at91_shdwc_base) return; - reset_type = at91_rstc_read(AT91_RSTC_SR) & AT91_RSTC_RSTTYP; + reset_type = at91_sys_read(AT91_RSTC_SR) & AT91_RSTC_RSTTYP; wake_type = at91_shdwc_read(AT91_SHDW_SR); switch (reset_type) { @@ -101,6 +102,10 @@ static void __init show_reset_status(void) } pr_info("AT91: Starting after %s %s\n", reason, r2); } +#else +static void __init show_reset_status(void) {} +#endif + static int at91_pm_valid_state(suspend_state_t state) { diff --git a/trunk/arch/arm/mach-at91/pm.h b/trunk/arch/arm/mach-at91/pm.h index 7eb40d24242f..ce9a20699111 100644 --- a/trunk/arch/arm/mach-at91/pm.h +++ b/trunk/arch/arm/mach-at91/pm.h @@ -25,21 +25,21 @@ static inline u32 sdram_selfrefresh_enable(void) : : "r" (0)) #elif defined(CONFIG_ARCH_AT91CAP9) -#include +#include static inline u32 sdram_selfrefresh_enable(void) { u32 saved_lpr, lpr; - saved_lpr = at91_ramc_read(0, AT91CAP9_DDRSDRC_LPR); + saved_lpr = at91_ramc_read(0, AT91_DDRSDRC_LPR); lpr = saved_lpr & ~AT91_DDRSDRC_LPCB; - at91_ramc_write(0, AT91CAP9_DDRSDRC_LPR, lpr | AT91_DDRSDRC_LPCB_SELF_REFRESH); + at91_ramc_write(0, AT91_DDRSDRC_LPR, lpr | AT91_DDRSDRC_LPCB_SELF_REFRESH); return saved_lpr; } -#define sdram_selfrefresh_disable(saved_lpr) at91_ramc_write(0, AT91CAP9_DDRSDRC_LPR, saved_lpr) +#define sdram_selfrefresh_disable(saved_lpr) at91_ramc_write(0, AT91_DDRSDRC_LPR, saved_lpr) #define wait_for_interrupt_enable() cpu_do_idle() #elif defined(CONFIG_ARCH_AT91SAM9G45) diff --git a/trunk/arch/arm/mach-at91/pm_slowclock.S b/trunk/arch/arm/mach-at91/pm_slowclock.S index 92dfb8461392..f7922a436172 100644 --- a/trunk/arch/arm/mach-at91/pm_slowclock.S +++ b/trunk/arch/arm/mach-at91/pm_slowclock.S @@ -18,8 +18,9 @@ #if defined(CONFIG_ARCH_AT91RM9200) #include -#elif defined(CONFIG_ARCH_AT91CAP9) \ - || defined(CONFIG_ARCH_AT91SAM9G45) +#elif defined(CONFIG_ARCH_AT91CAP9) +#include +#elif defined(CONFIG_ARCH_AT91SAM9G45) #include #else #include diff --git a/trunk/arch/arm/mach-at91/setup.c b/trunk/arch/arm/mach-at91/setup.c index 69d3fc4c46f3..8bdcc3cb6012 100644 --- a/trunk/arch/arm/mach-at91/setup.c +++ b/trunk/arch/arm/mach-at91/setup.c @@ -29,12 +29,9 @@ EXPORT_SYMBOL(at91_soc_initdata); void __init at91rm9200_set_type(int type) { if (type == ARCH_REVISON_9200_PQFP) - at91_soc_initdata.subtype = AT91_SOC_RM9200_PQFP; - else at91_soc_initdata.subtype = AT91_SOC_RM9200_BGA; - - pr_info("AT91: filled in soc subtype: %s\n", - at91_get_soc_subtype(&at91_soc_initdata)); + else + at91_soc_initdata.subtype = AT91_SOC_RM9200_PQFP; } void __init at91_init_irq_default(void) @@ -284,15 +281,6 @@ void __init at91_ioremap_shdwc(u32 base_addr) pm_power_off = at91sam9_poweroff; } -void __iomem *at91_rstc_base; - -void __init at91_ioremap_rstc(u32 base_addr) -{ - at91_rstc_base = ioremap(base_addr, 16); - if (!at91_rstc_base) - panic("Impossible to ioremap at91_rstc_base\n"); -} - void __init at91_initialize(unsigned long main_clock) { at91_boot_soc.ioremap_registers(); diff --git a/trunk/arch/arm/mach-bcmring/arch.c b/trunk/arch/arm/mach-bcmring/arch.c index 45c97b1ee9b1..9e5e7552498c 100644 --- a/trunk/arch/arm/mach-bcmring/arch.c +++ b/trunk/arch/arm/mach-bcmring/arch.c @@ -194,6 +194,6 @@ MACHINE_START(BCMRING, "BCMRING") .init_early = bcmring_init_early, .init_irq = bcmring_init_irq, .timer = &bcmring_timer, - .init_machine = bcmring_init_machine, + .init_machine = bcmring_init_machine .restart = bcmring_restart, MACHINE_END diff --git a/trunk/arch/arm/mach-bcmring/dma.c b/trunk/arch/arm/mach-bcmring/dma.c index 1024396797e1..1a1a27dd5654 100644 --- a/trunk/arch/arm/mach-bcmring/dma.c +++ b/trunk/arch/arm/mach-bcmring/dma.c @@ -33,11 +33,17 @@ #include +#include #include #include #include #include +/* I don't quite understand why dc4 fails when this is set to 1 and DMA is enabled */ +/* especially since dc4 doesn't use kmalloc'd memory. */ + +#define ALLOW_MAP_OF_KMALLOC_MEMORY 0 + /* ---- Public Variables ------------------------------------------------- */ /* ---- Private Constants and Types -------------------------------------- */ @@ -47,18 +53,58 @@ #define CONTROLLER_FROM_HANDLE(handle) (((handle) >> 4) & 0x0f) #define CHANNEL_FROM_HANDLE(handle) ((handle) & 0x0f) +#define DMA_MAP_DEBUG 0 + +#if DMA_MAP_DEBUG +# define DMA_MAP_PRINT(fmt, args...) printk("%s: " fmt, __func__, ## args) +#else +# define DMA_MAP_PRINT(fmt, args...) +#endif /* ---- Private Variables ------------------------------------------------ */ static DMA_Global_t gDMA; static struct proc_dir_entry *gDmaDir; +static atomic_t gDmaStatMemTypeKmalloc = ATOMIC_INIT(0); +static atomic_t gDmaStatMemTypeVmalloc = ATOMIC_INIT(0); +static atomic_t gDmaStatMemTypeUser = ATOMIC_INIT(0); +static atomic_t gDmaStatMemTypeCoherent = ATOMIC_INIT(0); + #include "dma_device.c" /* ---- Private Function Prototypes -------------------------------------- */ /* ---- Functions ------------------------------------------------------- */ +/****************************************************************************/ +/** +* Displays information for /proc/dma/mem-type +*/ +/****************************************************************************/ + +static int dma_proc_read_mem_type(char *buf, char **start, off_t offset, + int count, int *eof, void *data) +{ + int len = 0; + + len += sprintf(buf + len, "dma_map_mem statistics\n"); + len += + sprintf(buf + len, "coherent: %d\n", + atomic_read(&gDmaStatMemTypeCoherent)); + len += + sprintf(buf + len, "kmalloc: %d\n", + atomic_read(&gDmaStatMemTypeKmalloc)); + len += + sprintf(buf + len, "vmalloc: %d\n", + atomic_read(&gDmaStatMemTypeVmalloc)); + len += + sprintf(buf + len, "user: %d\n", + atomic_read(&gDmaStatMemTypeUser)); + + return len; +} + /****************************************************************************/ /** * Displays information for /proc/dma/channels @@ -800,6 +846,8 @@ int dma_init(void) dma_proc_read_channels, NULL); create_proc_read_entry("devices", 0, gDmaDir, dma_proc_read_devices, NULL); + create_proc_read_entry("mem-type", 0, gDmaDir, + dma_proc_read_mem_type, NULL); } out: @@ -1517,3 +1565,767 @@ int dma_set_device_handler(DMA_Device_t dev, /* Device to set the callback for. } EXPORT_SYMBOL(dma_set_device_handler); + +/****************************************************************************/ +/** +* Initializes a memory mapping structure +*/ +/****************************************************************************/ + +int dma_init_mem_map(DMA_MemMap_t *memMap) +{ + memset(memMap, 0, sizeof(*memMap)); + + sema_init(&memMap->lock, 1); + + return 0; +} + +EXPORT_SYMBOL(dma_init_mem_map); + +/****************************************************************************/ +/** +* Releases any memory currently being held by a memory mapping structure. +*/ +/****************************************************************************/ + +int dma_term_mem_map(DMA_MemMap_t *memMap) +{ + down(&memMap->lock); /* Just being paranoid */ + + /* Free up any allocated memory */ + + up(&memMap->lock); + memset(memMap, 0, sizeof(*memMap)); + + return 0; +} + +EXPORT_SYMBOL(dma_term_mem_map); + +/****************************************************************************/ +/** +* Looks at a memory address and categorizes it. +* +* @return One of the values from the DMA_MemType_t enumeration. +*/ +/****************************************************************************/ + +DMA_MemType_t dma_mem_type(void *addr) +{ + unsigned long addrVal = (unsigned long)addr; + + if (addrVal >= CONSISTENT_BASE) { + /* NOTE: DMA virtual memory space starts at 0xFFxxxxxx */ + + /* dma_alloc_xxx pages are physically and virtually contiguous */ + + return DMA_MEM_TYPE_DMA; + } + + /* Technically, we could add one more classification. Addresses between VMALLOC_END */ + /* and the beginning of the DMA virtual address could be considered to be I/O space. */ + /* Right now, nobody cares about this particular classification, so we ignore it. */ + + if (is_vmalloc_addr(addr)) { + /* Address comes from the vmalloc'd region. Pages are virtually */ + /* contiguous but NOT physically contiguous */ + + return DMA_MEM_TYPE_VMALLOC; + } + + if (addrVal >= PAGE_OFFSET) { + /* PAGE_OFFSET is typically 0xC0000000 */ + + /* kmalloc'd pages are physically contiguous */ + + return DMA_MEM_TYPE_KMALLOC; + } + + return DMA_MEM_TYPE_USER; +} + +EXPORT_SYMBOL(dma_mem_type); + +/****************************************************************************/ +/** +* Looks at a memory address and determines if we support DMA'ing to/from +* that type of memory. +* +* @return boolean - +* return value != 0 means dma supported +* return value == 0 means dma not supported +*/ +/****************************************************************************/ + +int dma_mem_supports_dma(void *addr) +{ + DMA_MemType_t memType = dma_mem_type(addr); + + return (memType == DMA_MEM_TYPE_DMA) +#if ALLOW_MAP_OF_KMALLOC_MEMORY + || (memType == DMA_MEM_TYPE_KMALLOC) +#endif + || (memType == DMA_MEM_TYPE_USER); +} + +EXPORT_SYMBOL(dma_mem_supports_dma); + +/****************************************************************************/ +/** +* Maps in a memory region such that it can be used for performing a DMA. +* +* @return +*/ +/****************************************************************************/ + +int dma_map_start(DMA_MemMap_t *memMap, /* Stores state information about the map */ + enum dma_data_direction dir /* Direction that the mapping will be going */ + ) { + int rc; + + down(&memMap->lock); + + DMA_MAP_PRINT("memMap: %p\n", memMap); + + if (memMap->inUse) { + printk(KERN_ERR "%s: memory map %p is already being used\n", + __func__, memMap); + rc = -EBUSY; + goto out; + } + + memMap->inUse = 1; + memMap->dir = dir; + memMap->numRegionsUsed = 0; + + rc = 0; + +out: + + DMA_MAP_PRINT("returning %d", rc); + + up(&memMap->lock); + + return rc; +} + +EXPORT_SYMBOL(dma_map_start); + +/****************************************************************************/ +/** +* Adds a segment of memory to a memory map. Each segment is both +* physically and virtually contiguous. +* +* @return 0 on success, error code otherwise. +*/ +/****************************************************************************/ + +static int dma_map_add_segment(DMA_MemMap_t *memMap, /* Stores state information about the map */ + DMA_Region_t *region, /* Region that the segment belongs to */ + void *virtAddr, /* Virtual address of the segment being added */ + dma_addr_t physAddr, /* Physical address of the segment being added */ + size_t numBytes /* Number of bytes of the segment being added */ + ) { + DMA_Segment_t *segment; + + DMA_MAP_PRINT("memMap:%p va:%p pa:0x%x #:%d\n", memMap, virtAddr, + physAddr, numBytes); + + /* Sanity check */ + + if (((unsigned long)virtAddr < (unsigned long)region->virtAddr) + || (((unsigned long)virtAddr + numBytes)) > + ((unsigned long)region->virtAddr + region->numBytes)) { + printk(KERN_ERR + "%s: virtAddr %p is outside region @ %p len: %d\n", + __func__, virtAddr, region->virtAddr, region->numBytes); + return -EINVAL; + } + + if (region->numSegmentsUsed > 0) { + /* Check to see if this segment is physically contiguous with the previous one */ + + segment = ®ion->segment[region->numSegmentsUsed - 1]; + + if ((segment->physAddr + segment->numBytes) == physAddr) { + /* It is - just add on to the end */ + + DMA_MAP_PRINT("appending %d bytes to last segment\n", + numBytes); + + segment->numBytes += numBytes; + + return 0; + } + } + + /* Reallocate to hold more segments, if required. */ + + if (region->numSegmentsUsed >= region->numSegmentsAllocated) { + DMA_Segment_t *newSegment; + size_t oldSize = + region->numSegmentsAllocated * sizeof(*newSegment); + int newAlloc = region->numSegmentsAllocated + 4; + size_t newSize = newAlloc * sizeof(*newSegment); + + newSegment = kmalloc(newSize, GFP_KERNEL); + if (newSegment == NULL) { + return -ENOMEM; + } + memcpy(newSegment, region->segment, oldSize); + memset(&((uint8_t *) newSegment)[oldSize], 0, + newSize - oldSize); + kfree(region->segment); + + region->numSegmentsAllocated = newAlloc; + region->segment = newSegment; + } + + segment = ®ion->segment[region->numSegmentsUsed]; + region->numSegmentsUsed++; + + segment->virtAddr = virtAddr; + segment->physAddr = physAddr; + segment->numBytes = numBytes; + + DMA_MAP_PRINT("returning success\n"); + + return 0; +} + +/****************************************************************************/ +/** +* Adds a region of memory to a memory map. Each region is virtually +* contiguous, but not necessarily physically contiguous. +* +* @return 0 on success, error code otherwise. +*/ +/****************************************************************************/ + +int dma_map_add_region(DMA_MemMap_t *memMap, /* Stores state information about the map */ + void *mem, /* Virtual address that we want to get a map of */ + size_t numBytes /* Number of bytes being mapped */ + ) { + unsigned long addr = (unsigned long)mem; + unsigned int offset; + int rc = 0; + DMA_Region_t *region; + dma_addr_t physAddr; + + down(&memMap->lock); + + DMA_MAP_PRINT("memMap:%p va:%p #:%d\n", memMap, mem, numBytes); + + if (!memMap->inUse) { + printk(KERN_ERR "%s: Make sure you call dma_map_start first\n", + __func__); + rc = -EINVAL; + goto out; + } + + /* Reallocate to hold more regions. */ + + if (memMap->numRegionsUsed >= memMap->numRegionsAllocated) { + DMA_Region_t *newRegion; + size_t oldSize = + memMap->numRegionsAllocated * sizeof(*newRegion); + int newAlloc = memMap->numRegionsAllocated + 4; + size_t newSize = newAlloc * sizeof(*newRegion); + + newRegion = kmalloc(newSize, GFP_KERNEL); + if (newRegion == NULL) { + rc = -ENOMEM; + goto out; + } + memcpy(newRegion, memMap->region, oldSize); + memset(&((uint8_t *) newRegion)[oldSize], 0, newSize - oldSize); + + kfree(memMap->region); + + memMap->numRegionsAllocated = newAlloc; + memMap->region = newRegion; + } + + region = &memMap->region[memMap->numRegionsUsed]; + memMap->numRegionsUsed++; + + offset = addr & ~PAGE_MASK; + + region->memType = dma_mem_type(mem); + region->virtAddr = mem; + region->numBytes = numBytes; + region->numSegmentsUsed = 0; + region->numLockedPages = 0; + region->lockedPages = NULL; + + switch (region->memType) { + case DMA_MEM_TYPE_VMALLOC: + { + atomic_inc(&gDmaStatMemTypeVmalloc); + + /* printk(KERN_ERR "%s: vmalloc'd pages are not supported\n", __func__); */ + + /* vmalloc'd pages are not physically contiguous */ + + rc = -EINVAL; + break; + } + + case DMA_MEM_TYPE_KMALLOC: + { + atomic_inc(&gDmaStatMemTypeKmalloc); + + /* kmalloc'd pages are physically contiguous, so they'll have exactly */ + /* one segment */ + +#if ALLOW_MAP_OF_KMALLOC_MEMORY + physAddr = + dma_map_single(NULL, mem, numBytes, memMap->dir); + rc = dma_map_add_segment(memMap, region, mem, physAddr, + numBytes); +#else + rc = -EINVAL; +#endif + break; + } + + case DMA_MEM_TYPE_DMA: + { + /* dma_alloc_xxx pages are physically contiguous */ + + atomic_inc(&gDmaStatMemTypeCoherent); + + physAddr = (vmalloc_to_pfn(mem) << PAGE_SHIFT) + offset; + + dma_sync_single_for_cpu(NULL, physAddr, numBytes, + memMap->dir); + rc = dma_map_add_segment(memMap, region, mem, physAddr, + numBytes); + break; + } + + case DMA_MEM_TYPE_USER: + { + size_t firstPageOffset; + size_t firstPageSize; + struct page **pages; + struct task_struct *userTask; + + atomic_inc(&gDmaStatMemTypeUser); + +#if 1 + /* If the pages are user pages, then the dma_mem_map_set_user_task function */ + /* must have been previously called. */ + + if (memMap->userTask == NULL) { + printk(KERN_ERR + "%s: must call dma_mem_map_set_user_task when using user-mode memory\n", + __func__); + return -EINVAL; + } + + /* User pages need to be locked. */ + + firstPageOffset = + (unsigned long)region->virtAddr & (PAGE_SIZE - 1); + firstPageSize = PAGE_SIZE - firstPageOffset; + + region->numLockedPages = (firstPageOffset + + region->numBytes + + PAGE_SIZE - 1) / PAGE_SIZE; + pages = + kmalloc(region->numLockedPages * + sizeof(struct page *), GFP_KERNEL); + + if (pages == NULL) { + region->numLockedPages = 0; + return -ENOMEM; + } + + userTask = memMap->userTask; + + down_read(&userTask->mm->mmap_sem); + rc = get_user_pages(userTask, /* task */ + userTask->mm, /* mm */ + (unsigned long)region->virtAddr, /* start */ + region->numLockedPages, /* len */ + memMap->dir == DMA_FROM_DEVICE, /* write */ + 0, /* force */ + pages, /* pages (array of pointers to page) */ + NULL); /* vmas */ + up_read(&userTask->mm->mmap_sem); + + if (rc != region->numLockedPages) { + kfree(pages); + region->numLockedPages = 0; + + if (rc >= 0) { + rc = -EINVAL; + } + } else { + uint8_t *virtAddr = region->virtAddr; + size_t bytesRemaining; + int pageIdx; + + rc = 0; /* Since get_user_pages returns +ve number */ + + region->lockedPages = pages; + + /* We've locked the user pages. Now we need to walk them and figure */ + /* out the physical addresses. */ + + /* The first page may be partial */ + + dma_map_add_segment(memMap, + region, + virtAddr, + PFN_PHYS(page_to_pfn + (pages[0])) + + firstPageOffset, + firstPageSize); + + virtAddr += firstPageSize; + bytesRemaining = + region->numBytes - firstPageSize; + + for (pageIdx = 1; + pageIdx < region->numLockedPages; + pageIdx++) { + size_t bytesThisPage = + (bytesRemaining > + PAGE_SIZE ? PAGE_SIZE : + bytesRemaining); + + DMA_MAP_PRINT + ("pageIdx:%d pages[pageIdx]=%p pfn=%u phys=%u\n", + pageIdx, pages[pageIdx], + page_to_pfn(pages[pageIdx]), + PFN_PHYS(page_to_pfn + (pages[pageIdx]))); + + dma_map_add_segment(memMap, + region, + virtAddr, + PFN_PHYS(page_to_pfn + (pages + [pageIdx])), + bytesThisPage); + + virtAddr += bytesThisPage; + bytesRemaining -= bytesThisPage; + } + } +#else + printk(KERN_ERR + "%s: User mode pages are not yet supported\n", + __func__); + + /* user pages are not physically contiguous */ + + rc = -EINVAL; +#endif + break; + } + + default: + { + printk(KERN_ERR "%s: Unsupported memory type: %d\n", + __func__, region->memType); + + rc = -EINVAL; + break; + } + } + + if (rc != 0) { + memMap->numRegionsUsed--; + } + +out: + + DMA_MAP_PRINT("returning %d\n", rc); + + up(&memMap->lock); + + return rc; +} + +EXPORT_SYMBOL(dma_map_add_segment); + +/****************************************************************************/ +/** +* Maps in a memory region such that it can be used for performing a DMA. +* +* @return 0 on success, error code otherwise. +*/ +/****************************************************************************/ + +int dma_map_mem(DMA_MemMap_t *memMap, /* Stores state information about the map */ + void *mem, /* Virtual address that we want to get a map of */ + size_t numBytes, /* Number of bytes being mapped */ + enum dma_data_direction dir /* Direction that the mapping will be going */ + ) { + int rc; + + rc = dma_map_start(memMap, dir); + if (rc == 0) { + rc = dma_map_add_region(memMap, mem, numBytes); + if (rc < 0) { + /* Since the add fails, this function will fail, and the caller won't */ + /* call unmap, so we need to do it here. */ + + dma_unmap(memMap, 0); + } + } + + return rc; +} + +EXPORT_SYMBOL(dma_map_mem); + +/****************************************************************************/ +/** +* Setup a descriptor ring for a given memory map. +* +* It is assumed that the descriptor ring has already been initialized, and +* this routine will only reallocate a new descriptor ring if the existing +* one is too small. +* +* @return 0 on success, error code otherwise. +*/ +/****************************************************************************/ + +int dma_map_create_descriptor_ring(DMA_Device_t dev, /* DMA device (where the ring is stored) */ + DMA_MemMap_t *memMap, /* Memory map that will be used */ + dma_addr_t devPhysAddr /* Physical address of device */ + ) { + int rc; + int numDescriptors; + DMA_DeviceAttribute_t *devAttr; + DMA_Region_t *region; + DMA_Segment_t *segment; + dma_addr_t srcPhysAddr; + dma_addr_t dstPhysAddr; + int regionIdx; + int segmentIdx; + + devAttr = &DMA_gDeviceAttribute[dev]; + + down(&memMap->lock); + + /* Figure out how many descriptors we need */ + + numDescriptors = 0; + for (regionIdx = 0; regionIdx < memMap->numRegionsUsed; regionIdx++) { + region = &memMap->region[regionIdx]; + + for (segmentIdx = 0; segmentIdx < region->numSegmentsUsed; + segmentIdx++) { + segment = ®ion->segment[segmentIdx]; + + if (memMap->dir == DMA_TO_DEVICE) { + srcPhysAddr = segment->physAddr; + dstPhysAddr = devPhysAddr; + } else { + srcPhysAddr = devPhysAddr; + dstPhysAddr = segment->physAddr; + } + + rc = + dma_calculate_descriptor_count(dev, srcPhysAddr, + dstPhysAddr, + segment-> + numBytes); + if (rc < 0) { + printk(KERN_ERR + "%s: dma_calculate_descriptor_count failed: %d\n", + __func__, rc); + goto out; + } + numDescriptors += rc; + } + } + + /* Adjust the size of the ring, if it isn't big enough */ + + if (numDescriptors > devAttr->ring.descriptorsAllocated) { + dma_free_descriptor_ring(&devAttr->ring); + rc = + dma_alloc_descriptor_ring(&devAttr->ring, + numDescriptors); + if (rc < 0) { + printk(KERN_ERR + "%s: dma_alloc_descriptor_ring failed: %d\n", + __func__, rc); + goto out; + } + } else { + rc = + dma_init_descriptor_ring(&devAttr->ring, + numDescriptors); + if (rc < 0) { + printk(KERN_ERR + "%s: dma_init_descriptor_ring failed: %d\n", + __func__, rc); + goto out; + } + } + + /* Populate the descriptors */ + + for (regionIdx = 0; regionIdx < memMap->numRegionsUsed; regionIdx++) { + region = &memMap->region[regionIdx]; + + for (segmentIdx = 0; segmentIdx < region->numSegmentsUsed; + segmentIdx++) { + segment = ®ion->segment[segmentIdx]; + + if (memMap->dir == DMA_TO_DEVICE) { + srcPhysAddr = segment->physAddr; + dstPhysAddr = devPhysAddr; + } else { + srcPhysAddr = devPhysAddr; + dstPhysAddr = segment->physAddr; + } + + rc = + dma_add_descriptors(&devAttr->ring, dev, + srcPhysAddr, dstPhysAddr, + segment->numBytes); + if (rc < 0) { + printk(KERN_ERR + "%s: dma_add_descriptors failed: %d\n", + __func__, rc); + goto out; + } + } + } + + rc = 0; + +out: + + up(&memMap->lock); + return rc; +} + +EXPORT_SYMBOL(dma_map_create_descriptor_ring); + +/****************************************************************************/ +/** +* Maps in a memory region such that it can be used for performing a DMA. +* +* @return +*/ +/****************************************************************************/ + +int dma_unmap(DMA_MemMap_t *memMap, /* Stores state information about the map */ + int dirtied /* non-zero if any of the pages were modified */ + ) { + + int rc = 0; + int regionIdx; + int segmentIdx; + DMA_Region_t *region; + DMA_Segment_t *segment; + + down(&memMap->lock); + + for (regionIdx = 0; regionIdx < memMap->numRegionsUsed; regionIdx++) { + region = &memMap->region[regionIdx]; + + for (segmentIdx = 0; segmentIdx < region->numSegmentsUsed; + segmentIdx++) { + segment = ®ion->segment[segmentIdx]; + + switch (region->memType) { + case DMA_MEM_TYPE_VMALLOC: + { + printk(KERN_ERR + "%s: vmalloc'd pages are not yet supported\n", + __func__); + rc = -EINVAL; + goto out; + } + + case DMA_MEM_TYPE_KMALLOC: + { +#if ALLOW_MAP_OF_KMALLOC_MEMORY + dma_unmap_single(NULL, + segment->physAddr, + segment->numBytes, + memMap->dir); +#endif + break; + } + + case DMA_MEM_TYPE_DMA: + { + dma_sync_single_for_cpu(NULL, + segment-> + physAddr, + segment-> + numBytes, + memMap->dir); + break; + } + + case DMA_MEM_TYPE_USER: + { + /* Nothing to do here. */ + + break; + } + + default: + { + printk(KERN_ERR + "%s: Unsupported memory type: %d\n", + __func__, region->memType); + rc = -EINVAL; + goto out; + } + } + + segment->virtAddr = NULL; + segment->physAddr = 0; + segment->numBytes = 0; + } + + if (region->numLockedPages > 0) { + int pageIdx; + + /* Some user pages were locked. We need to go and unlock them now. */ + + for (pageIdx = 0; pageIdx < region->numLockedPages; + pageIdx++) { + struct page *page = + region->lockedPages[pageIdx]; + + if (memMap->dir == DMA_FROM_DEVICE) { + SetPageDirty(page); + } + page_cache_release(page); + } + kfree(region->lockedPages); + region->numLockedPages = 0; + region->lockedPages = NULL; + } + + region->memType = DMA_MEM_TYPE_NONE; + region->virtAddr = NULL; + region->numBytes = 0; + region->numSegmentsUsed = 0; + } + memMap->userTask = NULL; + memMap->numRegionsUsed = 0; + memMap->inUse = 0; + +out: + up(&memMap->lock); + + return rc; +} + +EXPORT_SYMBOL(dma_unmap); diff --git a/trunk/arch/arm/mach-bcmring/include/mach/dma.h b/trunk/arch/arm/mach-bcmring/include/mach/dma.h index 72543781207b..1f2c5319c056 100644 --- a/trunk/arch/arm/mach-bcmring/include/mach/dma.h +++ b/trunk/arch/arm/mach-bcmring/include/mach/dma.h @@ -26,9 +26,15 @@ /* ---- Include Files ---------------------------------------------------- */ #include +#include #include #include #include +#include +#include +#include +#include +#include /* ---- Constants and Types ---------------------------------------------- */ @@ -105,6 +111,78 @@ typedef struct { } DMA_DescriptorRing_t; +/**************************************************************************** +* +* The DMA_MemType_t and DMA_MemMap_t are helper structures used to setup +* DMA chains from a variety of memory sources. +* +*****************************************************************************/ + +#define DMA_MEM_MAP_MIN_SIZE 4096 /* Pages less than this size are better */ + /* off not being DMA'd. */ + +typedef enum { + DMA_MEM_TYPE_NONE, /* Not a valid setting */ + DMA_MEM_TYPE_VMALLOC, /* Memory came from vmalloc call */ + DMA_MEM_TYPE_KMALLOC, /* Memory came from kmalloc call */ + DMA_MEM_TYPE_DMA, /* Memory came from dma_alloc_xxx call */ + DMA_MEM_TYPE_USER, /* Memory came from user space. */ + +} DMA_MemType_t; + +/* A segment represents a physically and virtually contiguous chunk of memory. */ +/* i.e. each segment can be DMA'd */ +/* A user of the DMA code will add memory regions. Each region may need to be */ +/* represented by one or more segments. */ + +typedef struct { + void *virtAddr; /* Virtual address used for this segment */ + dma_addr_t physAddr; /* Physical address this segment maps to */ + size_t numBytes; /* Size of the segment, in bytes */ + +} DMA_Segment_t; + +/* A region represents a virtually contiguous chunk of memory, which may be */ +/* made up of multiple segments. */ + +typedef struct { + DMA_MemType_t memType; + void *virtAddr; + size_t numBytes; + + /* Each region (virtually contiguous) consists of one or more segments. Each */ + /* segment is virtually and physically contiguous. */ + + int numSegmentsUsed; + int numSegmentsAllocated; + DMA_Segment_t *segment; + + /* When a region corresponds to user memory, we need to lock all of the pages */ + /* down before we can figure out the physical addresses. The lockedPage array contains */ + /* the pages that were locked, and which subsequently need to be unlocked once the */ + /* memory is unmapped. */ + + unsigned numLockedPages; + struct page **lockedPages; + +} DMA_Region_t; + +typedef struct { + int inUse; /* Is this mapping currently being used? */ + struct semaphore lock; /* Acquired when using this structure */ + enum dma_data_direction dir; /* Direction this transfer is intended for */ + + /* In the event that we're mapping user memory, we need to know which task */ + /* the memory is for, so that we can obtain the correct mm locks. */ + + struct task_struct *userTask; + + int numRegionsUsed; + int numRegionsAllocated; + DMA_Region_t *region; + +} DMA_MemMap_t; + /**************************************************************************** * * The DMA_DeviceAttribute_t contains information which describes a @@ -490,6 +568,124 @@ int dma_alloc_double_dst_descriptors(DMA_Handle_t handle, /* DMA Handle */ size_t numBytes /* Number of bytes in each destination buffer */ ); +/****************************************************************************/ +/** +* Initializes a DMA_MemMap_t data structure +*/ +/****************************************************************************/ + +int dma_init_mem_map(DMA_MemMap_t *memMap /* Stores state information about the map */ + ); + +/****************************************************************************/ +/** +* Releases any memory currently being held by a memory mapping structure. +*/ +/****************************************************************************/ + +int dma_term_mem_map(DMA_MemMap_t *memMap /* Stores state information about the map */ + ); + +/****************************************************************************/ +/** +* Looks at a memory address and categorizes it. +* +* @return One of the values from the DMA_MemType_t enumeration. +*/ +/****************************************************************************/ + +DMA_MemType_t dma_mem_type(void *addr); + +/****************************************************************************/ +/** +* Sets the process (aka userTask) associated with a mem map. This is +* required if user-mode segments will be added to the mapping. +*/ +/****************************************************************************/ + +static inline void dma_mem_map_set_user_task(DMA_MemMap_t *memMap, + struct task_struct *task) +{ + memMap->userTask = task; +} + +/****************************************************************************/ +/** +* Looks at a memory address and determines if we support DMA'ing to/from +* that type of memory. +* +* @return boolean - +* return value != 0 means dma supported +* return value == 0 means dma not supported +*/ +/****************************************************************************/ + +int dma_mem_supports_dma(void *addr); + +/****************************************************************************/ +/** +* Initializes a memory map for use. Since this function acquires a +* sempaphore within the memory map, it is VERY important that dma_unmap +* be called when you're finished using the map. +*/ +/****************************************************************************/ + +int dma_map_start(DMA_MemMap_t *memMap, /* Stores state information about the map */ + enum dma_data_direction dir /* Direction that the mapping will be going */ + ); + +/****************************************************************************/ +/** +* Adds a segment of memory to a memory map. +* +* @return 0 on success, error code otherwise. +*/ +/****************************************************************************/ + +int dma_map_add_region(DMA_MemMap_t *memMap, /* Stores state information about the map */ + void *mem, /* Virtual address that we want to get a map of */ + size_t numBytes /* Number of bytes being mapped */ + ); + +/****************************************************************************/ +/** +* Creates a descriptor ring from a memory mapping. +* +* @return 0 on success, error code otherwise. +*/ +/****************************************************************************/ + +int dma_map_create_descriptor_ring(DMA_Device_t dev, /* DMA device (where the ring is stored) */ + DMA_MemMap_t *memMap, /* Memory map that will be used */ + dma_addr_t devPhysAddr /* Physical address of device */ + ); + +/****************************************************************************/ +/** +* Maps in a memory region such that it can be used for performing a DMA. +* +* @return +*/ +/****************************************************************************/ + +int dma_map_mem(DMA_MemMap_t *memMap, /* Stores state information about the map */ + void *addr, /* Virtual address that we want to get a map of */ + size_t count, /* Number of bytes being mapped */ + enum dma_data_direction dir /* Direction that the mapping will be going */ + ); + +/****************************************************************************/ +/** +* Maps in a memory region such that it can be used for performing a DMA. +* +* @return +*/ +/****************************************************************************/ + +int dma_unmap(DMA_MemMap_t *memMap, /* Stores state information about the map */ + int dirtied /* non-zero if any of the pages were modified */ + ); + /****************************************************************************/ /** * Initiates a transfer when the descriptors have already been setup. diff --git a/trunk/arch/arm/mach-davinci/board-da850-evm.c b/trunk/arch/arm/mach-davinci/board-da850-evm.c index d5088900af6c..6b22b543a83f 100644 --- a/trunk/arch/arm/mach-davinci/board-da850-evm.c +++ b/trunk/arch/arm/mach-davinci/board-da850-evm.c @@ -44,7 +44,7 @@ #include #include -#define DA850_EVM_PHY_ID "davinci_mdio-0:00" +#define DA850_EVM_PHY_ID "0:00" #define DA850_LCD_PWR_PIN GPIO_TO_PIN(2, 8) #define DA850_LCD_BL_PIN GPIO_TO_PIN(2, 15) diff --git a/trunk/arch/arm/mach-davinci/board-dm365-evm.c b/trunk/arch/arm/mach-davinci/board-dm365-evm.c index 849311d3cb7c..346e1de2f5a8 100644 --- a/trunk/arch/arm/mach-davinci/board-dm365-evm.c +++ b/trunk/arch/arm/mach-davinci/board-dm365-evm.c @@ -54,7 +54,7 @@ static inline int have_tvp7002(void) return 0; } -#define DM365_EVM_PHY_ID "davinci_mdio-0:01" +#define DM365_EVM_PHY_ID "0:01" /* * A MAX-II CPLD is used for various board control functions. */ diff --git a/trunk/arch/arm/mach-davinci/board-dm644x-evm.c b/trunk/arch/arm/mach-davinci/board-dm644x-evm.c index 1247ecdcf752..a64b49cfedca 100644 --- a/trunk/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/trunk/arch/arm/mach-davinci/board-dm644x-evm.c @@ -40,7 +40,7 @@ #include #include -#define DM644X_EVM_PHY_ID "davinci_mdio-0:01" +#define DM644X_EVM_PHY_ID "0:01" #define LXT971_PHY_ID (0x001378e2) #define LXT971_PHY_MASK (0xfffffff0) diff --git a/trunk/arch/arm/mach-davinci/board-dm646x-evm.c b/trunk/arch/arm/mach-davinci/board-dm646x-evm.c index 872ac69fa049..64017558860b 100644 --- a/trunk/arch/arm/mach-davinci/board-dm646x-evm.c +++ b/trunk/arch/arm/mach-davinci/board-dm646x-evm.c @@ -736,7 +736,7 @@ static struct davinci_uart_config uart_config __initdata = { .enabled_uarts = (1 << 0), }; -#define DM646X_EVM_PHY_ID "davinci_mdio-0:01" +#define DM646X_EVM_PHY_ID "0:01" /* * The following EDMA channels/slots are not being used by drivers (for * example: Timer, GPIO, UART events etc) on dm646x, hence they are being diff --git a/trunk/arch/arm/mach-davinci/board-neuros-osd2.c b/trunk/arch/arm/mach-davinci/board-neuros-osd2.c index 8d34f513d415..6c4a16415d47 100644 --- a/trunk/arch/arm/mach-davinci/board-neuros-osd2.c +++ b/trunk/arch/arm/mach-davinci/board-neuros-osd2.c @@ -39,7 +39,7 @@ #include #include -#define NEUROS_OSD2_PHY_ID "davinci_mdio-0:01" +#define NEUROS_OSD2_PHY_ID "0:01" #define LXT971_PHY_ID 0x001378e2 #define LXT971_PHY_MASK 0xfffffff0 diff --git a/trunk/arch/arm/mach-davinci/board-omapl138-hawk.c b/trunk/arch/arm/mach-davinci/board-omapl138-hawk.c index 45e815760a27..e7c0c7c53493 100644 --- a/trunk/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/trunk/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -21,7 +21,7 @@ #include #include -#define HAWKBOARD_PHY_ID "davinci_mdio-0:07" +#define HAWKBOARD_PHY_ID "0:07" #define DA850_HAWK_MMCSD_CD_PIN GPIO_TO_PIN(3, 12) #define DA850_HAWK_MMCSD_WP_PIN GPIO_TO_PIN(3, 13) diff --git a/trunk/arch/arm/mach-davinci/board-sffsdr.c b/trunk/arch/arm/mach-davinci/board-sffsdr.c index 31da3c5b2ba3..0b136a831c59 100644 --- a/trunk/arch/arm/mach-davinci/board-sffsdr.c +++ b/trunk/arch/arm/mach-davinci/board-sffsdr.c @@ -42,7 +42,7 @@ #include #include -#define SFFSDR_PHY_ID "davinci_mdio-0:01" +#define SFFSDR_PHY_ID "0:01" static struct mtd_partition davinci_sffsdr_nandflash_partition[] = { /* U-Boot Environment: Block 0 * UBL: Block 1 diff --git a/trunk/arch/arm/mach-davinci/da850.c b/trunk/arch/arm/mach-davinci/da850.c index 992c4c410185..0ed7fdb64efb 100644 --- a/trunk/arch/arm/mach-davinci/da850.c +++ b/trunk/arch/arm/mach-davinci/da850.c @@ -153,6 +153,34 @@ static struct clk pll1_sysclk3 = { .div_reg = PLLDIV3, }; +static struct clk pll1_sysclk4 = { + .name = "pll1_sysclk4", + .parent = &pll1_clk, + .flags = CLK_PLL, + .div_reg = PLLDIV4, +}; + +static struct clk pll1_sysclk5 = { + .name = "pll1_sysclk5", + .parent = &pll1_clk, + .flags = CLK_PLL, + .div_reg = PLLDIV5, +}; + +static struct clk pll1_sysclk6 = { + .name = "pll0_sysclk6", + .parent = &pll0_clk, + .flags = CLK_PLL, + .div_reg = PLLDIV6, +}; + +static struct clk pll1_sysclk7 = { + .name = "pll1_sysclk7", + .parent = &pll1_clk, + .flags = CLK_PLL, + .div_reg = PLLDIV7, +}; + static struct clk i2c0_clk = { .name = "i2c0", .parent = &pll0_aux_clk, @@ -369,6 +397,10 @@ static struct clk_lookup da850_clks[] = { CLK(NULL, "pll1_aux", &pll1_aux_clk), CLK(NULL, "pll1_sysclk2", &pll1_sysclk2), CLK(NULL, "pll1_sysclk3", &pll1_sysclk3), + CLK(NULL, "pll1_sysclk4", &pll1_sysclk4), + CLK(NULL, "pll1_sysclk5", &pll1_sysclk5), + CLK(NULL, "pll1_sysclk6", &pll1_sysclk6), + CLK(NULL, "pll1_sysclk7", &pll1_sysclk7), CLK("i2c_davinci.1", NULL, &i2c0_clk), CLK(NULL, "timer0", &timerp64_0_clk), CLK("watchdog", NULL, &timerp64_1_clk), diff --git a/trunk/arch/arm/mach-exynos/hotplug.c b/trunk/arch/arm/mach-exynos/hotplug.c index dd1ad55524c9..da70e7e39937 100644 --- a/trunk/arch/arm/mach-exynos/hotplug.c +++ b/trunk/arch/arm/mach-exynos/hotplug.c @@ -16,7 +16,6 @@ #include #include -#include #include diff --git a/trunk/arch/arm/mach-exynos/platsmp.c b/trunk/arch/arm/mach-exynos/platsmp.c index 0f2035a1eb6e..683aec786b78 100644 --- a/trunk/arch/arm/mach-exynos/platsmp.c +++ b/trunk/arch/arm/mach-exynos/platsmp.c @@ -23,7 +23,6 @@ #include #include -#include #include #include diff --git a/trunk/arch/arm/mach-highbank/highbank.c b/trunk/arch/arm/mach-highbank/highbank.c index 8394d512a402..7afbe1e55beb 100644 --- a/trunk/arch/arm/mach-highbank/highbank.c +++ b/trunk/arch/arm/mach-highbank/highbank.c @@ -25,7 +25,6 @@ #include #include -#include #include #include #include @@ -73,7 +72,9 @@ static void __init highbank_map_io(void) void highbank_set_cpu_jump(int cpu, void *jump_addr) { +#ifdef CONFIG_SMP cpu = cpu_logical_map(cpu); +#endif writel(virt_to_phys(jump_addr), HB_JUMP_TABLE_VIRT(cpu)); __cpuc_flush_dcache_area(HB_JUMP_TABLE_VIRT(cpu), 16); outer_clean_range(HB_JUMP_TABLE_PHYS(cpu), diff --git a/trunk/arch/arm/mach-imx/Kconfig b/trunk/arch/arm/mach-imx/Kconfig index 4defb97bbfc8..09f357bcecde 100644 --- a/trunk/arch/arm/mach-imx/Kconfig +++ b/trunk/arch/arm/mach-imx/Kconfig @@ -87,6 +87,7 @@ config SOC_IMX35 config SOC_IMX5 select CPU_V7 + select ARM_L1_CACHE_SHIFT_6 select MXC_TZIC select ARCH_MXC_IOMUX_V3 select ARCH_MXC_AUDMUX_V2 diff --git a/trunk/arch/arm/mach-imx/clock-imx6q.c b/trunk/arch/arm/mach-imx/clock-imx6q.c index 2d88f8b9a454..9273c2a24b54 100644 --- a/trunk/arch/arm/mach-imx/clock-imx6q.c +++ b/trunk/arch/arm/mach-imx/clock-imx6q.c @@ -814,16 +814,6 @@ DEF_PFD(pll3_pfd_540m, PFD_480, PFD1, &pll3_usb_otg); DEF_PFD(pll3_pfd_508m, PFD_480, PFD2, &pll3_usb_otg); DEF_PFD(pll3_pfd_454m, PFD_480, PFD3, &pll3_usb_otg); -static unsigned long twd_clk_get_rate(struct clk *clk) -{ - return clk_get_rate(clk->parent) / 2; -} - -static struct clk twd_clk = { - .parent = &arm_clk, - .get_rate = twd_clk_get_rate, -}; - static unsigned long pll2_200m_get_rate(struct clk *clk) { return clk_get_rate(clk->parent) / 2; @@ -1904,7 +1894,6 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK("20ec000.sdma", NULL, sdma_clk), _REGISTER_CLOCK("20bc000.wdog", NULL, dummy_clk), _REGISTER_CLOCK("20c0000.wdog", NULL, dummy_clk), - _REGISTER_CLOCK("smp_twd", NULL, twd_clk), _REGISTER_CLOCK(NULL, "ckih", ckih_clk), _REGISTER_CLOCK(NULL, "ckil_clk", ckil_clk), _REGISTER_CLOCK(NULL, "aips_tz1_clk", aips_tz1_clk), diff --git a/trunk/arch/arm/mach-imx/mach-mx53_ard.c b/trunk/arch/arm/mach-imx/mach-mx53_ard.c index 753f4fc9ec04..08dfb7628d2d 100644 --- a/trunk/arch/arm/mach-imx/mach-mx53_ard.c +++ b/trunk/arch/arm/mach-imx/mach-mx53_ard.c @@ -188,10 +188,8 @@ static int weim_cs_config(void) return -ENOMEM; iomuxc_base = ioremap(MX53_IOMUXC_BASE_ADDR, SZ_4K); - if (!iomuxc_base) { - iounmap(weim_base); + if (!iomuxc_base) return -ENOMEM; - } /* CS1 timings for LAN9220 */ writel(0x20001, (weim_base + 0x18)); diff --git a/trunk/arch/arm/mach-imx/src.c b/trunk/arch/arm/mach-imx/src.c index e15f1555c59b..29bd1243781e 100644 --- a/trunk/arch/arm/mach-imx/src.c +++ b/trunk/arch/arm/mach-imx/src.c @@ -15,7 +15,6 @@ #include #include #include -#include #define SRC_SCR 0x000 #define SRC_GPR1 0x020 @@ -25,6 +24,10 @@ static void __iomem *src_base; +#ifndef CONFIG_SMP +#define cpu_logical_map(cpu) 0 +#endif + void imx_enable_cpu(int cpu, bool enable) { u32 mask, val; diff --git a/trunk/arch/arm/mach-msm/hotplug.c b/trunk/arch/arm/mach-msm/hotplug.c index a446fc14221f..41c252de0215 100644 --- a/trunk/arch/arm/mach-msm/hotplug.c +++ b/trunk/arch/arm/mach-msm/hotplug.c @@ -11,7 +11,6 @@ #include #include -#include extern volatile int pen_release; diff --git a/trunk/arch/arm/mach-msm/platsmp.c b/trunk/arch/arm/mach-msm/platsmp.c index db0117ec55f4..0b3e357c4c8c 100644 --- a/trunk/arch/arm/mach-msm/platsmp.c +++ b/trunk/arch/arm/mach-msm/platsmp.c @@ -20,7 +20,6 @@ #include #include #include -#include #include diff --git a/trunk/arch/arm/mach-omap2/Kconfig b/trunk/arch/arm/mach-omap2/Kconfig index d965da45160e..a8ba7b96dcd1 100644 --- a/trunk/arch/arm/mach-omap2/Kconfig +++ b/trunk/arch/arm/mach-omap2/Kconfig @@ -33,6 +33,7 @@ config ARCH_OMAP3 default y select CPU_V7 select USB_ARCH_HAS_EHCI + select ARM_L1_CACHE_SHIFT_6 if !ARCH_OMAP4 select ARCH_HAS_OPP select PM_OPP if PM select ARM_CPU_SUSPEND if PM @@ -213,12 +214,13 @@ config MACH_OMAP3_PANDORA depends on ARCH_OMAP3 default y select OMAP_PACKAGE_CBB - select REGULATOR_FIXED_VOLTAGE if REGULATOR + select REGULATOR_FIXED_VOLTAGE config MACH_OMAP3_TOUCHBOOK bool "OMAP3 Touch Book" depends on ARCH_OMAP3 default y + select BACKLIGHT_CLASS_DEVICE config MACH_OMAP_3430SDP bool "OMAP 3430 SDP board" @@ -264,7 +266,7 @@ config MACH_OMAP_ZOOM2 select SERIAL_8250 select SERIAL_CORE_CONSOLE select SERIAL_8250_CONSOLE - select REGULATOR_FIXED_VOLTAGE if REGULATOR + select REGULATOR_FIXED_VOLTAGE config MACH_OMAP_ZOOM3 bool "OMAP3630 Zoom3 board" @@ -274,7 +276,7 @@ config MACH_OMAP_ZOOM3 select SERIAL_8250 select SERIAL_CORE_CONSOLE select SERIAL_8250_CONSOLE - select REGULATOR_FIXED_VOLTAGE if REGULATOR + select REGULATOR_FIXED_VOLTAGE config MACH_CM_T35 bool "CompuLab CM-T35/CM-T3730 modules" @@ -333,7 +335,7 @@ config MACH_OMAP_4430SDP depends on ARCH_OMAP4 select OMAP_PACKAGE_CBL select OMAP_PACKAGE_CBS - select REGULATOR_FIXED_VOLTAGE if REGULATOR + select REGULATOR_FIXED_VOLTAGE config MACH_OMAP4_PANDA bool "OMAP4 Panda Board" @@ -341,7 +343,7 @@ config MACH_OMAP4_PANDA depends on ARCH_OMAP4 select OMAP_PACKAGE_CBL select OMAP_PACKAGE_CBS - select REGULATOR_FIXED_VOLTAGE if REGULATOR + select REGULATOR_FIXED_VOLTAGE config OMAP3_EMU bool "OMAP3 debugging peripherals" diff --git a/trunk/arch/arm/mach-omap2/board-4430sdp.c b/trunk/arch/arm/mach-omap2/board-4430sdp.c index 21fc87648660..39fba9df17fb 100644 --- a/trunk/arch/arm/mach-omap2/board-4430sdp.c +++ b/trunk/arch/arm/mach-omap2/board-4430sdp.c @@ -52,9 +52,8 @@ #define ETH_KS8851_QUART 138 #define OMAP4_SFH7741_SENSOR_OUTPUT_GPIO 184 #define OMAP4_SFH7741_ENABLE_GPIO 188 -#define HDMI_GPIO_CT_CP_HPD 60 /* HPD mode enable/disable */ +#define HDMI_GPIO_HPD 60 /* Hot plug pin for HDMI */ #define HDMI_GPIO_LS_OE 41 /* Level shifter for HDMI */ -#define HDMI_GPIO_HPD 63 /* Hotplug detect */ #define DISPLAY_SEL_GPIO 59 /* LCD2/PicoDLP switch */ #define DLP_POWER_ON_GPIO 40 @@ -604,9 +603,8 @@ static void __init omap_sfh7741prox_init(void) } static struct gpio sdp4430_hdmi_gpios[] = { - { HDMI_GPIO_CT_CP_HPD, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_ct_cp_hpd" }, + { HDMI_GPIO_HPD, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_hpd" }, { HDMI_GPIO_LS_OE, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_ls_oe" }, - { HDMI_GPIO_HPD, GPIOF_DIR_IN, "hdmi_gpio_hpd" }, }; static int sdp4430_panel_enable_hdmi(struct omap_dss_device *dssdev) @@ -623,7 +621,8 @@ static int sdp4430_panel_enable_hdmi(struct omap_dss_device *dssdev) static void sdp4430_panel_disable_hdmi(struct omap_dss_device *dssdev) { - gpio_free_array(sdp4430_hdmi_gpios, ARRAY_SIZE(sdp4430_hdmi_gpios)); + gpio_free(HDMI_GPIO_LS_OE); + gpio_free(HDMI_GPIO_HPD); } static struct nokia_dsi_panel_data dsi1_panel = { @@ -739,10 +738,6 @@ static void sdp4430_lcd_init(void) pr_err("%s: Could not get lcd2_reset_gpio\n", __func__); } -static struct omap_dss_hdmi_data sdp4430_hdmi_data = { - .hpd_gpio = HDMI_GPIO_HPD, -}; - static struct omap_dss_device sdp4430_hdmi_device = { .name = "hdmi", .driver_name = "hdmi_panel", @@ -750,7 +745,6 @@ static struct omap_dss_device sdp4430_hdmi_device = { .platform_enable = sdp4430_panel_enable_hdmi, .platform_disable = sdp4430_panel_disable_hdmi, .channel = OMAP_DSS_CHANNEL_DIGIT, - .data = &sdp4430_hdmi_data, }; static struct picodlp_panel_data sdp4430_picodlp_pdata = { @@ -835,10 +829,6 @@ static void omap_4430sdp_display_init(void) omap_hdmi_init(OMAP_HDMI_SDA_SCL_EXTERNAL_PULLUP); else omap_hdmi_init(0); - - omap_mux_init_gpio(HDMI_GPIO_LS_OE, OMAP_PIN_OUTPUT); - omap_mux_init_gpio(HDMI_GPIO_CT_CP_HPD, OMAP_PIN_OUTPUT); - omap_mux_init_gpio(HDMI_GPIO_HPD, OMAP_PIN_INPUT_PULLDOWN); } #ifdef CONFIG_OMAP_MUX diff --git a/trunk/arch/arm/mach-omap2/board-omap4panda.c b/trunk/arch/arm/mach-omap2/board-omap4panda.c index b7779c206a90..30ad40db2cf3 100644 --- a/trunk/arch/arm/mach-omap2/board-omap4panda.c +++ b/trunk/arch/arm/mach-omap2/board-omap4panda.c @@ -51,9 +51,8 @@ #define GPIO_HUB_NRESET 62 #define GPIO_WIFI_PMENA 43 #define GPIO_WIFI_IRQ 53 -#define HDMI_GPIO_CT_CP_HPD 60 /* HPD mode enable/disable */ +#define HDMI_GPIO_HPD 60 /* Hot plug pin for HDMI */ #define HDMI_GPIO_LS_OE 41 /* Level shifter for HDMI */ -#define HDMI_GPIO_HPD 63 /* Hotplug detect */ /* wl127x BT, FM, GPS connectivity chip */ static int wl1271_gpios[] = {46, -1, -1}; @@ -414,9 +413,8 @@ int __init omap4_panda_dvi_init(void) } static struct gpio panda_hdmi_gpios[] = { - { HDMI_GPIO_CT_CP_HPD, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_ct_cp_hpd" }, + { HDMI_GPIO_HPD, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_hpd" }, { HDMI_GPIO_LS_OE, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_ls_oe" }, - { HDMI_GPIO_HPD, GPIOF_DIR_IN, "hdmi_gpio_hpd" }, }; static int omap4_panda_panel_enable_hdmi(struct omap_dss_device *dssdev) @@ -433,13 +431,10 @@ static int omap4_panda_panel_enable_hdmi(struct omap_dss_device *dssdev) static void omap4_panda_panel_disable_hdmi(struct omap_dss_device *dssdev) { - gpio_free_array(panda_hdmi_gpios, ARRAY_SIZE(panda_hdmi_gpios)); + gpio_free(HDMI_GPIO_LS_OE); + gpio_free(HDMI_GPIO_HPD); } -static struct omap_dss_hdmi_data omap4_panda_hdmi_data = { - .hpd_gpio = HDMI_GPIO_HPD, -}; - static struct omap_dss_device omap4_panda_hdmi_device = { .name = "hdmi", .driver_name = "hdmi_panel", @@ -447,7 +442,6 @@ static struct omap_dss_device omap4_panda_hdmi_device = { .platform_enable = omap4_panda_panel_enable_hdmi, .platform_disable = omap4_panda_panel_disable_hdmi, .channel = OMAP_DSS_CHANNEL_DIGIT, - .data = &omap4_panda_hdmi_data, }; static struct omap_dss_device *omap4_panda_dss_devices[] = { @@ -479,10 +473,6 @@ void omap4_panda_display_init(void) omap_hdmi_init(OMAP_HDMI_SDA_SCL_EXTERNAL_PULLUP); else omap_hdmi_init(0); - - omap_mux_init_gpio(HDMI_GPIO_LS_OE, OMAP_PIN_OUTPUT); - omap_mux_init_gpio(HDMI_GPIO_CT_CP_HPD, OMAP_PIN_OUTPUT); - omap_mux_init_gpio(HDMI_GPIO_HPD, OMAP_PIN_INPUT_PULLDOWN); } static void __init omap4_panda_init(void) diff --git a/trunk/arch/arm/mach-omap2/devices.c b/trunk/arch/arm/mach-omap2/devices.c index 283d11eae693..0b510ad01a00 100644 --- a/trunk/arch/arm/mach-omap2/devices.c +++ b/trunk/arch/arm/mach-omap2/devices.c @@ -405,7 +405,6 @@ static int omap_mcspi_init(struct omap_hwmod *oh, void *unused) break; default: pr_err("Invalid McSPI Revision value\n"); - kfree(pdata); return -EINVAL; } diff --git a/trunk/arch/arm/mach-omap2/display.c b/trunk/arch/arm/mach-omap2/display.c index 3677b1f58b85..3c446d1a1781 100644 --- a/trunk/arch/arm/mach-omap2/display.c +++ b/trunk/arch/arm/mach-omap2/display.c @@ -103,8 +103,12 @@ static void omap4_hdmi_mux_pads(enum omap_hdmi_flags flags) u32 reg; u16 control_i2c_1; + /* PAD0_HDMI_HPD_PAD1_HDMI_CEC */ + omap_mux_init_signal("hdmi_hpd", + OMAP_PIN_INPUT_PULLUP); omap_mux_init_signal("hdmi_cec", OMAP_PIN_INPUT_PULLUP); + /* PAD0_HDMI_DDC_SCL_PAD1_HDMI_DDC_SDA */ omap_mux_init_signal("hdmi_ddc_scl", OMAP_PIN_INPUT_PULLUP); omap_mux_init_signal("hdmi_ddc_sda", diff --git a/trunk/arch/arm/mach-omap2/gpmc.c b/trunk/arch/arm/mach-omap2/gpmc.c index dfffbbf4c009..130034bf01d5 100644 --- a/trunk/arch/arm/mach-omap2/gpmc.c +++ b/trunk/arch/arm/mach-omap2/gpmc.c @@ -528,13 +528,7 @@ int gpmc_cs_configure(int cs, int cmd, int wval) case GPMC_CONFIG_DEV_SIZE: regval = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1); - - /* clear 2 target bits */ - regval &= ~GPMC_CONFIG1_DEVICESIZE(3); - - /* set the proper value */ regval |= GPMC_CONFIG1_DEVICESIZE(wval); - gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, regval); break; diff --git a/trunk/arch/arm/mach-omap2/hsmmc.c b/trunk/arch/arm/mach-omap2/hsmmc.c index ad0adb5a1e0e..bd844af13af5 100644 --- a/trunk/arch/arm/mach-omap2/hsmmc.c +++ b/trunk/arch/arm/mach-omap2/hsmmc.c @@ -175,15 +175,14 @@ static void hsmmc2_select_input_clk_src(struct omap_mmc_platform_data *mmc) { u32 reg; - reg = omap_ctrl_readl(control_devconf1_offset); - if (mmc->slots[0].internal_clock) + if (mmc->slots[0].internal_clock) { + reg = omap_ctrl_readl(control_devconf1_offset); reg |= OMAP2_MMCSDIO2ADPCLKISEL; - else - reg &= ~OMAP2_MMCSDIO2ADPCLKISEL; - omap_ctrl_writel(reg, control_devconf1_offset); + omap_ctrl_writel(reg, control_devconf1_offset); + } } -static void hsmmc2_before_set_reg(struct device *dev, int slot, +static void hsmmc23_before_set_reg(struct device *dev, int slot, int power_on, int vdd) { struct omap_mmc_platform_data *mmc = dev->platform_data; @@ -408,13 +407,14 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c, c->caps &= ~MMC_CAP_8_BIT_DATA; c->caps |= MMC_CAP_4_BIT_DATA; } + /* FALLTHROUGH */ + case 3: if (mmc->slots[0].features & HSMMC_HAS_PBIAS) { /* off-chip level shifting, or none */ - mmc->slots[0].before_set_reg = hsmmc2_before_set_reg; + mmc->slots[0].before_set_reg = hsmmc23_before_set_reg; mmc->slots[0].after_set_reg = NULL; } break; - case 3: case 4: case 5: mmc->slots[0].before_set_reg = NULL; diff --git a/trunk/arch/arm/mach-omap2/io.c b/trunk/arch/arm/mach-omap2/io.c index eb50c29fb644..3f174d51f67f 100644 --- a/trunk/arch/arm/mach-omap2/io.c +++ b/trunk/arch/arm/mach-omap2/io.c @@ -388,7 +388,7 @@ static void __init omap_hwmod_init_postsetup(void) omap_pm_if_early_init(); } -#ifdef CONFIG_SOC_OMAP2420 +#ifdef CONFIG_ARCH_OMAP2 void __init omap2420_init_early(void) { omap2_set_globals_242x(); @@ -400,9 +400,7 @@ void __init omap2420_init_early(void) omap_hwmod_init_postsetup(); omap2420_clk_init(); } -#endif -#ifdef CONFIG_SOC_OMAP2430 void __init omap2430_init_early(void) { omap2_set_globals_243x(); diff --git a/trunk/arch/arm/mach-omap2/omap_hwmod_2xxx_3xxx_ipblock_data.c b/trunk/arch/arm/mach-omap2/omap_hwmod_2xxx_3xxx_ipblock_data.c index f08e442af397..c11273da5dcc 100644 --- a/trunk/arch/arm/mach-omap2/omap_hwmod_2xxx_3xxx_ipblock_data.c +++ b/trunk/arch/arm/mach-omap2/omap_hwmod_2xxx_3xxx_ipblock_data.c @@ -55,6 +55,27 @@ struct omap_hwmod_class omap2_dss_hwmod_class = { .reset = omap_dss_reset, }; +/* + * 'dispc' class + * display controller + */ + +static struct omap_hwmod_class_sysconfig omap2_dispc_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_MIDLEMODE | + SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART | + MSTANDBY_FORCE | MSTANDBY_NO | MSTANDBY_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +struct omap_hwmod_class omap2_dispc_hwmod_class = { + .name = "dispc", + .sysc = &omap2_dispc_sysc, +}; + /* * 'rfbi' class * remote frame buffer interface diff --git a/trunk/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c b/trunk/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c index 2a6729741b06..177dee20faef 100644 --- a/trunk/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c +++ b/trunk/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c @@ -28,28 +28,6 @@ struct omap_hwmod_dma_info omap2xxx_dss_sdma_chs[] = { { .name = "dispc", .dma_req = 5 }, { .dma_req = -1 } }; - -/* - * 'dispc' class - * display controller - */ - -static struct omap_hwmod_class_sysconfig omap2_dispc_sysc = { - .rev_offs = 0x0000, - .sysc_offs = 0x0010, - .syss_offs = 0x0014, - .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_MIDLEMODE | - SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE), - .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART | - MSTANDBY_FORCE | MSTANDBY_NO | MSTANDBY_SMART), - .sysc_fields = &omap_hwmod_sysc_type1, -}; - -struct omap_hwmod_class omap2_dispc_hwmod_class = { - .name = "dispc", - .sysc = &omap2_dispc_sysc, -}; - /* OMAP2xxx Timer Common */ static struct omap_hwmod_class_sysconfig omap2xxx_timer_sysc = { .rev_offs = 0x0000, diff --git a/trunk/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/trunk/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c index 3c8dd928628e..5324e8d93bc0 100644 --- a/trunk/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c +++ b/trunk/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c @@ -1480,28 +1480,6 @@ static struct omap_hwmod omap3xxx_dss_core_hwmod = { .masters_cnt = ARRAY_SIZE(omap3xxx_dss_masters), }; -/* - * 'dispc' class - * display controller - */ - -static struct omap_hwmod_class_sysconfig omap3_dispc_sysc = { - .rev_offs = 0x0000, - .sysc_offs = 0x0010, - .syss_offs = 0x0014, - .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_MIDLEMODE | - SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE | - SYSC_HAS_ENAWAKEUP), - .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART | - MSTANDBY_FORCE | MSTANDBY_NO | MSTANDBY_SMART), - .sysc_fields = &omap_hwmod_sysc_type1, -}; - -static struct omap_hwmod_class omap3_dispc_hwmod_class = { - .name = "dispc", - .sysc = &omap3_dispc_sysc, -}; - /* l4_core -> dss_dispc */ static struct omap_hwmod_ocp_if omap3xxx_l4_core__dss_dispc = { .master = &omap3xxx_l4_core_hwmod, @@ -1525,7 +1503,7 @@ static struct omap_hwmod_ocp_if *omap3xxx_dss_dispc_slaves[] = { static struct omap_hwmod omap3xxx_dss_dispc_hwmod = { .name = "dss_dispc", - .class = &omap3_dispc_hwmod_class, + .class = &omap2_dispc_hwmod_class, .mpu_irqs = omap2_dispc_irqs, .main_clk = "dss1_alwon_fck", .prcm = { @@ -3545,6 +3523,12 @@ static __initdata struct omap_hwmod *omap3xxx_hwmods[] = { &omap3xxx_uart2_hwmod, &omap3xxx_uart3_hwmod, + /* dss class */ + &omap3xxx_dss_dispc_hwmod, + &omap3xxx_dss_dsi1_hwmod, + &omap3xxx_dss_rfbi_hwmod, + &omap3xxx_dss_venc_hwmod, + /* i2c class */ &omap3xxx_i2c1_hwmod, &omap3xxx_i2c2_hwmod, @@ -3651,15 +3635,6 @@ static __initdata struct omap_hwmod *am35xx_hwmods[] = { NULL }; -static __initdata struct omap_hwmod *omap3xxx_dss_hwmods[] = { - /* dss class */ - &omap3xxx_dss_dispc_hwmod, - &omap3xxx_dss_dsi1_hwmod, - &omap3xxx_dss_rfbi_hwmod, - &omap3xxx_dss_venc_hwmod, - NULL -}; - int __init omap3xxx_hwmod_init(void) { int r; @@ -3733,21 +3708,6 @@ int __init omap3xxx_hwmod_init(void) if (h) r = omap_hwmod_register(h); - if (r < 0) - return r; - - /* - * DSS code presumes that dss_core hwmod is handled first, - * _before_ any other DSS related hwmods so register common - * DSS hwmods last to ensure that dss_core is already registered. - * Otherwise some change things may happen, for ex. if dispc - * is handled before dss_core and DSS is enabled in bootloader - * DIPSC will be reset with outputs enabled which sometimes leads - * to unrecoverable L3 error. - * XXX The long-term fix to this is to ensure modules are set up - * in dependency order in the hwmod core code. - */ - r = omap_hwmod_register(omap3xxx_dss_hwmods); return r; } diff --git a/trunk/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/trunk/arch/arm/mach-omap2/omap_hwmod_44xx_data.c index ef0524c10a84..f9f151081760 100644 --- a/trunk/arch/arm/mach-omap2/omap_hwmod_44xx_data.c +++ b/trunk/arch/arm/mach-omap2/omap_hwmod_44xx_data.c @@ -1031,7 +1031,6 @@ static struct omap_hwmod_dma_info omap44xx_dmic_sdma_reqs[] = { static struct omap_hwmod_addr_space omap44xx_dmic_addrs[] = { { - .name = "mpu", .pa_start = 0x4012e000, .pa_end = 0x4012e07f, .flags = ADDR_TYPE_RT @@ -1050,7 +1049,6 @@ static struct omap_hwmod_ocp_if omap44xx_l4_abe__dmic = { static struct omap_hwmod_addr_space omap44xx_dmic_dma_addrs[] = { { - .name = "dma", .pa_start = 0x4902e000, .pa_end = 0x4902e07f, .flags = ADDR_TYPE_RT diff --git a/trunk/arch/arm/mach-omap2/prm2xxx_3xxx.c b/trunk/arch/arm/mach-omap2/prm2xxx_3xxx.c index 9ce765407ad5..c1c4d86a79a8 100644 --- a/trunk/arch/arm/mach-omap2/prm2xxx_3xxx.c +++ b/trunk/arch/arm/mach-omap2/prm2xxx_3xxx.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/smartreflex.c b/trunk/arch/arm/mach-omap2/smartreflex.c index 7e755bb0ffc4..9dd93453e563 100644 --- a/trunk/arch/arm/mach-omap2/smartreflex.c +++ b/trunk/arch/arm/mach-omap2/smartreflex.c @@ -897,7 +897,7 @@ static int __init omap_sr_probe(struct platform_device *pdev) ret = sr_late_init(sr_info); if (ret) { pr_warning("%s: Error in SR late init\n", __func__); - goto err_iounmap; + return ret; } } diff --git a/trunk/arch/arm/mach-omap2/timer.c b/trunk/arch/arm/mach-omap2/timer.c index 5c9acea95761..6eeff0e0ae01 100644 --- a/trunk/arch/arm/mach-omap2/timer.c +++ b/trunk/arch/arm/mach-omap2/timer.c @@ -270,7 +270,7 @@ static struct clocksource clocksource_gpt = { static u32 notrace dmtimer_read_sched_clock(void) { if (clksrc.reserved) - return __omap_dm_timer_read_counter(&clksrc, 1); + return __omap_dm_timer_read_counter(clksrc.io_base, 1); return 0; } diff --git a/trunk/arch/arm/mach-pxa/devices.c b/trunk/arch/arm/mach-pxa/devices.c index 5bc13121eac5..18fd177073f4 100644 --- a/trunk/arch/arm/mach-pxa/devices.c +++ b/trunk/arch/arm/mach-pxa/devices.c @@ -415,9 +415,29 @@ static struct resource pxa_rtc_resources[] = { }, }; +static struct resource sa1100_rtc_resources[] = { + [0] = { + .start = 0x40900000, + .end = 0x409000ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_RTC1Hz, + .end = IRQ_RTC1Hz, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = IRQ_RTCAlrm, + .end = IRQ_RTCAlrm, + .flags = IORESOURCE_IRQ, + }, +}; + struct platform_device sa1100_device_rtc = { .name = "sa1100-rtc", .id = -1, + .num_resources = ARRAY_SIZE(sa1100_rtc_resources), + .resource = sa1100_rtc_resources, }; struct platform_device pxa_device_rtc = { diff --git a/trunk/arch/arm/mach-pxa/pxa25x.c b/trunk/arch/arm/mach-pxa/pxa25x.c index 91e4f6c03766..adf058fa97ee 100644 --- a/trunk/arch/arm/mach-pxa/pxa25x.c +++ b/trunk/arch/arm/mach-pxa/pxa25x.c @@ -209,6 +209,8 @@ static struct clk_lookup pxa25x_clkregs[] = { INIT_CLKREG(&clk_pxa25x_gpio11, NULL, "GPIO11_CLK"), INIT_CLKREG(&clk_pxa25x_gpio12, NULL, "GPIO12_CLK"), INIT_CLKREG(&clk_pxa25x_mem, "pxa2xx-pcmcia", NULL), + INIT_CLKREG(&clk_dummy, "pxa-gpio", NULL), + INIT_CLKREG(&clk_dummy, "sa1100-rtc", NULL), }; static struct clk_lookup pxa25x_hwuart_clkreg = diff --git a/trunk/arch/arm/mach-pxa/pxa27x.c b/trunk/arch/arm/mach-pxa/pxa27x.c index aed6cbcf3866..180bd8675d4b 100644 --- a/trunk/arch/arm/mach-pxa/pxa27x.c +++ b/trunk/arch/arm/mach-pxa/pxa27x.c @@ -230,6 +230,8 @@ static struct clk_lookup pxa27x_clkregs[] = { INIT_CLKREG(&clk_pxa27x_im, NULL, "IMCLK"), INIT_CLKREG(&clk_pxa27x_memc, NULL, "MEMCLK"), INIT_CLKREG(&clk_pxa27x_mem, "pxa2xx-pcmcia", NULL), + INIT_CLKREG(&clk_dummy, "pxa-gpio", NULL), + INIT_CLKREG(&clk_dummy, "sa1100-rtc", NULL), }; #ifdef CONFIG_PM diff --git a/trunk/arch/arm/mach-pxa/pxa300.c b/trunk/arch/arm/mach-pxa/pxa300.c index 40bb16501d86..0388eda7878a 100644 --- a/trunk/arch/arm/mach-pxa/pxa300.c +++ b/trunk/arch/arm/mach-pxa/pxa300.c @@ -89,6 +89,7 @@ static DEFINE_PXA3_CKEN(gcu, PXA300_GCU, 0, 0); static struct clk_lookup common_clkregs[] = { INIT_CLKREG(&clk_common_nand, "pxa3xx-nand", NULL), INIT_CLKREG(&clk_gcu, "pxa3xx-gcu", NULL), + INIT_CLKREG(&clk_dummy, "sa1100-rtc", NULL), }; static DEFINE_PXA3_CKEN(pxa310_mmc3, MMC3, 19500000, 0); diff --git a/trunk/arch/arm/mach-pxa/pxa320.c b/trunk/arch/arm/mach-pxa/pxa320.c index 8d614ecd8e99..d487e1ff4c9a 100644 --- a/trunk/arch/arm/mach-pxa/pxa320.c +++ b/trunk/arch/arm/mach-pxa/pxa320.c @@ -83,6 +83,7 @@ static DEFINE_PXA3_CKEN(gcu, PXA320_GCU, 0, 0); static struct clk_lookup pxa320_clkregs[] = { INIT_CLKREG(&clk_pxa320_nand, "pxa3xx-nand", NULL), INIT_CLKREG(&clk_gcu, "pxa3xx-gcu", NULL), + INIT_CLKREG(&clk_dummy, "sa1100-rtc", NULL), }; static int __init pxa320_init(void) diff --git a/trunk/arch/arm/mach-pxa/pxa3xx.c b/trunk/arch/arm/mach-pxa/pxa3xx.c index 4f402afa6609..f107c71c7589 100644 --- a/trunk/arch/arm/mach-pxa/pxa3xx.c +++ b/trunk/arch/arm/mach-pxa/pxa3xx.c @@ -67,6 +67,7 @@ static struct clk_lookup pxa3xx_clkregs[] = { INIT_CLKREG(&clk_pxa3xx_pout, NULL, "CLK_POUT"), /* Power I2C clock is always on */ INIT_CLKREG(&clk_dummy, "pxa3xx-pwri2c.1", NULL), + INIT_CLKREG(&clk_dummy, "sa1100-rtc", NULL), INIT_CLKREG(&clk_pxa3xx_lcd, "pxa2xx-fb", NULL), INIT_CLKREG(&clk_pxa3xx_camera, NULL, "CAMCLK"), INIT_CLKREG(&clk_pxa3xx_ac97, NULL, "AC97CLK"), diff --git a/trunk/arch/arm/mach-pxa/pxa95x.c b/trunk/arch/arm/mach-pxa/pxa95x.c index d082a583df78..fccc644702e6 100644 --- a/trunk/arch/arm/mach-pxa/pxa95x.c +++ b/trunk/arch/arm/mach-pxa/pxa95x.c @@ -217,6 +217,7 @@ static struct clk_lookup pxa95x_clkregs[] = { INIT_CLKREG(&clk_pxa95x_pout, NULL, "CLK_POUT"), /* Power I2C clock is always on */ INIT_CLKREG(&clk_dummy, "pxa3xx-pwri2c.1", NULL), + INIT_CLKREG(&clk_dummy, "sa1100-rtc", NULL), INIT_CLKREG(&clk_pxa95x_lcd, "pxa2xx-fb", NULL), INIT_CLKREG(&clk_pxa95x_ffuart, "pxa2xx-uart.0", NULL), INIT_CLKREG(&clk_pxa95x_btuart, "pxa2xx-uart.1", NULL), diff --git a/trunk/arch/arm/mach-realview/hotplug.c b/trunk/arch/arm/mach-realview/hotplug.c index eb55f05bef3a..ac1aed2a8da4 100644 --- a/trunk/arch/arm/mach-realview/hotplug.c +++ b/trunk/arch/arm/mach-realview/hotplug.c @@ -13,7 +13,6 @@ #include #include -#include extern volatile int pen_release; diff --git a/trunk/arch/arm/mach-realview/include/mach/board-eb.h b/trunk/arch/arm/mach-realview/include/mach/board-eb.h index 124bce6b4d7b..794a8d91a6a6 100644 --- a/trunk/arch/arm/mach-realview/include/mach/board-eb.h +++ b/trunk/arch/arm/mach-realview/include/mach/board-eb.h @@ -47,23 +47,21 @@ #define REALVIEW_EB_USB_BASE 0x4F000000 /* USB */ #ifdef CONFIG_REALVIEW_EB_ARM11MP_REVB -#define REALVIEW_EB11MP_PRIV_MEM_BASE 0x1F000000 +#define REALVIEW_EB11MP_SCU_BASE 0x10100000 /* SCU registers */ +#define REALVIEW_EB11MP_GIC_CPU_BASE 0x10100100 /* Generic interrupt controller CPU interface */ +#define REALVIEW_EB11MP_TWD_BASE 0x10100600 +#define REALVIEW_EB11MP_GIC_DIST_BASE 0x10101000 /* Generic interrupt controller distributor */ #define REALVIEW_EB11MP_L220_BASE 0x10102000 /* L220 registers */ #define REALVIEW_EB11MP_SYS_PLD_CTRL1 0xD8 /* Register offset for MPCore sysctl */ #else -#define REALVIEW_EB11MP_PRIV_MEM_BASE 0x1F000000 +#define REALVIEW_EB11MP_SCU_BASE 0x1F000000 /* SCU registers */ +#define REALVIEW_EB11MP_GIC_CPU_BASE 0x1F000100 /* Generic interrupt controller CPU interface */ +#define REALVIEW_EB11MP_TWD_BASE 0x1F000600 +#define REALVIEW_EB11MP_GIC_DIST_BASE 0x1F001000 /* Generic interrupt controller distributor */ #define REALVIEW_EB11MP_L220_BASE 0x1F002000 /* L220 registers */ #define REALVIEW_EB11MP_SYS_PLD_CTRL1 0x74 /* Register offset for MPCore sysctl */ #endif -#define REALVIEW_EB11MP_PRIV_MEM_SIZE SZ_8K -#define REALVIEW_EB11MP_PRIV_MEM_OFF(x) (REALVIEW_EB11MP_PRIV_MEM_BASE + (x)) - -#define REALVIEW_EB11MP_SCU_BASE REALVIEW_EB11MP_PRIV_MEM_OFF(0) /* SCU registers */ -#define REALVIEW_EB11MP_GIC_CPU_BASE REALVIEW_EB11MP_PRIV_MEM_OFF(0x0100) /* Generic interrupt controller CPU interface */ -#define REALVIEW_EB11MP_TWD_BASE REALVIEW_EB11MP_PRIV_MEM_OFF(0x0600) -#define REALVIEW_EB11MP_GIC_DIST_BASE REALVIEW_EB11MP_PRIV_MEM_OFF(0x1000) /* Generic interrupt controller distributor */ - /* * Core tile identification (REALVIEW_SYS_PROCID) */ diff --git a/trunk/arch/arm/mach-realview/include/mach/board-pb11mp.h b/trunk/arch/arm/mach-realview/include/mach/board-pb11mp.h index aa2d4e02ea2c..7abf918b77e9 100644 --- a/trunk/arch/arm/mach-realview/include/mach/board-pb11mp.h +++ b/trunk/arch/arm/mach-realview/include/mach/board-pb11mp.h @@ -75,8 +75,6 @@ /* * Testchip peripheral and fpga gic regions */ -#define REALVIEW_TC11MP_PRIV_MEM_BASE 0x1F000000 -#define REALVIEW_TC11MP_PRIV_MEM_SIZE SZ_8K #define REALVIEW_TC11MP_SCU_BASE 0x1F000000 /* IRQ, Test chip */ #define REALVIEW_TC11MP_GIC_CPU_BASE 0x1F000100 /* Test chip interrupt controller CPU interface */ #define REALVIEW_TC11MP_TWD_BASE 0x1F000600 diff --git a/trunk/arch/arm/mach-realview/realview_eb.c b/trunk/arch/arm/mach-realview/realview_eb.c index 9578145f2df0..e62962117763 100644 --- a/trunk/arch/arm/mach-realview/realview_eb.c +++ b/trunk/arch/arm/mach-realview/realview_eb.c @@ -91,9 +91,14 @@ static struct map_desc realview_eb_io_desc[] __initdata = { static struct map_desc realview_eb11mp_io_desc[] __initdata = { { - .virtual = IO_ADDRESS(REALVIEW_EB11MP_PRIV_MEM_BASE), - .pfn = __phys_to_pfn(REALVIEW_EB11MP_PRIV_MEM_BASE), - .length = REALVIEW_EB11MP_PRIV_MEM_SIZE, + .virtual = IO_ADDRESS(REALVIEW_EB11MP_SCU_BASE), + .pfn = __phys_to_pfn(REALVIEW_EB11MP_SCU_BASE), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = IO_ADDRESS(REALVIEW_EB11MP_GIC_DIST_BASE), + .pfn = __phys_to_pfn(REALVIEW_EB11MP_GIC_DIST_BASE), + .length = SZ_4K, .type = MT_DEVICE, }, { .virtual = IO_ADDRESS(REALVIEW_EB11MP_L220_BASE), diff --git a/trunk/arch/arm/mach-realview/realview_pb11mp.c b/trunk/arch/arm/mach-realview/realview_pb11mp.c index 2147335f66f5..127a3fd42ab1 100644 --- a/trunk/arch/arm/mach-realview/realview_pb11mp.c +++ b/trunk/arch/arm/mach-realview/realview_pb11mp.c @@ -64,10 +64,15 @@ static struct map_desc realview_pb11mp_io_desc[] __initdata = { .pfn = __phys_to_pfn(REALVIEW_PB11MP_GIC_DIST_BASE), .length = SZ_4K, .type = MT_DEVICE, - }, { /* Maps the SCU, GIC CPU interface, TWD, GIC DIST */ - .virtual = IO_ADDRESS(REALVIEW_TC11MP_PRIV_MEM_BASE), - .pfn = __phys_to_pfn(REALVIEW_TC11MP_PRIV_MEM_BASE), - .length = REALVIEW_TC11MP_PRIV_MEM_SIZE, + }, { + .virtual = IO_ADDRESS(REALVIEW_TC11MP_GIC_CPU_BASE), + .pfn = __phys_to_pfn(REALVIEW_TC11MP_GIC_CPU_BASE), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = IO_ADDRESS(REALVIEW_TC11MP_GIC_DIST_BASE), + .pfn = __phys_to_pfn(REALVIEW_TC11MP_GIC_DIST_BASE), + .length = SZ_4K, .type = MT_DEVICE, }, { .virtual = IO_ADDRESS(REALVIEW_SCTL_BASE), diff --git a/trunk/arch/arm/mach-sa1100/assabet.c b/trunk/arch/arm/mach-sa1100/assabet.c index 0c4b76ab4d8e..ebafe8aa8956 100644 --- a/trunk/arch/arm/mach-sa1100/assabet.c +++ b/trunk/arch/arm/mach-sa1100/assabet.c @@ -202,6 +202,7 @@ static struct irda_platform_data assabet_irda_data = { static struct mcp_plat_data assabet_mcp_data = { .mccr0 = MCCR0_ADM, .sclk_rate = 11981000, + .codec = "ucb1x00", }; static void __init assabet_init(void) @@ -252,6 +253,17 @@ static void __init assabet_init(void) sa11x0_register_mtd(&assabet_flash_data, assabet_flash_resources, ARRAY_SIZE(assabet_flash_resources)); sa11x0_register_irda(&assabet_irda_data); + + /* + * Setup the PPC unit correctly. + */ + PPDR &= ~PPC_RXD4; + PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; + PSDR |= PPC_RXD4; + PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + + ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); sa11x0_register_mcp(&assabet_mcp_data); } diff --git a/trunk/arch/arm/mach-sa1100/cerf.c b/trunk/arch/arm/mach-sa1100/cerf.c index 11bb6d0b9be3..d12d0f48b1dc 100644 --- a/trunk/arch/arm/mach-sa1100/cerf.c +++ b/trunk/arch/arm/mach-sa1100/cerf.c @@ -124,12 +124,23 @@ static void __init cerf_map_io(void) static struct mcp_plat_data cerf_mcp_data = { .mccr0 = MCCR0_ADM, .sclk_rate = 11981000, + .codec = "ucb1x00", }; static void __init cerf_init(void) { platform_add_devices(cerf_devices, ARRAY_SIZE(cerf_devices)); sa11x0_register_mtd(&cerf_flash_data, &cerf_flash_resource, 1); + + /* + * Setup the PPC unit correctly. + */ + PPDR &= ~PPC_RXD4; + PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; + PSDR |= PPC_RXD4; + PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + sa11x0_register_mcp(&cerf_mcp_data); } diff --git a/trunk/arch/arm/mach-sa1100/clock.c b/trunk/arch/arm/mach-sa1100/clock.c index dab3c6347a8f..d6df9f6c9f7e 100644 --- a/trunk/arch/arm/mach-sa1100/clock.c +++ b/trunk/arch/arm/mach-sa1100/clock.c @@ -11,17 +11,39 @@ #include #include #include +#include +#include #include -/* - * Very simple clock implementation - we only have one clock to deal with. - */ +struct clkops { + void (*enable)(struct clk *); + void (*disable)(struct clk *); + unsigned long (*getrate)(struct clk *); +}; + struct clk { + const struct clkops *ops; + unsigned long rate; unsigned int enabled; }; -static void clk_gpio27_enable(void) +#define INIT_CLKREG(_clk, _devname, _conname) \ + { \ + .clk = _clk, \ + .dev_id = _devname, \ + .con_id = _conname, \ + } + +#define DEFINE_CLK(_name, _ops, _rate) \ +struct clk clk_##_name = { \ + .ops = _ops, \ + .rate = _rate, \ + } + +static DEFINE_SPINLOCK(clocks_lock); + +static void clk_gpio27_enable(struct clk *clk) { /* * First, set up the 3.6864MHz clock on GPIO 27 for the SA-1111: @@ -32,38 +54,22 @@ static void clk_gpio27_enable(void) TUCR = TUCR_3_6864MHz; } -static void clk_gpio27_disable(void) +static void clk_gpio27_disable(struct clk *clk) { TUCR = 0; GPDR &= ~GPIO_32_768kHz; GAFR &= ~GPIO_32_768kHz; } -static struct clk clk_gpio27; - -static DEFINE_SPINLOCK(clocks_lock); - -struct clk *clk_get(struct device *dev, const char *id) -{ - const char *devname = dev_name(dev); - - return strcmp(devname, "sa1111.0") ? ERR_PTR(-ENOENT) : &clk_gpio27; -} -EXPORT_SYMBOL(clk_get); - -void clk_put(struct clk *clk) -{ -} -EXPORT_SYMBOL(clk_put); - int clk_enable(struct clk *clk) { unsigned long flags; spin_lock_irqsave(&clocks_lock, flags); if (clk->enabled++ == 0) - clk_gpio27_enable(); + clk->ops->enable(clk); spin_unlock_irqrestore(&clocks_lock, flags); + return 0; } EXPORT_SYMBOL(clk_enable); @@ -76,13 +82,48 @@ void clk_disable(struct clk *clk) spin_lock_irqsave(&clocks_lock, flags); if (--clk->enabled == 0) - clk_gpio27_disable(); + clk->ops->disable(clk); spin_unlock_irqrestore(&clocks_lock, flags); } EXPORT_SYMBOL(clk_disable); unsigned long clk_get_rate(struct clk *clk) { - return 3686400; + unsigned long rate; + + rate = clk->rate; + if (clk->ops->getrate) + rate = clk->ops->getrate(clk); + + return rate; } EXPORT_SYMBOL(clk_get_rate); + +const struct clkops clk_gpio27_ops = { + .enable = clk_gpio27_enable, + .disable = clk_gpio27_disable, +}; + +static void clk_dummy_enable(struct clk *clk) { } +static void clk_dummy_disable(struct clk *clk) { } + +const struct clkops clk_dummy_ops = { + .enable = clk_dummy_enable, + .disable = clk_dummy_disable, +}; + +static DEFINE_CLK(gpio27, &clk_gpio27_ops, 3686400); +static DEFINE_CLK(dummy, &clk_dummy_ops, 0); + +static struct clk_lookup sa11xx_clkregs[] = { + INIT_CLKREG(&clk_gpio27, "sa1111.0", NULL), + INIT_CLKREG(&clk_dummy, "sa1100-rtc", NULL), +}; + +static int __init sa11xx_clk_init(void) +{ + clkdev_add_table(sa11xx_clkregs, ARRAY_SIZE(sa11xx_clkregs)); + return 0; +} + +postcore_initcall(sa11xx_clk_init); diff --git a/trunk/arch/arm/mach-sa1100/collie.c b/trunk/arch/arm/mach-sa1100/collie.c index fd5652118ed1..c483912d08af 100644 --- a/trunk/arch/arm/mach-sa1100/collie.c +++ b/trunk/arch/arm/mach-sa1100/collie.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -85,10 +86,15 @@ static struct scoop_pcmcia_config collie_pcmcia_config = { .num_devs = 1, }; +static struct ucb1x00_plat_data collie_ucb1x00_data = { + .gpio_base = COLLIE_TC35143_GPIO_BASE, +}; + static struct mcp_plat_data collie_mcp_data = { .mccr0 = MCCR0_ADM | MCCR0_ExtClk, .sclk_rate = 9216000, - .gpio_base = COLLIE_TC35143_GPIO_BASE, + .codec = "ucb1x00", + .codec_pdata = &collie_ucb1x00_data, }; /* @@ -138,6 +144,8 @@ static struct pda_power_pdata collie_power_data = { static struct resource collie_power_resource[] = { { .name = "ac", + .start = gpio_to_irq(COLLIE_GPIO_AC_IN), + .end = gpio_to_irq(COLLIE_GPIO_AC_IN), .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE, @@ -339,8 +347,7 @@ static void __init collie_init(void) GPSR |= _COLLIE_GPIO_UCB1x00_RESET; - collie_power_resource[0].start = gpio_to_irq(COLLIE_GPIO_AC_IN); - collie_power_resource[0].end = gpio_to_irq(COLLIE_GPIO_AC_IN); + platform_scoop_config = &collie_pcmcia_config; ret = platform_add_devices(devices, ARRAY_SIZE(devices)); @@ -350,6 +357,16 @@ static void __init collie_init(void) sa11x0_register_mtd(&collie_flash_data, collie_flash_resources, ARRAY_SIZE(collie_flash_resources)); + + /* + * Setup the PPC unit correctly. + */ + PPDR &= ~PPC_RXD4; + PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; + PSDR |= PPC_RXD4; + PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + sa11x0_register_mcp(&collie_mcp_data); sharpsl_save_param(); diff --git a/trunk/arch/arm/mach-sa1100/cpu-sa1100.c b/trunk/arch/arm/mach-sa1100/cpu-sa1100.c index 19b2053f5af4..aaa8acf76b7b 100644 --- a/trunk/arch/arm/mach-sa1100/cpu-sa1100.c +++ b/trunk/arch/arm/mach-sa1100/cpu-sa1100.c @@ -228,7 +228,7 @@ static int __init sa1100_cpu_init(struct cpufreq_policy *policy) return 0; } -static struct cpufreq_driver sa1100_driver __refdata = { +static struct cpufreq_driver sa1100_driver = { .flags = CPUFREQ_STICKY, .verify = sa11x0_verify_speed, .target = sa1100_target, diff --git a/trunk/arch/arm/mach-sa1100/generic.c b/trunk/arch/arm/mach-sa1100/generic.c index bb10ee2cb89f..e3a28ca2a7b7 100644 --- a/trunk/arch/arm/mach-sa1100/generic.c +++ b/trunk/arch/arm/mach-sa1100/generic.c @@ -217,10 +217,15 @@ static struct platform_device sa11x0uart3_device = { static struct resource sa11x0mcp_resources[] = { [0] = { .start = __PREG(Ser4MCCR0), - .end = __PREG(Ser4MCCR0) + 0xffff, + .end = __PREG(Ser4MCCR0) + 0x1C - 1, .flags = IORESOURCE_MEM, }, [1] = { + .start = __PREG(Ser4MCCR1), + .end = __PREG(Ser4MCCR1) + 0x4 - 1, + .flags = IORESOURCE_MEM, + }, + [2] = { .start = IRQ_Ser4MCP, .end = IRQ_Ser4MCP, .flags = IORESOURCE_IRQ, @@ -345,9 +350,29 @@ void sa11x0_register_irda(struct irda_platform_data *irda) sa11x0_register_device(&sa11x0ir_device, irda); } +static struct resource sa11x0rtc_resources[] = { + [0] = { + .start = 0x90010000, + .end = 0x900100ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_RTC1Hz, + .end = IRQ_RTC1Hz, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = IRQ_RTCAlrm, + .end = IRQ_RTCAlrm, + .flags = IORESOURCE_IRQ, + }, +}; + static struct platform_device sa11x0rtc_device = { .name = "sa1100-rtc", .id = -1, + .resource = sa11x0rtc_resources, + .num_resources = ARRAY_SIZE(sa11x0rtc_resources), }; static struct platform_device *sa11x0_devices[] __initdata = { diff --git a/trunk/arch/arm/mach-sa1100/include/mach/mcp.h b/trunk/arch/arm/mach-sa1100/include/mach/mcp.h index ed1a331508a7..586cec898b35 100644 --- a/trunk/arch/arm/mach-sa1100/include/mach/mcp.h +++ b/trunk/arch/arm/mach-sa1100/include/mach/mcp.h @@ -17,6 +17,8 @@ struct mcp_plat_data { u32 mccr1; unsigned int sclk_rate; int gpio_base; + const char *codec; + void *codec_pdata; }; #endif diff --git a/trunk/arch/arm/mach-sa1100/jornada720_ssp.c b/trunk/arch/arm/mach-sa1100/jornada720_ssp.c index b412fc09c80c..f50b00bd18a0 100644 --- a/trunk/arch/arm/mach-sa1100/jornada720_ssp.c +++ b/trunk/arch/arm/mach-sa1100/jornada720_ssp.c @@ -198,5 +198,3 @@ static int __init jornada_ssp_init(void) { return platform_driver_register(&jornadassp_driver); } - -module_init(jornada_ssp_init); diff --git a/trunk/arch/arm/mach-sa1100/lart.c b/trunk/arch/arm/mach-sa1100/lart.c index af4e2761f3db..d117ceab6215 100644 --- a/trunk/arch/arm/mach-sa1100/lart.c +++ b/trunk/arch/arm/mach-sa1100/lart.c @@ -24,10 +24,20 @@ static struct mcp_plat_data lart_mcp_data = { .mccr0 = MCCR0_ADM, .sclk_rate = 11981000, + .codec = "ucb1x00", }; static void __init lart_init(void) { + /* + * Setup the PPC unit correctly. + */ + PPDR &= ~PPC_RXD4; + PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; + PSDR |= PPC_RXD4; + PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + sa11x0_register_mcp(&lart_mcp_data); } diff --git a/trunk/arch/arm/mach-sa1100/shannon.c b/trunk/arch/arm/mach-sa1100/shannon.c index 318b2b766a0b..748d34435b3f 100644 --- a/trunk/arch/arm/mach-sa1100/shannon.c +++ b/trunk/arch/arm/mach-sa1100/shannon.c @@ -55,11 +55,22 @@ static struct resource shannon_flash_resource = { static struct mcp_plat_data shannon_mcp_data = { .mccr0 = MCCR0_ADM, .sclk_rate = 11981000, + .codec = "ucb1x00", }; static void __init shannon_init(void) { sa11x0_register_mtd(&shannon_flash_data, &shannon_flash_resource, 1); + + /* + * Setup the PPC unit correctly. + */ + PPDR &= ~PPC_RXD4; + PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; + PSDR |= PPC_RXD4; + PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + sa11x0_register_mcp(&shannon_mcp_data); } diff --git a/trunk/arch/arm/mach-sa1100/simpad.c b/trunk/arch/arm/mach-sa1100/simpad.c index e17c04d6e324..458ececefa58 100644 --- a/trunk/arch/arm/mach-sa1100/simpad.c +++ b/trunk/arch/arm/mach-sa1100/simpad.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -187,10 +188,15 @@ static struct resource simpad_flash_resources [] = { } }; +static struct ucb1x00_plat_data simpad_ucb1x00_data = { + .gpio_base = SIMPAD_UCB1X00_GPIO_BASE, +}; + static struct mcp_plat_data simpad_mcp_data = { .mccr0 = MCCR0_ADM, .sclk_rate = 11981000, - .gpio_base = SIMPAD_UCB1X00_GPIO_BASE, + .codec = "ucb1300", + .codec_pdata = &simpad_ucb1x00_data, }; @@ -378,6 +384,16 @@ static int __init simpad_init(void) sa11x0_register_mtd(&simpad_flash_data, simpad_flash_resources, ARRAY_SIZE(simpad_flash_resources)); + + /* + * Setup the PPC unit correctly. + */ + PPDR &= ~PPC_RXD4; + PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; + PSDR |= PPC_RXD4; + PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + sa11x0_register_mcp(&simpad_mcp_data); ret = platform_add_devices(devices, ARRAY_SIZE(devices)); diff --git a/trunk/arch/arm/mach-shmobile/setup-sh7372.c b/trunk/arch/arm/mach-shmobile/setup-sh7372.c index a83cf51fc099..6fcf304d3cdf 100644 --- a/trunk/arch/arm/mach-shmobile/setup-sh7372.c +++ b/trunk/arch/arm/mach-shmobile/setup-sh7372.c @@ -662,7 +662,6 @@ static struct sh_dmae_pdata usb_dma0_platform_data = { .dmaor_is_32bit = 1, .needs_tend_set = 1, .no_dmars = 1, - .slave_only = 1, }; static struct resource sh7372_usb_dmae0_resources[] = { @@ -724,7 +723,6 @@ static struct sh_dmae_pdata usb_dma1_platform_data = { .dmaor_is_32bit = 1, .needs_tend_set = 1, .no_dmars = 1, - .slave_only = 1, }; static struct resource sh7372_usb_dmae1_resources[] = { diff --git a/trunk/arch/arm/mach-shmobile/smp-r8a7779.c b/trunk/arch/arm/mach-shmobile/smp-r8a7779.c index 4fe2e9eaf501..cc97ef892d1b 100644 --- a/trunk/arch/arm/mach-shmobile/smp-r8a7779.c +++ b/trunk/arch/arm/mach-shmobile/smp-r8a7779.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/trunk/arch/arm/mach-shmobile/smp-sh73a0.c b/trunk/arch/arm/mach-shmobile/smp-sh73a0.c index 0d159d64a345..be1ade76ccc8 100644 --- a/trunk/arch/arm/mach-shmobile/smp-sh73a0.c +++ b/trunk/arch/arm/mach-shmobile/smp-sh73a0.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include diff --git a/trunk/arch/arm/mach-ux500/Kconfig b/trunk/arch/arm/mach-ux500/Kconfig index 52af00446a63..a3e0c8692f0d 100644 --- a/trunk/arch/arm/mach-ux500/Kconfig +++ b/trunk/arch/arm/mach-ux500/Kconfig @@ -7,7 +7,6 @@ config UX500_SOC_COMMON select HAS_MTU select ARM_ERRATA_753970 select ARM_ERRATA_754322 - select ARM_ERRATA_764369 menu "Ux500 SoC" diff --git a/trunk/arch/arm/mach-ux500/board-mop500-sdi.c b/trunk/arch/arm/mach-ux500/board-mop500-sdi.c index 5dde4d4ebe88..23be34b3bb6e 100644 --- a/trunk/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/trunk/arch/arm/mach-ux500/board-mop500-sdi.c @@ -261,8 +261,6 @@ void __init mop500_sdi_init(void) void __init snowball_sdi_init(void) { - /* On Snowball MMC_CAP_SD_HIGHSPEED isn't supported (Hardware issue?) */ - mop500_sdi0_data.capabilities &= ~MMC_CAP_SD_HIGHSPEED; /* On-board eMMC */ db8500_add_sdi4(&mop500_sdi4_data, U8500_SDI_V2_PERIPHID); /* External Micro SD slot */ diff --git a/trunk/arch/arm/mach-ux500/cache-l2x0.c b/trunk/arch/arm/mach-ux500/cache-l2x0.c index da5569d83d58..122ddde00ba7 100644 --- a/trunk/arch/arm/mach-ux500/cache-l2x0.c +++ b/trunk/arch/arm/mach-ux500/cache-l2x0.c @@ -12,6 +12,44 @@ static void __iomem *l2x0_base; +static inline void ux500_cache_wait(void __iomem *reg, unsigned long mask) +{ + /* wait for the operation to complete */ + while (readl_relaxed(reg) & mask) + cpu_relax(); +} + +static inline void ux500_cache_sync(void) +{ + writel_relaxed(0, l2x0_base + L2X0_CACHE_SYNC); + ux500_cache_wait(l2x0_base + L2X0_CACHE_SYNC, 1); +} + +/* + * The L2 cache cannot be turned off in the non-secure world. + * Dummy until a secure service is in place. + */ +static void ux500_l2x0_disable(void) +{ +} + +/* + * This is only called when doing a kexec, just after turning off the L2 + * and L1 cache, and it is surrounded by a spinlock in the generic version. + * However, we're not really turning off the L2 cache right now and the + * PL310 does not support exclusive accesses (used to implement the spinlock). + * So, the invalidation needs to be done without the spinlock. + */ +static void ux500_l2x0_inv_all(void) +{ + uint32_t l2x0_way_mask = (1<<16) - 1; /* Bitmask of active ways */ + + /* invalidate all ways */ + writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_INV_WAY); + ux500_cache_wait(l2x0_base + L2X0_INV_WAY, l2x0_way_mask); + ux500_cache_sync(); +} + static int __init ux500_l2x0_unlock(void) { int i; @@ -47,13 +85,9 @@ static int __init ux500_l2x0_init(void) /* 64KB way size, 8 way associativity, force WA */ l2x0_init(l2x0_base, 0x3e060000, 0xc0000fff); - /* - * We can't disable l2 as we are in non secure mode, currently - * this seems be called only during kexec path. So let's - * override outer.disable with nasty assignment until we have - * some SMI service available. - */ - outer_cache.disable = NULL; + /* Override invalidate function */ + outer_cache.disable = ux500_l2x0_disable; + outer_cache.inv_all = ux500_l2x0_inv_all; return 0; } diff --git a/trunk/arch/arm/mach-ux500/hotplug.c b/trunk/arch/arm/mach-ux500/hotplug.c index c76f0f456f04..572015e57cd9 100644 --- a/trunk/arch/arm/mach-ux500/hotplug.c +++ b/trunk/arch/arm/mach-ux500/hotplug.c @@ -13,7 +13,6 @@ #include #include -#include extern volatile int pen_release; diff --git a/trunk/arch/arm/mach-ux500/platsmp.c b/trunk/arch/arm/mach-ux500/platsmp.c index d2058ef8345f..a19e398dade3 100644 --- a/trunk/arch/arm/mach-ux500/platsmp.c +++ b/trunk/arch/arm/mach-ux500/platsmp.c @@ -19,7 +19,6 @@ #include #include -#include #include #include #include diff --git a/trunk/arch/arm/mach-ux500/usb.c b/trunk/arch/arm/mach-ux500/usb.c index 9f9e1c203061..0a01cbdfe063 100644 --- a/trunk/arch/arm/mach-ux500/usb.c +++ b/trunk/arch/arm/mach-ux500/usb.c @@ -95,7 +95,13 @@ static struct musb_hdrc_config musb_hdrc_config = { }; static struct musb_hdrc_platform_data musb_platform_data = { +#if defined(CONFIG_USB_MUSB_OTG) .mode = MUSB_OTG, +#elif defined(CONFIG_USB_MUSB_PERIPHERAL) + .mode = MUSB_PERIPHERAL, +#else /* defined(CONFIG_USB_MUSB_HOST) */ + .mode = MUSB_HOST, +#endif .config = &musb_hdrc_config, .board_data = &musb_board_data, }; diff --git a/trunk/arch/arm/mach-vexpress/ct-ca9x4.c b/trunk/arch/arm/mach-vexpress/ct-ca9x4.c index b1e87c184e54..2b1e836a76ed 100644 --- a/trunk/arch/arm/mach-vexpress/ct-ca9x4.c +++ b/trunk/arch/arm/mach-vexpress/ct-ca9x4.c @@ -217,7 +217,7 @@ static void __init ct_ca9x4_init(void) } #ifdef CONFIG_SMP -static void __init ct_ca9x4_init_cpu_map(void) +static void ct_ca9x4_init_cpu_map(void) { int i, ncores = scu_get_core_count(MMIO_P2V(A9_MPCORE_SCU)); @@ -233,7 +233,7 @@ static void __init ct_ca9x4_init_cpu_map(void) set_smp_cross_call(gic_raise_softirq); } -static void __init ct_ca9x4_smp_enable(unsigned int max_cpus) +static void ct_ca9x4_smp_enable(unsigned int max_cpus) { scu_enable(MMIO_P2V(A9_MPCORE_SCU)); } diff --git a/trunk/arch/arm/mach-vexpress/hotplug.c b/trunk/arch/arm/mach-vexpress/hotplug.c index 3034a4dab4a1..813ee08f96e6 100644 --- a/trunk/arch/arm/mach-vexpress/hotplug.c +++ b/trunk/arch/arm/mach-vexpress/hotplug.c @@ -13,7 +13,6 @@ #include #include -#include #include extern volatile int pen_release; diff --git a/trunk/arch/arm/mm/Kconfig b/trunk/arch/arm/mm/Kconfig index 1a3ca2488164..4cefb57d9ed2 100644 --- a/trunk/arch/arm/mm/Kconfig +++ b/trunk/arch/arm/mm/Kconfig @@ -882,7 +882,6 @@ config CACHE_XSC3L2 config ARM_L1_CACHE_SHIFT_6 bool - default y if CPU_V7 help Setting ARM L1 cache line size to 64 Bytes. diff --git a/trunk/arch/arm/mm/init.c b/trunk/arch/arm/mm/init.c index 5dc7d127a40f..6ec1226fc62d 100644 --- a/trunk/arch/arm/mm/init.c +++ b/trunk/arch/arm/mm/init.c @@ -310,7 +310,7 @@ static void arm_memory_present(void) static bool arm_memblock_steal_permitted = true; -phys_addr_t __init arm_memblock_steal(phys_addr_t size, phys_addr_t align) +phys_addr_t arm_memblock_steal(phys_addr_t size, phys_addr_t align) { phys_addr_t phys; diff --git a/trunk/arch/arm/mm/proc-v7.S b/trunk/arch/arm/mm/proc-v7.S index 0404ccbb8aa3..7e9b5bf910c1 100644 --- a/trunk/arch/arm/mm/proc-v7.S +++ b/trunk/arch/arm/mm/proc-v7.S @@ -148,6 +148,10 @@ ENDPROC(cpu_v7_do_resume) * Initialise TLB, Caches, and MMU state ready to switch the MMU * on. Return in r0 the new CP15 C1 control register setting. * + * We automatically detect if we have a Harvard cache, and use the + * Harvard cache control instructions insead of the unified cache + * control instructions. + * * This should be able to cover all ARMv7 cores. * * It is assumed that: @@ -247,7 +251,9 @@ __v7_setup: #endif 3: mov r10, #0 +#ifdef HARVARD_CACHE mcr p15, 0, r10, c7, c5, 0 @ I+BTB cache invalidate +#endif dsb #ifdef CONFIG_MMU mcr p15, 0, r10, c8, c7, 0 @ invalidate I + D TLBs @@ -323,6 +329,16 @@ __v7_ca5mp_proc_info: __v7_proc __v7_ca5mp_setup .size __v7_ca5mp_proc_info, . - __v7_ca5mp_proc_info + /* + * ARM Ltd. Cortex A7 processor. + */ + .type __v7_ca7mp_proc_info, #object +__v7_ca7mp_proc_info: + .long 0x410fc070 + .long 0xff0ffff0 + __v7_proc __v7_ca7mp_setup, hwcaps = HWCAP_IDIV + .size __v7_ca7mp_proc_info, . - __v7_ca7mp_proc_info + /* * ARM Ltd. Cortex A9 processor. */ @@ -334,16 +350,6 @@ __v7_ca9mp_proc_info: .size __v7_ca9mp_proc_info, . - __v7_ca9mp_proc_info #endif /* CONFIG_ARM_LPAE */ - /* - * ARM Ltd. Cortex A7 processor. - */ - .type __v7_ca7mp_proc_info, #object -__v7_ca7mp_proc_info: - .long 0x410fc070 - .long 0xff0ffff0 - __v7_proc __v7_ca7mp_setup, hwcaps = HWCAP_IDIV - .size __v7_ca7mp_proc_info, . - __v7_ca7mp_proc_info - /* * ARM Ltd. Cortex A15 processor. */ diff --git a/trunk/arch/arm/plat-mxc/include/mach/iomux-v1.h b/trunk/arch/arm/plat-mxc/include/mach/iomux-v1.h index f7d18046c04f..6fa8a707b9a0 100644 --- a/trunk/arch/arm/plat-mxc/include/mach/iomux-v1.h +++ b/trunk/arch/arm/plat-mxc/include/mach/iomux-v1.h @@ -96,6 +96,6 @@ extern int mxc_gpio_mode(int gpio_mode); extern int mxc_gpio_setup_multiple_pins(const int *pin_list, unsigned count, const char *label); -extern int imx_iomuxv1_init(void __iomem *base, int numports); +extern int __init imx_iomuxv1_init(void __iomem *base, int numports); #endif /* __MACH_IOMUX_V1_H__ */ diff --git a/trunk/arch/arm/plat-versatile/platsmp.c b/trunk/arch/arm/plat-versatile/platsmp.c index 49c7db48c7f1..92f18d372b69 100644 --- a/trunk/arch/arm/plat-versatile/platsmp.c +++ b/trunk/arch/arm/plat-versatile/platsmp.c @@ -16,7 +16,6 @@ #include #include -#include #include /* diff --git a/trunk/arch/avr32/Kconfig b/trunk/arch/avr32/Kconfig index 3dea7231f637..197e96f70405 100644 --- a/trunk/arch/avr32/Kconfig +++ b/trunk/arch/avr32/Kconfig @@ -8,7 +8,6 @@ config AVR32 select HAVE_KPROBES select HAVE_GENERIC_HARDIRQS select GENERIC_IRQ_PROBE - select GENERIC_ATOMIC64 select HARDIRQS_SW_RESEND select GENERIC_IRQ_SHOW select ARCH_HAVE_NMI_SAFE_CMPXCHG diff --git a/trunk/arch/m68k/atari/config.c b/trunk/arch/m68k/atari/config.c index c4ac15c4f065..4203d101363c 100644 --- a/trunk/arch/m68k/atari/config.c +++ b/trunk/arch/m68k/atari/config.c @@ -414,9 +414,9 @@ void __init config_atari(void) * FDC val = 4 -> Supervisor only */ asm volatile ("\n" " .chip 68030\n" - " pmove %0,%/tt1\n" + " pmove %0@,%/tt1\n" " .chip 68k" - : : "m" (tt1_val)); + : : "a" (&tt1_val)); } else { asm volatile ("\n" " .chip 68040\n" @@ -569,10 +569,10 @@ static void atari_reset(void) : "d0"); } else asm volatile ("\n" - " pmove %0,%%tc\n" + " pmove %0@,%%tc\n" " jmp %1@" : /* no outputs */ - : "m" (tc_val), "a" (reset_addr)); + : "a" (&tc_val), "a" (reset_addr)); } diff --git a/trunk/arch/m68k/include/asm/irq.h b/trunk/arch/m68k/include/asm/irq.h index c1155f0e22cc..0e89fa05de0e 100644 --- a/trunk/arch/m68k/include/asm/irq.h +++ b/trunk/arch/m68k/include/asm/irq.h @@ -50,6 +50,19 @@ #define IRQ_USER 8 +/* + * various flags for request_irq() - the Amiga now uses the standard + * mechanism like all other architectures - IRQF_DISABLED and + * IRQF_SHARED are your friends. + */ +#ifndef MACH_AMIGA_ONLY +#define IRQ_FLG_LOCK (0x0001) /* handler is not replaceable */ +#define IRQ_FLG_REPLACE (0x0002) /* replace existing handler */ +#define IRQ_FLG_FAST (0x0004) +#define IRQ_FLG_SLOW (0x0008) +#define IRQ_FLG_STD (0x8000) /* internally used */ +#endif + struct irq_data; struct irq_chip; struct irq_desc; diff --git a/trunk/arch/m68k/kernel/process_mm.c b/trunk/arch/m68k/kernel/process_mm.c index 099283ee1a8f..125f34e00bf0 100644 --- a/trunk/arch/m68k/kernel/process_mm.c +++ b/trunk/arch/m68k/kernel/process_mm.c @@ -172,7 +172,7 @@ void flush_thread(void) current->thread.fs = __USER_DS; if (!FPU_IS_EMU) - asm volatile("frestore %0": :"m" (zero)); + asm volatile ("frestore %0@" : : "a" (&zero) : "memory"); } /* diff --git a/trunk/arch/m68k/kernel/process_no.c b/trunk/arch/m68k/kernel/process_no.c index 5e1078cabe0e..69c1803fcf1b 100644 --- a/trunk/arch/m68k/kernel/process_no.c +++ b/trunk/arch/m68k/kernel/process_no.c @@ -163,8 +163,8 @@ void flush_thread(void) #ifdef CONFIG_FPU if (!FPU_IS_EMU) asm volatile (".chip 68k/68881\n\t" - "frestore %0\n\t" - ".chip 68k" : : "m" (zero)); + "frestore %0@\n\t" + ".chip 68k" : : "a" (&zero)); #endif } diff --git a/trunk/arch/m68k/kernel/traps.c b/trunk/arch/m68k/kernel/traps.c index daaa9187654c..a76452ca964e 100644 --- a/trunk/arch/m68k/kernel/traps.c +++ b/trunk/arch/m68k/kernel/traps.c @@ -552,13 +552,13 @@ static inline void bus_error030 (struct frame *fp) #ifdef DEBUG asm volatile ("ptestr %3,%2@,#7,%0\n\t" - "pmove %%psr,%1" - : "=a&" (desc), "=m" (temp) - : "a" (addr), "d" (ssw)); + "pmove %%psr,%1@" + : "=a&" (desc) + : "a" (&temp), "a" (addr), "d" (ssw)); #else asm volatile ("ptestr %2,%1@,#7\n\t" - "pmove %%psr,%0" - : "=m" (temp) : "a" (addr), "d" (ssw)); + "pmove %%psr,%0@" + : : "a" (&temp), "a" (addr), "d" (ssw)); #endif mmusr = temp; @@ -605,18 +605,20 @@ static inline void bus_error030 (struct frame *fp) !(ssw & RW) ? "write" : "read", addr, fp->ptregs.pc, ssw); asm volatile ("ptestr #1,%1@,#0\n\t" - "pmove %%psr,%0" - : "=m" (temp) - : "a" (addr)); + "pmove %%psr,%0@" + : /* no outputs */ + : "a" (&temp), "a" (addr)); mmusr = temp; printk ("level 0 mmusr is %#x\n", mmusr); #if 0 - asm volatile ("pmove %%tt0,%0" - : "=m" (tlong)); + asm volatile ("pmove %%tt0,%0@" + : /* no outputs */ + : "a" (&tlong)); printk("tt0 is %#lx, ", tlong); - asm volatile ("pmove %%tt1,%0" - : "=m" (tlong)); + asm volatile ("pmove %%tt1,%0@" + : /* no outputs */ + : "a" (&tlong)); printk("tt1 is %#lx\n", tlong); #endif #ifdef DEBUG @@ -666,13 +668,13 @@ static inline void bus_error030 (struct frame *fp) #ifdef DEBUG asm volatile ("ptestr #1,%2@,#7,%0\n\t" - "pmove %%psr,%1" - : "=a&" (desc), "=m" (temp) - : "a" (addr)); + "pmove %%psr,%1@" + : "=a&" (desc) + : "a" (&temp), "a" (addr)); #else asm volatile ("ptestr #1,%1@,#7\n\t" - "pmove %%psr,%0" - : "=m" (temp) : "a" (addr)); + "pmove %%psr,%0@" + : : "a" (&temp), "a" (addr)); #endif mmusr = temp; diff --git a/trunk/arch/m68k/mm/cache.c b/trunk/arch/m68k/mm/cache.c index 3d84c1f2ffb2..95d0bf66e2e2 100644 --- a/trunk/arch/m68k/mm/cache.c +++ b/trunk/arch/m68k/mm/cache.c @@ -52,9 +52,9 @@ static unsigned long virt_to_phys_slow(unsigned long vaddr) unsigned long *descaddr; asm volatile ("ptestr %3,%2@,#7,%0\n\t" - "pmove %%psr,%1" - : "=a&" (descaddr), "=m" (mmusr) - : "a" (vaddr), "d" (get_fs().seg)); + "pmove %%psr,%1@" + : "=a&" (descaddr) + : "a" (&mmusr), "a" (vaddr), "d" (get_fs().seg)); if (mmusr & (MMU_I|MMU_B|MMU_L)) return 0; descaddr = phys_to_virt((unsigned long)descaddr); diff --git a/trunk/arch/microblaze/Kconfig b/trunk/arch/microblaze/Kconfig index c8d6efb99dbf..74f23a460ba2 100644 --- a/trunk/arch/microblaze/Kconfig +++ b/trunk/arch/microblaze/Kconfig @@ -19,7 +19,6 @@ config MICROBLAZE select GENERIC_IRQ_SHOW select GENERIC_PCI_IOMAP select GENERIC_CPU_DEVICES - select GENERIC_ATOMIC64 config SWAP def_bool n diff --git a/trunk/arch/microblaze/include/asm/atomic.h b/trunk/arch/microblaze/include/asm/atomic.h index 615f53992c65..6d2e1d418be7 100644 --- a/trunk/arch/microblaze/include/asm/atomic.h +++ b/trunk/arch/microblaze/include/asm/atomic.h @@ -2,7 +2,6 @@ #define _ASM_MICROBLAZE_ATOMIC_H #include -#include /* * Atomically test *v and decrement if it is greater than 0. diff --git a/trunk/arch/microblaze/kernel/setup.c b/trunk/arch/microblaze/kernel/setup.c index 604cd9dd1333..d4fc1a971779 100644 --- a/trunk/arch/microblaze/kernel/setup.c +++ b/trunk/arch/microblaze/kernel/setup.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -226,5 +227,23 @@ static int __init setup_bus_notifier(void) return 0; } - arch_initcall(setup_bus_notifier); + +static DEFINE_PER_CPU(struct cpu, cpu_devices); + +static int __init topology_init(void) +{ + int i, ret; + + for_each_present_cpu(i) { + struct cpu *c = &per_cpu(cpu_devices, i); + + ret = register_cpu(c, i); + if (ret) + printk(KERN_WARNING "topology_init: register_cpu %d " + "failed (%d)\n", i, ret); + } + + return 0; +} +subsys_initcall(topology_init); diff --git a/trunk/arch/mips/Kconfig b/trunk/arch/mips/Kconfig index 5ab6e89603c5..c4c1312473fb 100644 --- a/trunk/arch/mips/Kconfig +++ b/trunk/arch/mips/Kconfig @@ -2356,7 +2356,6 @@ config PCI depends on HW_HAS_PCI select PCI_DOMAINS select GENERIC_PCI_IOMAP - select NO_GENERIC_PCI_IOPORT_MAP help Find out whether you have a PCI motherboard. PCI is the name of a bus system, i.e. the way the CPU talks to the other stuff inside diff --git a/trunk/arch/mips/lib/iomap-pci.c b/trunk/arch/mips/lib/iomap-pci.c index fd35daa45314..2635b1a96333 100644 --- a/trunk/arch/mips/lib/iomap-pci.c +++ b/trunk/arch/mips/lib/iomap-pci.c @@ -10,8 +10,8 @@ #include #include -void __iomem *__pci_ioport_map(struct pci_dev *dev, - unsigned long port, unsigned int nr) +static void __iomem *ioport_map_pci(struct pci_dev *dev, + unsigned long port, unsigned int nr) { struct pci_controller *ctrl = dev->bus->sysdata; unsigned long base = ctrl->io_map_base; diff --git a/trunk/arch/powerpc/boot/dts/fsl/mpc8536si-post.dtsi b/trunk/arch/powerpc/boot/dts/fsl/mpc8536si-post.dtsi index b37da56018b6..89af62637707 100644 --- a/trunk/arch/powerpc/boot/dts/fsl/mpc8536si-post.dtsi +++ b/trunk/arch/powerpc/boot/dts/fsl/mpc8536si-post.dtsi @@ -236,10 +236,6 @@ }; /include/ "pq3-esdhc-0.dtsi" - sdhc@2e000 { - compatible = "fsl,mpc8536-esdhc", "fsl,esdhc"; - }; - /include/ "pq3-sec3.0-0.dtsi" /include/ "pq3-mpic.dtsi" /include/ "pq3-mpic-timer-B.dtsi" diff --git a/trunk/arch/powerpc/boot/dts/fsl/p1010si-post.dtsi b/trunk/arch/powerpc/boot/dts/fsl/p1010si-post.dtsi index a97d1263372c..bd9e163c764b 100644 --- a/trunk/arch/powerpc/boot/dts/fsl/p1010si-post.dtsi +++ b/trunk/arch/powerpc/boot/dts/fsl/p1010si-post.dtsi @@ -158,8 +158,7 @@ /include/ "pq3-usb2-dr-0.dtsi" /include/ "pq3-esdhc-0.dtsi" sdhc@2e000 { - compatible = "fsl,p1010-esdhc", "fsl,esdhc"; - sdhci,auto-cmd12; + fsl,sdhci-auto-cmd12; }; /include/ "pq3-sec4.4-0.dtsi" diff --git a/trunk/arch/powerpc/boot/dts/fsl/p1020si-post.dtsi b/trunk/arch/powerpc/boot/dts/fsl/p1020si-post.dtsi index 5de5fc351314..fc924c5ffebe 100644 --- a/trunk/arch/powerpc/boot/dts/fsl/p1020si-post.dtsi +++ b/trunk/arch/powerpc/boot/dts/fsl/p1020si-post.dtsi @@ -145,10 +145,6 @@ /include/ "pq3-usb2-dr-1.dtsi" /include/ "pq3-esdhc-0.dtsi" - sdhc@2e000 { - compatible = "fsl,p1020-esdhc", "fsl,esdhc"; - sdhci,auto-cmd12; - }; /include/ "pq3-sec3.3-0.dtsi" /include/ "pq3-mpic.dtsi" diff --git a/trunk/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi b/trunk/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi index ff9ed1d87929..16239b199d0a 100644 --- a/trunk/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi +++ b/trunk/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi @@ -203,8 +203,7 @@ /include/ "pq3-esdhc-0.dtsi" sdhc@2e000 { - compatible = "fsl,p1022-esdhc", "fsl,esdhc"; - sdhci,auto-cmd12; + fsl,sdhci-auto-cmd12; }; /include/ "pq3-sec3.3-0.dtsi" diff --git a/trunk/arch/powerpc/boot/dts/fsl/p2020si-post.dtsi b/trunk/arch/powerpc/boot/dts/fsl/p2020si-post.dtsi index 332e9e75e6c2..c041050561a7 100644 --- a/trunk/arch/powerpc/boot/dts/fsl/p2020si-post.dtsi +++ b/trunk/arch/powerpc/boot/dts/fsl/p2020si-post.dtsi @@ -182,10 +182,6 @@ /include/ "pq3-etsec1-1.dtsi" /include/ "pq3-etsec1-2.dtsi" /include/ "pq3-esdhc-0.dtsi" - sdhc@2e000 { - compatible = "fsl,p2020-esdhc", "fsl,esdhc"; - }; - /include/ "pq3-sec3.1-0.dtsi" /include/ "pq3-mpic.dtsi" /include/ "pq3-mpic-timer-B.dtsi" diff --git a/trunk/arch/powerpc/boot/dts/p1020rdb.dtsi b/trunk/arch/powerpc/boot/dts/p1020rdb.dtsi index 1fb7e0e0940f..b5bd86f4baf2 100644 --- a/trunk/arch/powerpc/boot/dts/p1020rdb.dtsi +++ b/trunk/arch/powerpc/boot/dts/p1020rdb.dtsi @@ -1,7 +1,7 @@ /* * P1020 RDB Device Tree Source stub (no addresses or top-level ranges) * - * Copyright 2011-2012 Freescale Semiconductor Inc. + * Copyright 2011 Freescale Semiconductor Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -190,16 +190,17 @@ usb@22000 { phy_type = "ulpi"; - dr_mode = "host"; }; - /* USB2 is shared with localbus. It is used - only in case of SPI and SD boot after - appropriate device-tree fixup done by uboot */ + /* USB2 is shared with localbus, so it must be disabled + by default. We can't put 'status = "disabled";' here + since U-Boot doesn't clear the status property when + it enables USB2. OTOH, U-Boot does create a new node + when there isn't any. So, just comment it out. usb@23000 { phy_type = "ulpi"; - dr_mode = "host"; }; + */ mdio@24000 { phy0: ethernet-phy@0 { diff --git a/trunk/arch/powerpc/boot/dts/p1021mds.dts b/trunk/arch/powerpc/boot/dts/p1021mds.dts index 97116f198a37..d9540791e434 100644 --- a/trunk/arch/powerpc/boot/dts/p1021mds.dts +++ b/trunk/arch/powerpc/boot/dts/p1021mds.dts @@ -1,7 +1,7 @@ /* * P1021 MDS Device Tree Source * - * Copyright 2010,2012 Freescale Semiconductor Inc. + * Copyright 2010 Freescale Semiconductor Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -151,7 +151,6 @@ usb@22000 { phy_type = "ulpi"; - dr_mode = "host"; }; mdio@24000 { diff --git a/trunk/arch/powerpc/boot/dts/p2020ds.dtsi b/trunk/arch/powerpc/boot/dts/p2020ds.dtsi index d3b939c573b0..c1cf6cef4dd6 100644 --- a/trunk/arch/powerpc/boot/dts/p2020ds.dtsi +++ b/trunk/arch/powerpc/boot/dts/p2020ds.dtsi @@ -1,7 +1,7 @@ /* * P2020DS Device Tree Source stub (no addresses or top-level ranges) * - * Copyright 2011-2012 Freescale Semiconductor Inc. + * Copyright 2011 Freescale Semiconductor Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -134,7 +134,6 @@ &board_soc { usb@22000 { phy_type = "ulpi"; - dr_mode = "host"; }; mdio@24520 { diff --git a/trunk/arch/powerpc/boot/dts/p2020rdb.dts b/trunk/arch/powerpc/boot/dts/p2020rdb.dts index eb8a6aa2bda5..26759a591712 100644 --- a/trunk/arch/powerpc/boot/dts/p2020rdb.dts +++ b/trunk/arch/powerpc/boot/dts/p2020rdb.dts @@ -1,7 +1,7 @@ /* * P2020 RDB Device Tree Source * - * Copyright 2009-2012 Freescale Semiconductor Inc. + * Copyright 2009-2011 Freescale Semiconductor Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -197,7 +197,6 @@ usb@22000 { phy_type = "ulpi"; - dr_mode = "host"; }; mdio@24520 { diff --git a/trunk/arch/powerpc/kernel/crash.c b/trunk/arch/powerpc/kernel/crash.c index abef75176c07..28be3452e67a 100644 --- a/trunk/arch/powerpc/kernel/crash.c +++ b/trunk/arch/powerpc/kernel/crash.c @@ -46,6 +46,7 @@ /* This keeps a track of which one is the crashing cpu. */ int crashing_cpu = -1; +static atomic_t cpus_in_crash; static int time_to_dump; #define CRASH_HANDLER_MAX 3 @@ -65,7 +66,6 @@ static int handle_fault(struct pt_regs *regs) #ifdef CONFIG_SMP -static atomic_t cpus_in_crash; void crash_ipi_callback(struct pt_regs *regs) { static cpumask_t cpus_state_saved = CPU_MASK_NONE; diff --git a/trunk/arch/powerpc/kernel/legacy_serial.c b/trunk/arch/powerpc/kernel/legacy_serial.c index bedd12e1cfbc..3fea3689527e 100644 --- a/trunk/arch/powerpc/kernel/legacy_serial.c +++ b/trunk/arch/powerpc/kernel/legacy_serial.c @@ -442,10 +442,8 @@ static void __init fixup_port_irq(int index, port->irq = virq; -#ifdef CONFIG_SERIAL_8250_FSL if (of_device_is_compatible(np, "fsl,ns16550")) port->handle_irq = fsl8250_handle_irq; -#endif } static void __init fixup_port_pio(int index, diff --git a/trunk/arch/powerpc/platforms/85xx/p1022_ds.c b/trunk/arch/powerpc/platforms/85xx/p1022_ds.c index b0984ada3f83..bb3d84f4046f 100644 --- a/trunk/arch/powerpc/platforms/85xx/p1022_ds.c +++ b/trunk/arch/powerpc/platforms/85xx/p1022_ds.c @@ -25,7 +25,6 @@ #include #include -#include #include #include "smp.h" diff --git a/trunk/arch/powerpc/platforms/powernv/pci-ioda.c b/trunk/arch/powerpc/platforms/powernv/pci-ioda.c index 5e155dfc4320..f31162cfdaa9 100644 --- a/trunk/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/trunk/arch/powerpc/platforms/powernv/pci-ioda.c @@ -204,10 +204,11 @@ static void __devinit pnv_ioda_offset_bus(struct pci_bus *bus, pr_devel(" -> OBR %s [%x] +%016llx\n", bus->self ? pci_name(bus->self) : "root", flags, offset); - pci_bus_for_each_resource(bus, r, i) { + for (i = 0; i < 2; i++) { + r = bus->resource[i]; if (r && (r->flags & flags)) { - r->start += offset; - r->end += offset; + bus->resource[i]->start += offset; + bus->resource[i]->end += offset; } } list_for_each_entry(dev, &bus->devices, bus_list) @@ -287,17 +288,12 @@ static void __devinit pnv_ioda_calc_bus(struct pci_bus *bus, unsigned int flags, * assignment algorithm is going to be uber-trivial for now, we * can try to be smarter later at filling out holes. */ - if (bus->self) { - /* No offset for downstream bridges */ - start = 0; - } else { - /* Offset from the root */ - if (flags & IORESOURCE_IO) - /* Don't hand out IO 0 */ - start = hose->io_resource.start + 0x1000; - else - start = hose->mem_resources[0].start; - } + start = bus->self ? 0 : bus->resource[bres]->start; + + /* Don't hand out IO 0 */ + if ((flags & IORESOURCE_IO) && !bus->self) + start += 0x1000; + while(!list_empty(&head)) { w = list_first_entry(&head, struct resource_wrap, link); list_del(&w->link); @@ -325,20 +321,13 @@ static void __devinit pnv_ioda_calc_bus(struct pci_bus *bus, unsigned int flags, empty: /* Only setup P2P's, not the PHB itself */ if (bus->self) { - struct resource *res = bus->resource[bres]; - - if (WARN_ON(res == NULL)) - return; + WARN_ON(bus->resource[bres] == NULL); + bus->resource[bres]->start = 0; + bus->resource[bres]->flags = (*size) ? flags : 0; + bus->resource[bres]->end = (*size) ? (*size - 1) : 0; - /* - * FIXME: We should probably export and call - * pci_bridge_check_ranges() to properly re-initialize - * the PCI portion of the flags here, and to detect - * what the bridge actually supports. - */ - res->start = 0; - res->flags = (*size) ? flags : 0; - res->end = (*size) ? (*size - 1) : 0; + /* Clear prefetch bus resources for now */ + bus->resource[2]->flags = 0; } pr_devel("<- CBR %s [%x] *size=%016llx *align=%016llx\n", diff --git a/trunk/arch/powerpc/platforms/pseries/Kconfig b/trunk/arch/powerpc/platforms/pseries/Kconfig index 31f22c1f657d..ae7b6d41fed3 100644 --- a/trunk/arch/powerpc/platforms/pseries/Kconfig +++ b/trunk/arch/powerpc/platforms/pseries/Kconfig @@ -122,7 +122,7 @@ config DTL Say N if you are unsure. config PSERIES_IDLE - bool "Cpuidle driver for pSeries platforms" + tristate "Cpuidle driver for pSeries platforms" depends on CPU_IDLE depends on PPC_PSERIES default y diff --git a/trunk/arch/powerpc/sysdev/fsl_pci.c b/trunk/arch/powerpc/sysdev/fsl_pci.c index 30eb17ecad49..3b61e8cf3421 100644 --- a/trunk/arch/powerpc/sysdev/fsl_pci.c +++ b/trunk/arch/powerpc/sysdev/fsl_pci.c @@ -205,12 +205,12 @@ static void __init setup_pci_atmu(struct pci_controller *hose, if (paddr_hi == paddr_lo) { pr_err("%s: No outbound window space\n", name); - goto out; + return ; } if (paddr_lo == 0) { pr_err("%s: No space for inbound window\n", name); - goto out; + return ; } /* setup PCSRBAR/PEXCSRBAR */ @@ -357,7 +357,6 @@ static void __init setup_pci_atmu(struct pci_controller *hose, (u64)hose->dma_window_size); } -out: iounmap(pci); } diff --git a/trunk/arch/s390/Makefile b/trunk/arch/s390/Makefile index 0ad2f1e1ce9e..e9f353341693 100644 --- a/trunk/arch/s390/Makefile +++ b/trunk/arch/s390/Makefile @@ -88,6 +88,7 @@ KBUILD_CFLAGS += -pipe -fno-strength-reduce -Wno-sign-compare KBUILD_AFLAGS += $(aflags-y) OBJCOPYFLAGS := -O binary +LDFLAGS_vmlinux := -e start head-y := arch/s390/kernel/head.o head-y += arch/s390/kernel/$(if $(CONFIG_64BIT),head64.o,head31.o) diff --git a/trunk/arch/s390/kernel/vmlinux.lds.S b/trunk/arch/s390/kernel/vmlinux.lds.S index 21109c63eb12..e4c79ebb40e6 100644 --- a/trunk/arch/s390/kernel/vmlinux.lds.S +++ b/trunk/arch/s390/kernel/vmlinux.lds.S @@ -9,12 +9,12 @@ #ifndef CONFIG_64BIT OUTPUT_FORMAT("elf32-s390", "elf32-s390", "elf32-s390") OUTPUT_ARCH(s390) -ENTRY(startup) +ENTRY(_start) jiffies = jiffies_64 + 4; #else OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390") OUTPUT_ARCH(s390:64-bit) -ENTRY(startup) +ENTRY(_start) jiffies = jiffies_64; #endif diff --git a/trunk/arch/sh/Kconfig b/trunk/arch/sh/Kconfig index 713fb58ca507..3c8db65c89e5 100644 --- a/trunk/arch/sh/Kconfig +++ b/trunk/arch/sh/Kconfig @@ -859,7 +859,6 @@ config PCI depends on SYS_SUPPORTS_PCI select PCI_DOMAINS select GENERIC_PCI_IOMAP - select NO_GENERIC_PCI_IOPORT_MAP help Find out whether you have a PCI motherboard. PCI is the name of a bus system, i.e. the way the CPU talks to the other stuff inside diff --git a/trunk/arch/sh/drivers/pci/pci.c b/trunk/arch/sh/drivers/pci/pci.c index 1e7b0e2e764d..8f18dd090a66 100644 --- a/trunk/arch/sh/drivers/pci/pci.c +++ b/trunk/arch/sh/drivers/pci/pci.c @@ -356,8 +356,8 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, #ifndef CONFIG_GENERIC_IOMAP -void __iomem *__pci_ioport_map(struct pci_dev *dev, - unsigned long port, unsigned int nr) +static void __iomem *ioport_map_pci(struct pci_dev *dev, + unsigned long port, unsigned int nr) { struct pci_channel *chan = dev->sysdata; diff --git a/trunk/arch/sparc/Kconfig b/trunk/arch/sparc/Kconfig index ca5580e4d813..96657992a72e 100644 --- a/trunk/arch/sparc/Kconfig +++ b/trunk/arch/sparc/Kconfig @@ -33,7 +33,6 @@ config SPARC config SPARC32 def_bool !64BIT select GENERIC_ATOMIC64 - select CLZ_TAB config SPARC64 def_bool 64BIT diff --git a/trunk/arch/sparc/kernel/sun4m_irq.c b/trunk/arch/sparc/kernel/sun4m_irq.c index e61165161dd3..422c16dad1f6 100644 --- a/trunk/arch/sparc/kernel/sun4m_irq.c +++ b/trunk/arch/sparc/kernel/sun4m_irq.c @@ -399,9 +399,6 @@ static void __init sun4m_init_timers(irq_handler_t counter_fn) timers_global = (void __iomem *) (unsigned long) addr[num_cpu_timers]; - /* Every per-cpu timer works in timer mode */ - sbus_writel(0x00000000, &timers_global->timer_config); - sbus_writel((((1000000/HZ) + 1) << 10), &timers_global->l10_limit); master_l10_counter = &timers_global->l10_count; diff --git a/trunk/arch/sparc/lib/divdi3.S b/trunk/arch/sparc/lib/divdi3.S index d74bc0925f2d..681b3683da9e 100644 --- a/trunk/arch/sparc/lib/divdi3.S +++ b/trunk/arch/sparc/lib/divdi3.S @@ -17,9 +17,23 @@ along with GNU CC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + .data + .align 8 + .globl __clz_tab +__clz_tab: + .byte 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 + .byte 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 + .byte 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 + .byte 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 + .byte 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 + .byte 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 + .byte 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 + .byte 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 + .size __clz_tab,256 + .global .udiv + .text .align 4 - .global .udiv .globl __divdi3 __divdi3: save %sp,-104,%sp diff --git a/trunk/arch/x86/Kconfig b/trunk/arch/x86/Kconfig index 5bed94e189fa..864cc6e6ac8e 100644 --- a/trunk/arch/x86/Kconfig +++ b/trunk/arch/x86/Kconfig @@ -360,6 +360,7 @@ config X86_NUMACHIP depends on NUMA depends on SMP depends on X86_X2APIC + depends on !EDAC_AMD64 ---help--- Adds support for Numascale NumaChip large-SMP systems. Needed to enable more than ~168 cores. diff --git a/trunk/arch/x86/boot/compressed/misc.c b/trunk/arch/x86/boot/compressed/misc.c index 7116dcba0c9e..3a19d04cebeb 100644 --- a/trunk/arch/x86/boot/compressed/misc.c +++ b/trunk/arch/x86/boot/compressed/misc.c @@ -321,8 +321,6 @@ static void parse_elf(void *output) default: /* Ignore other PT_* */ break; } } - - free(phdrs); } asmlinkage void decompress_kernel(void *rmode, memptr heap, diff --git a/trunk/arch/x86/include/asm/cmpxchg.h b/trunk/arch/x86/include/asm/cmpxchg.h index b3b733262909..0c9fa2745f13 100644 --- a/trunk/arch/x86/include/asm/cmpxchg.h +++ b/trunk/arch/x86/include/asm/cmpxchg.h @@ -145,13 +145,13 @@ extern void __add_wrong_size(void) #ifdef __HAVE_ARCH_CMPXCHG #define cmpxchg(ptr, old, new) \ - __cmpxchg(ptr, old, new, sizeof(*(ptr))) + __cmpxchg((ptr), (old), (new), sizeof(*ptr)) #define sync_cmpxchg(ptr, old, new) \ - __sync_cmpxchg(ptr, old, new, sizeof(*(ptr))) + __sync_cmpxchg((ptr), (old), (new), sizeof(*ptr)) #define cmpxchg_local(ptr, old, new) \ - __cmpxchg_local(ptr, old, new, sizeof(*(ptr))) + __cmpxchg_local((ptr), (old), (new), sizeof(*ptr)) #endif /* diff --git a/trunk/arch/x86/include/asm/cpufeature.h b/trunk/arch/x86/include/asm/cpufeature.h index 8d67d428b0f9..17c5d4bdee5e 100644 --- a/trunk/arch/x86/include/asm/cpufeature.h +++ b/trunk/arch/x86/include/asm/cpufeature.h @@ -159,7 +159,6 @@ #define X86_FEATURE_WDT (6*32+13) /* Watchdog timer */ #define X86_FEATURE_LWP (6*32+15) /* Light Weight Profiling */ #define X86_FEATURE_FMA4 (6*32+16) /* 4 operands MAC instructions */ -#define X86_FEATURE_TCE (6*32+17) /* translation cache extension */ #define X86_FEATURE_NODEID_MSR (6*32+19) /* NodeId MSR */ #define X86_FEATURE_TBM (6*32+21) /* trailing bit manipulations */ #define X86_FEATURE_TOPOEXT (6*32+22) /* topology extensions CPUID leafs */ diff --git a/trunk/arch/x86/include/asm/kvm_emulate.h b/trunk/arch/x86/include/asm/kvm_emulate.h index 7b9cfc4878af..ab4092e3214e 100644 --- a/trunk/arch/x86/include/asm/kvm_emulate.h +++ b/trunk/arch/x86/include/asm/kvm_emulate.h @@ -190,9 +190,6 @@ struct x86_emulate_ops { int (*intercept)(struct x86_emulate_ctxt *ctxt, struct x86_instruction_info *info, enum x86_intercept_stage stage); - - bool (*get_cpuid)(struct x86_emulate_ctxt *ctxt, - u32 *eax, u32 *ebx, u32 *ecx, u32 *edx); }; typedef u32 __attribute__((vector_size(16))) sse128_t; @@ -301,19 +298,6 @@ struct x86_emulate_ctxt { #define X86EMUL_MODE_PROT (X86EMUL_MODE_PROT16|X86EMUL_MODE_PROT32| \ X86EMUL_MODE_PROT64) -/* CPUID vendors */ -#define X86EMUL_CPUID_VENDOR_AuthenticAMD_ebx 0x68747541 -#define X86EMUL_CPUID_VENDOR_AuthenticAMD_ecx 0x444d4163 -#define X86EMUL_CPUID_VENDOR_AuthenticAMD_edx 0x69746e65 - -#define X86EMUL_CPUID_VENDOR_AMDisbetterI_ebx 0x69444d41 -#define X86EMUL_CPUID_VENDOR_AMDisbetterI_ecx 0x21726574 -#define X86EMUL_CPUID_VENDOR_AMDisbetterI_edx 0x74656273 - -#define X86EMUL_CPUID_VENDOR_GenuineIntel_ebx 0x756e6547 -#define X86EMUL_CPUID_VENDOR_GenuineIntel_ecx 0x6c65746e -#define X86EMUL_CPUID_VENDOR_GenuineIntel_edx 0x49656e69 - enum x86_intercept_stage { X86_ICTP_NONE = 0, /* Allow zero-init to not match anything */ X86_ICPT_PRE_EXCEPT, diff --git a/trunk/arch/x86/include/asm/uv/uv_hub.h b/trunk/arch/x86/include/asm/uv/uv_hub.h index 21f7385badb8..54a13aaebc40 100644 --- a/trunk/arch/x86/include/asm/uv/uv_hub.h +++ b/trunk/arch/x86/include/asm/uv/uv_hub.h @@ -318,13 +318,13 @@ uv_gpa_in_mmr_space(unsigned long gpa) /* UV global physical address --> socket phys RAM */ static inline unsigned long uv_gpa_to_soc_phys_ram(unsigned long gpa) { - unsigned long paddr; + unsigned long paddr = gpa & uv_hub_info->gpa_mask; unsigned long remap_base = uv_hub_info->lowmem_remap_base; unsigned long remap_top = uv_hub_info->lowmem_remap_top; gpa = ((gpa << uv_hub_info->m_shift) >> uv_hub_info->m_shift) | ((gpa >> uv_hub_info->n_lshift) << uv_hub_info->m_val); - paddr = gpa & uv_hub_info->gpa_mask; + gpa = gpa & uv_hub_info->gpa_mask; if (paddr >= remap_base && paddr < remap_base + remap_top) paddr -= remap_base; return paddr; 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/dumpstack.c b/trunk/arch/x86/kernel/dumpstack.c index 4025fe4f928f..1aae78f775fc 100644 --- a/trunk/arch/x86/kernel/dumpstack.c +++ b/trunk/arch/x86/kernel/dumpstack.c @@ -252,8 +252,7 @@ int __kprobes __die(const char *str, struct pt_regs *regs, long err) unsigned short ss; unsigned long sp; #endif - printk(KERN_DEFAULT - "%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter); + printk(KERN_EMERG "%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter); #ifdef CONFIG_PREEMPT printk("PREEMPT "); #endif diff --git a/trunk/arch/x86/kernel/dumpstack_64.c b/trunk/arch/x86/kernel/dumpstack_64.c index 17107bd6e1f0..6d728d9284bd 100644 --- a/trunk/arch/x86/kernel/dumpstack_64.c +++ b/trunk/arch/x86/kernel/dumpstack_64.c @@ -129,7 +129,7 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, if (!stack) { if (regs) stack = (unsigned long *)regs->sp; - else if (task != current) + else if (task && task != current) stack = (unsigned long *)task->thread.sp; else stack = &dummy; @@ -269,11 +269,11 @@ void show_registers(struct pt_regs *regs) unsigned char c; u8 *ip; - printk(KERN_DEFAULT "Stack:\n"); + printk(KERN_EMERG "Stack:\n"); show_stack_log_lvl(NULL, regs, (unsigned long *)sp, - 0, KERN_DEFAULT); + 0, KERN_EMERG); - printk(KERN_DEFAULT "Code: "); + printk(KERN_EMERG "Code: "); ip = (u8 *)regs->ip - code_prologue; if (ip < (u8 *)PAGE_OFFSET || probe_kernel_address(ip, c)) { diff --git a/trunk/arch/x86/kernel/microcode_amd.c b/trunk/arch/x86/kernel/microcode_amd.c index ac0417be9131..fe86493f3ed1 100644 --- a/trunk/arch/x86/kernel/microcode_amd.c +++ b/trunk/arch/x86/kernel/microcode_amd.c @@ -311,33 +311,13 @@ generic_load_microcode(int cpu, const u8 *data, size_t size) return state; } -/* - * AMD microcode firmware naming convention, up to family 15h they are in - * the legacy file: - * - * amd-ucode/microcode_amd.bin - * - * This legacy file is always smaller than 2K in size. - * - * Starting at family 15h they are in family specific firmware files: - * - * amd-ucode/microcode_amd_fam15h.bin - * amd-ucode/microcode_amd_fam16h.bin - * ... - * - * These might be larger than 2K. - */ static enum ucode_state request_microcode_amd(int cpu, struct device *device) { - char fw_name[36] = "amd-ucode/microcode_amd.bin"; + const char *fw_name = "amd-ucode/microcode_amd.bin"; const struct firmware *fw; enum ucode_state ret = UCODE_NFOUND; - struct cpuinfo_x86 *c = &cpu_data(cpu); - - if (c->x86 >= 0x15) - snprintf(fw_name, sizeof(fw_name), "amd-ucode/microcode_amd_fam%.2xh.bin", c->x86); - if (request_firmware(&fw, (const char *)fw_name, device)) { + if (request_firmware(&fw, fw_name, device)) { pr_err("failed to load file %s\n", fw_name); goto out; } diff --git a/trunk/arch/x86/kernel/reboot.c b/trunk/arch/x86/kernel/reboot.c index d840e69a853c..37a458b521a6 100644 --- a/trunk/arch/x86/kernel/reboot.c +++ b/trunk/arch/x86/kernel/reboot.c @@ -39,14 +39,6 @@ static int reboot_mode; enum reboot_type reboot_type = BOOT_ACPI; int reboot_force; -/* This variable is used privately to keep track of whether or not - * reboot_type is still set to its default value (i.e., reboot= hasn't - * been set on the command line). This is needed so that we can - * suppress DMI scanning for reboot quirks. Without it, it's - * impossible to override a faulty reboot quirk without recompiling. - */ -static int reboot_default = 1; - #if defined(CONFIG_X86_32) && defined(CONFIG_SMP) static int reboot_cpu = -1; #endif @@ -75,12 +67,6 @@ bool port_cf9_safe = false; static int __init reboot_setup(char *str) { for (;;) { - /* Having anything passed on the command line via - * reboot= will cause us to disable DMI checking - * below. - */ - reboot_default = 0; - switch (*str) { case 'w': reboot_mode = 0x1234; @@ -309,6 +295,14 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { DMI_MATCH(DMI_BOARD_NAME, "P4S800"), }, }, + { /* Handle problems with rebooting on VersaLogic Menlow boards */ + .callback = set_bios_reboot, + .ident = "VersaLogic Menlow based board", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "VersaLogic Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "VersaLogic Menlow board"), + }, + }, { /* Handle reboot issue on Acer Aspire one */ .callback = set_kbd_reboot, .ident = "Acer Aspire One A110", @@ -322,12 +316,7 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { static int __init reboot_init(void) { - /* Only do the DMI check if reboot_type hasn't been overridden - * on the command line - */ - if (reboot_default) { - dmi_check_system(reboot_dmi_table); - } + dmi_check_system(reboot_dmi_table); return 0; } core_initcall(reboot_init); @@ -476,12 +465,7 @@ static struct dmi_system_id __initdata pci_reboot_dmi_table[] = { static int __init pci_reboot_init(void) { - /* Only do the DMI check if reboot_type hasn't been overridden - * on the command line - */ - if (reboot_default) { - dmi_check_system(pci_reboot_dmi_table); - } + dmi_check_system(pci_reboot_dmi_table); return 0; } core_initcall(pci_reboot_init); diff --git a/trunk/arch/x86/kvm/emulate.c b/trunk/arch/x86/kvm/emulate.c index 0982507b962a..05a562b85025 100644 --- a/trunk/arch/x86/kvm/emulate.c +++ b/trunk/arch/x86/kvm/emulate.c @@ -1891,51 +1891,6 @@ setup_syscalls_segments(struct x86_emulate_ctxt *ctxt, ss->p = 1; } -static bool em_syscall_is_enabled(struct x86_emulate_ctxt *ctxt) -{ - struct x86_emulate_ops *ops = ctxt->ops; - u32 eax, ebx, ecx, edx; - - /* - * syscall should always be enabled in longmode - so only become - * vendor specific (cpuid) if other modes are active... - */ - if (ctxt->mode == X86EMUL_MODE_PROT64) - return true; - - eax = 0x00000000; - ecx = 0x00000000; - if (ops->get_cpuid(ctxt, &eax, &ebx, &ecx, &edx)) { - /* - * Intel ("GenuineIntel") - * remark: Intel CPUs only support "syscall" in 64bit - * longmode. Also an 64bit guest with a - * 32bit compat-app running will #UD !! While this - * behaviour can be fixed (by emulating) into AMD - * response - CPUs of AMD can't behave like Intel. - */ - if (ebx == X86EMUL_CPUID_VENDOR_GenuineIntel_ebx && - ecx == X86EMUL_CPUID_VENDOR_GenuineIntel_ecx && - edx == X86EMUL_CPUID_VENDOR_GenuineIntel_edx) - return false; - - /* AMD ("AuthenticAMD") */ - if (ebx == X86EMUL_CPUID_VENDOR_AuthenticAMD_ebx && - ecx == X86EMUL_CPUID_VENDOR_AuthenticAMD_ecx && - edx == X86EMUL_CPUID_VENDOR_AuthenticAMD_edx) - return true; - - /* AMD ("AMDisbetter!") */ - if (ebx == X86EMUL_CPUID_VENDOR_AMDisbetterI_ebx && - ecx == X86EMUL_CPUID_VENDOR_AMDisbetterI_ecx && - edx == X86EMUL_CPUID_VENDOR_AMDisbetterI_edx) - return true; - } - - /* default: (not Intel, not AMD), apply Intel's stricter rules... */ - return false; -} - static int em_syscall(struct x86_emulate_ctxt *ctxt) { struct x86_emulate_ops *ops = ctxt->ops; @@ -1949,15 +1904,9 @@ static int em_syscall(struct x86_emulate_ctxt *ctxt) ctxt->mode == X86EMUL_MODE_VM86) return emulate_ud(ctxt); - if (!(em_syscall_is_enabled(ctxt))) - return emulate_ud(ctxt); - ops->get_msr(ctxt, MSR_EFER, &efer); setup_syscalls_segments(ctxt, &cs, &ss); - if (!(efer & EFER_SCE)) - return emulate_ud(ctxt); - ops->get_msr(ctxt, MSR_STAR, &msr_data); msr_data >>= 32; cs_sel = (u16)(msr_data & 0xfffc); diff --git a/trunk/arch/x86/kvm/x86.c b/trunk/arch/x86/kvm/x86.c index 9cbfc0698118..14d6cadc4ba6 100644 --- a/trunk/arch/x86/kvm/x86.c +++ b/trunk/arch/x86/kvm/x86.c @@ -1495,8 +1495,6 @@ static void record_steal_time(struct kvm_vcpu *vcpu) int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) { - bool pr = false; - switch (msr) { case MSR_EFER: return set_efer(vcpu, data); @@ -1637,18 +1635,6 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) pr_unimpl(vcpu, "unimplemented perfctr wrmsr: " "0x%x data 0x%llx\n", msr, data); break; - case MSR_P6_PERFCTR0: - case MSR_P6_PERFCTR1: - pr = true; - case MSR_P6_EVNTSEL0: - case MSR_P6_EVNTSEL1: - if (kvm_pmu_msr(vcpu, msr)) - return kvm_pmu_set_msr(vcpu, msr, data); - - if (pr || data != 0) - pr_unimpl(vcpu, "disabled perfctr wrmsr: " - "0x%x data 0x%llx\n", msr, data); - break; case MSR_K7_CLK_CTL: /* * Ignore all writes to this no longer documented MSR. @@ -1849,14 +1835,6 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) case MSR_FAM10H_MMIO_CONF_BASE: data = 0; break; - case MSR_P6_PERFCTR0: - case MSR_P6_PERFCTR1: - case MSR_P6_EVNTSEL0: - case MSR_P6_EVNTSEL1: - if (kvm_pmu_msr(vcpu, msr)) - return kvm_pmu_get_msr(vcpu, msr, pdata); - data = 0; - break; case MSR_IA32_UCODE_REV: data = 0x100000000ULL; break; @@ -4202,28 +4180,6 @@ static int emulator_intercept(struct x86_emulate_ctxt *ctxt, return kvm_x86_ops->check_intercept(emul_to_vcpu(ctxt), info, stage); } -static bool emulator_get_cpuid(struct x86_emulate_ctxt *ctxt, - u32 *eax, u32 *ebx, u32 *ecx, u32 *edx) -{ - struct kvm_cpuid_entry2 *cpuid = NULL; - - if (eax && ecx) - cpuid = kvm_find_cpuid_entry(emul_to_vcpu(ctxt), - *eax, *ecx); - - if (cpuid) { - *eax = cpuid->eax; - *ecx = cpuid->ecx; - if (ebx) - *ebx = cpuid->ebx; - if (edx) - *edx = cpuid->edx; - return true; - } - - return false; -} - static struct x86_emulate_ops emulate_ops = { .read_std = kvm_read_guest_virt_system, .write_std = kvm_write_guest_virt_system, @@ -4255,7 +4211,6 @@ static struct x86_emulate_ops emulate_ops = { .get_fpu = emulator_get_fpu, .put_fpu = emulator_put_fpu, .intercept = emulator_intercept, - .get_cpuid = emulator_get_cpuid, }; static void cache_all_regs(struct kvm_vcpu *vcpu) diff --git a/trunk/arch/x86/mm/fault.c b/trunk/arch/x86/mm/fault.c index f0b4caf85c1a..9d74824a708d 100644 --- a/trunk/arch/x86/mm/fault.c +++ b/trunk/arch/x86/mm/fault.c @@ -673,7 +673,7 @@ no_context(struct pt_regs *regs, unsigned long error_code, stackend = end_of_stack(tsk); if (tsk != &init_task && *stackend != STACK_END_MAGIC) - printk(KERN_EMERG "Thread overran stack, or stack corrupted\n"); + printk(KERN_ALERT "Thread overran stack, or stack corrupted\n"); tsk->thread.cr2 = address; tsk->thread.trap_no = 14; @@ -684,7 +684,7 @@ no_context(struct pt_regs *regs, unsigned long error_code, sig = 0; /* Executive summary in case the body of the oops scrolled away */ - printk(KERN_DEFAULT "CR2: %016lx\n", address); + printk(KERN_EMERG "CR2: %016lx\n", address); oops_end(flags, regs, sig); } diff --git a/trunk/arch/x86/platform/uv/tlb_uv.c b/trunk/arch/x86/platform/uv/tlb_uv.c index 3ae0e61abd23..9be4cff00a2d 100644 --- a/trunk/arch/x86/platform/uv/tlb_uv.c +++ b/trunk/arch/x86/platform/uv/tlb_uv.c @@ -1851,8 +1851,6 @@ static void __init init_per_cpu_tunables(void) bcp->cong_reps = congested_reps; bcp->cong_period = congested_period; bcp->clocks_per_100_usec = usec_2_cycles(100); - spin_lock_init(&bcp->queue_lock); - spin_lock_init(&bcp->uvhub_lock); } } diff --git a/trunk/arch/x86/platform/uv/uv_irq.c b/trunk/arch/x86/platform/uv/uv_irq.c index f25c2765a5c9..374a05d8ad22 100644 --- a/trunk/arch/x86/platform/uv/uv_irq.c +++ b/trunk/arch/x86/platform/uv/uv_irq.c @@ -25,7 +25,7 @@ struct uv_irq_2_mmr_pnode{ int irq; }; -static DEFINE_SPINLOCK(uv_irq_lock); +static spinlock_t uv_irq_lock; static struct rb_root uv_irq_root; static int uv_set_irq_affinity(struct irq_data *, const struct cpumask *, bool); diff --git a/trunk/arch/x86/xen/spinlock.c b/trunk/arch/x86/xen/spinlock.c index d69cc6c3f808..cc9b1e182fcf 100644 --- a/trunk/arch/x86/xen/spinlock.c +++ b/trunk/arch/x86/xen/spinlock.c @@ -116,26 +116,9 @@ static inline void spin_time_accum_blocked(u64 start) } #endif /* CONFIG_XEN_DEBUG_FS */ -/* - * Size struct xen_spinlock so it's the same as arch_spinlock_t. - */ -#if NR_CPUS < 256 -typedef u8 xen_spinners_t; -# define inc_spinners(xl) \ - asm(LOCK_PREFIX " incb %0" : "+m" ((xl)->spinners) : : "memory"); -# define dec_spinners(xl) \ - asm(LOCK_PREFIX " decb %0" : "+m" ((xl)->spinners) : : "memory"); -#else -typedef u16 xen_spinners_t; -# define inc_spinners(xl) \ - asm(LOCK_PREFIX " incw %0" : "+m" ((xl)->spinners) : : "memory"); -# define dec_spinners(xl) \ - asm(LOCK_PREFIX " decw %0" : "+m" ((xl)->spinners) : : "memory"); -#endif - struct xen_spinlock { unsigned char lock; /* 0 -> free; 1 -> locked */ - xen_spinners_t spinners; /* count of waiting cpus */ + unsigned short spinners; /* count of waiting cpus */ }; static int xen_spin_is_locked(struct arch_spinlock *lock) @@ -181,7 +164,8 @@ static inline struct xen_spinlock *spinning_lock(struct xen_spinlock *xl) wmb(); /* set lock of interest before count */ - inc_spinners(xl); + asm(LOCK_PREFIX " incw %0" + : "+m" (xl->spinners) : : "memory"); return prev; } @@ -192,7 +176,8 @@ static inline struct xen_spinlock *spinning_lock(struct xen_spinlock *xl) */ static inline void unspinning_lock(struct xen_spinlock *xl, struct xen_spinlock *prev) { - dec_spinners(xl); + asm(LOCK_PREFIX " decw %0" + : "+m" (xl->spinners) : : "memory"); wmb(); /* decrement count before restoring lock */ __this_cpu_write(lock_spinners, prev); } @@ -388,8 +373,6 @@ void xen_uninit_lock_cpu(int cpu) void __init xen_init_spinlocks(void) { - BUILD_BUG_ON(sizeof(struct xen_spinlock) > sizeof(arch_spinlock_t)); - pv_lock_ops.spin_is_locked = xen_spin_is_locked; pv_lock_ops.spin_is_contended = xen_spin_is_contended; pv_lock_ops.spin_lock = xen_spin_lock; diff --git a/trunk/arch/xtensa/include/asm/string.h b/trunk/arch/xtensa/include/asm/string.h index 405a8c49ff2c..5fb8c27cbef5 100644 --- a/trunk/arch/xtensa/include/asm/string.h +++ b/trunk/arch/xtensa/include/asm/string.h @@ -118,4 +118,7 @@ extern void *memmove(void *__dest, __const__ void *__src, size_t __n); /* Don't build bcopy at all ... */ #define __HAVE_ARCH_BCOPY +#define __HAVE_ARCH_MEMSCAN +#define memscan memchr + #endif /* _XTENSA_STRING_H */ diff --git a/trunk/crypto/sha512_generic.c b/trunk/crypto/sha512_generic.c index 88f160b77b1f..9ed9f60316e5 100644 --- a/trunk/crypto/sha512_generic.c +++ b/trunk/crypto/sha512_generic.c @@ -21,6 +21,8 @@ #include #include +static DEFINE_PER_CPU(u64[80], msg_schedule); + static inline u64 Ch(u64 x, u64 y, u64 z) { return z ^ (x & (y ^ z)); @@ -78,7 +80,7 @@ static inline void LOAD_OP(int I, u64 *W, const u8 *input) static inline void BLEND_OP(int I, u64 *W) { - W[I % 16] += s1(W[(I-2) % 16]) + W[(I-7) % 16] + s0(W[(I-15) % 16]); + W[I] = s1(W[I-2]) + W[I-7] + s0(W[I-15]) + W[I-16]; } static void @@ -87,48 +89,38 @@ sha512_transform(u64 *state, const u8 *input) u64 a, b, c, d, e, f, g, h, t1, t2; int i; - u64 W[16]; + u64 *W = get_cpu_var(msg_schedule); /* load the input */ for (i = 0; i < 16; i++) LOAD_OP(i, W, input); + for (i = 16; i < 80; i++) { + BLEND_OP(i, W); + } + /* 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]; -#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); + /* now iterate */ + for (i=0; i<80; i+=8) { + 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; + t1 = g + e1(d) + Ch(d,e,f) + sha512_K[i+1] + W[i+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+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+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+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+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+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+7]; + t2 = e0(b) + Maj(b,c,d); e+=t1; a=t1+t2; } state[0] += a; state[1] += b; state[2] += c; state[3] += d; @@ -136,6 +128,8 @@ sha512_transform(u64 *state, const u8 *input) /* erase our data */ a = b = c = d = e = f = g = h = t1 = t2 = 0; + memset(W, 0, sizeof(__get_cpu_var(msg_schedule))); + put_cpu_var(msg_schedule); } static int diff --git a/trunk/drivers/acpi/Makefile b/trunk/drivers/acpi/Makefile index 1567028d2038..c07f44f05f9d 100644 --- a/trunk/drivers/acpi/Makefile +++ b/trunk/drivers/acpi/Makefile @@ -19,6 +19,7 @@ obj-y += acpi.o \ # All the builtin files are in the "acpi." module_param namespace. acpi-y += osl.o utils.o reboot.o +acpi-y += atomicio.o acpi-y += nvs.o # sleep related files diff --git a/trunk/drivers/acpi/apei/apei-base.c b/trunk/drivers/acpi/apei/apei-base.c index e5d53b7ddc7e..e45350cb6ac8 100644 --- a/trunk/drivers/acpi/apei/apei-base.c +++ b/trunk/drivers/acpi/apei/apei-base.c @@ -596,19 +596,33 @@ int apei_read(u64 *val, struct acpi_generic_address *reg) { int rc; u64 address; + u32 tmp, width = reg->bit_width; acpi_status status; rc = apei_check_gar(reg, &address); if (rc) return rc; + if (width == 64) + width = 32; /* Break into two 32-bit transfers */ + *val = 0; switch(reg->space_id) { case ACPI_ADR_SPACE_SYSTEM_MEMORY: - status = acpi_os_read_memory64((acpi_physical_address) - address, val, reg->bit_width); + status = acpi_os_read_memory((acpi_physical_address) + address, &tmp, width); if (ACPI_FAILURE(status)) return -EIO; + *val = tmp; + + if (reg->bit_width == 64) { + /* Read the top 32 bits */ + status = acpi_os_read_memory((acpi_physical_address) + (address + 4), &tmp, 32); + if (ACPI_FAILURE(status)) + return -EIO; + *val |= ((u64)tmp << 32); + } break; case ACPI_ADR_SPACE_SYSTEM_IO: status = acpi_os_read_port(address, (u32 *)val, reg->bit_width); @@ -628,18 +642,31 @@ int apei_write(u64 val, struct acpi_generic_address *reg) { int rc; u64 address; + u32 width = reg->bit_width; acpi_status status; rc = apei_check_gar(reg, &address); if (rc) return rc; + if (width == 64) + width = 32; /* Break into two 32-bit transfers */ + switch (reg->space_id) { case ACPI_ADR_SPACE_SYSTEM_MEMORY: - status = acpi_os_write_memory64((acpi_physical_address) - address, val, reg->bit_width); + status = acpi_os_write_memory((acpi_physical_address) + address, ACPI_LODWORD(val), + width); if (ACPI_FAILURE(status)) return -EIO; + + if (reg->bit_width == 64) { + status = acpi_os_write_memory((acpi_physical_address) + (address + 4), + ACPI_HIDWORD(val), 32); + if (ACPI_FAILURE(status)) + return -EIO; + } break; case ACPI_ADR_SPACE_SYSTEM_IO: status = acpi_os_write_port(address, val, reg->bit_width); diff --git a/trunk/drivers/acpi/apei/einj.c b/trunk/drivers/acpi/apei/einj.c index 4ca087dd5f4f..5b898d4dda99 100644 --- a/trunk/drivers/acpi/apei/einj.c +++ b/trunk/drivers/acpi/apei/einj.c @@ -141,6 +141,21 @@ static DEFINE_MUTEX(einj_mutex); static void *einj_param; +#ifndef readq +static inline __u64 readq(volatile void __iomem *addr) +{ + return ((__u64)readl(addr+4) << 32) + readl(addr); +} +#endif + +#ifndef writeq +static inline void writeq(__u64 val, volatile void __iomem *addr) +{ + writel(val, addr); + writel(val >> 32, addr+4); +} +#endif + static void einj_exec_ctx_init(struct apei_exec_context *ctx) { apei_exec_ctx_init(ctx, einj_ins_type, ARRAY_SIZE(einj_ins_type), @@ -189,21 +204,22 @@ static int einj_timedout(u64 *t) static void check_vendor_extension(u64 paddr, struct set_error_type_with_address *v5param) { - int offset = v5param->vendor_extension; + int offset = readl(&v5param->vendor_extension); struct vendor_error_type_extension *v; u32 sbdf; if (!offset) return; - v = acpi_os_map_memory(paddr + offset, sizeof(*v)); + v = ioremap(paddr + offset, sizeof(*v)); if (!v) return; - sbdf = v->pcie_sbdf; + sbdf = readl(&v->pcie_sbdf); sprintf(vendor_dev, "%x:%x:%x.%x vendor_id=%x device_id=%x rev_id=%x\n", sbdf >> 24, (sbdf >> 16) & 0xff, (sbdf >> 11) & 0x1f, (sbdf >> 8) & 0x7, - v->vendor_id, v->device_id, v->rev_id); - acpi_os_unmap_memory(v, sizeof(*v)); + readw(&v->vendor_id), readw(&v->device_id), + readb(&v->rev_id)); + iounmap(v); } static void *einj_get_parameter_address(void) @@ -231,7 +247,7 @@ static void *einj_get_parameter_address(void) if (paddrv5) { struct set_error_type_with_address *v5param; - v5param = acpi_os_map_memory(paddrv5, sizeof(*v5param)); + v5param = ioremap(paddrv5, sizeof(*v5param)); if (v5param) { acpi5 = 1; check_vendor_extension(paddrv5, v5param); @@ -241,17 +257,17 @@ static void *einj_get_parameter_address(void) if (paddrv4) { struct einj_parameter *v4param; - v4param = acpi_os_map_memory(paddrv4, sizeof(*v4param)); + v4param = ioremap(paddrv4, sizeof(*v4param)); if (!v4param) - return NULL; - if (v4param->reserved1 || v4param->reserved2) { - acpi_os_unmap_memory(v4param, sizeof(*v4param)); - return NULL; + return 0; + if (readq(&v4param->reserved1) || readq(&v4param->reserved2)) { + iounmap(v4param); + return 0; } return v4param; } - return NULL; + return 0; } /* do sanity check to trigger table */ @@ -260,7 +276,7 @@ static int einj_check_trigger_header(struct acpi_einj_trigger *trigger_tab) if (trigger_tab->header_size != sizeof(struct acpi_einj_trigger)) return -EINVAL; if (trigger_tab->table_size > PAGE_SIZE || - trigger_tab->table_size < trigger_tab->header_size) + trigger_tab->table_size <= trigger_tab->header_size) return -EINVAL; if (trigger_tab->entry_count != (trigger_tab->table_size - trigger_tab->header_size) / @@ -324,11 +340,6 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, "The trigger error action table is invalid\n"); goto out_rel_header; } - - /* No action structures in the TRIGGER_ERROR table, nothing to do */ - if (!trigger_tab->entry_count) - goto out_rel_header; - rc = -EIO; table_size = trigger_tab->table_size; r = request_mem_region(trigger_paddr + sizeof(*trigger_tab), @@ -424,41 +435,41 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2) if (acpi5) { struct set_error_type_with_address *v5param = einj_param; - v5param->type = type; + writel(type, &v5param->type); if (type & 0x80000000) { switch (vendor_flags) { case SETWA_FLAGS_APICID: - v5param->apicid = param1; + writel(param1, &v5param->apicid); break; case SETWA_FLAGS_MEM: - v5param->memory_address = param1; - v5param->memory_address_range = param2; + writeq(param1, &v5param->memory_address); + writeq(param2, &v5param->memory_address_range); break; case SETWA_FLAGS_PCIE_SBDF: - v5param->pcie_sbdf = param1; + writel(param1, &v5param->pcie_sbdf); break; } - v5param->flags = vendor_flags; + writel(vendor_flags, &v5param->flags); } else { switch (type) { case ACPI_EINJ_PROCESSOR_CORRECTABLE: case ACPI_EINJ_PROCESSOR_UNCORRECTABLE: case ACPI_EINJ_PROCESSOR_FATAL: - v5param->apicid = param1; - v5param->flags = SETWA_FLAGS_APICID; + writel(param1, &v5param->apicid); + writel(SETWA_FLAGS_APICID, &v5param->flags); break; case ACPI_EINJ_MEMORY_CORRECTABLE: case ACPI_EINJ_MEMORY_UNCORRECTABLE: case ACPI_EINJ_MEMORY_FATAL: - v5param->memory_address = param1; - v5param->memory_address_range = param2; - v5param->flags = SETWA_FLAGS_MEM; + writeq(param1, &v5param->memory_address); + writeq(param2, &v5param->memory_address_range); + writel(SETWA_FLAGS_MEM, &v5param->flags); break; case ACPI_EINJ_PCIX_CORRECTABLE: case ACPI_EINJ_PCIX_UNCORRECTABLE: case ACPI_EINJ_PCIX_FATAL: - v5param->pcie_sbdf = param1; - v5param->flags = SETWA_FLAGS_PCIE_SBDF; + writel(param1, &v5param->pcie_sbdf); + writel(SETWA_FLAGS_PCIE_SBDF, &v5param->flags); break; } } @@ -468,8 +479,8 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2) return rc; if (einj_param) { struct einj_parameter *v4param = einj_param; - v4param->param1 = param1; - v4param->param2 = param2; + writeq(param1, &v4param->param1); + writeq(param2, &v4param->param2); } } rc = apei_exec_run(&ctx, ACPI_EINJ_EXECUTE_OPERATION); @@ -720,13 +731,8 @@ static int __init einj_init(void) return 0; err_unmap: - if (einj_param) { - acpi_size size = (acpi5) ? - sizeof(struct set_error_type_with_address) : - sizeof(struct einj_parameter); - - acpi_os_unmap_memory(einj_param, size); - } + if (einj_param) + iounmap(einj_param); apei_exec_post_unmap_gars(&ctx); err_release: apei_resources_release(&einj_resources); @@ -742,13 +748,8 @@ static void __exit einj_exit(void) { struct apei_exec_context ctx; - if (einj_param) { - acpi_size size = (acpi5) ? - sizeof(struct set_error_type_with_address) : - sizeof(struct einj_parameter); - - acpi_os_unmap_memory(einj_param, size); - } + if (einj_param) + iounmap(einj_param); einj_exec_ctx_init(&ctx); apei_exec_post_unmap_gars(&ctx); apei_resources_release(&einj_resources); diff --git a/trunk/drivers/acpi/atomicio.c b/trunk/drivers/acpi/atomicio.c new file mode 100644 index 000000000000..d4a5b3d3657b --- /dev/null +++ b/trunk/drivers/acpi/atomicio.c @@ -0,0 +1,422 @@ +/* + * atomicio.c - ACPI IO memory pre-mapping/post-unmapping, then + * accessing in atomic context. + * + * This is used for NMI handler to access IO memory area, because + * ioremap/iounmap can not be used in NMI handler. The IO memory area + * is pre-mapped in process context and accessed in NMI handler. + * + * Copyright (C) 2009-2010, Intel Corp. + * Author: Huang Ying + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ACPI_PFX "ACPI: " + +static LIST_HEAD(acpi_iomaps); +/* + * Used for mutual exclusion between writers of acpi_iomaps list, for + * synchronization between readers and writer, RCU is used. + */ +static DEFINE_SPINLOCK(acpi_iomaps_lock); + +struct acpi_iomap { + struct list_head list; + void __iomem *vaddr; + unsigned long size; + phys_addr_t paddr; + struct kref ref; +}; + +/* acpi_iomaps_lock or RCU read lock must be held before calling */ +static struct acpi_iomap *__acpi_find_iomap(phys_addr_t paddr, + unsigned long size) +{ + struct acpi_iomap *map; + + list_for_each_entry_rcu(map, &acpi_iomaps, list) { + if (map->paddr + map->size >= paddr + size && + map->paddr <= paddr) + return map; + } + return NULL; +} + +/* + * Atomic "ioremap" used by NMI handler, if the specified IO memory + * area is not pre-mapped, NULL will be returned. + * + * acpi_iomaps_lock or RCU read lock must be held before calling + */ +static void __iomem *__acpi_ioremap_fast(phys_addr_t paddr, + unsigned long size) +{ + struct acpi_iomap *map; + + map = __acpi_find_iomap(paddr, size/8); + if (map) + return map->vaddr + (paddr - map->paddr); + else + return NULL; +} + +/* acpi_iomaps_lock must be held before calling */ +static void __iomem *__acpi_try_ioremap(phys_addr_t paddr, + unsigned long size) +{ + struct acpi_iomap *map; + + map = __acpi_find_iomap(paddr, size); + if (map) { + kref_get(&map->ref); + return map->vaddr + (paddr - map->paddr); + } else + return NULL; +} + +#ifndef CONFIG_IA64 +#define should_use_kmap(pfn) page_is_ram(pfn) +#else +/* ioremap will take care of cache attributes */ +#define should_use_kmap(pfn) 0 +#endif + +static void __iomem *acpi_map(phys_addr_t pg_off, unsigned long pg_sz) +{ + unsigned long pfn; + + pfn = pg_off >> PAGE_SHIFT; + if (should_use_kmap(pfn)) { + if (pg_sz > PAGE_SIZE) + return NULL; + return (void __iomem __force *)kmap(pfn_to_page(pfn)); + } else + return ioremap(pg_off, pg_sz); +} + +static void acpi_unmap(phys_addr_t pg_off, void __iomem *vaddr) +{ + unsigned long pfn; + + pfn = pg_off >> PAGE_SHIFT; + if (page_is_ram(pfn)) + kunmap(pfn_to_page(pfn)); + else + iounmap(vaddr); +} + +/* + * Used to pre-map the specified IO memory area. First try to find + * whether the area is already pre-mapped, if it is, increase the + * reference count (in __acpi_try_ioremap) and return; otherwise, do + * the real ioremap, and add the mapping into acpi_iomaps list. + */ +static void __iomem *acpi_pre_map(phys_addr_t paddr, + unsigned long size) +{ + void __iomem *vaddr; + struct acpi_iomap *map; + unsigned long pg_sz, flags; + phys_addr_t pg_off; + + spin_lock_irqsave(&acpi_iomaps_lock, flags); + vaddr = __acpi_try_ioremap(paddr, size); + spin_unlock_irqrestore(&acpi_iomaps_lock, flags); + if (vaddr) + return vaddr; + + pg_off = paddr & PAGE_MASK; + pg_sz = ((paddr + size + PAGE_SIZE - 1) & PAGE_MASK) - pg_off; + vaddr = acpi_map(pg_off, pg_sz); + if (!vaddr) + return NULL; + map = kmalloc(sizeof(*map), GFP_KERNEL); + if (!map) + goto err_unmap; + INIT_LIST_HEAD(&map->list); + map->paddr = pg_off; + map->size = pg_sz; + map->vaddr = vaddr; + kref_init(&map->ref); + + spin_lock_irqsave(&acpi_iomaps_lock, flags); + vaddr = __acpi_try_ioremap(paddr, size); + if (vaddr) { + spin_unlock_irqrestore(&acpi_iomaps_lock, flags); + acpi_unmap(pg_off, map->vaddr); + kfree(map); + return vaddr; + } + list_add_tail_rcu(&map->list, &acpi_iomaps); + spin_unlock_irqrestore(&acpi_iomaps_lock, flags); + + return map->vaddr + (paddr - map->paddr); +err_unmap: + acpi_unmap(pg_off, vaddr); + return NULL; +} + +/* acpi_iomaps_lock must be held before calling */ +static void __acpi_kref_del_iomap(struct kref *ref) +{ + struct acpi_iomap *map; + + map = container_of(ref, struct acpi_iomap, ref); + list_del_rcu(&map->list); +} + +/* + * Used to post-unmap the specified IO memory area. The iounmap is + * done only if the reference count goes zero. + */ +static void acpi_post_unmap(phys_addr_t paddr, unsigned long size) +{ + struct acpi_iomap *map; + unsigned long flags; + int del; + + spin_lock_irqsave(&acpi_iomaps_lock, flags); + map = __acpi_find_iomap(paddr, size); + BUG_ON(!map); + del = kref_put(&map->ref, __acpi_kref_del_iomap); + spin_unlock_irqrestore(&acpi_iomaps_lock, flags); + + if (!del) + return; + + synchronize_rcu(); + acpi_unmap(map->paddr, map->vaddr); + kfree(map); +} + +/* In NMI handler, should set silent = 1 */ +static int acpi_check_gar(struct acpi_generic_address *reg, + u64 *paddr, int silent) +{ + u32 width, space_id; + + width = reg->bit_width; + space_id = reg->space_id; + /* Handle possible alignment issues */ + memcpy(paddr, ®->address, sizeof(*paddr)); + if (!*paddr) { + if (!silent) + pr_warning(FW_BUG ACPI_PFX + "Invalid physical address in GAR [0x%llx/%u/%u]\n", + *paddr, width, space_id); + return -EINVAL; + } + + if ((width != 8) && (width != 16) && (width != 32) && (width != 64)) { + if (!silent) + pr_warning(FW_BUG ACPI_PFX + "Invalid bit width in GAR [0x%llx/%u/%u]\n", + *paddr, width, space_id); + return -EINVAL; + } + + if (space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY && + space_id != ACPI_ADR_SPACE_SYSTEM_IO) { + if (!silent) + pr_warning(FW_BUG ACPI_PFX + "Invalid address space type in GAR [0x%llx/%u/%u]\n", + *paddr, width, space_id); + return -EINVAL; + } + + return 0; +} + +/* Pre-map, working on GAR */ +int acpi_pre_map_gar(struct acpi_generic_address *reg) +{ + u64 paddr; + void __iomem *vaddr; + int rc; + + if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) + return 0; + + rc = acpi_check_gar(reg, &paddr, 0); + if (rc) + return rc; + + vaddr = acpi_pre_map(paddr, reg->bit_width / 8); + if (!vaddr) + return -EIO; + + return 0; +} +EXPORT_SYMBOL_GPL(acpi_pre_map_gar); + +/* Post-unmap, working on GAR */ +int acpi_post_unmap_gar(struct acpi_generic_address *reg) +{ + u64 paddr; + int rc; + + if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) + return 0; + + rc = acpi_check_gar(reg, &paddr, 0); + if (rc) + return rc; + + acpi_post_unmap(paddr, reg->bit_width / 8); + + return 0; +} +EXPORT_SYMBOL_GPL(acpi_post_unmap_gar); + +#ifdef readq +static inline u64 read64(const volatile void __iomem *addr) +{ + return readq(addr); +} +#else +static inline u64 read64(const volatile void __iomem *addr) +{ + u64 l, h; + l = readl(addr); + h = readl(addr+4); + return l | (h << 32); +} +#endif + +/* + * Can be used in atomic (including NMI) or process context. RCU read + * lock can only be released after the IO memory area accessing. + */ +static int acpi_atomic_read_mem(u64 paddr, u64 *val, u32 width) +{ + void __iomem *addr; + + rcu_read_lock(); + addr = __acpi_ioremap_fast(paddr, width); + switch (width) { + case 8: + *val = readb(addr); + break; + case 16: + *val = readw(addr); + break; + case 32: + *val = readl(addr); + break; + case 64: + *val = read64(addr); + break; + default: + return -EINVAL; + } + rcu_read_unlock(); + + return 0; +} + +#ifdef writeq +static inline void write64(u64 val, volatile void __iomem *addr) +{ + writeq(val, addr); +} +#else +static inline void write64(u64 val, volatile void __iomem *addr) +{ + writel(val, addr); + writel(val>>32, addr+4); +} +#endif + +static int acpi_atomic_write_mem(u64 paddr, u64 val, u32 width) +{ + void __iomem *addr; + + rcu_read_lock(); + addr = __acpi_ioremap_fast(paddr, width); + switch (width) { + case 8: + writeb(val, addr); + break; + case 16: + writew(val, addr); + break; + case 32: + writel(val, addr); + break; + case 64: + write64(val, addr); + break; + default: + return -EINVAL; + } + rcu_read_unlock(); + + return 0; +} + +/* GAR accessing in atomic (including NMI) or process context */ +int acpi_atomic_read(u64 *val, struct acpi_generic_address *reg) +{ + u64 paddr; + int rc; + + rc = acpi_check_gar(reg, &paddr, 1); + if (rc) + return rc; + + *val = 0; + switch (reg->space_id) { + case ACPI_ADR_SPACE_SYSTEM_MEMORY: + return acpi_atomic_read_mem(paddr, val, reg->bit_width); + case ACPI_ADR_SPACE_SYSTEM_IO: + return acpi_os_read_port(paddr, (u32 *)val, reg->bit_width); + default: + return -EINVAL; + } +} +EXPORT_SYMBOL_GPL(acpi_atomic_read); + +int acpi_atomic_write(u64 val, struct acpi_generic_address *reg) +{ + u64 paddr; + int rc; + + rc = acpi_check_gar(reg, &paddr, 1); + if (rc) + return rc; + + switch (reg->space_id) { + case ACPI_ADR_SPACE_SYSTEM_MEMORY: + return acpi_atomic_write_mem(paddr, val, reg->bit_width); + case ACPI_ADR_SPACE_SYSTEM_IO: + return acpi_os_write_port(paddr, val, reg->bit_width); + default: + return -EINVAL; + } +} +EXPORT_SYMBOL_GPL(acpi_atomic_write); diff --git a/trunk/drivers/acpi/osl.c b/trunk/drivers/acpi/osl.c index 412a1e04a922..fcc12d842bcc 100644 --- a/trunk/drivers/acpi/osl.c +++ b/trunk/drivers/acpi/osl.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include @@ -322,37 +321,6 @@ acpi_map_lookup_virt(void __iomem *virt, acpi_size size) return NULL; } -#ifndef CONFIG_IA64 -#define should_use_kmap(pfn) page_is_ram(pfn) -#else -/* ioremap will take care of cache attributes */ -#define should_use_kmap(pfn) 0 -#endif - -static void __iomem *acpi_map(acpi_physical_address pg_off, unsigned long pg_sz) -{ - unsigned long pfn; - - pfn = pg_off >> PAGE_SHIFT; - if (should_use_kmap(pfn)) { - if (pg_sz > PAGE_SIZE) - return NULL; - return (void __iomem __force *)kmap(pfn_to_page(pfn)); - } else - return acpi_os_ioremap(pg_off, pg_sz); -} - -static void acpi_unmap(acpi_physical_address pg_off, void __iomem *vaddr) -{ - unsigned long pfn; - - pfn = pg_off >> PAGE_SHIFT; - if (page_is_ram(pfn)) - kunmap(pfn_to_page(pfn)); - else - iounmap(vaddr); -} - void __iomem *__init_refok acpi_os_map_memory(acpi_physical_address phys, acpi_size size) { @@ -385,7 +353,7 @@ acpi_os_map_memory(acpi_physical_address phys, acpi_size size) pg_off = round_down(phys, PAGE_SIZE); pg_sz = round_up(phys + size, PAGE_SIZE) - pg_off; - virt = acpi_map(pg_off, pg_sz); + virt = acpi_os_ioremap(pg_off, pg_sz); if (!virt) { mutex_unlock(&acpi_ioremap_lock); kfree(map); @@ -416,7 +384,7 @@ static void acpi_os_map_cleanup(struct acpi_ioremap *map) { if (!map->refcount) { synchronize_rcu(); - acpi_unmap(map->phys, map->virt); + iounmap(map->virt); kfree(map); } } @@ -742,67 +710,6 @@ acpi_os_read_memory(acpi_physical_address phys_addr, u32 * value, u32 width) return AE_OK; } -#ifdef readq -static inline u64 read64(const volatile void __iomem *addr) -{ - return readq(addr); -} -#else -static inline u64 read64(const volatile void __iomem *addr) -{ - u64 l, h; - l = readl(addr); - h = readl(addr+4); - return l | (h << 32); -} -#endif - -acpi_status -acpi_os_read_memory64(acpi_physical_address phys_addr, u64 *value, u32 width) -{ - void __iomem *virt_addr; - unsigned int size = width / 8; - bool unmap = false; - u64 dummy; - - rcu_read_lock(); - virt_addr = acpi_map_vaddr_lookup(phys_addr, size); - if (!virt_addr) { - rcu_read_unlock(); - virt_addr = acpi_os_ioremap(phys_addr, size); - if (!virt_addr) - return AE_BAD_ADDRESS; - unmap = true; - } - - if (!value) - value = &dummy; - - switch (width) { - case 8: - *(u8 *) value = readb(virt_addr); - break; - case 16: - *(u16 *) value = readw(virt_addr); - break; - case 32: - *(u32 *) value = readl(virt_addr); - break; - case 64: - *(u64 *) value = read64(virt_addr); - break; - default: - BUG(); - } - - if (unmap) - iounmap(virt_addr); - else - rcu_read_unlock(); - - return AE_OK; -} - acpi_status acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width) { @@ -842,61 +749,6 @@ acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width) return AE_OK; } -#ifdef writeq -static inline void write64(u64 val, volatile void __iomem *addr) -{ - writeq(val, addr); -} -#else -static inline void write64(u64 val, volatile void __iomem *addr) -{ - writel(val, addr); - writel(val>>32, addr+4); -} -#endif - -acpi_status -acpi_os_write_memory64(acpi_physical_address phys_addr, u64 value, u32 width) -{ - void __iomem *virt_addr; - unsigned int size = width / 8; - bool unmap = false; - - rcu_read_lock(); - virt_addr = acpi_map_vaddr_lookup(phys_addr, size); - if (!virt_addr) { - rcu_read_unlock(); - virt_addr = acpi_os_ioremap(phys_addr, size); - if (!virt_addr) - return AE_BAD_ADDRESS; - unmap = true; - } - - switch (width) { - case 8: - writeb(value, virt_addr); - break; - case 16: - writew(value, virt_addr); - break; - case 32: - writel(value, virt_addr); - break; - case 64: - write64(value, virt_addr); - break; - default: - BUG(); - } - - if (unmap) - iounmap(virt_addr); - else - rcu_read_unlock(); - - return AE_OK; -} - acpi_status acpi_os_read_pci_configuration(struct acpi_pci_id * pci_id, u32 reg, u64 *value, u32 width) diff --git a/trunk/drivers/acpi/processor_driver.c b/trunk/drivers/acpi/processor_driver.c index 8ae05ce18500..0034ede38710 100644 --- a/trunk/drivers/acpi/processor_driver.c +++ b/trunk/drivers/acpi/processor_driver.c @@ -84,7 +84,7 @@ static int acpi_processor_remove(struct acpi_device *device, int type); static void acpi_processor_notify(struct acpi_device *device, u32 event); static acpi_status acpi_processor_hotadd_init(struct acpi_processor *pr); static int acpi_processor_handle_eject(struct acpi_processor *pr); -static int acpi_processor_start(struct acpi_processor *pr); + static const struct acpi_device_id processor_device_ids[] = { {ACPI_PROCESSOR_OBJECT_HID, 0}, @@ -423,29 +423,10 @@ static int acpi_cpu_soft_notify(struct notifier_block *nfb, struct acpi_processor *pr = per_cpu(processors, cpu); if (action == CPU_ONLINE && pr) { - /* CPU got physically hotplugged and onlined the first time: - * Initialize missing things - */ - if (pr->flags.need_hotplug_init) { - struct cpuidle_driver *idle_driver = - cpuidle_get_driver(); - - printk(KERN_INFO "Will online and init hotplugged " - "CPU: %d\n", pr->id); - WARN(acpi_processor_start(pr), "Failed to start CPU:" - " %d\n", pr->id); - pr->flags.need_hotplug_init = 0; - if (idle_driver && !strcmp(idle_driver->name, - "intel_idle")) { - intel_idle_cpu_init(pr->id); - } - /* Normal CPU soft online event */ - } else { - acpi_processor_ppc_has_changed(pr, 0); - acpi_processor_cst_has_changed(pr); - acpi_processor_reevaluate_tstate(pr, action); - acpi_processor_tstate_has_changed(pr); - } + acpi_processor_ppc_has_changed(pr, 0); + acpi_processor_hotplug(pr); + acpi_processor_reevaluate_tstate(pr, action); + acpi_processor_tstate_has_changed(pr); } if (action == CPU_DEAD && pr) { /* invalidate the flag.throttling after one CPU is offline */ @@ -459,71 +440,6 @@ static struct notifier_block acpi_cpu_notifier = .notifier_call = acpi_cpu_soft_notify, }; -/* - * acpi_processor_start() is called by the cpu_hotplug_notifier func: - * acpi_cpu_soft_notify(). Getting it __cpuinit{data} is difficult, the - * root cause seem to be that acpi_processor_uninstall_hotplug_notify() - * is in the module_exit (__exit) func. Allowing acpi_processor_start() - * to not be in __cpuinit section, but being called from __cpuinit funcs - * via __ref looks like the right thing to do here. - */ -static __ref int acpi_processor_start(struct acpi_processor *pr) -{ - struct acpi_device *device = per_cpu(processor_device_array, pr->id); - int result = 0; - -#ifdef CONFIG_CPU_FREQ - acpi_processor_ppc_has_changed(pr, 0); -#endif - acpi_processor_get_throttling_info(pr); - acpi_processor_get_limit_info(pr); - - if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver) - acpi_processor_power_init(pr, device); - - pr->cdev = thermal_cooling_device_register("Processor", device, - &processor_cooling_ops); - if (IS_ERR(pr->cdev)) { - result = PTR_ERR(pr->cdev); - goto err_power_exit; - } - - dev_dbg(&device->dev, "registered as cooling_device%d\n", - pr->cdev->id); - - result = sysfs_create_link(&device->dev.kobj, - &pr->cdev->device.kobj, - "thermal_cooling"); - if (result) { - printk(KERN_ERR PREFIX "Create sysfs link\n"); - goto err_thermal_unregister; - } - result = sysfs_create_link(&pr->cdev->device.kobj, - &device->dev.kobj, - "device"); - if (result) { - printk(KERN_ERR PREFIX "Create sysfs link\n"); - goto err_remove_sysfs_thermal; - } - - return 0; - -err_remove_sysfs_thermal: - sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); -err_thermal_unregister: - thermal_cooling_device_unregister(pr->cdev); -err_power_exit: - acpi_processor_power_exit(pr, device); - - return result; -} - -/* - * Do not put anything in here which needs the core to be online. - * For example MSR access or setting up things which check for cpuinfo_x86 - * (cpu_data(cpu)) values, like CPU feature flags, family, model, etc. - * Such things have to be put in and set up above in acpi_processor_start() - */ static int __cpuinit acpi_processor_add(struct acpi_device *device) { struct acpi_processor *pr = NULL; @@ -579,20 +495,48 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device) goto err_free_cpumask; } - /* - * Do not start hotplugged CPUs now, but when they - * are onlined the first time - */ - if (pr->flags.need_hotplug_init) - return 0; +#ifdef CONFIG_CPU_FREQ + acpi_processor_ppc_has_changed(pr, 0); +#endif + acpi_processor_get_throttling_info(pr); + acpi_processor_get_limit_info(pr); + + if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver) + acpi_processor_power_init(pr, device); + + pr->cdev = thermal_cooling_device_register("Processor", device, + &processor_cooling_ops); + if (IS_ERR(pr->cdev)) { + result = PTR_ERR(pr->cdev); + goto err_power_exit; + } + + dev_dbg(&device->dev, "registered as cooling_device%d\n", + pr->cdev->id); - result = acpi_processor_start(pr); - if (result) + result = sysfs_create_link(&device->dev.kobj, + &pr->cdev->device.kobj, + "thermal_cooling"); + if (result) { + printk(KERN_ERR PREFIX "Create sysfs link\n"); + goto err_thermal_unregister; + } + result = sysfs_create_link(&pr->cdev->device.kobj, + &device->dev.kobj, + "device"); + if (result) { + printk(KERN_ERR PREFIX "Create sysfs link\n"); goto err_remove_sysfs; + } return 0; err_remove_sysfs: + sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); +err_thermal_unregister: + thermal_cooling_device_unregister(pr->cdev); +err_power_exit: + acpi_processor_power_exit(pr, device); sysfs_remove_link(&device->dev.kobj, "sysdev"); err_free_cpumask: free_cpumask_var(pr->throttling.shared_cpu_map); @@ -791,17 +735,6 @@ static acpi_status acpi_processor_hotadd_init(struct acpi_processor *pr) return AE_ERROR; } - /* CPU got hot-plugged, but cpu_data is not initialized yet - * Set flag to delay cpu_idle/throttling initialization - * in: - * acpi_processor_add() - * acpi_processor_get_info() - * and do it when the CPU gets online the first time - * TBD: Cleanup above functions and try to do this more elegant. - */ - printk(KERN_INFO "CPU %d got hotplugged\n", pr->id); - pr->flags.need_hotplug_init = 1; - return AE_OK; } diff --git a/trunk/drivers/acpi/sleep.c b/trunk/drivers/acpi/sleep.c index ca191ff97844..0a7ed69546ba 100644 --- a/trunk/drivers/acpi/sleep.c +++ b/trunk/drivers/acpi/sleep.c @@ -438,14 +438,6 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = { }, { .callback = init_nvs_nosave, - .ident = "Sony Vaio VPCCW29FX", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "VPCCW29FX"), - }, - }, - { - .callback = init_nvs_nosave, .ident = "Averatec AV1020-ED2", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "AVERATEC"), diff --git a/trunk/drivers/base/Makefile b/trunk/drivers/base/Makefile index 610f9997a403..2c8272dd93c4 100644 --- a/trunk/drivers/base/Makefile +++ b/trunk/drivers/base/Makefile @@ -1,6 +1,6 @@ # Makefile for the Linux device tree -obj-y := core.o bus.o dd.o syscore.o \ +obj-y := core.o sys.o bus.o dd.o syscore.o \ driver.o class.o platform.o \ cpu.o firmware.o init.o map.o devres.o \ attribute_container.o transport_class.o \ diff --git a/trunk/drivers/base/core.c b/trunk/drivers/base/core.c index 74dda4f697f9..4a67cc0c8b37 100644 --- a/trunk/drivers/base/core.c +++ b/trunk/drivers/base/core.c @@ -632,11 +632,6 @@ static void klist_children_put(struct klist_node *n) * may be used for reference counting of @dev after calling this * function. * - * All fields in @dev must be initialized by the caller to 0, except - * for those explicitly set to some other value. The simplest - * approach is to use kzalloc() to allocate the structure containing - * @dev. - * * NOTE: Use put_device() to give up your reference instead of freeing * @dev directly once you have called this function. */ @@ -935,13 +930,6 @@ int device_private_init(struct device *dev) * to the global and sibling lists for the device, then * adds it to the other relevant subsystems of the driver model. * - * Do not call this routine or device_register() more than once for - * any device structure. The driver model core is not designed to work - * with devices that get unregistered and then spring back to life. - * (Among other things, it's very hard to guarantee that all references - * to the previous incarnation of @dev have been dropped.) Allocate - * and register a fresh new struct device instead. - * * NOTE: _Never_ directly free @dev after calling this function, even * if it returned an error! Always use put_device() to give up your * reference instead. @@ -1034,7 +1022,7 @@ int device_add(struct device *dev) device_pm_add(dev); /* Notify clients of device addition. This call must come - * after dpm_sysfs_add() and before kobject_uevent(). + * after dpm_sysf_add() and before kobject_uevent(). */ if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, @@ -1102,9 +1090,6 @@ int device_add(struct device *dev) * have a clearly defined need to use and refcount the device * before it is added to the hierarchy. * - * For more information, see the kerneldoc for device_initialize() - * and device_add(). - * * NOTE: _Never_ directly free @dev after calling this function, even * if it returned an error! Always use put_device() to give up the * reference initialized in this function instead. 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/base/regmap/regmap.c b/trunk/drivers/base/regmap/regmap.c index 65558034318f..be10a4ff6609 100644 --- a/trunk/drivers/base/regmap/regmap.c +++ b/trunk/drivers/base/regmap/regmap.c @@ -284,9 +284,6 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) map->precious_reg = config->precious_reg; map->cache_type = config->cache_type; - map->cache_bypass = false; - map->cache_only = false; - ret = regcache_init(map, config); mutex_unlock(&map->lock); diff --git a/trunk/drivers/base/sys.c b/trunk/drivers/base/sys.c new file mode 100644 index 000000000000..409f5ce78829 --- /dev/null +++ b/trunk/drivers/base/sys.c @@ -0,0 +1,383 @@ +/* + * sys.c - pseudo-bus for system 'devices' (cpus, PICs, timers, etc) + * + * Copyright (c) 2002-3 Patrick Mochel + * 2002-3 Open Source Development Lab + * + * This file is released under the GPLv2 + * + * This exports a 'system' bus type. + * By default, a 'sys' bus gets added to the root of the system. There will + * always be core system devices. Devices can use sysdev_register() to + * add themselves as children of the system bus. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "base.h" + +#define to_sysdev(k) container_of(k, struct sys_device, kobj) +#define to_sysdev_attr(a) container_of(a, struct sysdev_attribute, attr) + + +static ssize_t +sysdev_show(struct kobject *kobj, struct attribute *attr, char *buffer) +{ + struct sys_device *sysdev = to_sysdev(kobj); + struct sysdev_attribute *sysdev_attr = to_sysdev_attr(attr); + + if (sysdev_attr->show) + return sysdev_attr->show(sysdev, sysdev_attr, buffer); + return -EIO; +} + + +static ssize_t +sysdev_store(struct kobject *kobj, struct attribute *attr, + const char *buffer, size_t count) +{ + struct sys_device *sysdev = to_sysdev(kobj); + struct sysdev_attribute *sysdev_attr = to_sysdev_attr(attr); + + if (sysdev_attr->store) + return sysdev_attr->store(sysdev, sysdev_attr, buffer, count); + return -EIO; +} + +static const struct sysfs_ops sysfs_ops = { + .show = sysdev_show, + .store = sysdev_store, +}; + +static struct kobj_type ktype_sysdev = { + .sysfs_ops = &sysfs_ops, +}; + + +int sysdev_create_file(struct sys_device *s, struct sysdev_attribute *a) +{ + return sysfs_create_file(&s->kobj, &a->attr); +} + + +void sysdev_remove_file(struct sys_device *s, struct sysdev_attribute *a) +{ + sysfs_remove_file(&s->kobj, &a->attr); +} + +EXPORT_SYMBOL_GPL(sysdev_create_file); +EXPORT_SYMBOL_GPL(sysdev_remove_file); + +#define to_sysdev_class(k) container_of(k, struct sysdev_class, kset.kobj) +#define to_sysdev_class_attr(a) container_of(a, \ + struct sysdev_class_attribute, attr) + +static ssize_t sysdev_class_show(struct kobject *kobj, struct attribute *attr, + char *buffer) +{ + struct sysdev_class *class = to_sysdev_class(kobj); + struct sysdev_class_attribute *class_attr = to_sysdev_class_attr(attr); + + if (class_attr->show) + return class_attr->show(class, class_attr, buffer); + return -EIO; +} + +static ssize_t sysdev_class_store(struct kobject *kobj, struct attribute *attr, + const char *buffer, size_t count) +{ + struct sysdev_class *class = to_sysdev_class(kobj); + struct sysdev_class_attribute *class_attr = to_sysdev_class_attr(attr); + + if (class_attr->store) + return class_attr->store(class, class_attr, buffer, count); + return -EIO; +} + +static const struct sysfs_ops sysfs_class_ops = { + .show = sysdev_class_show, + .store = sysdev_class_store, +}; + +static struct kobj_type ktype_sysdev_class = { + .sysfs_ops = &sysfs_class_ops, +}; + +int sysdev_class_create_file(struct sysdev_class *c, + struct sysdev_class_attribute *a) +{ + return sysfs_create_file(&c->kset.kobj, &a->attr); +} +EXPORT_SYMBOL_GPL(sysdev_class_create_file); + +void sysdev_class_remove_file(struct sysdev_class *c, + struct sysdev_class_attribute *a) +{ + sysfs_remove_file(&c->kset.kobj, &a->attr); +} +EXPORT_SYMBOL_GPL(sysdev_class_remove_file); + +extern struct kset *system_kset; + +int sysdev_class_register(struct sysdev_class *cls) +{ + int retval; + + pr_debug("Registering sysdev class '%s'\n", cls->name); + + INIT_LIST_HEAD(&cls->drivers); + memset(&cls->kset.kobj, 0x00, sizeof(struct kobject)); + cls->kset.kobj.parent = &system_kset->kobj; + cls->kset.kobj.ktype = &ktype_sysdev_class; + cls->kset.kobj.kset = system_kset; + + retval = kobject_set_name(&cls->kset.kobj, "%s", cls->name); + if (retval) + return retval; + + retval = kset_register(&cls->kset); + if (!retval && cls->attrs) + retval = sysfs_create_files(&cls->kset.kobj, + (const struct attribute **)cls->attrs); + return retval; +} + +void sysdev_class_unregister(struct sysdev_class *cls) +{ + pr_debug("Unregistering sysdev class '%s'\n", + kobject_name(&cls->kset.kobj)); + if (cls->attrs) + sysfs_remove_files(&cls->kset.kobj, + (const struct attribute **)cls->attrs); + kset_unregister(&cls->kset); +} + +EXPORT_SYMBOL_GPL(sysdev_class_register); +EXPORT_SYMBOL_GPL(sysdev_class_unregister); + +static DEFINE_MUTEX(sysdev_drivers_lock); + +/* + * @dev != NULL means that we're unwinding because some drv->add() + * failed for some reason. You need to grab sysdev_drivers_lock before + * calling this. + */ +static void __sysdev_driver_remove(struct sysdev_class *cls, + struct sysdev_driver *drv, + struct sys_device *from_dev) +{ + struct sys_device *dev = from_dev; + + list_del_init(&drv->entry); + if (!cls) + return; + + if (!drv->remove) + goto kset_put; + + if (dev) + list_for_each_entry_continue_reverse(dev, &cls->kset.list, + kobj.entry) + drv->remove(dev); + else + list_for_each_entry(dev, &cls->kset.list, kobj.entry) + drv->remove(dev); + +kset_put: + kset_put(&cls->kset); +} + +/** + * sysdev_driver_register - Register auxiliary driver + * @cls: Device class driver belongs to. + * @drv: Driver. + * + * @drv is inserted into @cls->drivers to be + * called on each operation on devices of that class. The refcount + * of @cls is incremented. + */ +int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv) +{ + struct sys_device *dev = NULL; + int err = 0; + + if (!cls) { + WARN(1, KERN_WARNING "sysdev: invalid class passed to %s!\n", + __func__); + return -EINVAL; + } + + /* Check whether this driver has already been added to a class. */ + if (drv->entry.next && !list_empty(&drv->entry)) + WARN(1, KERN_WARNING "sysdev: class %s: driver (%p) has already" + " been registered to a class, something is wrong, but " + "will forge on!\n", cls->name, drv); + + mutex_lock(&sysdev_drivers_lock); + if (cls && kset_get(&cls->kset)) { + list_add_tail(&drv->entry, &cls->drivers); + + /* If devices of this class already exist, tell the driver */ + if (drv->add) { + list_for_each_entry(dev, &cls->kset.list, kobj.entry) { + err = drv->add(dev); + if (err) + goto unwind; + } + } + } else { + err = -EINVAL; + WARN(1, KERN_ERR "%s: invalid device class\n", __func__); + } + + goto unlock; + +unwind: + __sysdev_driver_remove(cls, drv, dev); + +unlock: + mutex_unlock(&sysdev_drivers_lock); + return err; +} + +/** + * sysdev_driver_unregister - Remove an auxiliary driver. + * @cls: Class driver belongs to. + * @drv: Driver. + */ +void sysdev_driver_unregister(struct sysdev_class *cls, + struct sysdev_driver *drv) +{ + mutex_lock(&sysdev_drivers_lock); + __sysdev_driver_remove(cls, drv, NULL); + mutex_unlock(&sysdev_drivers_lock); +} +EXPORT_SYMBOL_GPL(sysdev_driver_register); +EXPORT_SYMBOL_GPL(sysdev_driver_unregister); + +/** + * sysdev_register - add a system device to the tree + * @sysdev: device in question + * + */ +int sysdev_register(struct sys_device *sysdev) +{ + int error; + struct sysdev_class *cls = sysdev->cls; + + if (!cls) + return -EINVAL; + + pr_debug("Registering sys device of class '%s'\n", + kobject_name(&cls->kset.kobj)); + + /* initialize the kobject to 0, in case it had previously been used */ + memset(&sysdev->kobj, 0x00, sizeof(struct kobject)); + + /* Make sure the kset is set */ + sysdev->kobj.kset = &cls->kset; + + /* Register the object */ + error = kobject_init_and_add(&sysdev->kobj, &ktype_sysdev, NULL, + "%s%d", kobject_name(&cls->kset.kobj), + sysdev->id); + + if (!error) { + struct sysdev_driver *drv; + + pr_debug("Registering sys device '%s'\n", + kobject_name(&sysdev->kobj)); + + mutex_lock(&sysdev_drivers_lock); + /* Generic notification is implicit, because it's that + * code that should have called us. + */ + + /* Notify class auxiliary drivers */ + list_for_each_entry(drv, &cls->drivers, entry) { + if (drv->add) + drv->add(sysdev); + } + mutex_unlock(&sysdev_drivers_lock); + kobject_uevent(&sysdev->kobj, KOBJ_ADD); + } + + return error; +} + +void sysdev_unregister(struct sys_device *sysdev) +{ + struct sysdev_driver *drv; + + mutex_lock(&sysdev_drivers_lock); + list_for_each_entry(drv, &sysdev->cls->drivers, entry) { + if (drv->remove) + drv->remove(sysdev); + } + mutex_unlock(&sysdev_drivers_lock); + + kobject_put(&sysdev->kobj); +} + +EXPORT_SYMBOL_GPL(sysdev_register); +EXPORT_SYMBOL_GPL(sysdev_unregister); + +#define to_ext_attr(x) container_of(x, struct sysdev_ext_attribute, attr) + +ssize_t sysdev_store_ulong(struct sys_device *sysdev, + struct sysdev_attribute *attr, + const char *buf, size_t size) +{ + struct sysdev_ext_attribute *ea = to_ext_attr(attr); + char *end; + unsigned long new = simple_strtoul(buf, &end, 0); + if (end == buf) + return -EINVAL; + *(unsigned long *)(ea->var) = new; + /* Always return full write size even if we didn't consume all */ + return size; +} +EXPORT_SYMBOL_GPL(sysdev_store_ulong); + +ssize_t sysdev_show_ulong(struct sys_device *sysdev, + struct sysdev_attribute *attr, + char *buf) +{ + struct sysdev_ext_attribute *ea = to_ext_attr(attr); + return snprintf(buf, PAGE_SIZE, "%lx\n", *(unsigned long *)(ea->var)); +} +EXPORT_SYMBOL_GPL(sysdev_show_ulong); + +ssize_t sysdev_store_int(struct sys_device *sysdev, + struct sysdev_attribute *attr, + const char *buf, size_t size) +{ + struct sysdev_ext_attribute *ea = to_ext_attr(attr); + char *end; + long new = simple_strtol(buf, &end, 0); + if (end == buf || new > INT_MAX || new < INT_MIN) + return -EINVAL; + *(int *)(ea->var) = new; + /* Always return full write size even if we didn't consume all */ + return size; +} +EXPORT_SYMBOL_GPL(sysdev_store_int); + +ssize_t sysdev_show_int(struct sys_device *sysdev, + struct sysdev_attribute *attr, + char *buf) +{ + struct sysdev_ext_attribute *ea = to_ext_attr(attr); + return snprintf(buf, PAGE_SIZE, "%d\n", *(int *)(ea->var)); +} +EXPORT_SYMBOL_GPL(sysdev_show_int); + diff --git a/trunk/drivers/block/rbd.c b/trunk/drivers/block/rbd.c index a6278e7e61a0..3fd31dec8c9c 100644 --- a/trunk/drivers/block/rbd.c +++ b/trunk/drivers/block/rbd.c @@ -380,7 +380,6 @@ static int rbd_get_client(struct rbd_device *rbd_dev, const char *mon_addr, rbdc = __rbd_client_find(opt); if (rbdc) { ceph_destroy_options(opt); - kfree(rbd_opts); /* using an existing client */ kref_get(&rbdc->kref); @@ -407,15 +406,15 @@ static int rbd_get_client(struct rbd_device *rbd_dev, const char *mon_addr, /* * Destroy ceph client - * - * Caller must hold node_lock. */ static void rbd_client_release(struct kref *kref) { struct rbd_client *rbdc = container_of(kref, struct rbd_client, kref); dout("rbd_release_client %p\n", rbdc); + spin_lock(&node_lock); list_del(&rbdc->node); + spin_unlock(&node_lock); ceph_destroy_client(rbdc->client); kfree(rbdc->rbd_opts); @@ -428,9 +427,7 @@ static void rbd_client_release(struct kref *kref) */ static void rbd_put_client(struct rbd_device *rbd_dev) { - spin_lock(&node_lock); kref_put(&rbd_dev->rbd_client->kref, rbd_client_release); - spin_unlock(&node_lock); rbd_dev->rbd_client = NULL; rbd_dev->client = NULL; } diff --git a/trunk/drivers/char/agp/backend.c b/trunk/drivers/char/agp/backend.c index 317c28ce8328..4b71647782d0 100644 --- a/trunk/drivers/char/agp/backend.c +++ b/trunk/drivers/char/agp/backend.c @@ -194,10 +194,10 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) err_out: if (bridge->driver->needs_scratch_page) { - struct page *page = bridge->scratch_page_page; + void *va = page_address(bridge->scratch_page_page); - bridge->driver->agp_destroy_page(page, AGP_PAGE_DESTROY_UNMAP); - bridge->driver->agp_destroy_page(page, AGP_PAGE_DESTROY_FREE); + bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_UNMAP); + bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_FREE); } if (got_gatt) bridge->driver->free_gatt_table(bridge); @@ -221,10 +221,10 @@ static void agp_backend_cleanup(struct agp_bridge_data *bridge) if (bridge->driver->agp_destroy_page && bridge->driver->needs_scratch_page) { - struct page *page = bridge->scratch_page_page; + void *va = page_address(bridge->scratch_page_page); - bridge->driver->agp_destroy_page(page, AGP_PAGE_DESTROY_UNMAP); - bridge->driver->agp_destroy_page(page, AGP_PAGE_DESTROY_FREE); + bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_UNMAP); + bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_FREE); } } diff --git a/trunk/drivers/dma/at_hdmac.c b/trunk/drivers/dma/at_hdmac.c index f4aed5fc2cb6..97f87b29b9f3 100644 --- a/trunk/drivers/dma/at_hdmac.c +++ b/trunk/drivers/dma/at_hdmac.c @@ -1343,7 +1343,7 @@ static int __init at_dma_probe(struct platform_device *pdev) tasklet_init(&atchan->tasklet, atc_tasklet, (unsigned long)atchan); - atc_enable_chan_irq(atdma, i); + atc_enable_irq(atchan); } /* set base routines */ @@ -1410,7 +1410,7 @@ static int __exit at_dma_remove(struct platform_device *pdev) struct at_dma_chan *atchan = to_at_dma_chan(chan); /* Disable interrupts */ - atc_disable_chan_irq(atdma, chan->chan_id); + atc_disable_irq(atchan); tasklet_disable(&atchan->tasklet); tasklet_kill(&atchan->tasklet); diff --git a/trunk/drivers/dma/at_hdmac_regs.h b/trunk/drivers/dma/at_hdmac_regs.h index a8d3277d60b5..dcaedfc181cf 100644 --- a/trunk/drivers/dma/at_hdmac_regs.h +++ b/trunk/drivers/dma/at_hdmac_regs.h @@ -327,27 +327,28 @@ static void atc_dump_lli(struct at_dma_chan *atchan, struct at_lli *lli) } -static void atc_setup_irq(struct at_dma *atdma, int chan_id, int on) +static void atc_setup_irq(struct at_dma_chan *atchan, int on) { - u32 ebci; + struct at_dma *atdma = to_at_dma(atchan->chan_common.device); + u32 ebci; /* enable interrupts on buffer transfer completion & error */ - ebci = AT_DMA_BTC(chan_id) - | AT_DMA_ERR(chan_id); + ebci = AT_DMA_BTC(atchan->chan_common.chan_id) + | AT_DMA_ERR(atchan->chan_common.chan_id); if (on) dma_writel(atdma, EBCIER, ebci); else dma_writel(atdma, EBCIDR, ebci); } -static void atc_enable_chan_irq(struct at_dma *atdma, int chan_id) +static inline void atc_enable_irq(struct at_dma_chan *atchan) { - atc_setup_irq(atdma, chan_id, 1); + atc_setup_irq(atchan, 1); } -static void atc_disable_chan_irq(struct at_dma *atdma, int chan_id) +static inline void atc_disable_irq(struct at_dma_chan *atchan) { - atc_setup_irq(atdma, chan_id, 0); + atc_setup_irq(atchan, 0); } diff --git a/trunk/drivers/dma/dmatest.c b/trunk/drivers/dma/dmatest.c index 24225f0fdcd8..2b8661b54eaf 100644 --- a/trunk/drivers/dma/dmatest.c +++ b/trunk/drivers/dma/dmatest.c @@ -599,7 +599,7 @@ static int dmatest_add_channel(struct dma_chan *chan) } if (dma_has_cap(DMA_PQ, dma_dev->cap_mask)) { cnt = dmatest_add_threads(dtc, DMA_PQ); - thread_count += cnt > 0 ? cnt : 0; + thread_count += cnt > 0 ?: 0; } pr_info("dmatest: Started %u threads using %s\n", diff --git a/trunk/drivers/dma/imx-sdma.c b/trunk/drivers/dma/imx-sdma.c index 8bc5acf36ee5..a8af379680c1 100644 --- a/trunk/drivers/dma/imx-sdma.c +++ b/trunk/drivers/dma/imx-sdma.c @@ -1102,13 +1102,11 @@ static int sdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, case DMA_SLAVE_CONFIG: if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { sdmac->per_address = dmaengine_cfg->src_addr; - sdmac->watermark_level = dmaengine_cfg->src_maxburst * - dmaengine_cfg->src_addr_width; + sdmac->watermark_level = dmaengine_cfg->src_maxburst; sdmac->word_size = dmaengine_cfg->src_addr_width; } else { sdmac->per_address = dmaengine_cfg->dst_addr; - sdmac->watermark_level = dmaengine_cfg->dst_maxburst * - dmaengine_cfg->dst_addr_width; + sdmac->watermark_level = dmaengine_cfg->dst_maxburst; sdmac->word_size = dmaengine_cfg->dst_addr_width; } sdmac->direction = dmaengine_cfg->direction; diff --git a/trunk/drivers/dma/shdma.c b/trunk/drivers/dma/shdma.c index 812fd76e9c18..54043cd831c8 100644 --- a/trunk/drivers/dma/shdma.c +++ b/trunk/drivers/dma/shdma.c @@ -1262,8 +1262,7 @@ static int __init sh_dmae_probe(struct platform_device *pdev) INIT_LIST_HEAD(&shdev->common.channels); - if (!pdata->slave_only) - dma_cap_set(DMA_MEMCPY, shdev->common.cap_mask); + dma_cap_set(DMA_MEMCPY, shdev->common.cap_mask); if (pdata->slave && pdata->slave_num) dma_cap_set(DMA_SLAVE, shdev->common.cap_mask); diff --git a/trunk/drivers/firewire/ohci.c b/trunk/drivers/firewire/ohci.c index 7f5f0da726da..6628feaa7622 100644 --- a/trunk/drivers/firewire/ohci.c +++ b/trunk/drivers/firewire/ohci.c @@ -263,7 +263,6 @@ static inline struct fw_ohci *fw_ohci(struct fw_card *card) static char ohci_driver_name[] = KBUILD_MODNAME; #define PCI_DEVICE_ID_AGERE_FW643 0x5901 -#define PCI_DEVICE_ID_CREATIVE_SB1394 0x4001 #define PCI_DEVICE_ID_JMICRON_JMB38X_FW 0x2380 #define PCI_DEVICE_ID_TI_TSB12LV22 0x8009 #define PCI_DEVICE_ID_TI_TSB12LV26 0x8020 @@ -290,9 +289,6 @@ static const struct { {PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_AGERE_FW643, 6, QUIRK_NO_MSI}, - {PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_SB1394, PCI_ANY_ID, - QUIRK_RESET_PACKET}, - {PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB38X_FW, PCI_ANY_ID, QUIRK_NO_MSI}, @@ -303,7 +299,7 @@ static const struct { QUIRK_NO_MSI}, {PCI_VENDOR_ID_RICOH, PCI_ANY_ID, PCI_ANY_ID, - QUIRK_CYCLE_TIMER | QUIRK_NO_MSI}, + QUIRK_CYCLE_TIMER}, {PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_TSB12LV22, PCI_ANY_ID, QUIRK_CYCLE_TIMER | QUIRK_RESET_PACKET | QUIRK_NO_1394A}, diff --git a/trunk/drivers/gpio/gpio-lpc32xx.c b/trunk/drivers/gpio/gpio-lpc32xx.c index ddfacc5ce56d..5b6948081f8f 100644 --- a/trunk/drivers/gpio/gpio-lpc32xx.c +++ b/trunk/drivers/gpio/gpio-lpc32xx.c @@ -96,7 +96,7 @@ static const char *gpio_p2_names[LPC32XX_GPIO_P2_MAX] = { }; static const char *gpio_p3_names[LPC32XX_GPIO_P3_MAX] = { - "gpio00", "gpio01", "gpio02", "gpio03", + "gpi000", "gpio01", "gpio02", "gpio03", "gpio04", "gpio05" }; diff --git a/trunk/drivers/gpio/gpio-ml-ioh.c b/trunk/drivers/gpio/gpio-ml-ioh.c index f0febe5b8221..03d6dd5dcb77 100644 --- a/trunk/drivers/gpio/gpio-ml-ioh.c +++ b/trunk/drivers/gpio/gpio-ml-ioh.c @@ -448,7 +448,6 @@ static int __devinit ioh_gpio_probe(struct pci_dev *pdev, chip->reg = chip->base; chip->ch = i; mutex_init(&chip->lock); - spin_lock_init(&chip->spinlock); ioh_gpio_setup(chip, num_ports[i]); ret = gpiochip_add(&chip->gpio); if (ret) { diff --git a/trunk/drivers/gpio/gpio-pch.c b/trunk/drivers/gpio/gpio-pch.c index e8729cc2ba2b..68fa55e86eb1 100644 --- a/trunk/drivers/gpio/gpio-pch.c +++ b/trunk/drivers/gpio/gpio-pch.c @@ -392,7 +392,6 @@ static int __devinit pch_gpio_probe(struct pci_dev *pdev, chip->reg = chip->base; pci_set_drvdata(pdev, chip); mutex_init(&chip->lock); - spin_lock_init(&chip->spinlock); pch_gpio_setup(chip); ret = gpiochip_add(&chip->gpio); if (ret) { diff --git a/trunk/drivers/gpio/gpio-samsung.c b/trunk/drivers/gpio/gpio-samsung.c index 0a79a1167a25..a7661773c052 100644 --- a/trunk/drivers/gpio/gpio-samsung.c +++ b/trunk/drivers/gpio/gpio-samsung.c @@ -2387,30 +2387,27 @@ static struct samsung_gpio_chip exynos4_gpios_3[] = { }; #if defined(CONFIG_ARCH_EXYNOS4) && defined(CONFIG_OF) -static int exynos4_gpio_xlate(struct gpio_chip *gc, - const struct of_phandle_args *gpiospec, u32 *flags) +static int exynos4_gpio_xlate(struct gpio_chip *gc, struct device_node *np, + const void *gpio_spec, u32 *flags) { - unsigned int pin; + const __be32 *gpio = gpio_spec; + const u32 n = be32_to_cpup(gpio); + unsigned int pin = gc->base + be32_to_cpu(gpio[0]); if (WARN_ON(gc->of_gpio_n_cells < 4)) return -EINVAL; - if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells)) + if (n > gc->ngpio) return -EINVAL; - if (gpiospec->args[0] > gc->ngpio) - return -EINVAL; - - pin = gc->base + gpiospec->args[0]; - - if (s3c_gpio_cfgpin(pin, S3C_GPIO_SFN(gpiospec->args[1]))) + if (s3c_gpio_cfgpin(pin, S3C_GPIO_SFN(be32_to_cpu(gpio[1])))) pr_warn("gpio_xlate: failed to set pin function\n"); - if (s3c_gpio_setpull(pin, gpiospec->args[2])) + if (s3c_gpio_setpull(pin, be32_to_cpu(gpio[2]))) pr_warn("gpio_xlate: failed to set pin pull up/down\n"); - if (s5p_gpio_set_drvstr(pin, gpiospec->args[3])) + if (s5p_gpio_set_drvstr(pin, be32_to_cpu(gpio[3]))) pr_warn("gpio_xlate: failed to set pin drive strength\n"); - return gpiospec->args[0]; + return n; } static const struct of_device_id exynos4_gpio_dt_match[] __initdata = { diff --git a/trunk/drivers/gpu/drm/drm_auth.c b/trunk/drivers/gpu/drm/drm_auth.c index ba23790450e9..3f46772f0cb2 100644 --- a/trunk/drivers/gpu/drm/drm_auth.c +++ b/trunk/drivers/gpu/drm/drm_auth.c @@ -101,7 +101,7 @@ static int drm_add_magic(struct drm_master *master, struct drm_file *priv, * Searches and unlinks the entry in drm_device::magiclist with the magic * number hash key, while holding the drm_device::struct_mutex lock. */ -int drm_remove_magic(struct drm_master *master, drm_magic_t magic) +static int drm_remove_magic(struct drm_master *master, drm_magic_t magic) { struct drm_magic_entry *pt; struct drm_hash_item *hash; @@ -136,8 +136,6 @@ int drm_remove_magic(struct drm_master *master, drm_magic_t magic) * If there is a magic number in drm_file::magic then use it, otherwise * searches an unique non-zero magic number and add it associating it with \p * file_priv. - * This ioctl needs protection by the drm_global_mutex, which protects - * struct drm_file::magic and struct drm_magic_entry::priv. */ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -175,8 +173,6 @@ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) * \return zero if authentication successed, or a negative number otherwise. * * Checks if \p file_priv is associated with the magic number passed in \arg. - * This ioctl needs protection by the drm_global_mutex, which protects - * struct drm_file::magic and struct drm_magic_entry::priv. */ int drm_authmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) diff --git a/trunk/drivers/gpu/drm/drm_fops.c b/trunk/drivers/gpu/drm/drm_fops.c index 6263b0147598..c00cf154cc0b 100644 --- a/trunk/drivers/gpu/drm/drm_fops.c +++ b/trunk/drivers/gpu/drm/drm_fops.c @@ -487,11 +487,6 @@ int drm_release(struct inode *inode, struct file *filp) (long)old_encode_dev(file_priv->minor->device), dev->open_count); - /* Release any auth tokens that might point to this file_priv, - (do that under the drm_global_mutex) */ - if (file_priv->magic) - (void) drm_remove_magic(file_priv->master, file_priv->magic); - /* if the master has gone away we can't do anything with the lock */ if (file_priv->minor->master) drm_master_release(dev, filp); diff --git a/trunk/drivers/gpu/drm/drm_gem.c b/trunk/drivers/gpu/drm/drm_gem.c index f8625e290728..396e60ce8114 100644 --- a/trunk/drivers/gpu/drm/drm_gem.c +++ b/trunk/drivers/gpu/drm/drm_gem.c @@ -140,7 +140,7 @@ int drm_gem_object_init(struct drm_device *dev, obj->dev = dev; obj->filp = shmem_file_setup("drm mm object", size, VM_NORESERVE); if (IS_ERR(obj->filp)) - return PTR_ERR(obj->filp); + return -ENOMEM; kref_init(&obj->refcount); atomic_set(&obj->handle_count, 0); 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/exynos/Kconfig b/trunk/drivers/gpu/drm/exynos/Kconfig index b9e5266c341b..f9aaa56eae07 100644 --- a/trunk/drivers/gpu/drm/exynos/Kconfig +++ b/trunk/drivers/gpu/drm/exynos/Kconfig @@ -13,7 +13,7 @@ config DRM_EXYNOS config DRM_EXYNOS_FIMD tristate "Exynos DRM FIMD" - depends on DRM_EXYNOS && !FB_S3C + depends on DRM_EXYNOS default n help Choose this option if you want to use Exynos FIMD for DRM. @@ -21,7 +21,7 @@ config DRM_EXYNOS_FIMD config DRM_EXYNOS_HDMI tristate "Exynos DRM HDMI" - depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_TV + depends on DRM_EXYNOS help Choose this option if you want to use Exynos HDMI for DRM. If M is selected, the module will be called exynos_drm_hdmi diff --git a/trunk/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/trunk/drivers/gpu/drm/exynos/exynos_drm_fimd.c index b6a737d196ae..ca83139cd309 100644 --- a/trunk/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/trunk/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -158,8 +158,7 @@ static void fimd_dpms(struct device *subdrv_dev, int mode) case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_OFF: - if (!ctx->suspended) - pm_runtime_put_sync(subdrv_dev); + pm_runtime_put_sync(subdrv_dev); break; default: DRM_DEBUG_KMS("unspecified mode %d\n", mode); @@ -735,46 +734,6 @@ static void fimd_clear_win(struct fimd_context *ctx, int win) writel(val, ctx->regs + SHADOWCON); } -static int fimd_power_on(struct fimd_context *ctx, bool enable) -{ - struct exynos_drm_subdrv *subdrv = &ctx->subdrv; - struct device *dev = subdrv->manager.dev; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (enable != false && enable != true) - return -EINVAL; - - if (enable) { - int ret; - - ret = clk_enable(ctx->bus_clk); - if (ret < 0) - return ret; - - ret = clk_enable(ctx->lcd_clk); - if (ret < 0) { - clk_disable(ctx->bus_clk); - return ret; - } - - ctx->suspended = false; - - /* if vblank was enabled status, enable it again. */ - if (test_and_clear_bit(0, &ctx->irq_flags)) - fimd_enable_vblank(dev); - - fimd_apply(dev); - } else { - clk_disable(ctx->lcd_clk); - clk_disable(ctx->bus_clk); - - ctx->suspended = true; - } - - return 0; -} - static int __devinit fimd_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -952,30 +911,39 @@ static int __devexit fimd_remove(struct platform_device *pdev) #ifdef CONFIG_PM_SLEEP static int fimd_suspend(struct device *dev) { - struct fimd_context *ctx = get_fimd_context(dev); + int ret; if (pm_runtime_suspended(dev)) return 0; - /* - * do not use pm_runtime_suspend(). if pm_runtime_suspend() is - * called here, an error would be returned by that interface - * because the usage_count of pm runtime is more than 1. - */ - return fimd_power_on(ctx, false); + ret = pm_runtime_suspend(dev); + if (ret < 0) + return ret; + + return 0; } static int fimd_resume(struct device *dev) { - struct fimd_context *ctx = get_fimd_context(dev); + int ret; - /* - * if entered to sleep when lcd panel was on, the usage_count - * of pm runtime would still be 1 so in this case, fimd driver - * should be on directly not drawing on pm runtime interface. - */ - if (!pm_runtime_suspended(dev)) - return fimd_power_on(ctx, true); + ret = pm_runtime_resume(dev); + if (ret < 0) { + DRM_ERROR("failed to resume runtime pm.\n"); + return ret; + } + + pm_runtime_disable(dev); + + ret = pm_runtime_set_active(dev); + if (ret < 0) { + DRM_ERROR("failed to active runtime pm.\n"); + pm_runtime_enable(dev); + pm_runtime_suspend(dev); + return ret; + } + + pm_runtime_enable(dev); return 0; } @@ -988,16 +956,39 @@ static int fimd_runtime_suspend(struct device *dev) DRM_DEBUG_KMS("%s\n", __FILE__); - return fimd_power_on(ctx, false); + clk_disable(ctx->lcd_clk); + clk_disable(ctx->bus_clk); + + ctx->suspended = true; + return 0; } static int fimd_runtime_resume(struct device *dev) { struct fimd_context *ctx = get_fimd_context(dev); + int ret; DRM_DEBUG_KMS("%s\n", __FILE__); - return fimd_power_on(ctx, true); + ret = clk_enable(ctx->bus_clk); + if (ret < 0) + return ret; + + ret = clk_enable(ctx->lcd_clk); + if (ret < 0) { + clk_disable(ctx->bus_clk); + return ret; + } + + ctx->suspended = false; + + /* if vblank was enabled status, enable it again. */ + if (test_and_clear_bit(0, &ctx->irq_flags)) + fimd_enable_vblank(dev); + + fimd_apply(dev); + + return 0; } #endif diff --git a/trunk/drivers/gpu/drm/exynos/exynos_hdmi.c b/trunk/drivers/gpu/drm/exynos/exynos_hdmi.c index 3429d3fd93f3..f48f7ce92f5f 100644 --- a/trunk/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/trunk/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -1116,8 +1116,8 @@ static int __devinit hdmi_probe(struct platform_device *pdev) err_iomap: iounmap(hdata->regs); err_req_region: - release_mem_region(hdata->regs_res->start, - resource_size(hdata->regs_res)); + release_resource(hdata->regs_res); + kfree(hdata->regs_res); err_resource: hdmi_resources_cleanup(hdata); err_data: @@ -1145,8 +1145,8 @@ static int __devexit hdmi_remove(struct platform_device *pdev) iounmap(hdata->regs); - release_mem_region(hdata->regs_res->start, - resource_size(hdata->regs_res)); + release_resource(hdata->regs_res); + kfree(hdata->regs_res); /* hdmiphy i2c driver */ i2c_del_driver(&hdmiphy_driver); diff --git a/trunk/drivers/gpu/drm/gma500/framebuffer.c b/trunk/drivers/gpu/drm/gma500/framebuffer.c index 830dfdd6bf15..791c0ef1a65b 100644 --- a/trunk/drivers/gpu/drm/gma500/framebuffer.c +++ b/trunk/drivers/gpu/drm/gma500/framebuffer.c @@ -113,12 +113,12 @@ static int psbfb_pan(struct fb_var_screeninfo *var, struct fb_info *info) void psbfb_suspend(struct drm_device *dev) { - struct drm_framebuffer *fb; + 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 psb_framebuffer *psbfb = to_psb_fb(fb); struct fb_info *info = psbfb->fbdev; fb_set_suspend(info, 1); drm_fb_helper_blank(FB_BLANK_POWERDOWN, info); @@ -129,12 +129,12 @@ void psbfb_suspend(struct drm_device *dev) void psbfb_resume(struct drm_device *dev) { - struct drm_framebuffer *fb; + 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 psb_framebuffer *psbfb = to_psb_fb(fb); struct fb_info *info = psbfb->fbdev; fb_set_suspend(info, 0); drm_fb_helper_blank(FB_BLANK_UNBLANK, info); diff --git a/trunk/drivers/gpu/drm/i810/i810_dma.c b/trunk/drivers/gpu/drm/i810/i810_dma.c index 7f4b4e10246e..f7c17b239833 100644 --- a/trunk/drivers/gpu/drm/i810/i810_dma.c +++ b/trunk/drivers/gpu/drm/i810/i810_dma.c @@ -886,7 +886,7 @@ static int i810_flush_queue(struct drm_device *dev) } /* Must be called with the lock held */ -static void i810_reclaim_buffers(struct drm_device *dev, +void i810_driver_reclaim_buffers(struct drm_device *dev, struct drm_file *file_priv) { struct drm_device_dma *dma = dev->dma; @@ -1223,12 +1223,17 @@ void i810_driver_preclose(struct drm_device *dev, struct drm_file *file_priv) if (dev_priv->page_flipping) i810_do_cleanup_pageflip(dev); } -} -void i810_driver_reclaim_buffers_locked(struct drm_device *dev, - struct drm_file *file_priv) -{ - i810_reclaim_buffers(dev, file_priv); + if (file_priv->master && file_priv->master->lock.hw_lock) { + drm_idlelock_take(&file_priv->master->lock); + i810_driver_reclaim_buffers(dev, file_priv); + drm_idlelock_release(&file_priv->master->lock); + } else { + /* master disappeared, clean up stuff anyway and hope nothing + * goes wrong */ + i810_driver_reclaim_buffers(dev, file_priv); + } + } int i810_driver_dma_quiescent(struct drm_device *dev) diff --git a/trunk/drivers/gpu/drm/i810/i810_drv.c b/trunk/drivers/gpu/drm/i810/i810_drv.c index ec12f7dc717a..053f1ee58393 100644 --- a/trunk/drivers/gpu/drm/i810/i810_drv.c +++ b/trunk/drivers/gpu/drm/i810/i810_drv.c @@ -63,7 +63,6 @@ static struct drm_driver driver = { .lastclose = i810_driver_lastclose, .preclose = i810_driver_preclose, .device_is_agp = i810_driver_device_is_agp, - .reclaim_buffers_locked = i810_driver_reclaim_buffers_locked, .dma_quiescent = i810_driver_dma_quiescent, .ioctls = i810_ioctls, .fops = &i810_driver_fops, diff --git a/trunk/drivers/gpu/drm/i810/i810_drv.h b/trunk/drivers/gpu/drm/i810/i810_drv.h index c9339f481795..6e0acad9e0f5 100644 --- a/trunk/drivers/gpu/drm/i810/i810_drv.h +++ b/trunk/drivers/gpu/drm/i810/i810_drv.h @@ -116,14 +116,12 @@ typedef struct drm_i810_private { /* i810_dma.c */ extern int i810_driver_dma_quiescent(struct drm_device *dev); -extern void i810_driver_reclaim_buffers_locked(struct drm_device *dev, - struct drm_file *file_priv); +void i810_driver_reclaim_buffers(struct drm_device *dev, + struct drm_file *file_priv); extern int i810_driver_load(struct drm_device *, unsigned long flags); extern void i810_driver_lastclose(struct drm_device *dev); extern void i810_driver_preclose(struct drm_device *dev, struct drm_file *file_priv); -extern void i810_driver_reclaim_buffers_locked(struct drm_device *dev, - struct drm_file *file_priv); extern int i810_driver_device_is_agp(struct drm_device *dev); extern long i810_ioctl(struct file *file, unsigned int cmd, unsigned long arg); diff --git a/trunk/drivers/gpu/drm/i915/i915_debugfs.c b/trunk/drivers/gpu/drm/i915/i915_debugfs.c index deaa657292b4..11807989f918 100644 --- a/trunk/drivers/gpu/drm/i915/i915_debugfs.c +++ b/trunk/drivers/gpu/drm/i915/i915_debugfs.c @@ -121,11 +121,11 @@ static const char *cache_level_str(int type) static void describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) { - seq_printf(m, "%p: %s%s %8zdKiB %04x %04x %d %d%s%s%s", + seq_printf(m, "%p: %s%s %8zd %04x %04x %d %d%s%s%s", &obj->base, get_pin_flag(obj), get_tiling_flag(obj), - obj->base.size / 1024, + obj->base.size, obj->base.read_domains, obj->base.write_domain, obj->last_rendering_seqno, @@ -653,7 +653,7 @@ static int i915_ringbuffer_info(struct seq_file *m, void *data) seq_printf(m, " Size : %08x\n", ring->size); seq_printf(m, " Active : %08x\n", intel_ring_get_active_head(ring)); seq_printf(m, " NOPID : %08x\n", I915_READ_NOPID(ring)); - if (IS_GEN6(dev) || IS_GEN7(dev)) { + if (IS_GEN6(dev)) { seq_printf(m, " Sync 0 : %08x\n", I915_READ_SYNC_0(ring)); seq_printf(m, " Sync 1 : %08x\n", I915_READ_SYNC_1(ring)); } @@ -1075,7 +1075,6 @@ static int gen6_drpc_info(struct seq_file *m) struct drm_device *dev = node->minor->dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 rpmodectl1, gt_core_status, rcctl1; - unsigned forcewake_count; int count=0, ret; @@ -1083,13 +1082,9 @@ static int gen6_drpc_info(struct seq_file *m) if (ret) return ret; - spin_lock_irq(&dev_priv->gt_lock); - forcewake_count = dev_priv->forcewake_count; - spin_unlock_irq(&dev_priv->gt_lock); - - if (forcewake_count) { - seq_printf(m, "RC information inaccurate because somebody " - "holds a forcewake reference \n"); + if (atomic_read(&dev_priv->forcewake_count)) { + seq_printf(m, "RC information inaccurate because userspace " + "holds a reference \n"); } else { /* NB: we cannot use forcewake, else we read the wrong values */ while (count++ < 50 && (I915_READ_NOTRACE(FORCEWAKE_ACK) & 1)) @@ -1111,7 +1106,7 @@ static int gen6_drpc_info(struct seq_file *m) seq_printf(m, "SW control enabled: %s\n", yesno((rpmodectl1 & GEN6_RP_MEDIA_MODE_MASK) == GEN6_RP_MEDIA_SW_MODE)); - seq_printf(m, "RC1e Enabled: %s\n", + seq_printf(m, "RC6 Enabled: %s\n", yesno(rcctl1 & GEN6_RC_CTL_RC1e_ENABLE)); seq_printf(m, "RC6 Enabled: %s\n", yesno(rcctl1 & GEN6_RC_CTL_RC6_ENABLE)); @@ -1403,13 +1398,9 @@ static int i915_gen6_forcewake_count_info(struct seq_file *m, void *data) struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; struct drm_i915_private *dev_priv = dev->dev_private; - unsigned forcewake_count; - - spin_lock_irq(&dev_priv->gt_lock); - forcewake_count = dev_priv->forcewake_count; - spin_unlock_irq(&dev_priv->gt_lock); - seq_printf(m, "forcewake count = %u\n", forcewake_count); + seq_printf(m, "forcewake count = %d\n", + atomic_read(&dev_priv->forcewake_count)); return 0; } @@ -1674,7 +1665,7 @@ static int i915_forcewake_open(struct inode *inode, struct file *file) struct drm_i915_private *dev_priv = dev->dev_private; int ret; - if (INTEL_INFO(dev)->gen < 6) + if (!IS_GEN6(dev)) return 0; ret = mutex_lock_interruptible(&dev->struct_mutex); @@ -1691,7 +1682,7 @@ int i915_forcewake_release(struct inode *inode, struct file *file) struct drm_device *dev = inode->i_private; struct drm_i915_private *dev_priv = dev->dev_private; - if (INTEL_INFO(dev)->gen < 6) + if (!IS_GEN6(dev)) return 0; /* diff --git a/trunk/drivers/gpu/drm/i915/i915_dma.c b/trunk/drivers/gpu/drm/i915/i915_dma.c index ddfe3d902b2a..5f4d5893e983 100644 --- a/trunk/drivers/gpu/drm/i915/i915_dma.c +++ b/trunk/drivers/gpu/drm/i915/i915_dma.c @@ -2045,7 +2045,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) if (!IS_I945G(dev) && !IS_I945GM(dev)) pci_enable_msi(dev->pdev); - spin_lock_init(&dev_priv->gt_lock); spin_lock_init(&dev_priv->irq_lock); spin_lock_init(&dev_priv->error_lock); spin_lock_init(&dev_priv->rps_lock); diff --git a/trunk/drivers/gpu/drm/i915/i915_drv.c b/trunk/drivers/gpu/drm/i915/i915_drv.c index 308f81913562..8f7187915b0d 100644 --- a/trunk/drivers/gpu/drm/i915/i915_drv.c +++ b/trunk/drivers/gpu/drm/i915/i915_drv.c @@ -368,12 +368,11 @@ void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv) */ void gen6_gt_force_wake_get(struct drm_i915_private *dev_priv) { - unsigned long irqflags; + WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex)); - spin_lock_irqsave(&dev_priv->gt_lock, irqflags); - if (dev_priv->forcewake_count++ == 0) + /* Forcewake is atomic in case we get in here without the lock */ + if (atomic_add_return(1, &dev_priv->forcewake_count) == 1) dev_priv->display.force_wake_get(dev_priv); - spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags); } void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv) @@ -393,12 +392,10 @@ void __gen6_gt_force_wake_mt_put(struct drm_i915_private *dev_priv) */ void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv) { - unsigned long irqflags; + WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex)); - spin_lock_irqsave(&dev_priv->gt_lock, irqflags); - if (--dev_priv->forcewake_count == 0) + if (atomic_dec_and_test(&dev_priv->forcewake_count)) dev_priv->display.force_wake_put(dev_priv); - spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags); } void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv) @@ -600,36 +597,9 @@ static int ironlake_do_reset(struct drm_device *dev, u8 flags) static int gen6_do_reset(struct drm_device *dev, u8 flags) { struct drm_i915_private *dev_priv = dev->dev_private; - int ret; - unsigned long irqflags; - /* Hold gt_lock across reset to prevent any register access - * with forcewake not set correctly - */ - spin_lock_irqsave(&dev_priv->gt_lock, irqflags); - - /* Reset the chip */ - - /* GEN6_GDRST is not in the gt power well, no need to check - * for fifo space for the write or forcewake the chip for - * the read - */ - I915_WRITE_NOTRACE(GEN6_GDRST, GEN6_GRDOM_FULL); - - /* Spin waiting for the device to ack the reset request */ - ret = wait_for((I915_READ_NOTRACE(GEN6_GDRST) & GEN6_GRDOM_FULL) == 0, 500); - - /* If reset with a user forcewake, try to restore, otherwise turn it off */ - if (dev_priv->forcewake_count) - dev_priv->display.force_wake_get(dev_priv); - else - dev_priv->display.force_wake_put(dev_priv); - - /* Restore fifo count */ - dev_priv->gt_fifo_count = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES); - - spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags); - return ret; + I915_WRITE(GEN6_GDRST, GEN6_GRDOM_FULL); + return wait_for((I915_READ(GEN6_GDRST) & GEN6_GRDOM_FULL) == 0, 500); } /** @@ -673,6 +643,9 @@ int i915_reset(struct drm_device *dev, u8 flags) case 7: case 6: ret = gen6_do_reset(dev, flags); + /* If reset with a user forcewake, try to restore */ + if (atomic_read(&dev_priv->forcewake_count)) + __gen6_gt_force_wake_get(dev_priv); break; case 5: ret = ironlake_do_reset(dev, flags); @@ -954,14 +927,9 @@ MODULE_LICENSE("GPL and additional rights"); u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg) { \ u##x val = 0; \ if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \ - unsigned long irqflags; \ - spin_lock_irqsave(&dev_priv->gt_lock, irqflags); \ - if (dev_priv->forcewake_count == 0) \ - dev_priv->display.force_wake_get(dev_priv); \ + gen6_gt_force_wake_get(dev_priv); \ val = read##y(dev_priv->regs + reg); \ - if (dev_priv->forcewake_count == 0) \ - dev_priv->display.force_wake_put(dev_priv); \ - spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags); \ + gen6_gt_force_wake_put(dev_priv); \ } else { \ val = read##y(dev_priv->regs + reg); \ } \ diff --git a/trunk/drivers/gpu/drm/i915/i915_drv.h b/trunk/drivers/gpu/drm/i915/i915_drv.h index 9689ca38b2b3..602bc80baabb 100644 --- a/trunk/drivers/gpu/drm/i915/i915_drv.h +++ b/trunk/drivers/gpu/drm/i915/i915_drv.h @@ -288,13 +288,7 @@ typedef struct drm_i915_private { int relative_constants_mode; void __iomem *regs; - /** gt_fifo_count and the subsequent register write are synchronized - * with dev->struct_mutex. */ - unsigned gt_fifo_count; - /** forcewake_count is protected by gt_lock */ - unsigned forcewake_count; - /** gt_lock is also taken in irq contexts. */ - struct spinlock gt_lock; + u32 gt_fifo_count; struct intel_gmbus { struct i2c_adapter adapter; @@ -747,6 +741,8 @@ typedef struct drm_i915_private { struct drm_property *broadcast_rgb_property; struct drm_property *force_audio_property; + + atomic_t forcewake_count; } drm_i915_private_t; enum i915_cache_level { diff --git a/trunk/drivers/gpu/drm/i915/i915_irq.c b/trunk/drivers/gpu/drm/i915/i915_irq.c index 5bd4361ea84d..5d433fc11ace 100644 --- a/trunk/drivers/gpu/drm/i915/i915_irq.c +++ b/trunk/drivers/gpu/drm/i915/i915_irq.c @@ -1751,8 +1751,7 @@ static void ironlake_irq_preinstall(struct drm_device *dev) INIT_WORK(&dev_priv->rps_work, gen6_pm_rps_work); I915_WRITE(HWSTAM, 0xeffe); - - if (IS_GEN6(dev)) { + if (IS_GEN6(dev) || IS_GEN7(dev)) { /* Workaround stalls observed on Sandy Bridge GPUs by * making the blitter command streamer generate a * write to the Hardware Status Page for diff --git a/trunk/drivers/gpu/drm/i915/i915_suspend.c b/trunk/drivers/gpu/drm/i915/i915_suspend.c index 2b5eb229ff2c..7886e4fb60e3 100644 --- a/trunk/drivers/gpu/drm/i915/i915_suspend.c +++ b/trunk/drivers/gpu/drm/i915/i915_suspend.c @@ -28,19 +28,14 @@ #include "drm.h" #include "i915_drm.h" #include "intel_drv.h" -#include "i915_reg.h" static bool i915_pipe_enabled(struct drm_device *dev, enum pipe pipe) { struct drm_i915_private *dev_priv = dev->dev_private; u32 dpll_reg; - /* On IVB, 3rd pipe shares PLL with another one */ - if (pipe > 1) - return false; - if (HAS_PCH_SPLIT(dev)) - dpll_reg = PCH_DPLL(pipe); + dpll_reg = (pipe == PIPE_A) ? _PCH_DPLL_A : _PCH_DPLL_B; else dpll_reg = (pipe == PIPE_A) ? _DPLL_A : _DPLL_B; @@ -827,7 +822,7 @@ int i915_save_state(struct drm_device *dev) if (IS_IRONLAKE_M(dev)) ironlake_disable_drps(dev); - if (INTEL_INFO(dev)->gen >= 6) + if (IS_GEN6(dev)) gen6_disable_rps(dev); /* Cache mode state */ @@ -886,7 +881,7 @@ int i915_restore_state(struct drm_device *dev) intel_init_emon(dev); } - if (INTEL_INFO(dev)->gen >= 6) { + if (IS_GEN6(dev)) { gen6_enable_rps(dev_priv); gen6_update_ring_freq(dev_priv); } diff --git a/trunk/drivers/gpu/drm/i915/intel_bios.h b/trunk/drivers/gpu/drm/i915/intel_bios.h index dbda6e3bdf07..8af3735e27c6 100644 --- a/trunk/drivers/gpu/drm/i915/intel_bios.h +++ b/trunk/drivers/gpu/drm/i915/intel_bios.h @@ -467,12 +467,8 @@ struct edp_link_params { struct bdb_edp { struct edp_power_seq power_seqs[16]; u32 color_depth; - struct edp_link_params link_params[16]; u32 sdrrs_msa_timing_delay; - - /* ith bit indicates enabled/disabled for (i+1)th panel */ - u16 edp_s3d_feature; - u16 edp_t3_optimization; + struct edp_link_params link_params[16]; } __attribute__ ((packed)); void intel_setup_bios(struct drm_device *dev); diff --git a/trunk/drivers/gpu/drm/i915/intel_crt.c b/trunk/drivers/gpu/drm/i915/intel_crt.c index dd729d46a61f..fee0ad02c6d0 100644 --- a/trunk/drivers/gpu/drm/i915/intel_crt.c +++ b/trunk/drivers/gpu/drm/i915/intel_crt.c @@ -24,7 +24,6 @@ * Eric Anholt */ -#include #include #include #include "drmP.h" @@ -541,24 +540,6 @@ static const struct drm_encoder_funcs intel_crt_enc_funcs = { .destroy = intel_encoder_destroy, }; -static int __init intel_no_crt_dmi_callback(const struct dmi_system_id *id) -{ - DRM_DEBUG_KMS("Skipping CRT initialization for %s\n", id->ident); - return 1; -} - -static const struct dmi_system_id intel_no_crt[] = { - { - .callback = intel_no_crt_dmi_callback, - .ident = "ACER ZGB", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ACER"), - DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"), - }, - }, - { } -}; - void intel_crt_init(struct drm_device *dev) { struct drm_connector *connector; @@ -566,10 +547,6 @@ void intel_crt_init(struct drm_device *dev) struct intel_connector *intel_connector; struct drm_i915_private *dev_priv = dev->dev_private; - /* Skip machines without VGA that falsely report hotplug events */ - if (dmi_check_system(intel_no_crt)) - return; - crt = kzalloc(sizeof(struct intel_crt), GFP_KERNEL); if (!crt) return; diff --git a/trunk/drivers/gpu/drm/i915/intel_display.c b/trunk/drivers/gpu/drm/i915/intel_display.c index 00fbff5ddd81..2a3f707caab8 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) | @@ -5809,15 +5808,12 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, if (is_lvds) { temp = I915_READ(PCH_LVDS); temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; - if (HAS_PCH_CPT(dev)) { - temp &= ~PORT_TRANS_SEL_MASK; + if (HAS_PCH_CPT(dev)) temp |= PORT_TRANS_SEL_CPT(pipe); - } else { - if (pipe == 1) - temp |= LVDS_PIPEB_SELECT; - else - temp &= ~LVDS_PIPEB_SELECT; - } + else if (pipe == 1) + temp |= LVDS_PIPEB_SELECT; + else + temp &= ~LVDS_PIPEB_SELECT; /* set the corresponsding LVDS_BORDER bit */ temp |= dev_priv->lvds_border_bits; @@ -5903,7 +5899,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 +5909,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) | @@ -9030,9 +9025,12 @@ void intel_modeset_init(struct drm_device *dev) for (i = 0; i < dev_priv->num_pipe; i++) { intel_crtc_init(dev, i); - ret = intel_plane_init(dev, i); - if (ret) - DRM_DEBUG_KMS("plane %d init failed: %d\n", i, ret); + if (HAS_PCH_SPLIT(dev)) { + ret = intel_plane_init(dev, i); + if (ret) + DRM_ERROR("plane %d init failed: %d\n", + i, ret); + } } /* Just disable it once at startup */ 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..e44191132ac4 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", @@ -716,14 +708,6 @@ static const struct dmi_system_id intel_no_lvds[] = { }, }, { - .callback = intel_no_lvds_dmi_callback, - .ident = "Clientron E830", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Clientron"), - DMI_MATCH(DMI_PRODUCT_NAME, "E830"), - }, - }, - { .callback = intel_no_lvds_dmi_callback, .ident = "Asus EeeBox PC EB1007", .matches = { diff --git a/trunk/drivers/gpu/drm/i915/intel_ringbuffer.c b/trunk/drivers/gpu/drm/i915/intel_ringbuffer.c index 1ab842c6032e..77e729d4e4f0 100644 --- a/trunk/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/trunk/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -635,19 +635,6 @@ render_ring_add_request(struct intel_ring_buffer *ring, return 0; } -static u32 -gen6_ring_get_seqno(struct intel_ring_buffer *ring) -{ - struct drm_device *dev = ring->dev; - - /* Workaround to force correct ordering between irq and seqno writes on - * ivb (and maybe also on snb) by reading from a CS register (like - * ACTHD) before reading the status page. */ - if (IS_GEN7(dev)) - intel_ring_get_active_head(ring); - return intel_read_status_page(ring, I915_GEM_HWS_INDEX); -} - static u32 ring_get_seqno(struct intel_ring_buffer *ring) { @@ -804,6 +791,17 @@ ring_add_request(struct intel_ring_buffer *ring, return 0; } +static bool +gen7_blt_ring_get_irq(struct intel_ring_buffer *ring) +{ + /* The BLT ring on IVB appears to have broken synchronization + * between the seqno write and the interrupt, so that the + * interrupt appears first. Returning false here makes + * i915_wait_request() do a polling loop, instead. + */ + return false; +} + static bool gen6_ring_get_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag) { @@ -813,12 +811,6 @@ gen6_ring_get_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag) if (!dev->irq_enabled) return false; - /* It looks like we need to prevent the gt from suspending while waiting - * for an notifiy irq, otherwise irqs seem to get lost on at least the - * blt/bsd rings on ivb. */ - if (IS_GEN7(dev)) - gen6_gt_force_wake_get(dev_priv); - spin_lock(&ring->irq_lock); if (ring->irq_refcount++ == 0) { ring->irq_mask &= ~rflag; @@ -843,9 +835,6 @@ gen6_ring_put_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag) ironlake_disable_irq(dev_priv, gflag); } spin_unlock(&ring->irq_lock); - - if (IS_GEN7(dev)) - gen6_gt_force_wake_put(dev_priv); } static bool @@ -1352,7 +1341,7 @@ static const struct intel_ring_buffer gen6_bsd_ring = { .write_tail = gen6_bsd_ring_write_tail, .flush = gen6_ring_flush, .add_request = gen6_add_request, - .get_seqno = gen6_ring_get_seqno, + .get_seqno = ring_get_seqno, .irq_get = gen6_bsd_ring_get_irq, .irq_put = gen6_bsd_ring_put_irq, .dispatch_execbuffer = gen6_ring_dispatch_execbuffer, @@ -1487,7 +1476,7 @@ static const struct intel_ring_buffer gen6_blt_ring = { .write_tail = ring_write_tail, .flush = blt_ring_flush, .add_request = gen6_add_request, - .get_seqno = gen6_ring_get_seqno, + .get_seqno = ring_get_seqno, .irq_get = blt_ring_get_irq, .irq_put = blt_ring_put_irq, .dispatch_execbuffer = gen6_ring_dispatch_execbuffer, @@ -1510,7 +1499,6 @@ int intel_init_render_ring_buffer(struct drm_device *dev) ring->flush = gen6_render_ring_flush; ring->irq_get = gen6_render_ring_get_irq; ring->irq_put = gen6_render_ring_put_irq; - ring->get_seqno = gen6_ring_get_seqno; } else if (IS_GEN5(dev)) { ring->add_request = pc_render_add_request; ring->get_seqno = pc_render_get_seqno; @@ -1589,5 +1577,8 @@ int intel_init_blt_ring_buffer(struct drm_device *dev) *ring = gen6_blt_ring; + if (IS_GEN7(dev)) + ring->irq_get = gen7_blt_ring_get_irq; + return intel_init_ring_buffer(dev, ring); } diff --git a/trunk/drivers/gpu/drm/i915/intel_sdvo.c b/trunk/drivers/gpu/drm/i915/intel_sdvo.c index e334ec33a47d..f7b9268df266 100644 --- a/trunk/drivers/gpu/drm/i915/intel_sdvo.c +++ b/trunk/drivers/gpu/drm/i915/intel_sdvo.c @@ -1066,13 +1066,15 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, /* Set the SDVO control regs. */ if (INTEL_INFO(dev)->gen >= 4) { - /* The real mode polarity is set by the SDVO commands, using - * struct intel_sdvo_dtd. */ - sdvox = SDVO_VSYNC_ACTIVE_HIGH | SDVO_HSYNC_ACTIVE_HIGH; + sdvox = 0; if (intel_sdvo->is_hdmi) sdvox |= intel_sdvo->color_range; if (INTEL_INFO(dev)->gen < 5) sdvox |= SDVO_BORDER_ENABLE; + if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) + sdvox |= SDVO_VSYNC_ACTIVE_HIGH; + if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) + sdvox |= SDVO_HSYNC_ACTIVE_HIGH; } else { sdvox = I915_READ(intel_sdvo->sdvo_reg); switch (intel_sdvo->sdvo_reg) { diff --git a/trunk/drivers/gpu/drm/i915/intel_sprite.c b/trunk/drivers/gpu/drm/i915/intel_sprite.c index 2288abf88cce..d13989fda501 100644 --- a/trunk/drivers/gpu/drm/i915/intel_sprite.c +++ b/trunk/drivers/gpu/drm/i915/intel_sprite.c @@ -466,8 +466,10 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, mutex_lock(&dev->struct_mutex); ret = intel_pin_and_fence_fb_obj(dev, obj, NULL); - if (ret) + if (ret) { + DRM_ERROR("failed to pin object\n"); goto out_unlock; + } intel_plane->obj = obj; @@ -630,8 +632,10 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe) unsigned long possible_crtcs; int ret; - if (!(IS_GEN6(dev) || IS_GEN7(dev))) + if (!(IS_GEN6(dev) || IS_GEN7(dev))) { + DRM_ERROR("new plane code only for SNB+\n"); return -ENODEV; + } intel_plane = kzalloc(sizeof(struct intel_plane), GFP_KERNEL); if (!intel_plane) diff --git a/trunk/drivers/gpu/drm/i915/intel_tv.c b/trunk/drivers/gpu/drm/i915/intel_tv.c index 1571be37ce3e..f3c6a9a8b081 100644 --- a/trunk/drivers/gpu/drm/i915/intel_tv.c +++ b/trunk/drivers/gpu/drm/i915/intel_tv.c @@ -417,7 +417,7 @@ static const struct tv_mode tv_modes[] = { { .name = "NTSC-M", .clock = 108000, - .refresh = 59940, + .refresh = 29970, .oversample = TV_OVERSAMPLE_8X, .component_only = 0, /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ @@ -460,7 +460,7 @@ static const struct tv_mode tv_modes[] = { { .name = "NTSC-443", .clock = 108000, - .refresh = 59940, + .refresh = 29970, .oversample = TV_OVERSAMPLE_8X, .component_only = 0, /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */ @@ -502,7 +502,7 @@ static const struct tv_mode tv_modes[] = { { .name = "NTSC-J", .clock = 108000, - .refresh = 59940, + .refresh = 29970, .oversample = TV_OVERSAMPLE_8X, .component_only = 0, @@ -545,7 +545,7 @@ static const struct tv_mode tv_modes[] = { { .name = "PAL-M", .clock = 108000, - .refresh = 59940, + .refresh = 29970, .oversample = TV_OVERSAMPLE_8X, .component_only = 0, @@ -589,7 +589,7 @@ static const struct tv_mode tv_modes[] = { /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */ .name = "PAL-N", .clock = 108000, - .refresh = 50000, + .refresh = 25000, .oversample = TV_OVERSAMPLE_8X, .component_only = 0, @@ -634,7 +634,7 @@ static const struct tv_mode tv_modes[] = { /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */ .name = "PAL", .clock = 108000, - .refresh = 50000, + .refresh = 25000, .oversample = TV_OVERSAMPLE_8X, .component_only = 0, @@ -673,6 +673,78 @@ static const struct tv_mode tv_modes[] = { .filter_table = filter_table, }, + { + .name = "480p@59.94Hz", + .clock = 107520, + .refresh = 59940, + .oversample = TV_OVERSAMPLE_4X, + .component_only = 1, + + .hsync_end = 64, .hblank_end = 122, + .hblank_start = 842, .htotal = 857, + + .progressive = true, .trilevel_sync = false, + + .vsync_start_f1 = 12, .vsync_start_f2 = 12, + .vsync_len = 12, + + .veq_ena = false, + + .vi_end_f1 = 44, .vi_end_f2 = 44, + .nbr_end = 479, + + .burst_ena = false, + + .filter_table = filter_table, + }, + { + .name = "480p@60Hz", + .clock = 107520, + .refresh = 60000, + .oversample = TV_OVERSAMPLE_4X, + .component_only = 1, + + .hsync_end = 64, .hblank_end = 122, + .hblank_start = 842, .htotal = 856, + + .progressive = true, .trilevel_sync = false, + + .vsync_start_f1 = 12, .vsync_start_f2 = 12, + .vsync_len = 12, + + .veq_ena = false, + + .vi_end_f1 = 44, .vi_end_f2 = 44, + .nbr_end = 479, + + .burst_ena = false, + + .filter_table = filter_table, + }, + { + .name = "576p", + .clock = 107520, + .refresh = 50000, + .oversample = TV_OVERSAMPLE_4X, + .component_only = 1, + + .hsync_end = 64, .hblank_end = 139, + .hblank_start = 859, .htotal = 863, + + .progressive = true, .trilevel_sync = false, + + .vsync_start_f1 = 10, .vsync_start_f2 = 10, + .vsync_len = 10, + + .veq_ena = false, + + .vi_end_f1 = 48, .vi_end_f2 = 48, + .nbr_end = 575, + + .burst_ena = false, + + .filter_table = filter_table, + }, { .name = "720p@60Hz", .clock = 148800, @@ -697,6 +769,30 @@ static const struct tv_mode tv_modes[] = { .filter_table = filter_table, }, + { + .name = "720p@59.94Hz", + .clock = 148800, + .refresh = 59940, + .oversample = TV_OVERSAMPLE_2X, + .component_only = 1, + + .hsync_end = 80, .hblank_end = 300, + .hblank_start = 1580, .htotal = 1651, + + .progressive = true, .trilevel_sync = true, + + .vsync_start_f1 = 10, .vsync_start_f2 = 10, + .vsync_len = 10, + + .veq_ena = false, + + .vi_end_f1 = 29, .vi_end_f2 = 29, + .nbr_end = 719, + + .burst_ena = false, + + .filter_table = filter_table, + }, { .name = "720p@50Hz", .clock = 148800, @@ -725,7 +821,7 @@ static const struct tv_mode tv_modes[] = { { .name = "1080i@50Hz", .clock = 148800, - .refresh = 50000, + .refresh = 25000, .oversample = TV_OVERSAMPLE_2X, .component_only = 1, @@ -751,7 +847,7 @@ static const struct tv_mode tv_modes[] = { { .name = "1080i@60Hz", .clock = 148800, - .refresh = 60000, + .refresh = 30000, .oversample = TV_OVERSAMPLE_2X, .component_only = 1, @@ -772,6 +868,32 @@ static const struct tv_mode tv_modes[] = { .burst_ena = false, + .filter_table = filter_table, + }, + { + .name = "1080i@59.94Hz", + .clock = 148800, + .refresh = 29970, + .oversample = TV_OVERSAMPLE_2X, + .component_only = 1, + + .hsync_end = 88, .hblank_end = 235, + .hblank_start = 2155, .htotal = 2201, + + .progressive = false, .trilevel_sync = true, + + .vsync_start_f1 = 4, .vsync_start_f2 = 5, + .vsync_len = 10, + + .veq_ena = true, .veq_start_f1 = 4, + .veq_start_f2 = 4, .veq_len = 10, + + + .vi_end_f1 = 21, .vi_end_f2 = 22, + .nbr_end = 539, + + .burst_ena = false, + .filter_table = filter_table, }, }; diff --git a/trunk/drivers/gpu/drm/nouveau/nouveau_bios.h b/trunk/drivers/gpu/drm/nouveau/nouveau_bios.h index a37c31e358aa..1e382ad5a2b8 100644 --- a/trunk/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/trunk/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -54,10 +54,9 @@ struct bit_entry { int bit_table(struct drm_device *, u8 id, struct bit_entry *); enum dcb_gpio_tag { - DCB_GPIO_PANEL_POWER = 0x01, - DCB_GPIO_TVDAC0 = 0x0c, + DCB_GPIO_TVDAC0 = 0xc, DCB_GPIO_TVDAC1 = 0x2d, - DCB_GPIO_PWM_FAN = 0x09, + DCB_GPIO_PWM_FAN = 0x9, DCB_GPIO_FAN_SENSE = 0x3d, DCB_GPIO_UNUSED = 0xff }; diff --git a/trunk/drivers/gpu/drm/nouveau/nouveau_bo.c b/trunk/drivers/gpu/drm/nouveau/nouveau_bo.c index ec54364ac828..724b41a2b9e9 100644 --- a/trunk/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/trunk/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -812,10 +812,6 @@ nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem) struct nouveau_bo *nvbo = nouveau_bo(bo); struct nouveau_vma *vma; - /* ttm can now (stupidly) pass the driver bos it didn't create... */ - if (bo->destroy != nouveau_bo_del_ttm) - return; - list_for_each_entry(vma, &nvbo->vma_list, head) { if (new_mem && new_mem->mem_type == TTM_PL_VRAM) { nouveau_vm_map(vma, new_mem->mm_node); diff --git a/trunk/drivers/gpu/drm/nouveau/nouveau_display.c b/trunk/drivers/gpu/drm/nouveau/nouveau_display.c index 795a9e3c990a..3cb52bc52b21 100644 --- a/trunk/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/trunk/drivers/gpu/drm/nouveau/nouveau_display.c @@ -219,16 +219,6 @@ nouveau_display_init(struct drm_device *dev) if (ret) return ret; - /* power on internal panel if it's not already. the init tables of - * some vbios default this to off for some reason, causing the - * panel to not work after resume - */ - if (nouveau_gpio_func_get(dev, DCB_GPIO_PANEL_POWER) == 0) { - nouveau_gpio_func_set(dev, DCB_GPIO_PANEL_POWER, true); - msleep(300); - } - - /* enable polling for external displays */ drm_kms_helper_poll_enable(dev); /* enable hotplug interrupts */ diff --git a/trunk/drivers/gpu/drm/nouveau/nouveau_drv.c b/trunk/drivers/gpu/drm/nouveau/nouveau_drv.c index 81d7962e7252..e4a7cfe7898d 100644 --- a/trunk/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/trunk/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -124,7 +124,7 @@ MODULE_PARM_DESC(ctxfw, "Use external HUB/GPC ucode (fermi)\n"); int nouveau_ctxfw; module_param_named(ctxfw, nouveau_ctxfw, int, 0400); -MODULE_PARM_DESC(mxmdcb, "Santise DCB table according to MXM-SIS\n"); +MODULE_PARM_DESC(ctxfw, "Santise DCB table according to MXM-SIS\n"); int nouveau_mxmdcb = 1; module_param_named(mxmdcb, nouveau_mxmdcb, int, 0400); diff --git a/trunk/drivers/gpu/drm/nouveau/nouveau_gem.c b/trunk/drivers/gpu/drm/nouveau/nouveau_gem.c index 7ce3fde40743..5f0bc57fdaab 100644 --- a/trunk/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/trunk/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -379,25 +379,6 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv, return 0; } -static int -validate_sync(struct nouveau_channel *chan, struct nouveau_bo *nvbo) -{ - struct nouveau_fence *fence = NULL; - int ret = 0; - - spin_lock(&nvbo->bo.bdev->fence_lock); - if (nvbo->bo.sync_obj) - fence = nouveau_fence_ref(nvbo->bo.sync_obj); - spin_unlock(&nvbo->bo.bdev->fence_lock); - - if (fence) { - ret = nouveau_fence_sync(fence, chan); - nouveau_fence_unref(&fence); - } - - return ret; -} - static int validate_list(struct nouveau_channel *chan, struct list_head *list, struct drm_nouveau_gem_pushbuf_bo *pbbo, uint64_t user_pbbo_ptr) @@ -412,7 +393,7 @@ validate_list(struct nouveau_channel *chan, struct list_head *list, list_for_each_entry(nvbo, list, entry) { struct drm_nouveau_gem_pushbuf_bo *b = &pbbo[nvbo->pbbo_index]; - ret = validate_sync(chan, nvbo); + ret = nouveau_fence_sync(nvbo->bo.sync_obj, chan); if (unlikely(ret)) { NV_ERROR(dev, "fail pre-validate sync\n"); return ret; @@ -435,7 +416,7 @@ validate_list(struct nouveau_channel *chan, struct list_head *list, return ret; } - ret = validate_sync(chan, nvbo); + ret = nouveau_fence_sync(nvbo->bo.sync_obj, chan); if (unlikely(ret)) { NV_ERROR(dev, "fail post-validate sync\n"); return ret; diff --git a/trunk/drivers/gpu/drm/nouveau/nouveau_mxm.c b/trunk/drivers/gpu/drm/nouveau/nouveau_mxm.c index e5a64f0f4cb7..8bccddf4eff0 100644 --- a/trunk/drivers/gpu/drm/nouveau/nouveau_mxm.c +++ b/trunk/drivers/gpu/drm/nouveau/nouveau_mxm.c @@ -656,16 +656,7 @@ nouveau_mxm_init(struct drm_device *dev) if (mxm_shadow(dev, mxm[0])) { MXM_MSG(dev, "failed to locate valid SIS\n"); -#if 0 - /* we should, perhaps, fall back to some kind of limited - * mode here if the x86 vbios hasn't already done the - * work for us (so we prevent loading with completely - * whacked vbios tables). - */ return -EINVAL; -#else - return 0; -#endif } MXM_MSG(dev, "MXMS Version %d.%d\n", diff --git a/trunk/drivers/gpu/drm/nouveau/nv50_pm.c b/trunk/drivers/gpu/drm/nouveau/nv50_pm.c index ec5481dfcd82..03937212e9d8 100644 --- a/trunk/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/trunk/drivers/gpu/drm/nouveau/nv50_pm.c @@ -495,9 +495,9 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nv50_pm_state *info; struct pll_lims pll; - int clk, ret = -EINVAL; + int ret = -EINVAL; int N, M, P1, P2; - u32 out; + u32 clk, out; if (dev_priv->chipset == 0xaa || dev_priv->chipset == 0xac) diff --git a/trunk/drivers/gpu/drm/radeon/atombios_crtc.c b/trunk/drivers/gpu/drm/radeon/atombios_crtc.c index 742f17f009a9..0fda830ef806 100644 --- a/trunk/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/trunk/drivers/gpu/drm/radeon/atombios_crtc.c @@ -355,12 +355,15 @@ static void atombios_crtc_set_timing(struct drm_crtc *crtc, atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); } -static void atombios_disable_ss(struct radeon_device *rdev, int pll_id) +static void atombios_disable_ss(struct drm_crtc *crtc) { + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; u32 ss_cntl; if (ASIC_IS_DCE4(rdev)) { - switch (pll_id) { + switch (radeon_crtc->pll_id) { case ATOM_PPLL1: ss_cntl = RREG32(EVERGREEN_P1PLL_SS_CNTL); ss_cntl &= ~EVERGREEN_PxPLL_SS_EN; @@ -376,7 +379,7 @@ static void atombios_disable_ss(struct radeon_device *rdev, int pll_id) return; } } else if (ASIC_IS_AVIVO(rdev)) { - switch (pll_id) { + switch (radeon_crtc->pll_id) { case ATOM_PPLL1: ss_cntl = RREG32(AVIVO_P1PLL_INT_SS_CNTL); ss_cntl &= ~1; @@ -403,11 +406,13 @@ union atom_enable_ss { ENABLE_SPREAD_SPECTRUM_ON_PPLL_V3 v3; }; -static void atombios_crtc_program_ss(struct radeon_device *rdev, +static void atombios_crtc_program_ss(struct drm_crtc *crtc, int enable, int pll_id, struct radeon_atom_ss *ss) { + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; int index = GetIndexIntoMasterTable(COMMAND, EnableSpreadSpectrumOnPPLL); union atom_enable_ss args; @@ -474,7 +479,7 @@ static void atombios_crtc_program_ss(struct radeon_device *rdev, } else if (ASIC_IS_AVIVO(rdev)) { if ((enable == ATOM_DISABLE) || (ss->percentage == 0) || (ss->type & ATOM_EXTERNAL_SS_MASK)) { - atombios_disable_ss(rdev, pll_id); + atombios_disable_ss(crtc); return; } args.lvds_ss_2.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage); @@ -486,7 +491,7 @@ static void atombios_crtc_program_ss(struct radeon_device *rdev, } else { if ((enable == ATOM_DISABLE) || (ss->percentage == 0) || (ss->type & ATOM_EXTERNAL_SS_MASK)) { - atombios_disable_ss(rdev, pll_id); + atombios_disable_ss(crtc); return; } args.lvds_ss.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage); @@ -518,7 +523,6 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, int encoder_mode = 0; u32 dp_clock = mode->clock; int bpc = 8; - bool is_duallink = false; /* reset the pll flags */ pll->flags = 0; @@ -553,7 +557,6 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, if (connector && connector->display_info.bpc) bpc = connector->display_info.bpc; encoder_mode = atombios_get_encoder_mode(encoder); - is_duallink = radeon_dig_monitor_is_duallink(encoder, mode->clock); if ((radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) || (radeon_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE)) { if (connector) { @@ -649,7 +652,7 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, if (dig->coherent_mode) args.v3.sInput.ucDispPllConfig |= DISPPLL_CONFIG_COHERENT_MODE; - if (is_duallink) + if (mode->clock > 165000) args.v3.sInput.ucDispPllConfig |= DISPPLL_CONFIG_DUAL_LINK; } @@ -699,9 +702,11 @@ union set_pixel_clock { /* on DCE5, make sure the voltage is high enough to support the * required disp clk. */ -static void atombios_crtc_set_dcpll(struct radeon_device *rdev, +static void atombios_crtc_set_dcpll(struct drm_crtc *crtc, u32 dispclk) { + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; u8 frev, crev; int index; union set_pixel_clock args; @@ -991,7 +996,7 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode radeon_compute_pll_legacy(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div, &ref_div, &post_div); - atombios_crtc_program_ss(rdev, ATOM_DISABLE, radeon_crtc->pll_id, &ss); + atombios_crtc_program_ss(crtc, ATOM_DISABLE, radeon_crtc->pll_id, &ss); atombios_crtc_program_pll(crtc, radeon_crtc->crtc_id, radeon_crtc->pll_id, encoder_mode, radeon_encoder->encoder_id, mode->clock, @@ -1014,7 +1019,7 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode ss.step = step_size; } - atombios_crtc_program_ss(rdev, ATOM_ENABLE, radeon_crtc->pll_id, &ss); + atombios_crtc_program_ss(crtc, ATOM_ENABLE, radeon_crtc->pll_id, &ss); } } @@ -1184,7 +1189,7 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, WREG32(EVERGREEN_GRPH_ENABLE + radeon_crtc->crtc_offset, 1); WREG32(EVERGREEN_DESKTOP_HEIGHT + radeon_crtc->crtc_offset, - target_fb->height); + crtc->mode.vdisplay); x &= ~3; y &= ~1; WREG32(EVERGREEN_VIEWPORT_START + radeon_crtc->crtc_offset, @@ -1353,7 +1358,7 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc, WREG32(AVIVO_D1GRPH_ENABLE + radeon_crtc->crtc_offset, 1); WREG32(AVIVO_D1MODE_DESKTOP_HEIGHT + radeon_crtc->crtc_offset, - target_fb->height); + crtc->mode.vdisplay); x &= ~3; y &= ~1; WREG32(AVIVO_D1MODE_VIEWPORT_START + radeon_crtc->crtc_offset, @@ -1489,24 +1494,6 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc) } -void radeon_atom_dcpll_init(struct radeon_device *rdev) -{ - /* always set DCPLL */ - if (ASIC_IS_DCE4(rdev)) { - struct radeon_atom_ss ss; - bool ss_enabled = radeon_atombios_get_asic_ss_info(rdev, &ss, - ASIC_INTERNAL_SS_ON_DCPLL, - rdev->clock.default_dispclk); - if (ss_enabled) - atombios_crtc_program_ss(rdev, ATOM_DISABLE, ATOM_DCPLL, &ss); - /* XXX: DCE5, make sure voltage, dispclk is high enough */ - atombios_crtc_set_dcpll(rdev, rdev->clock.default_dispclk); - if (ss_enabled) - atombios_crtc_program_ss(rdev, ATOM_ENABLE, ATOM_DCPLL, &ss); - } - -} - int atombios_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, @@ -1528,6 +1515,19 @@ int atombios_crtc_mode_set(struct drm_crtc *crtc, } } + /* always set DCPLL */ + if (ASIC_IS_DCE4(rdev)) { + struct radeon_atom_ss ss; + bool ss_enabled = radeon_atombios_get_asic_ss_info(rdev, &ss, + ASIC_INTERNAL_SS_ON_DCPLL, + rdev->clock.default_dispclk); + if (ss_enabled) + atombios_crtc_program_ss(crtc, ATOM_DISABLE, ATOM_DCPLL, &ss); + /* XXX: DCE5, make sure voltage, dispclk is high enough */ + atombios_crtc_set_dcpll(crtc, rdev->clock.default_dispclk); + if (ss_enabled) + atombios_crtc_program_ss(crtc, ATOM_ENABLE, ATOM_DCPLL, &ss); + } atombios_crtc_set_pll(crtc, adjusted_mode); if (ASIC_IS_DCE4(rdev)) diff --git a/trunk/drivers/gpu/drm/radeon/atombios_dp.c b/trunk/drivers/gpu/drm/radeon/atombios_dp.c index 552b436451fd..6fb335a4fdda 100644 --- a/trunk/drivers/gpu/drm/radeon/atombios_dp.c +++ b/trunk/drivers/gpu/drm/radeon/atombios_dp.c @@ -549,8 +549,8 @@ bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector) return false; } -int radeon_dp_get_panel_mode(struct drm_encoder *encoder, - struct drm_connector *connector) +static void radeon_dp_set_panel_mode(struct drm_encoder *encoder, + struct drm_connector *connector) { struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; @@ -558,33 +558,28 @@ int radeon_dp_get_panel_mode(struct drm_encoder *encoder, int panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE; if (!ASIC_IS_DCE4(rdev)) - return panel_mode; + return; if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) == ENCODER_OBJECT_ID_NUTMEG) panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE; else if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) == - ENCODER_OBJECT_ID_TRAVIS) { - u8 id[6]; - int i; - for (i = 0; i < 6; i++) - id[i] = radeon_read_dpcd_reg(radeon_connector, 0x503 + i); - if (id[0] == 0x73 && - id[1] == 0x69 && - id[2] == 0x76 && - id[3] == 0x61 && - id[4] == 0x72 && - id[5] == 0x54) - panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE; - else - panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; - } else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { + ENCODER_OBJECT_ID_TRAVIS) + panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; + else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { u8 tmp = radeon_read_dpcd_reg(radeon_connector, DP_EDP_CONFIGURATION_CAP); if (tmp & 1) panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; } - return panel_mode; + atombios_dig_encoder_setup(encoder, + ATOM_ENCODER_CMD_SETUP_PANEL_MODE, + panel_mode); + + if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) && + (panel_mode == DP_PANEL_MODE_INTERNAL_DP2_MODE)) { + radeon_write_dpcd_reg(radeon_connector, DP_EDP_CONFIGURATION_SET, 1); + } } void radeon_dp_set_link_config(struct drm_connector *connector, @@ -722,8 +717,6 @@ static void radeon_dp_set_tp(struct radeon_dp_link_train_info *dp_info, int tp) static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info) { - struct radeon_encoder *radeon_encoder = to_radeon_encoder(dp_info->encoder); - struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; u8 tmp; /* power up the sink */ @@ -739,10 +732,7 @@ static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info) radeon_write_dpcd_reg(dp_info->radeon_connector, DP_DOWNSPREAD_CTRL, 0); - if ((dp_info->connector->connector_type == DRM_MODE_CONNECTOR_eDP) && - (dig->panel_mode == DP_PANEL_MODE_INTERNAL_DP2_MODE)) { - radeon_write_dpcd_reg(dp_info->radeon_connector, DP_EDP_CONFIGURATION_SET, 1); - } + radeon_dp_set_panel_mode(dp_info->encoder, dp_info->connector); /* set the lane count on the sink */ tmp = dp_info->dp_lane_count; diff --git a/trunk/drivers/gpu/drm/radeon/atombios_encoders.c b/trunk/drivers/gpu/drm/radeon/atombios_encoders.c index b88c4608731b..f1f06ca9f1f5 100644 --- a/trunk/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/trunk/drivers/gpu/drm/radeon/atombios_encoders.c @@ -57,6 +57,22 @@ static inline bool radeon_encoder_is_digital(struct drm_encoder *encoder) } } +static struct drm_connector * +radeon_get_connector_for_encoder_init(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_connector *connector; + struct radeon_connector *radeon_connector; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + radeon_connector = to_radeon_connector(connector); + if (radeon_encoder->devices & radeon_connector->devices) + return connector; + } + return NULL; +} + static bool radeon_atom_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -237,7 +253,7 @@ atombios_dvo_setup(struct drm_encoder *encoder, int action) /* R4xx, R5xx */ args.ext_tmds.sXTmdsEncoder.ucEnable = action; - if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + if (radeon_encoder->pixel_clock > 165000) args.ext_tmds.sXTmdsEncoder.ucMisc |= PANEL_ENCODER_MISC_DUAL; args.ext_tmds.sXTmdsEncoder.ucMisc |= ATOM_PANEL_MISC_888RGB; @@ -249,7 +265,7 @@ atombios_dvo_setup(struct drm_encoder *encoder, int action) /* DFP1, CRT1, TV1 depending on the type of port */ args.dvo.sDVOEncoder.ucDeviceType = ATOM_DEVICE_DFP1_INDEX; - if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + if (radeon_encoder->pixel_clock > 165000) args.dvo.sDVOEncoder.usDevAttr.sDigAttrib.ucAttribute |= PANEL_ENCODER_MISC_DUAL; break; case 3: @@ -333,7 +349,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action) } else { if (dig->linkb) args.v1.ucMisc |= PANEL_ENCODER_MISC_TMDS_LINKB; - if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + if (radeon_encoder->pixel_clock > 165000) args.v1.ucMisc |= PANEL_ENCODER_MISC_DUAL; /*if (pScrn->rgbBits == 8) */ args.v1.ucMisc |= ATOM_PANEL_MISC_888RGB; @@ -372,7 +388,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action) } else { if (dig->linkb) args.v2.ucMisc |= PANEL_ENCODER_MISC_TMDS_LINKB; - if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + if (radeon_encoder->pixel_clock > 165000) args.v2.ucMisc |= PANEL_ENCODER_MISC_DUAL; } break; @@ -416,7 +432,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder) switch (connector->connector_type) { case DRM_MODE_CONNECTOR_DVII: case DRM_MODE_CONNECTOR_HDMIB: /* HDMI-B is basically DL-DVI; analog works fine */ - if (drm_detect_hdmi_monitor(radeon_connector->edid) && + if (drm_detect_monitor_audio(radeon_connector->edid) && radeon_audio) return ATOM_ENCODER_MODE_HDMI; else if (radeon_connector->use_digital) @@ -427,7 +443,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder) case DRM_MODE_CONNECTOR_DVID: case DRM_MODE_CONNECTOR_HDMIA: default: - if (drm_detect_hdmi_monitor(radeon_connector->edid) && + if (drm_detect_monitor_audio(radeon_connector->edid) && radeon_audio) return ATOM_ENCODER_MODE_HDMI; else @@ -441,7 +457,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder) if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) return ATOM_ENCODER_MODE_DP; - else if (drm_detect_hdmi_monitor(radeon_connector->edid) && + else if (drm_detect_monitor_audio(radeon_connector->edid) && radeon_audio) return ATOM_ENCODER_MODE_HDMI; else @@ -571,7 +587,7 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mo if (ENCODER_MODE_IS_DP(args.v1.ucEncoderMode)) args.v1.ucLaneNum = dp_lane_count; - else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + else if (radeon_encoder->pixel_clock > 165000) args.v1.ucLaneNum = 8; else args.v1.ucLaneNum = 4; @@ -606,7 +622,7 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mo if (ENCODER_MODE_IS_DP(args.v1.ucEncoderMode)) args.v3.ucLaneNum = dp_lane_count; - else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + else if (radeon_encoder->pixel_clock > 165000) args.v3.ucLaneNum = 8; else args.v3.ucLaneNum = 4; @@ -646,7 +662,7 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mo if (ENCODER_MODE_IS_DP(args.v1.ucEncoderMode)) args.v4.ucLaneNum = dp_lane_count; - else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + else if (radeon_encoder->pixel_clock > 165000) args.v4.ucLaneNum = 8; else args.v4.ucLaneNum = 4; @@ -790,7 +806,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t if (is_dp) args.v1.usPixelClock = cpu_to_le16(dp_clock / 10); - else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + else if (radeon_encoder->pixel_clock > 165000) args.v1.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10); else args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); @@ -805,8 +821,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t if ((rdev->flags & RADEON_IS_IGP) && (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_UNIPHY)) { - if (is_dp || - !radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) { + if (is_dp || (radeon_encoder->pixel_clock <= 165000)) { if (igp_lane_info & 0x1) args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_3; else if (igp_lane_info & 0x2) @@ -833,7 +848,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { if (dig->coherent_mode) args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT; - if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + if (radeon_encoder->pixel_clock > 165000) args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_8LANE_LINK; } break; @@ -848,7 +863,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t if (is_dp) args.v2.usPixelClock = cpu_to_le16(dp_clock / 10); - else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + else if (radeon_encoder->pixel_clock > 165000) args.v2.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10); else args.v2.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); @@ -876,7 +891,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t } else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { if (dig->coherent_mode) args.v2.acConfig.fCoherentMode = 1; - if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + if (radeon_encoder->pixel_clock > 165000) args.v2.acConfig.fDualLinkConnector = 1; } break; @@ -891,7 +906,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t if (is_dp) args.v3.usPixelClock = cpu_to_le16(dp_clock / 10); - else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + else if (radeon_encoder->pixel_clock > 165000) args.v3.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10); else args.v3.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); @@ -899,7 +914,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t if (is_dp) args.v3.ucLaneNum = dp_lane_count; - else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + else if (radeon_encoder->pixel_clock > 165000) args.v3.ucLaneNum = 8; else args.v3.ucLaneNum = 4; @@ -936,7 +951,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { if (dig->coherent_mode) args.v3.acConfig.fCoherentMode = 1; - if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + if (radeon_encoder->pixel_clock > 165000) args.v3.acConfig.fDualLinkConnector = 1; } break; @@ -951,7 +966,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t if (is_dp) args.v4.usPixelClock = cpu_to_le16(dp_clock / 10); - else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + else if (radeon_encoder->pixel_clock > 165000) args.v4.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10); else args.v4.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); @@ -959,7 +974,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t if (is_dp) args.v4.ucLaneNum = dp_lane_count; - else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + else if (radeon_encoder->pixel_clock > 165000) args.v4.ucLaneNum = 8; else args.v4.ucLaneNum = 4; @@ -999,7 +1014,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { if (dig->coherent_mode) args.v4.acConfig.fCoherentMode = 1; - if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + if (radeon_encoder->pixel_clock > 165000) args.v4.acConfig.fDualLinkConnector = 1; } break; @@ -1122,7 +1137,7 @@ atombios_external_encoder_setup(struct drm_encoder *encoder, if (dp_clock == 270000) args.v1.sDigEncoder.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ; args.v1.sDigEncoder.ucLaneNum = dp_lane_count; - } else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + } else if (radeon_encoder->pixel_clock > 165000) args.v1.sDigEncoder.ucLaneNum = 8; else args.v1.sDigEncoder.ucLaneNum = 4; @@ -1141,7 +1156,7 @@ atombios_external_encoder_setup(struct drm_encoder *encoder, else if (dp_clock == 540000) args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_DPLINKRATE_5_40GHZ; args.v3.sExtEncoder.ucLaneNum = dp_lane_count; - } else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) + } else if (radeon_encoder->pixel_clock > 165000) args.v3.sExtEncoder.ucLaneNum = 8; else args.v3.sExtEncoder.ucLaneNum = 4; @@ -1326,8 +1341,7 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode) switch (mode) { case DRM_MODE_DPMS_ON: /* some early dce3.2 boards have a bug in their transmitter control table */ - if ((rdev->family == CHIP_RV710) || (rdev->family == CHIP_RV730) || - ASIC_IS_DCE41(rdev) || ASIC_IS_DCE5(rdev)) + if ((rdev->family == CHIP_RV710) || (rdev->family == CHIP_RV730)) atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0); else atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT, 0, 0); @@ -1337,6 +1351,8 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode) ATOM_TRANSMITTER_ACTION_POWER_ON); radeon_dig_connector->edp_on = true; } + if (ASIC_IS_DCE4(rdev)) + atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0); radeon_dp_link_train(encoder, connector); if (ASIC_IS_DCE4(rdev)) atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_ON, 0); @@ -1347,10 +1363,7 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode) case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_OFF: - if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE5(rdev)) - atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0); - else - atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT, 0, 0); + atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT, 0, 0); if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)) && connector) { if (ASIC_IS_DCE4(rdev)) atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0); @@ -1797,21 +1810,7 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder, case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: - if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE5(rdev)) { - struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); - struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; - - if (!connector) - dig->panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE; - else - dig->panel_mode = radeon_dp_get_panel_mode(encoder, connector); - - /* setup and enable the encoder */ - atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_SETUP, 0); - atombios_dig_encoder_setup(encoder, - ATOM_ENCODER_CMD_SETUP_PANEL_MODE, - dig->panel_mode); - } else if (ASIC_IS_DCE4(rdev)) { + if (ASIC_IS_DCE4(rdev)) { /* disable the transmitter */ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0); /* setup and enable the encoder */ diff --git a/trunk/drivers/gpu/drm/radeon/evergreen.c b/trunk/drivers/gpu/drm/radeon/evergreen.c index 9be353b894cc..636660fca8c2 100644 --- a/trunk/drivers/gpu/drm/radeon/evergreen.c +++ b/trunk/drivers/gpu/drm/radeon/evergreen.c @@ -1455,7 +1455,6 @@ int evergreen_cp_resume(struct radeon_device *rdev) #endif WREG32(CP_RB_CNTL, tmp); WREG32(CP_SEM_WAIT_TIMER, 0x0); - WREG32(CP_SEM_INCOMPLETE_TIMER_CNTL, 0x0); /* Set the write pointer delay */ WREG32(CP_RB_WPTR_DELAY, 0); @@ -3191,7 +3190,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/gpu/drm/radeon/evergreend.h b/trunk/drivers/gpu/drm/radeon/evergreend.h index 74713d42df29..b502216d42af 100644 --- a/trunk/drivers/gpu/drm/radeon/evergreend.h +++ b/trunk/drivers/gpu/drm/radeon/evergreend.h @@ -108,7 +108,6 @@ #define CP_RB_WPTR_ADDR_HI 0xC11C #define CP_RB_WPTR_DELAY 0x8704 #define CP_SEM_WAIT_TIMER 0x85BC -#define CP_SEM_INCOMPLETE_TIMER_CNTL 0x85C8 #define CP_DEBUG 0xC1FC diff --git a/trunk/drivers/gpu/drm/radeon/ni.c b/trunk/drivers/gpu/drm/radeon/ni.c index db09065e68fd..321137295400 100644 --- a/trunk/drivers/gpu/drm/radeon/ni.c +++ b/trunk/drivers/gpu/drm/radeon/ni.c @@ -1219,7 +1219,6 @@ int cayman_cp_resume(struct radeon_device *rdev) RREG32(GRBM_SOFT_RESET); WREG32(CP_SEM_WAIT_TIMER, 0x0); - WREG32(CP_SEM_INCOMPLETE_TIMER_CNTL, 0x0); /* Set the write pointer delay */ WREG32(CP_RB_WPTR_DELAY, 0); diff --git a/trunk/drivers/gpu/drm/radeon/nid.h b/trunk/drivers/gpu/drm/radeon/nid.h index 9a7f3b6e02de..f9df2a645e79 100644 --- a/trunk/drivers/gpu/drm/radeon/nid.h +++ b/trunk/drivers/gpu/drm/radeon/nid.h @@ -222,7 +222,6 @@ #define SCRATCH_UMSK 0x8540 #define SCRATCH_ADDR 0x8544 #define CP_SEM_WAIT_TIMER 0x85BC -#define CP_SEM_INCOMPLETE_TIMER_CNTL 0x85C8 #define CP_COHER_CNTL2 0x85E8 #define CP_ME_CNTL 0x86D8 #define CP_ME_HALT (1 << 28) diff --git a/trunk/drivers/gpu/drm/radeon/r600_blit_kms.c b/trunk/drivers/gpu/drm/radeon/r600_blit_kms.c index accc032c103f..d996f4381130 100644 --- a/trunk/drivers/gpu/drm/radeon/r600_blit_kms.c +++ b/trunk/drivers/gpu/drm/radeon/r600_blit_kms.c @@ -468,42 +468,27 @@ set_default_state(struct radeon_device *rdev) radeon_ring_write(ring, sq_stack_resource_mgmt_2); } -#define I2F_MAX_BITS 15 -#define I2F_MAX_INPUT ((1 << I2F_MAX_BITS) - 1) -#define I2F_SHIFT (24 - I2F_MAX_BITS) - -/* - * Converts unsigned integer into 32-bit IEEE floating point representation. - * Conversion is not universal and only works for the range from 0 - * to 2^I2F_MAX_BITS-1. Currently we only use it with inputs between - * 0 and 16384 (inclusive), so I2F_MAX_BITS=15 is enough. If necessary, - * I2F_MAX_BITS can be increased, but that will add to the loop iterations - * and slow us down. Conversion is done by shifting the input and counting - * down until the first 1 reaches bit position 23. The resulting counter - * and the shifted input are, respectively, the exponent and the fraction. - * The sign is always zero. - */ static uint32_t i2f(uint32_t input) { u32 result, i, exponent, fraction; - WARN_ON_ONCE(input > I2F_MAX_INPUT); - - if ((input & I2F_MAX_INPUT) == 0) - result = 0; + if ((input & 0x3fff) == 0) + result = 0; /* 0 is a special case */ else { - exponent = 126 + I2F_MAX_BITS; - fraction = (input & I2F_MAX_INPUT) << I2F_SHIFT; - - for (i = 0; i < I2F_MAX_BITS; i++) { + exponent = 140; /* exponent biased by 127; */ + fraction = (input & 0x3fff) << 10; /* cheat and only + handle numbers below 2^^15 */ + for (i = 0; i < 14; i++) { if (fraction & 0x800000) break; else { - fraction = fraction << 1; + fraction = fraction << 1; /* keep + shifting left until top bit = 1 */ exponent = exponent - 1; } } - result = exponent << 23 | (fraction & 0x7fffff); + result = exponent << 23 | (fraction & 0x7fffff); /* mask + off top bit; assumed 1 */ } return result; } diff --git a/trunk/drivers/gpu/drm/radeon/radeon.h b/trunk/drivers/gpu/drm/radeon/radeon.h index 1668ec1ee770..73e05cb85eca 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon.h +++ b/trunk/drivers/gpu/drm/radeon/radeon.h @@ -156,47 +156,6 @@ static inline int radeon_atrm_get_bios_chunk(uint8_t *bios, int offset, int len) bool radeon_get_bios(struct radeon_device *rdev); -/* - * Mutex which allows recursive locking from the same process. - */ -struct radeon_mutex { - struct mutex mutex; - struct task_struct *owner; - int level; -}; - -static inline void radeon_mutex_init(struct radeon_mutex *mutex) -{ - mutex_init(&mutex->mutex); - mutex->owner = NULL; - mutex->level = 0; -} - -static inline void radeon_mutex_lock(struct radeon_mutex *mutex) -{ - if (mutex_trylock(&mutex->mutex)) { - /* The mutex was unlocked before, so it's ours now */ - mutex->owner = current; - } else if (mutex->owner != current) { - /* Another process locked the mutex, take it */ - mutex_lock(&mutex->mutex); - mutex->owner = current; - } - /* Otherwise the mutex was already locked by this process */ - - mutex->level++; -} - -static inline void radeon_mutex_unlock(struct radeon_mutex *mutex) -{ - if (--mutex->level > 0) - return; - - mutex->owner = NULL; - mutex_unlock(&mutex->mutex); -} - - /* * Dummy page */ @@ -639,7 +598,7 @@ struct radeon_ib { * mutex protects scheduled_ibs, ready, alloc_bm */ struct radeon_ib_pool { - struct radeon_mutex mutex; + struct mutex mutex; struct radeon_sa_manager sa_manager; struct radeon_ib ibs[RADEON_IB_POOL_SIZE]; bool ready; @@ -1395,6 +1354,47 @@ struct r600_vram_scratch { }; +/* + * Mutex which allows recursive locking from the same process. + */ +struct radeon_mutex { + struct mutex mutex; + struct task_struct *owner; + int level; +}; + +static inline void radeon_mutex_init(struct radeon_mutex *mutex) +{ + mutex_init(&mutex->mutex); + mutex->owner = NULL; + mutex->level = 0; +} + +static inline void radeon_mutex_lock(struct radeon_mutex *mutex) +{ + if (mutex_trylock(&mutex->mutex)) { + /* The mutex was unlocked before, so it's ours now */ + mutex->owner = current; + } else if (mutex->owner != current) { + /* Another process locked the mutex, take it */ + mutex_lock(&mutex->mutex); + mutex->owner = current; + } + /* Otherwise the mutex was already locked by this process */ + + mutex->level++; +} + +static inline void radeon_mutex_unlock(struct radeon_mutex *mutex) +{ + if (--mutex->level > 0) + return; + + mutex->owner = NULL; + mutex_unlock(&mutex->mutex); +} + + /* * Core structure, functions and helpers. */ diff --git a/trunk/drivers/gpu/drm/radeon/radeon_atpx_handler.c b/trunk/drivers/gpu/drm/radeon/radeon_atpx_handler.c index 98724fcb0088..9d95792bea3e 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_atpx_handler.c +++ b/trunk/drivers/gpu/drm/radeon/radeon_atpx_handler.c @@ -58,8 +58,7 @@ static int radeon_atrm_call(acpi_handle atrm_handle, uint8_t *bios, } obj = (union acpi_object *)buffer.pointer; - memcpy(bios+offset, obj->buffer.pointer, obj->buffer.length); - len = obj->buffer.length; + memcpy(bios+offset, obj->buffer.pointer, len); kfree(buffer.pointer); return len; } diff --git a/trunk/drivers/gpu/drm/radeon/radeon_bios.c b/trunk/drivers/gpu/drm/radeon/radeon_bios.c index 501f4881e5aa..229a20f10e2b 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_bios.c +++ b/trunk/drivers/gpu/drm/radeon/radeon_bios.c @@ -120,7 +120,7 @@ static bool radeon_atrm_get_bios(struct radeon_device *rdev) ret = radeon_atrm_get_bios_chunk(rdev->bios, (i * ATRM_BIOS_PAGE), ATRM_BIOS_PAGE); - if (ret < ATRM_BIOS_PAGE) + if (ret <= 0) break; } diff --git a/trunk/drivers/gpu/drm/radeon/radeon_device.c b/trunk/drivers/gpu/drm/radeon/radeon_device.c index 49f7cb7e226b..0afb13bd8dca 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_device.c +++ b/trunk/drivers/gpu/drm/radeon/radeon_device.c @@ -720,7 +720,7 @@ int radeon_device_init(struct radeon_device *rdev, /* mutex initialization are all done here so we * can recall function without having locking issues */ radeon_mutex_init(&rdev->cs_mutex); - radeon_mutex_init(&rdev->ib_pool.mutex); + mutex_init(&rdev->ib_pool.mutex); for (i = 0; i < RADEON_NUM_RINGS; ++i) mutex_init(&rdev->ring[i].mutex); mutex_init(&rdev->dc_hw_i2c_mutex); @@ -883,8 +883,6 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state) if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; - drm_kms_helper_poll_disable(dev); - /* turn off display hw */ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); @@ -961,11 +959,9 @@ int radeon_resume_kms(struct drm_device *dev) radeon_fbdev_set_suspend(rdev, 0); console_unlock(); - /* init dig PHYs, disp eng pll */ - if (rdev->is_atom_bios) { + /* init dig PHYs */ + if (rdev->is_atom_bios) radeon_atom_encoder_init(rdev); - radeon_atom_dcpll_init(rdev); - } /* reset hpd state */ radeon_hpd_init(rdev); /* blat the mode back in */ @@ -974,8 +970,6 @@ int radeon_resume_kms(struct drm_device *dev) list_for_each_entry(connector, &dev->mode_config.connector_list, head) { drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); } - - drm_kms_helper_poll_enable(dev); return 0; } diff --git a/trunk/drivers/gpu/drm/radeon/radeon_display.c b/trunk/drivers/gpu/drm/radeon/radeon_display.c index 8c49fef1ce78..d3ffc18774a6 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_display.c +++ b/trunk/drivers/gpu/drm/radeon/radeon_display.c @@ -1305,11 +1305,9 @@ int radeon_modeset_init(struct radeon_device *rdev) return ret; } - /* init dig PHYs, disp eng pll */ - if (rdev->is_atom_bios) { + /* init dig PHYs */ + if (rdev->is_atom_bios) radeon_atom_encoder_init(rdev); - radeon_atom_dcpll_init(rdev); - } /* initialize hpd */ radeon_hpd_init(rdev); diff --git a/trunk/drivers/gpu/drm/radeon/radeon_encoders.c b/trunk/drivers/gpu/drm/radeon/radeon_encoders.c index 9419c51bcf50..4b27efa4405b 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_encoders.c +++ b/trunk/drivers/gpu/drm/radeon/radeon_encoders.c @@ -202,22 +202,6 @@ radeon_get_connector_for_encoder(struct drm_encoder *encoder) return NULL; } -struct drm_connector * -radeon_get_connector_for_encoder_init(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct drm_connector *connector; - struct radeon_connector *radeon_connector; - - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - radeon_connector = to_radeon_connector(connector); - if (radeon_encoder->devices & radeon_connector->devices) - return connector; - } - return NULL; -} - struct drm_encoder *radeon_get_external_encoder(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; @@ -304,64 +288,3 @@ void radeon_panel_mode_fixup(struct drm_encoder *encoder, } -bool radeon_dig_monitor_is_duallink(struct drm_encoder *encoder, - u32 pixel_clock) -{ - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct drm_connector *connector; - struct radeon_connector *radeon_connector; - struct radeon_connector_atom_dig *dig_connector; - - connector = radeon_get_connector_for_encoder(encoder); - /* if we don't have an active device yet, just use one of - * the connectors tied to the encoder. - */ - if (!connector) - connector = radeon_get_connector_for_encoder_init(encoder); - radeon_connector = to_radeon_connector(connector); - - switch (connector->connector_type) { - case DRM_MODE_CONNECTOR_DVII: - case DRM_MODE_CONNECTOR_HDMIB: - if (radeon_connector->use_digital) { - /* HDMI 1.3 supports up to 340 Mhz over single link */ - if (ASIC_IS_DCE3(rdev) && drm_detect_hdmi_monitor(radeon_connector->edid)) { - if (pixel_clock > 340000) - return true; - else - return false; - } else { - if (pixel_clock > 165000) - return true; - else - return false; - } - } else - return false; - case DRM_MODE_CONNECTOR_DVID: - case DRM_MODE_CONNECTOR_HDMIA: - case DRM_MODE_CONNECTOR_DisplayPort: - dig_connector = radeon_connector->con_priv; - if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || - (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) - return false; - else { - /* HDMI 1.3 supports up to 340 Mhz over single link */ - if (ASIC_IS_DCE3(rdev) && drm_detect_hdmi_monitor(radeon_connector->edid)) { - if (pixel_clock > 340000) - return true; - else - return false; - } else { - if (pixel_clock > 165000) - return true; - else - return false; - } - } - default: - return false; - } -} - diff --git a/trunk/drivers/gpu/drm/radeon/radeon_i2c.c b/trunk/drivers/gpu/drm/radeon/radeon_i2c.c index 98a8ad680109..7bb1b079f480 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_i2c.c +++ b/trunk/drivers/gpu/drm/radeon/radeon_i2c.c @@ -897,7 +897,6 @@ struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev, i2c->rec = *rec; i2c->adapter.owner = THIS_MODULE; i2c->adapter.class = I2C_CLASS_DDC; - i2c->adapter.dev.parent = &dev->pdev->dev; i2c->dev = dev; i2c_set_adapdata(&i2c->adapter, i2c); if (rec->mm_i2c || @@ -958,7 +957,6 @@ struct radeon_i2c_chan *radeon_i2c_create_dp(struct drm_device *dev, i2c->rec = *rec; i2c->adapter.owner = THIS_MODULE; i2c->adapter.class = I2C_CLASS_DDC; - i2c->adapter.dev.parent = &dev->pdev->dev; i2c->dev = dev; snprintf(i2c->adapter.name, sizeof(i2c->adapter.name), "Radeon aux bus %s", name); diff --git a/trunk/drivers/gpu/drm/radeon/radeon_irq_kms.c b/trunk/drivers/gpu/drm/radeon/radeon_irq_kms.c index 66d5fe1c8174..be38921bf761 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/trunk/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -135,12 +135,6 @@ static bool radeon_msi_ok(struct radeon_device *rdev) (rdev->pdev->subsystem_device == 0x30c2)) return true; - /* Dell RS690 only seems to work with MSIs. */ - if ((rdev->pdev->device == 0x791f) && - (rdev->pdev->subsystem_vendor == 0x1028) && - (rdev->pdev->subsystem_device == 0x01fc)) - return true; - /* Dell RS690 only seems to work with MSIs. */ if ((rdev->pdev->device == 0x791f) && (rdev->pdev->subsystem_vendor == 0x1028) && diff --git a/trunk/drivers/gpu/drm/radeon/radeon_mode.h b/trunk/drivers/gpu/drm/radeon/radeon_mode.h index 4330e3253573..08ff857c8fd6 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_mode.h +++ b/trunk/drivers/gpu/drm/radeon/radeon_mode.h @@ -362,7 +362,6 @@ struct radeon_encoder_atom_dig { struct backlight_device *bl_dev; int dpms_mode; uint8_t backlight_level; - int panel_mode; }; struct radeon_encoder_atom_dac { @@ -467,10 +466,6 @@ radeon_atombios_get_tv_info(struct radeon_device *rdev); extern struct drm_connector * radeon_get_connector_for_encoder(struct drm_encoder *encoder); -extern struct drm_connector * -radeon_get_connector_for_encoder_init(struct drm_encoder *encoder); -extern bool radeon_dig_monitor_is_duallink(struct drm_encoder *encoder, - u32 pixel_clock); extern u16 radeon_encoder_get_dp_bridge_encoder_id(struct drm_encoder *encoder); extern u16 radeon_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *connector); @@ -487,11 +482,8 @@ extern void radeon_dp_link_train(struct drm_encoder *encoder, extern bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector); extern u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector); extern bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector); -extern int radeon_dp_get_panel_mode(struct drm_encoder *encoder, - struct drm_connector *connector); extern void atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mode); extern void radeon_atom_encoder_init(struct radeon_device *rdev); -extern void radeon_atom_dcpll_init(struct radeon_device *rdev); extern void atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t lane_num, uint8_t lane_set); diff --git a/trunk/drivers/gpu/drm/radeon/radeon_ring.c b/trunk/drivers/gpu/drm/radeon/radeon_ring.c index 30a4c5014c8b..e8bc70933d1b 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_ring.c +++ b/trunk/drivers/gpu/drm/radeon/radeon_ring.c @@ -109,12 +109,12 @@ int radeon_ib_get(struct radeon_device *rdev, int ring, return r; } - radeon_mutex_lock(&rdev->ib_pool.mutex); + mutex_lock(&rdev->ib_pool.mutex); idx = rdev->ib_pool.head_id; retry: if (cretry > 5) { dev_err(rdev->dev, "failed to get an ib after 5 retry\n"); - radeon_mutex_unlock(&rdev->ib_pool.mutex); + mutex_unlock(&rdev->ib_pool.mutex); radeon_fence_unref(&fence); return -ENOMEM; } @@ -139,7 +139,7 @@ int radeon_ib_get(struct radeon_device *rdev, int ring, */ rdev->ib_pool.head_id = (1 + idx); rdev->ib_pool.head_id &= (RADEON_IB_POOL_SIZE - 1); - radeon_mutex_unlock(&rdev->ib_pool.mutex); + mutex_unlock(&rdev->ib_pool.mutex); return 0; } } @@ -158,7 +158,7 @@ int radeon_ib_get(struct radeon_device *rdev, int ring, } idx = (idx + 1) & (RADEON_IB_POOL_SIZE - 1); } - radeon_mutex_unlock(&rdev->ib_pool.mutex); + mutex_unlock(&rdev->ib_pool.mutex); radeon_fence_unref(&fence); return r; } @@ -171,12 +171,12 @@ void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib **ib) if (tmp == NULL) { return; } - radeon_mutex_lock(&rdev->ib_pool.mutex); + mutex_lock(&rdev->ib_pool.mutex); if (tmp->fence && !tmp->fence->emitted) { radeon_sa_bo_free(rdev, &tmp->sa_bo); radeon_fence_unref(&tmp->fence); } - radeon_mutex_unlock(&rdev->ib_pool.mutex); + mutex_unlock(&rdev->ib_pool.mutex); } int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib) @@ -204,25 +204,22 @@ int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib) int radeon_ib_pool_init(struct radeon_device *rdev) { - struct radeon_sa_manager tmp; int i, r; - r = radeon_sa_bo_manager_init(rdev, &tmp, + mutex_lock(&rdev->ib_pool.mutex); + if (rdev->ib_pool.ready) { + mutex_unlock(&rdev->ib_pool.mutex); + return 0; + } + + r = radeon_sa_bo_manager_init(rdev, &rdev->ib_pool.sa_manager, RADEON_IB_POOL_SIZE*64*1024, RADEON_GEM_DOMAIN_GTT); if (r) { + mutex_unlock(&rdev->ib_pool.mutex); return r; } - radeon_mutex_lock(&rdev->ib_pool.mutex); - if (rdev->ib_pool.ready) { - radeon_mutex_unlock(&rdev->ib_pool.mutex); - radeon_sa_bo_manager_fini(rdev, &tmp); - return 0; - } - - rdev->ib_pool.sa_manager = tmp; - INIT_LIST_HEAD(&rdev->ib_pool.sa_manager.sa_bo); for (i = 0; i < RADEON_IB_POOL_SIZE; i++) { rdev->ib_pool.ibs[i].fence = NULL; rdev->ib_pool.ibs[i].idx = i; @@ -239,7 +236,7 @@ int radeon_ib_pool_init(struct radeon_device *rdev) if (radeon_debugfs_ring_init(rdev)) { DRM_ERROR("Failed to register debugfs file for rings !\n"); } - radeon_mutex_unlock(&rdev->ib_pool.mutex); + mutex_unlock(&rdev->ib_pool.mutex); return 0; } @@ -247,7 +244,7 @@ void radeon_ib_pool_fini(struct radeon_device *rdev) { unsigned i; - radeon_mutex_lock(&rdev->ib_pool.mutex); + mutex_lock(&rdev->ib_pool.mutex); if (rdev->ib_pool.ready) { for (i = 0; i < RADEON_IB_POOL_SIZE; i++) { radeon_sa_bo_free(rdev, &rdev->ib_pool.ibs[i].sa_bo); @@ -256,7 +253,7 @@ void radeon_ib_pool_fini(struct radeon_device *rdev) radeon_sa_bo_manager_fini(rdev, &rdev->ib_pool.sa_manager); rdev->ib_pool.ready = false; } - radeon_mutex_unlock(&rdev->ib_pool.mutex); + mutex_unlock(&rdev->ib_pool.mutex); } int radeon_ib_pool_start(struct radeon_device *rdev) diff --git a/trunk/drivers/gpu/drm/sis/sis_drv.c b/trunk/drivers/gpu/drm/sis/sis_drv.c index 573220cc5269..06da063ece2e 100644 --- a/trunk/drivers/gpu/drm/sis/sis_drv.c +++ b/trunk/drivers/gpu/drm/sis/sis_drv.c @@ -40,6 +40,7 @@ static struct pci_device_id pciidlist[] = { static int sis_driver_load(struct drm_device *dev, unsigned long chipset) { drm_sis_private_t *dev_priv; + int ret; dev_priv = kzalloc(sizeof(drm_sis_private_t), GFP_KERNEL); if (dev_priv == NULL) @@ -49,7 +50,7 @@ static int sis_driver_load(struct drm_device *dev, unsigned long chipset) dev_priv->chipset = chipset; idr_init(&dev->object_name_idr); - return 0; + return ret; } static int sis_driver_unload(struct drm_device *dev) diff --git a/trunk/drivers/gpu/drm/ttm/ttm_bo.c b/trunk/drivers/gpu/drm/ttm/ttm_bo.c index 7c3a57de8187..2f0eab66ece6 100644 --- a/trunk/drivers/gpu/drm/ttm/ttm_bo.c +++ b/trunk/drivers/gpu/drm/ttm/ttm_bo.c @@ -404,9 +404,6 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, } } - if (bdev->driver->move_notify) - bdev->driver->move_notify(bo, mem); - if (!(old_man->flags & TTM_MEMTYPE_FLAG_FIXED) && !(new_man->flags & TTM_MEMTYPE_FLAG_FIXED)) ret = ttm_bo_move_ttm(bo, evict, no_wait_reserve, no_wait_gpu, mem); @@ -416,17 +413,11 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, else ret = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, mem); - if (ret) { - if (bdev->driver->move_notify) { - struct ttm_mem_reg tmp_mem = *mem; - *mem = bo->mem; - bo->mem = tmp_mem; - bdev->driver->move_notify(bo, mem); - bo->mem = *mem; - } - + if (ret) goto out_err; - } + + if (bdev->driver->move_notify) + bdev->driver->move_notify(bo, mem); moved: if (bo->evicted) { diff --git a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index b66ef0e3cde1..0af6ebdf205d 100644 --- a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -378,7 +378,7 @@ int vmw_framebuffer_create_handle(struct drm_framebuffer *fb, unsigned int *handle) { if (handle) - *handle = 0; + handle = 0; return 0; } diff --git a/trunk/drivers/hid/hid-hyperv.c b/trunk/drivers/hid/hid-hyperv.c index 406632472c1b..0c33ae9cf0f0 100644 --- a/trunk/drivers/hid/hid-hyperv.c +++ b/trunk/drivers/hid/hid-hyperv.c @@ -548,7 +548,6 @@ static int mousevsc_remove(struct hv_device *dev) struct mousevsc_dev *input_dev = hv_get_drvdata(dev); vmbus_close(dev->channel); - hid_hw_stop(input_dev->hid_device); hid_destroy_device(input_dev->hid_device); mousevsc_free_device(input_dev); diff --git a/trunk/drivers/hid/hid-wacom.c b/trunk/drivers/hid/hid-wacom.c index acab74cde727..b47e58b52d9f 100644 --- a/trunk/drivers/hid/hid-wacom.c +++ b/trunk/drivers/hid/hid-wacom.c @@ -531,6 +531,7 @@ static int wacom_probe(struct hid_device *hdev, wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY; wdata->battery.use_for_apm = 0; + power_supply_powers(&wdata->battery, &hdev->dev); ret = power_supply_register(&hdev->dev, &wdata->battery); if (ret) { @@ -539,8 +540,6 @@ static int wacom_probe(struct hid_device *hdev, goto err_battery; } - power_supply_powers(&wdata->battery, &hdev->dev); - wdata->ac.properties = wacom_ac_props; wdata->ac.num_properties = ARRAY_SIZE(wacom_ac_props); wdata->ac.get_property = wacom_ac_get_property; @@ -548,14 +547,14 @@ static int wacom_probe(struct hid_device *hdev, wdata->ac.type = POWER_SUPPLY_TYPE_MAINS; wdata->ac.use_for_apm = 0; + power_supply_powers(&wdata->battery, &hdev->dev); + ret = power_supply_register(&hdev->dev, &wdata->ac); if (ret) { hid_warn(hdev, "can't create ac battery attribute, err: %d\n", ret); goto err_ac; } - - power_supply_powers(&wdata->ac, &hdev->dev); #endif return 0; diff --git a/trunk/drivers/hid/hid-wiimote-core.c b/trunk/drivers/hid/hid-wiimote-core.c index cac3589b1ed5..fc253b472f9d 100644 --- a/trunk/drivers/hid/hid-wiimote-core.c +++ b/trunk/drivers/hid/hid-wiimote-core.c @@ -1226,14 +1226,14 @@ static int wiimote_hid_probe(struct hid_device *hdev, wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY; wdata->battery.use_for_apm = 0; + power_supply_powers(&wdata->battery, &hdev->dev); + ret = power_supply_register(&wdata->hdev->dev, &wdata->battery); if (ret) { hid_err(hdev, "Cannot register battery device\n"); goto err_battery; } - power_supply_powers(&wdata->battery, &hdev->dev); - ret = wiimote_leds_create(wdata); if (ret) goto err_free; diff --git a/trunk/drivers/hid/usbhid/hiddev.c b/trunk/drivers/hid/usbhid/hiddev.c index b1ec0e2aeb57..7c297d305d5d 100644 --- a/trunk/drivers/hid/usbhid/hiddev.c +++ b/trunk/drivers/hid/usbhid/hiddev.c @@ -922,11 +922,11 @@ void hiddev_disconnect(struct hid_device *hid) struct hiddev *hiddev = hid->hiddev; struct usbhid_device *usbhid = hid->driver_data; - usb_deregister_dev(usbhid->intf, &hiddev_class); - mutex_lock(&hiddev->existancelock); hiddev->exist = 0; + usb_deregister_dev(usbhid->intf, &hiddev_class); + if (hiddev->open) { mutex_unlock(&hiddev->existancelock); usbhid_close(hiddev->hid); diff --git a/trunk/drivers/hwmon/f71805f.c b/trunk/drivers/hwmon/f71805f.c index 6dbfd3e516e4..92f949767ece 100644 --- a/trunk/drivers/hwmon/f71805f.c +++ b/trunk/drivers/hwmon/f71805f.c @@ -283,11 +283,11 @@ static inline long temp_from_reg(u8 reg) static inline u8 temp_to_reg(long val) { - if (val <= 0) - return 0; - if (val >= 1000 * 0xff) - return 0xff; - return (val + 500) / 1000; + if (val < 0) + val = 0; + else if (val > 1000 * 0xff) + val = 0xff; + return ((val + 500) / 1000); } /* 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/sht15.c b/trunk/drivers/hwmon/sht15.c index 91fdd1fe18b0..6ddeae049058 100644 --- a/trunk/drivers/hwmon/sht15.c +++ b/trunk/drivers/hwmon/sht15.c @@ -883,7 +883,7 @@ static int sht15_invalidate_voltage(struct notifier_block *nb, static int __devinit sht15_probe(struct platform_device *pdev) { - int ret; + int ret = 0; struct sht15_data *data = kzalloc(sizeof(*data), GFP_KERNEL); u8 status = 0; @@ -901,7 +901,6 @@ static int __devinit sht15_probe(struct platform_device *pdev) init_waitqueue_head(&data->wait_queue); if (pdev->dev.platform_data == NULL) { - ret = -EINVAL; dev_err(&pdev->dev, "no platform data supplied\n"); goto err_free_data; } diff --git a/trunk/drivers/hwmon/w83627ehf.c b/trunk/drivers/hwmon/w83627ehf.c index 5276d1933dbc..0e0af0445222 100644 --- a/trunk/drivers/hwmon/w83627ehf.c +++ b/trunk/drivers/hwmon/w83627ehf.c @@ -1319,7 +1319,6 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr, { struct w83627ehf_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - struct w83627ehf_sio_data *sio_data = dev->platform_data; int nr = sensor_attr->index; unsigned long val; int err; @@ -1331,11 +1330,6 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr, if (val > 1) return -EINVAL; - - /* On NCT67766F, DC mode is only supported for pwm1 */ - if (sio_data->kind == nct6776 && nr && val != 1) - return -EINVAL; - mutex_lock(&data->update_lock); reg = w83627ehf_read_value(data, W83627EHF_REG_PWM_ENABLE[nr]); data->pwm_mode[nr] = val; @@ -1920,26 +1914,9 @@ w83627ehf_check_fan_inputs(const struct w83627ehf_sio_data *sio_data, fan4min = 0; fan5pin = 0; } else if (sio_data->kind == nct6776) { - bool gpok = superio_inb(sio_data->sioreg, 0x27) & 0x80; - - superio_select(sio_data->sioreg, W83627EHF_LD_HWM); - regval = superio_inb(sio_data->sioreg, SIO_REG_ENABLE); - - if (regval & 0x80) - fan3pin = gpok; - else - fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40); - - if (regval & 0x40) - fan4pin = gpok; - else - fan4pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x01); - - if (regval & 0x20) - fan5pin = gpok; - else - fan5pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x02); - + fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40); + fan4pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x01); + fan5pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x02); fan4min = fan4pin; } else if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) { fan3pin = 1; @@ -2349,6 +2326,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/i2c/busses/i2c-omap.c b/trunk/drivers/i2c/busses/i2c-omap.c index 801df6000e9b..f713eac55047 100644 --- a/trunk/drivers/i2c/busses/i2c-omap.c +++ b/trunk/drivers/i2c/busses/i2c-omap.c @@ -1018,7 +1018,7 @@ omap_i2c_probe(struct platform_device *pdev) goto err_release_region; } - match = of_match_device(of_match_ptr(omap_i2c_of_match), &pdev->dev); + match = of_match_device(omap_i2c_of_match, &pdev->dev); if (match) { u32 freq = 100000; /* default to 100000 Hz */ diff --git a/trunk/drivers/idle/intel_idle.c b/trunk/drivers/idle/intel_idle.c index 54ab97bae042..20bce51c2e82 100644 --- a/trunk/drivers/idle/intel_idle.c +++ b/trunk/drivers/idle/intel_idle.c @@ -527,7 +527,7 @@ int intel_idle_cpu_init(int cpu) return 0; } -EXPORT_SYMBOL_GPL(intel_idle_cpu_init); + static int __init intel_idle_init(void) { diff --git a/trunk/drivers/infiniband/core/ucma.c b/trunk/drivers/infiniband/core/ucma.c index 5034a87cc72d..b37b0c02a7b9 100644 --- a/trunk/drivers/infiniband/core/ucma.c +++ b/trunk/drivers/infiniband/core/ucma.c @@ -808,12 +808,9 @@ static ssize_t ucma_accept(struct ucma_file *file, const char __user *inbuf, return PTR_ERR(ctx); if (cmd.conn_param.valid) { + ctx->uid = cmd.uid; ucma_copy_conn_param(&conn_param, &cmd.conn_param); - mutex_lock(&file->mut); ret = rdma_accept(ctx->cm_id, &conn_param); - if (!ret) - ctx->uid = cmd.uid; - mutex_unlock(&file->mut); } else ret = rdma_accept(ctx->cm_id, NULL); diff --git a/trunk/drivers/infiniband/core/uverbs_cmd.c b/trunk/drivers/infiniband/core/uverbs_cmd.c index 4d27e4c3fe34..b930da4c0c63 100644 --- a/trunk/drivers/infiniband/core/uverbs_cmd.c +++ b/trunk/drivers/infiniband/core/uverbs_cmd.c @@ -1485,7 +1485,6 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file, qp->event_handler = attr.event_handler; qp->qp_context = attr.qp_context; qp->qp_type = attr.qp_type; - atomic_set(&qp->usecnt, 0); atomic_inc(&pd->usecnt); atomic_inc(&attr.send_cq->usecnt); if (attr.recv_cq) diff --git a/trunk/drivers/infiniband/core/verbs.c b/trunk/drivers/infiniband/core/verbs.c index 575b78045aaf..602b1bd723a9 100644 --- a/trunk/drivers/infiniband/core/verbs.c +++ b/trunk/drivers/infiniband/core/verbs.c @@ -421,7 +421,6 @@ struct ib_qp *ib_create_qp(struct ib_pd *pd, qp->uobject = NULL; qp->qp_type = qp_init_attr->qp_type; - atomic_set(&qp->usecnt, 0); if (qp_init_attr->qp_type == IB_QPT_XRC_TGT) { qp->event_handler = __ib_shared_qp_event_handler; qp->qp_context = qp; @@ -431,6 +430,7 @@ struct ib_qp *ib_create_qp(struct ib_pd *pd, qp->xrcd = qp_init_attr->xrcd; atomic_inc(&qp_init_attr->xrcd->usecnt); INIT_LIST_HEAD(&qp->open_list); + atomic_set(&qp->usecnt, 0); real_qp = qp; qp = __ib_open_qp(real_qp, qp_init_attr->event_handler, diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_fs.c b/trunk/drivers/infiniband/hw/ipath/ipath_fs.c index a4de9d58e9b4..b7d4216db3c3 100644 --- a/trunk/drivers/infiniband/hw/ipath/ipath_fs.c +++ b/trunk/drivers/infiniband/hw/ipath/ipath_fs.c @@ -89,7 +89,7 @@ static int create_file(const char *name, umode_t mode, error = ipathfs_mknod(parent->d_inode, *dentry, mode, fops, data); else - error = PTR_ERR(*dentry); + error = PTR_ERR(dentry); mutex_unlock(&parent->d_inode->i_mutex); return error; diff --git a/trunk/drivers/infiniband/hw/mlx4/mad.c b/trunk/drivers/infiniband/hw/mlx4/mad.c index 259b0670b51c..95c94d8f0254 100644 --- a/trunk/drivers/infiniband/hw/mlx4/mad.c +++ b/trunk/drivers/infiniband/hw/mlx4/mad.c @@ -257,9 +257,12 @@ static int ib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num, return IB_MAD_RESULT_SUCCESS; /* - * Don't process SMInfo queries -- the SMA can't handle them. + * Don't process SMInfo queries or vendor-specific + * MADs -- the SMA can't handle them. */ - if (in_mad->mad_hdr.attr_id == IB_SMP_ATTR_SM_INFO) + if (in_mad->mad_hdr.attr_id == IB_SMP_ATTR_SM_INFO || + ((in_mad->mad_hdr.attr_id & IB_SMP_ATTR_VENDOR_MASK) == + IB_SMP_ATTR_VENDOR_MASK)) return IB_MAD_RESULT_SUCCESS; } else if (in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_PERF_MGMT || in_mad->mad_hdr.mgmt_class == MLX4_IB_VENDOR_CLASS1 || diff --git a/trunk/drivers/infiniband/hw/nes/nes.c b/trunk/drivers/infiniband/hw/nes/nes.c index 7140199f562e..7013da5e9eda 100644 --- a/trunk/drivers/infiniband/hw/nes/nes.c +++ b/trunk/drivers/infiniband/hw/nes/nes.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. + * Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two diff --git a/trunk/drivers/infiniband/hw/nes/nes.h b/trunk/drivers/infiniband/hw/nes/nes.h index c438e4691b3c..568b4f11380a 100644 --- a/trunk/drivers/infiniband/hw/nes/nes.h +++ b/trunk/drivers/infiniband/hw/nes/nes.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. + * Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two diff --git a/trunk/drivers/infiniband/hw/nes/nes_cm.c b/trunk/drivers/infiniband/hw/nes/nes_cm.c index a4972abedef1..425065b36b8c 100644 --- a/trunk/drivers/infiniband/hw/nes/nes_cm.c +++ b/trunk/drivers/infiniband/hw/nes/nes_cm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. + * Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -233,7 +233,6 @@ static int send_mpa_reject(struct nes_cm_node *cm_node) u8 *start_ptr = &start_addr; u8 **start_buff = &start_ptr; u16 buff_len = 0; - struct ietf_mpa_v1 *mpa_frame; skb = dev_alloc_skb(MAX_CM_BUFFER); if (!skb) { @@ -243,8 +242,6 @@ static int send_mpa_reject(struct nes_cm_node *cm_node) /* send an MPA reject frame */ cm_build_mpa_frame(cm_node, start_buff, &buff_len, NULL, MPA_KEY_REPLY); - mpa_frame = (struct ietf_mpa_v1 *)*start_buff; - mpa_frame->flags |= IETF_MPA_FLAGS_REJECT; form_cm_frame(skb, cm_node, NULL, 0, *start_buff, buff_len, SET_ACK | SET_FIN); cm_node->state = NES_CM_STATE_FIN_WAIT1; @@ -1363,7 +1360,8 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi if (!memcmp(nesadapter->arp_table[arpindex].mac_addr, neigh->ha, ETH_ALEN)) { /* Mac address same as in nes_arp_table */ - goto out; + ip_rt_put(rt); + return rc; } nes_manage_arp_cache(nesvnic->netdev, @@ -1379,8 +1377,6 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi neigh_event_send(neigh, NULL); } } - -out: rcu_read_unlock(); ip_rt_put(rt); return rc; diff --git a/trunk/drivers/infiniband/hw/nes/nes_cm.h b/trunk/drivers/infiniband/hw/nes/nes_cm.h index 4646e6666087..bdfa1fbb35fc 100644 --- a/trunk/drivers/infiniband/hw/nes/nes_cm.h +++ b/trunk/drivers/infiniband/hw/nes/nes_cm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. + * Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/trunk/drivers/infiniband/hw/nes/nes_context.h b/trunk/drivers/infiniband/hw/nes/nes_context.h index a69eef16d72d..b4393a16099d 100644 --- a/trunk/drivers/infiniband/hw/nes/nes_context.h +++ b/trunk/drivers/infiniband/hw/nes/nes_context.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. + * Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/trunk/drivers/infiniband/hw/nes/nes_hw.c b/trunk/drivers/infiniband/hw/nes/nes_hw.c index d42c9f435b1b..055f4b545df0 100644 --- a/trunk/drivers/infiniband/hw/nes/nes_hw.c +++ b/trunk/drivers/infiniband/hw/nes/nes_hw.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. + * Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/trunk/drivers/infiniband/hw/nes/nes_hw.h b/trunk/drivers/infiniband/hw/nes/nes_hw.h index d748e4b31b8d..0b590e152c6a 100644 --- a/trunk/drivers/infiniband/hw/nes/nes_hw.h +++ b/trunk/drivers/infiniband/hw/nes/nes_hw.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. +* Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/trunk/drivers/infiniband/hw/nes/nes_mgt.c b/trunk/drivers/infiniband/hw/nes/nes_mgt.c index 3ba7be369452..b3b2a240c6e9 100644 --- a/trunk/drivers/infiniband/hw/nes/nes_mgt.c +++ b/trunk/drivers/infiniband/hw/nes/nes_mgt.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2011 Intel-NE, Inc. All rights reserved. + * Copyright (c) 2006 - 2009 Intel-NE, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/trunk/drivers/infiniband/hw/nes/nes_mgt.h b/trunk/drivers/infiniband/hw/nes/nes_mgt.h index 4f7f701c4a81..8c8af254555a 100644 --- a/trunk/drivers/infiniband/hw/nes/nes_mgt.h +++ b/trunk/drivers/infiniband/hw/nes/nes_mgt.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2006 - 2011 Intel-NE, Inc. All rights reserved. +* Copyright (c) 2010 Intel-NE, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/trunk/drivers/infiniband/hw/nes/nes_nic.c b/trunk/drivers/infiniband/hw/nes/nes_nic.c index f3a3ecf8d09e..4b3fa711a247 100644 --- a/trunk/drivers/infiniband/hw/nes/nes_nic.c +++ b/trunk/drivers/infiniband/hw/nes/nes_nic.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. + * Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/trunk/drivers/infiniband/hw/nes/nes_user.h b/trunk/drivers/infiniband/hw/nes/nes_user.h index 4926de744488..71e133ab209b 100644 --- a/trunk/drivers/infiniband/hw/nes/nes_user.h +++ b/trunk/drivers/infiniband/hw/nes/nes_user.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. + * Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. * Copyright (c) 2005 Topspin Communications. All rights reserved. * Copyright (c) 2005 Cisco Systems. All rights reserved. * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved. diff --git a/trunk/drivers/infiniband/hw/nes/nes_utils.c b/trunk/drivers/infiniband/hw/nes/nes_utils.c index e98f4fc0b768..8b4c2ff54888 100644 --- a/trunk/drivers/infiniband/hw/nes/nes_utils.c +++ b/trunk/drivers/infiniband/hw/nes/nes_utils.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. + * Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/trunk/drivers/infiniband/hw/nes/nes_verbs.c b/trunk/drivers/infiniband/hw/nes/nes_verbs.c index 0927b5cc65d3..5095bc41c6cc 100644 --- a/trunk/drivers/infiniband/hw/nes/nes_verbs.c +++ b/trunk/drivers/infiniband/hw/nes/nes_verbs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. + * Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -3427,8 +3427,6 @@ static int nes_post_send(struct ib_qp *ibqp, struct ib_send_wr *ib_wr, set_wqe_32bit_value(wqe->wqe_words, NES_IWARP_SQ_FMR_WQE_LENGTH_LOW_IDX, ib_wr->wr.fast_reg.length); - set_wqe_32bit_value(wqe->wqe_words, - NES_IWARP_SQ_FMR_WQE_LENGTH_HIGH_IDX, 0); set_wqe_32bit_value(wqe->wqe_words, NES_IWARP_SQ_FMR_WQE_MR_STAG_IDX, ib_wr->wr.fast_reg.rkey); @@ -3726,7 +3724,7 @@ static int nes_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry) entry->opcode = IB_WC_SEND; break; case NES_IWARP_SQ_OP_LOCINV: - entry->opcode = IB_WC_LOCAL_INV; + entry->opcode = IB_WR_LOCAL_INV; break; case NES_IWARP_SQ_OP_FAST_REG: entry->opcode = IB_WC_FAST_REG_MR; diff --git a/trunk/drivers/infiniband/hw/nes/nes_verbs.h b/trunk/drivers/infiniband/hw/nes/nes_verbs.h index 0eff7c44d76b..fe6b6e92fa90 100644 --- a/trunk/drivers/infiniband/hw/nes/nes_verbs.h +++ b/trunk/drivers/infiniband/hw/nes/nes_verbs.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. + * Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two diff --git a/trunk/drivers/infiniband/hw/qib/qib_iba6120.c b/trunk/drivers/infiniband/hw/qib/qib_iba6120.c index d0c64d514813..4f18e2d332df 100644 --- a/trunk/drivers/infiniband/hw/qib/qib_iba6120.c +++ b/trunk/drivers/infiniband/hw/qib/qib_iba6120.c @@ -2105,7 +2105,7 @@ static void alloc_dummy_hdrq(struct qib_devdata *dd) dd->cspec->dummy_hdrq = dma_alloc_coherent(&dd->pcidev->dev, dd->rcd[0]->rcvhdrq_size, &dd->cspec->dummy_hdrq_phys, - GFP_ATOMIC | __GFP_COMP); + GFP_KERNEL | __GFP_COMP); if (!dd->cspec->dummy_hdrq) { qib_devinfo(dd->pcidev, "Couldn't allocate dummy hdrq\n"); /* fallback to just 0'ing */ diff --git a/trunk/drivers/infiniband/hw/qib/qib_pcie.c b/trunk/drivers/infiniband/hw/qib/qib_pcie.c index 0fde788e1100..f695061d688e 100644 --- a/trunk/drivers/infiniband/hw/qib/qib_pcie.c +++ b/trunk/drivers/infiniband/hw/qib/qib_pcie.c @@ -560,7 +560,7 @@ static int qib_tune_pcie_coalesce(struct qib_devdata *dd) * BIOS may not set PCIe bus-utilization parameters for best performance. * Check and optionally adjust them to maximize our throughput. */ -static int qib_pcie_caps; +static int qib_pcie_caps = 0x51; module_param_named(pcie_caps, qib_pcie_caps, int, S_IRUGO); MODULE_PARM_DESC(pcie_caps, "Max PCIe tuning: Payload (0..3), ReadReq (4..7)"); 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/infiniband/ulp/srpt/ib_srpt.c b/trunk/drivers/infiniband/ulp/srpt/ib_srpt.c index 2b73d43cd691..cd5d05e22a77 100644 --- a/trunk/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/trunk/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -69,8 +69,8 @@ MODULE_LICENSE("Dual BSD/GPL"); */ static u64 srpt_service_guid; -static DEFINE_SPINLOCK(srpt_dev_lock); /* Protects srpt_dev_list. */ -static LIST_HEAD(srpt_dev_list); /* List of srpt_device structures. */ +static spinlock_t srpt_dev_lock; /* Protects srpt_dev_list. */ +static struct list_head srpt_dev_list; /* List of srpt_device structures. */ static unsigned srp_max_req_size = DEFAULT_MAX_REQ_SIZE; module_param(srp_max_req_size, int, 0444); @@ -687,7 +687,6 @@ static struct srpt_ioctx **srpt_alloc_ioctx_ring(struct srpt_device *sdev, while (--i >= 0) srpt_free_ioctx(sdev, ring[i], dma_size, dir); kfree(ring); - ring = NULL; out: return ring; } @@ -2596,7 +2595,7 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, } ch->sess = transport_init_session(); - if (IS_ERR(ch->sess)) { + if (!ch->sess) { rej->reason = __constant_cpu_to_be32( SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); pr_debug("Failed to create session\n"); @@ -3265,7 +3264,8 @@ static void srpt_add_one(struct ib_device *device) for (i = 0; i < sdev->srq_size; ++i) srpt_post_recv(sdev, sdev->ioctx_ring[i]); - WARN_ON(sdev->device->phys_port_cnt > ARRAY_SIZE(sdev->port)); + WARN_ON(sdev->device->phys_port_cnt + > sizeof(sdev->port)/sizeof(sdev->port[0])); for (i = 1; i <= sdev->device->phys_port_cnt; i++) { sport = &sdev->port[i - 1]; @@ -4010,10 +4010,13 @@ static int __init srpt_init_module(void) goto out; } + spin_lock_init(&srpt_dev_lock); + INIT_LIST_HEAD(&srpt_dev_list); + + ret = -ENODEV; srpt_target = target_fabric_configfs_init(THIS_MODULE, "srpt"); - if (IS_ERR(srpt_target)) { + if (!srpt_target) { printk(KERN_ERR "couldn't register\n"); - ret = PTR_ERR(srpt_target); goto out; } diff --git a/trunk/drivers/infiniband/ulp/srpt/ib_srpt.h b/trunk/drivers/infiniband/ulp/srpt/ib_srpt.h index 61e52b830816..b4b4bbcd7f16 100644 --- a/trunk/drivers/infiniband/ulp/srpt/ib_srpt.h +++ b/trunk/drivers/infiniband/ulp/srpt/ib_srpt.h @@ -35,6 +35,7 @@ #ifndef IB_SRPT_H #define IB_SRPT_H +#include #include #include #include diff --git a/trunk/drivers/input/evdev.c b/trunk/drivers/input/evdev.c index afc166fcc3d9..76457d50bc34 100644 --- a/trunk/drivers/input/evdev.c +++ b/trunk/drivers/input/evdev.c @@ -386,7 +386,7 @@ static ssize_t evdev_read(struct file *file, char __user *buffer, struct evdev_client *client = file->private_data; struct evdev *evdev = client->evdev; struct input_event event; - int retval = 0; + int retval; if (count < input_event_size()) return -EINVAL; diff --git a/trunk/drivers/input/keyboard/twl4030_keypad.c b/trunk/drivers/input/keyboard/twl4030_keypad.c index 67bec14e8b96..a588578037eb 100644 --- a/trunk/drivers/input/keyboard/twl4030_keypad.c +++ b/trunk/drivers/input/keyboard/twl4030_keypad.c @@ -34,6 +34,7 @@ #include #include + /* * The TWL4030 family chips include a keypad controller that supports * up to an 8x8 switch matrix. The controller can issue system wakeup @@ -301,7 +302,7 @@ static int __devinit twl4030_kp_program(struct twl4030_keypad *kp) if (twl4030_kpwrite_u8(kp, i, KEYP_DEB) < 0) return -EIO; - /* Set timeout period to 200 ms */ + /* Set timeout period to 100 ms */ i = KEYP_PERIOD_US(200000, PTV_PRESCALER); if (twl4030_kpwrite_u8(kp, (i & 0xFF), KEYP_TIMEOUT_L) < 0) return -EIO; @@ -465,3 +466,4 @@ MODULE_AUTHOR("Texas Instruments"); MODULE_DESCRIPTION("TWL4030 Keypad Driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:twl4030_keypad"); + diff --git a/trunk/drivers/input/serio/i8042-x86ia64io.h b/trunk/drivers/input/serio/i8042-x86ia64io.h index 5ec774d6c82b..b4cfc6c8be89 100644 --- a/trunk/drivers/input/serio/i8042-x86ia64io.h +++ b/trunk/drivers/input/serio/i8042-x86ia64io.h @@ -512,13 +512,6 @@ static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1720"), }, }, - { - /* Lenovo Ideapad U455 */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "20046"), - }, - }, { } }; diff --git a/trunk/drivers/input/serio/serio_raw.c b/trunk/drivers/input/serio/serio_raw.c index 4494233d331a..8250299fd64f 100644 --- a/trunk/drivers/input/serio/serio_raw.c +++ b/trunk/drivers/input/serio/serio_raw.c @@ -164,8 +164,7 @@ static ssize_t serio_raw_read(struct file *file, char __user *buffer, struct serio_raw_client *client = file->private_data; struct serio_raw *serio_raw = client->serio_raw; char uninitialized_var(c); - ssize_t read = 0; - int retval; + ssize_t retval = 0; if (serio_raw->dead) return -ENODEV; @@ -181,15 +180,13 @@ static ssize_t serio_raw_read(struct file *file, char __user *buffer, if (serio_raw->dead) return -ENODEV; - while (read < count && serio_raw_fetch_byte(serio_raw, &c)) { - if (put_user(c, buffer++)) { - retval = -EFAULT; - break; - } - read++; + while (retval < count && serio_raw_fetch_byte(serio_raw, &c)) { + if (put_user(c, buffer++)) + return -EFAULT; + retval++; } - return read ?: retval; + return retval; } static ssize_t serio_raw_write(struct file *file, const char __user *buffer, diff --git a/trunk/drivers/iommu/amd_iommu.c b/trunk/drivers/iommu/amd_iommu.c index f75e0608be5b..cce1f03b8895 100644 --- a/trunk/drivers/iommu/amd_iommu.c +++ b/trunk/drivers/iommu/amd_iommu.c @@ -2863,9 +2863,6 @@ static unsigned device_dma_ops_init(void) for_each_pci_dev(pdev) { if (!check_device(&pdev->dev)) { - - iommu_ignore_device(&pdev->dev); - unhandled += 1; continue; } diff --git a/trunk/drivers/iommu/msm_iommu.c b/trunk/drivers/iommu/msm_iommu.c index cee307e86606..08a90b88e40d 100644 --- a/trunk/drivers/iommu/msm_iommu.c +++ b/trunk/drivers/iommu/msm_iommu.c @@ -482,19 +482,23 @@ static size_t msm_iommu_unmap(struct iommu_domain *domain, unsigned long va, priv = domain->priv; - if (!priv) + if (!priv) { + ret = -ENODEV; goto fail; + } fl_table = priv->pgtable; if (len != SZ_16M && len != SZ_1M && len != SZ_64K && len != SZ_4K) { pr_debug("Bad length: %d\n", len); + ret = -EINVAL; goto fail; } if (!fl_table) { pr_debug("Null page table\n"); + ret = -EINVAL; goto fail; } @@ -503,6 +507,7 @@ static size_t msm_iommu_unmap(struct iommu_domain *domain, unsigned long va, if (*fl_pte == 0) { pr_debug("First level PTE is 0\n"); + ret = -ENODEV; goto fail; } 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/leds/leds-lm3530.c b/trunk/drivers/leds/leds-lm3530.c index e59c166a0ce2..45e6878d7374 100644 --- a/trunk/drivers/leds/leds-lm3530.c +++ b/trunk/drivers/leds/leds-lm3530.c @@ -164,8 +164,8 @@ static int lm3530_init_registers(struct lm3530_data *drvdata) if (drvdata->mode == LM3530_BL_MODE_ALS) { if (pltfm->als_vmax == 0) { - pltfm->als_vmin = 0; - pltfm->als_vmax = LM3530_ALS_WINDOW_mV; + pltfm->als_vmin = als_vmin = 0; + pltfm->als_vmin = als_vmax = LM3530_ALS_WINDOW_mV; } als_vmin = pltfm->als_vmin; diff --git a/trunk/drivers/md/dm-raid.c b/trunk/drivers/md/dm-raid.c index 86cb7e5d83d5..c2907d836e4e 100644 --- a/trunk/drivers/md/dm-raid.c +++ b/trunk/drivers/md/dm-raid.c @@ -56,8 +56,7 @@ struct raid_dev { struct raid_set { struct dm_target *ti; - uint32_t bitmap_loaded; - uint32_t print_flags; + uint64_t print_flags; struct mddev md; struct raid_type *raid_type; @@ -1086,7 +1085,7 @@ static int raid_status(struct dm_target *ti, status_type_t type, raid_param_cnt += 2; } - raid_param_cnt += (hweight32(rs->print_flags & ~DMPF_REBUILD) * 2); + raid_param_cnt += (hweight64(rs->print_flags & ~DMPF_REBUILD) * 2); if (rs->print_flags & (DMPF_SYNC | DMPF_NOSYNC)) raid_param_cnt--; @@ -1198,12 +1197,7 @@ static void raid_resume(struct dm_target *ti) { struct raid_set *rs = ti->private; - if (!rs->bitmap_loaded) { - bitmap_load(&rs->md); - rs->bitmap_loaded = 1; - } else - md_wakeup_thread(rs->md.thread); - + bitmap_load(&rs->md); mddev_resume(&rs->md); } diff --git a/trunk/drivers/md/md.c b/trunk/drivers/md/md.c index ce88755baf4a..9417ae2fa0bb 100644 --- a/trunk/drivers/md/md.c +++ b/trunk/drivers/md/md.c @@ -7333,8 +7333,7 @@ void md_do_sync(struct mddev *mddev) printk(KERN_INFO "md: checkpointing %s of %s.\n", desc, mdname(mddev)); - mddev->recovery_cp = - mddev->curr_resync_completed; + mddev->recovery_cp = mddev->curr_resync; } } else mddev->recovery_cp = MaxSector; @@ -7352,9 +7351,9 @@ void md_do_sync(struct mddev *mddev) rcu_read_unlock(); } } - skip: set_bit(MD_CHANGE_DEVS, &mddev->flags); + skip: if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery)) { /* We completed so min/max setting can be forgotten if used. */ if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) diff --git a/trunk/drivers/media/dvb/dvb-usb/anysee.c b/trunk/drivers/media/dvb/dvb-usb/anysee.c index cf0c318d6989..1455e2644ab5 100644 --- a/trunk/drivers/media/dvb/dvb-usb/anysee.c +++ b/trunk/drivers/media/dvb/dvb-usb/anysee.c @@ -887,7 +887,8 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap) /* attach demod */ adap->fe_adap[state->fe_id].fe = dvb_attach(cxd2820r_attach, - &anysee_cxd2820r_config, &adap->dev->i2c_adap); + &anysee_cxd2820r_config, &adap->dev->i2c_adap, + NULL); state->has_ci = true; @@ -1188,14 +1189,6 @@ static int anysee_ci_init(struct dvb_usb_device *d) if (ret) return ret; - ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 2)|(0 << 1)|(0 << 0), 0x07); - if (ret) - return ret; - - ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 2)|(1 << 1)|(1 << 0), 0x07); - if (ret) - return ret; - ret = dvb_ca_en50221_init(&d->adapter[0].dvb_adap, &state->ci, 0, 1); if (ret) return ret; diff --git a/trunk/drivers/media/dvb/dvb-usb/cinergyT2-fe.c b/trunk/drivers/media/dvb/dvb-usb/cinergyT2-fe.c index 1efc028a76c9..8a57ed8272de 100644 --- a/trunk/drivers/media/dvb/dvb-usb/cinergyT2-fe.c +++ b/trunk/drivers/media/dvb/dvb-usb/cinergyT2-fe.c @@ -276,15 +276,14 @@ static int cinergyt2_fe_set_frontend(struct dvb_frontend *fe) param.flags = 0; switch (fep->bandwidth_hz) { - default: case 8000000: - param.bandwidth = 8; + param.bandwidth = 0; break; case 7000000: - param.bandwidth = 7; + param.bandwidth = 1; break; case 6000000: - param.bandwidth = 6; + param.bandwidth = 2; break; } diff --git a/trunk/drivers/media/dvb/frontends/cxd2820r.h b/trunk/drivers/media/dvb/frontends/cxd2820r.h index 5aa306ebb7ef..cf0f546aa1d1 100644 --- a/trunk/drivers/media/dvb/frontends/cxd2820r.h +++ b/trunk/drivers/media/dvb/frontends/cxd2820r.h @@ -77,12 +77,14 @@ struct cxd2820r_config { (defined(CONFIG_DVB_CXD2820R_MODULE) && defined(MODULE)) extern struct dvb_frontend *cxd2820r_attach( const struct cxd2820r_config *config, - struct i2c_adapter *i2c + struct i2c_adapter *i2c, + struct dvb_frontend *fe ); #else static inline struct dvb_frontend *cxd2820r_attach( const struct cxd2820r_config *config, - struct i2c_adapter *i2c + struct i2c_adapter *i2c, + struct dvb_frontend *fe ) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); diff --git a/trunk/drivers/media/dvb/frontends/cxd2820r_core.c b/trunk/drivers/media/dvb/frontends/cxd2820r_core.c index 5c7c2aaf9bf5..caae7f79c837 100644 --- a/trunk/drivers/media/dvb/frontends/cxd2820r_core.c +++ b/trunk/drivers/media/dvb/frontends/cxd2820r_core.c @@ -482,19 +482,10 @@ static enum dvbfe_search cxd2820r_search(struct dvb_frontend *fe) /* switch between DVB-T and DVB-T2 when tune fails */ if (priv->last_tune_failed) { - if (priv->delivery_system == SYS_DVBT) { - ret = cxd2820r_sleep_t(fe); - if (ret) - goto error; - + if (priv->delivery_system == SYS_DVBT) c->delivery_system = SYS_DVBT2; - } else if (priv->delivery_system == SYS_DVBT2) { - ret = cxd2820r_sleep_t2(fe); - if (ret) - goto error; - + else if (priv->delivery_system == SYS_DVBT2) c->delivery_system = SYS_DVBT; - } } /* set frontend */ @@ -571,7 +562,7 @@ static const struct dvb_frontend_ops cxd2820r_ops = { .delsys = { SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A }, /* default: DVB-T/T2 */ .info = { - .name = "Sony CXD2820R", + .name = "Sony CXD2820R (DVB-T/T2)", .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | @@ -581,9 +572,7 @@ static const struct dvb_frontend_ops cxd2820r_ops = { FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_QAM_16 | - FE_CAN_QAM_32 | FE_CAN_QAM_64 | - FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | @@ -613,7 +602,8 @@ static const struct dvb_frontend_ops cxd2820r_ops = { }; struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *cfg, - struct i2c_adapter *i2c) + struct i2c_adapter *i2c, + struct dvb_frontend *fe) { struct cxd2820r_priv *priv = NULL; int ret; diff --git a/trunk/drivers/media/video/atmel-isi.c b/trunk/drivers/media/video/atmel-isi.c index ec3f6a06f9c3..9fe4519176a4 100644 --- a/trunk/drivers/media/video/atmel-isi.c +++ b/trunk/drivers/media/video/atmel-isi.c @@ -922,9 +922,7 @@ static int __devexit atmel_isi_remove(struct platform_device *pdev) isi->fb_descriptors_phys); iounmap(isi->regs); - clk_unprepare(isi->mck); clk_put(isi->mck); - clk_unprepare(isi->pclk); clk_put(isi->pclk); kfree(isi); @@ -957,10 +955,6 @@ static int __devinit atmel_isi_probe(struct platform_device *pdev) if (IS_ERR(pclk)) return PTR_ERR(pclk); - ret = clk_prepare(pclk); - if (ret) - goto err_clk_prepare_pclk; - isi = kzalloc(sizeof(struct atmel_isi), GFP_KERNEL); if (!isi) { ret = -ENOMEM; @@ -984,10 +978,6 @@ static int __devinit atmel_isi_probe(struct platform_device *pdev) goto err_clk_get; } - ret = clk_prepare(isi->mck); - if (ret) - goto err_clk_prepare_mck; - /* Set ISI_MCK's frequency, it should be faster than pixel clock */ ret = clk_set_rate(isi->mck, pdata->mck_hz); if (ret < 0) @@ -1069,14 +1059,10 @@ static int __devinit atmel_isi_probe(struct platform_device *pdev) isi->fb_descriptors_phys); err_alloc_descriptors: err_set_mck_rate: - clk_unprepare(isi->mck); -err_clk_prepare_mck: clk_put(isi->mck); err_clk_get: kfree(isi); err_alloc_isi: - clk_unprepare(pclk); -err_clk_prepare_pclk: clk_put(pclk); return ret; diff --git a/trunk/drivers/media/video/em28xx/em28xx-dvb.c b/trunk/drivers/media/video/em28xx/em28xx-dvb.c index aabbf4854f66..9449423098e0 100644 --- a/trunk/drivers/media/video/em28xx/em28xx-dvb.c +++ b/trunk/drivers/media/video/em28xx/em28xx-dvb.c @@ -853,7 +853,8 @@ static int em28xx_dvb_init(struct em28xx *dev) case EM28174_BOARD_PCTV_290E: dvb->fe[0] = dvb_attach(cxd2820r_attach, &em28xx_cxd2820r_config, - &dev->i2c_adap); + &dev->i2c_adap, + NULL); if (dvb->fe[0]) { /* FE 0 attach tuner */ if (!dvb_attach(tda18271_attach, diff --git a/trunk/drivers/mfd/mcp-core.c b/trunk/drivers/mfd/mcp-core.c index 86cc3f7841cd..63be60bc3455 100644 --- a/trunk/drivers/mfd/mcp-core.c +++ b/trunk/drivers/mfd/mcp-core.c @@ -26,9 +26,35 @@ #define to_mcp(d) container_of(d, struct mcp, attached_device) #define to_mcp_driver(d) container_of(d, struct mcp_driver, drv) +static const struct mcp_device_id *mcp_match_id(const struct mcp_device_id *id, + const char *codec) +{ + while (id->name[0]) { + if (strcmp(codec, id->name) == 0) + return id; + id++; + } + return NULL; +} + +const struct mcp_device_id *mcp_get_device_id(const struct mcp *mcp) +{ + const struct mcp_driver *driver = + to_mcp_driver(mcp->attached_device.driver); + + return mcp_match_id(driver->id_table, mcp->codec); +} +EXPORT_SYMBOL(mcp_get_device_id); + static int mcp_bus_match(struct device *dev, struct device_driver *drv) { - return 1; + const struct mcp *mcp = to_mcp(dev); + const struct mcp_driver *driver = to_mcp_driver(drv); + + if (driver->id_table) + return !!mcp_match_id(driver->id_table, mcp->codec); + + return 0; } static int mcp_bus_probe(struct device *dev) @@ -74,9 +100,18 @@ static int mcp_bus_resume(struct device *dev) return ret; } +static int mcp_bus_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct mcp *mcp = to_mcp(dev); + + add_uevent_var(env, "MODALIAS=%s%s", MCP_MODULE_PREFIX, mcp->codec); + return 0; +} + static struct bus_type mcp_bus_type = { .name = "mcp", .match = mcp_bus_match, + .uevent = mcp_bus_uevent, .probe = mcp_bus_probe, .remove = mcp_bus_remove, .suspend = mcp_bus_suspend, @@ -93,11 +128,9 @@ static struct bus_type mcp_bus_type = { */ void mcp_set_telecom_divisor(struct mcp *mcp, unsigned int div) { - unsigned long flags; - - spin_lock_irqsave(&mcp->lock, flags); + spin_lock_irq(&mcp->lock); mcp->ops->set_telecom_divisor(mcp, div); - spin_unlock_irqrestore(&mcp->lock, flags); + spin_unlock_irq(&mcp->lock); } EXPORT_SYMBOL(mcp_set_telecom_divisor); @@ -110,11 +143,9 @@ EXPORT_SYMBOL(mcp_set_telecom_divisor); */ void mcp_set_audio_divisor(struct mcp *mcp, unsigned int div) { - unsigned long flags; - - spin_lock_irqsave(&mcp->lock, flags); + spin_lock_irq(&mcp->lock); mcp->ops->set_audio_divisor(mcp, div); - spin_unlock_irqrestore(&mcp->lock, flags); + spin_unlock_irq(&mcp->lock); } EXPORT_SYMBOL(mcp_set_audio_divisor); @@ -167,11 +198,10 @@ EXPORT_SYMBOL(mcp_reg_read); */ void mcp_enable(struct mcp *mcp) { - unsigned long flags; - spin_lock_irqsave(&mcp->lock, flags); + spin_lock_irq(&mcp->lock); if (mcp->use_count++ == 0) mcp->ops->enable(mcp); - spin_unlock_irqrestore(&mcp->lock, flags); + spin_unlock_irq(&mcp->lock); } EXPORT_SYMBOL(mcp_enable); @@ -217,9 +247,14 @@ struct mcp *mcp_host_alloc(struct device *parent, size_t size) } EXPORT_SYMBOL(mcp_host_alloc); -int mcp_host_register(struct mcp *mcp) +int mcp_host_register(struct mcp *mcp, void *pdata) { + if (!mcp->codec) + return -EINVAL; + + mcp->attached_device.platform_data = pdata; dev_set_name(&mcp->attached_device, "mcp0"); + request_module("%s%s", MCP_MODULE_PREFIX, mcp->codec); return device_register(&mcp->attached_device); } EXPORT_SYMBOL(mcp_host_register); diff --git a/trunk/drivers/mfd/mcp-sa11x0.c b/trunk/drivers/mfd/mcp-sa11x0.c index 02c53a0766c4..9adc2eb69492 100644 --- a/trunk/drivers/mfd/mcp-sa11x0.c +++ b/trunk/drivers/mfd/mcp-sa11x0.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -26,12 +27,19 @@ #include #include -#include - +/* Register offsets */ +#define MCCR0 0x00 +#define MCDR0 0x08 +#define MCDR1 0x0C +#define MCDR2 0x10 +#define MCSR 0x18 +#define MCCR1 0x00 struct mcp_sa11x0 { - u32 mccr0; - u32 mccr1; + u32 mccr0; + u32 mccr1; + unsigned char *mccr0_base; + unsigned char *mccr1_base; }; #define priv(mcp) ((struct mcp_sa11x0 *)mcp_priv(mcp)) @@ -39,25 +47,25 @@ struct mcp_sa11x0 { static void mcp_sa11x0_set_telecom_divisor(struct mcp *mcp, unsigned int divisor) { - unsigned int mccr0; + struct mcp_sa11x0 *priv = priv(mcp); divisor /= 32; - mccr0 = Ser4MCCR0 & ~0x00007f00; - mccr0 |= divisor << 8; - Ser4MCCR0 = mccr0; + priv->mccr0 &= ~0x00007f00; + priv->mccr0 |= divisor << 8; + __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0); } static void mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor) { - unsigned int mccr0; + struct mcp_sa11x0 *priv = priv(mcp); divisor /= 32; - mccr0 = Ser4MCCR0 & ~0x0000007f; - mccr0 |= divisor; - Ser4MCCR0 = mccr0; + priv->mccr0 &= ~0x0000007f; + priv->mccr0 |= divisor; + __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0); } /* @@ -71,12 +79,16 @@ mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val) { int ret = -ETIME; int i; + u32 mcpreg; + struct mcp_sa11x0 *priv = priv(mcp); - Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff); + mcpreg = reg << 17 | MCDR2_Wr | (val & 0xffff); + __raw_writel(mcpreg, priv->mccr0_base + MCDR2); for (i = 0; i < 2; i++) { udelay(mcp->rw_timeout); - if (Ser4MCSR & MCSR_CWC) { + mcpreg = __raw_readl(priv->mccr0_base + MCSR); + if (mcpreg & MCSR_CWC) { ret = 0; break; } @@ -97,13 +109,18 @@ mcp_sa11x0_read(struct mcp *mcp, unsigned int reg) { int ret = -ETIME; int i; + u32 mcpreg; + struct mcp_sa11x0 *priv = priv(mcp); - Ser4MCDR2 = reg << 17 | MCDR2_Rd; + mcpreg = reg << 17 | MCDR2_Rd; + __raw_writel(mcpreg, priv->mccr0_base + MCDR2); for (i = 0; i < 2; i++) { udelay(mcp->rw_timeout); - if (Ser4MCSR & MCSR_CRC) { - ret = Ser4MCDR2 & 0xffff; + mcpreg = __raw_readl(priv->mccr0_base + MCSR); + if (mcpreg & MCSR_CRC) { + ret = __raw_readl(priv->mccr0_base + MCDR2) + & 0xffff; break; } } @@ -116,13 +133,19 @@ mcp_sa11x0_read(struct mcp *mcp, unsigned int reg) static void mcp_sa11x0_enable(struct mcp *mcp) { - Ser4MCSR = -1; - Ser4MCCR0 |= MCCR0_MCE; + struct mcp_sa11x0 *priv = priv(mcp); + + __raw_writel(-1, priv->mccr0_base + MCSR); + priv->mccr0 |= MCCR0_MCE; + __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0); } static void mcp_sa11x0_disable(struct mcp *mcp) { - Ser4MCCR0 &= ~MCCR0_MCE; + struct mcp_sa11x0 *priv = priv(mcp); + + priv->mccr0 &= ~MCCR0_MCE; + __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0); } /* @@ -142,50 +165,69 @@ static int mcp_sa11x0_probe(struct platform_device *pdev) struct mcp_plat_data *data = pdev->dev.platform_data; struct mcp *mcp; int ret; + struct mcp_sa11x0 *priv; + struct resource *res_mem0, *res_mem1; + u32 size0, size1; if (!data) return -ENODEV; - if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp")) + if (!data->codec) + return -ENODEV; + + res_mem0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_mem0) + return -ENODEV; + size0 = res_mem0->end - res_mem0->start + 1; + + res_mem1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res_mem1) + return -ENODEV; + size1 = res_mem1->end - res_mem1->start + 1; + + if (!request_mem_region(res_mem0->start, size0, "sa11x0-mcp")) return -EBUSY; + if (!request_mem_region(res_mem1->start, size1, "sa11x0-mcp")) { + ret = -EBUSY; + goto release; + } + mcp = mcp_host_alloc(&pdev->dev, sizeof(struct mcp_sa11x0)); if (!mcp) { ret = -ENOMEM; - goto release; + goto release2; } + priv = priv(mcp); + mcp->owner = THIS_MODULE; mcp->ops = &mcp_sa11x0; mcp->sclk_rate = data->sclk_rate; - mcp->dma_audio_rd = DMA_Ser4MCP0Rd; - mcp->dma_audio_wr = DMA_Ser4MCP0Wr; - mcp->dma_telco_rd = DMA_Ser4MCP1Rd; - mcp->dma_telco_wr = DMA_Ser4MCP1Wr; - mcp->gpio_base = data->gpio_base; + mcp->dma_audio_rd = DDAR_DevAdd(res_mem0->start + MCDR0) + + DDAR_DevRd + DDAR_Brst4 + DDAR_8BitDev; + mcp->dma_audio_wr = DDAR_DevAdd(res_mem0->start + MCDR0) + + DDAR_DevWr + DDAR_Brst4 + DDAR_8BitDev; + mcp->dma_telco_rd = DDAR_DevAdd(res_mem0->start + MCDR1) + + DDAR_DevRd + DDAR_Brst4 + DDAR_8BitDev; + mcp->dma_telco_wr = DDAR_DevAdd(res_mem0->start + MCDR1) + + DDAR_DevWr + DDAR_Brst4 + DDAR_8BitDev; + mcp->codec = data->codec; platform_set_drvdata(pdev, mcp); - if (machine_is_assabet()) { - ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); - } - - /* - * Setup the PPC unit correctly. - */ - PPDR &= ~PPC_RXD4; - PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; - PSDR |= PPC_RXD4; - PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); - PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); - /* * Initialise device. Note that we initially * set the sampling rate to minimum. */ - Ser4MCSR = -1; - Ser4MCCR1 = data->mccr1; - Ser4MCCR0 = data->mccr0 | 0x7f7f; + priv->mccr0_base = ioremap(res_mem0->start, size0); + priv->mccr1_base = ioremap(res_mem1->start, size1); + + __raw_writel(-1, priv->mccr0_base + MCSR); + priv->mccr1 = data->mccr1; + priv->mccr0 = data->mccr0 | 0x7f7f; + __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0); + __raw_writel(priv->mccr1, priv->mccr1_base + MCCR1); /* * Calculate the read/write timeout (us) from the bit clock @@ -195,36 +237,53 @@ static int mcp_sa11x0_probe(struct platform_device *pdev) mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) / mcp->sclk_rate; - ret = mcp_host_register(mcp); + ret = mcp_host_register(mcp, data->codec_pdata); if (ret == 0) goto out; + release2: + release_mem_region(res_mem1->start, size1); release: - release_mem_region(0x80060000, 0x60); + release_mem_region(res_mem0->start, size0); platform_set_drvdata(pdev, NULL); out: return ret; } -static int mcp_sa11x0_remove(struct platform_device *dev) +static int mcp_sa11x0_remove(struct platform_device *pdev) { - struct mcp *mcp = platform_get_drvdata(dev); + struct mcp *mcp = platform_get_drvdata(pdev); + struct mcp_sa11x0 *priv = priv(mcp); + struct resource *res_mem; + u32 size; - platform_set_drvdata(dev, NULL); + platform_set_drvdata(pdev, NULL); mcp_host_unregister(mcp); - release_mem_region(0x80060000, 0x60); + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res_mem) { + size = res_mem->end - res_mem->start + 1; + release_mem_region(res_mem->start, size); + } + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res_mem) { + size = res_mem->end - res_mem->start + 1; + release_mem_region(res_mem->start, size); + } + iounmap(priv->mccr0_base); + iounmap(priv->mccr1_base); return 0; } static int mcp_sa11x0_suspend(struct platform_device *dev, pm_message_t state) { struct mcp *mcp = platform_get_drvdata(dev); + struct mcp_sa11x0 *priv = priv(mcp); + u32 mccr0; - priv(mcp)->mccr0 = Ser4MCCR0; - priv(mcp)->mccr1 = Ser4MCCR1; - Ser4MCCR0 &= ~MCCR0_MCE; + mccr0 = priv->mccr0 & ~MCCR0_MCE; + __raw_writel(mccr0, priv->mccr0_base + MCCR0); return 0; } @@ -232,9 +291,10 @@ static int mcp_sa11x0_suspend(struct platform_device *dev, pm_message_t state) static int mcp_sa11x0_resume(struct platform_device *dev) { struct mcp *mcp = platform_get_drvdata(dev); + struct mcp_sa11x0 *priv = priv(mcp); - Ser4MCCR1 = priv(mcp)->mccr1; - Ser4MCCR0 = priv(mcp)->mccr0; + __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0); + __raw_writel(priv->mccr1, priv->mccr1_base + MCCR1); return 0; } @@ -251,6 +311,7 @@ static struct platform_driver mcp_sa11x0_driver = { .resume = mcp_sa11x0_resume, .driver = { .name = "sa11x0-mcp", + .owner = THIS_MODULE, }, }; diff --git a/trunk/drivers/mfd/twl6040-core.c b/trunk/drivers/mfd/twl6040-core.c index b2d8e512d3cb..dda86293dc9f 100644 --- a/trunk/drivers/mfd/twl6040-core.c +++ b/trunk/drivers/mfd/twl6040-core.c @@ -282,7 +282,6 @@ int twl6040_power(struct twl6040 *twl6040, int on) /* Default PLL configuration after power up */ twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL; twl6040->sysclk = 19200000; - twl6040->mclk = 32768; } else { /* already powered-down */ if (!twl6040->power_count) { @@ -306,7 +305,6 @@ int twl6040_power(struct twl6040 *twl6040, int on) twl6040_power_down(twl6040); } twl6040->sysclk = 0; - twl6040->mclk = 0; } out: @@ -326,38 +324,23 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, hppllctl = twl6040_reg_read(twl6040, TWL6040_REG_HPPLLCTL); lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL); - /* Force full reconfiguration when switching between PLL */ - if (pll_id != twl6040->pll) { - twl6040->sysclk = 0; - twl6040->mclk = 0; - } - switch (pll_id) { case TWL6040_SYSCLK_SEL_LPPLL: /* low-power PLL divider */ - /* Change the sysclk configuration only if it has been canged */ - if (twl6040->sysclk != freq_out) { - switch (freq_out) { - case 17640000: - lppllctl |= TWL6040_LPLLFIN; - break; - case 19200000: - lppllctl &= ~TWL6040_LPLLFIN; - break; - default: - dev_err(twl6040->dev, - "freq_out %d not supported\n", - freq_out); - ret = -EINVAL; - goto pll_out; - } - twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, - lppllctl); - } - - /* The PLL in use has not been change, we can exit */ - if (twl6040->pll == pll_id) + switch (freq_out) { + case 17640000: + lppllctl |= TWL6040_LPLLFIN; + break; + case 19200000: + lppllctl &= ~TWL6040_LPLLFIN; break; + default: + dev_err(twl6040->dev, + "freq_out %d not supported\n", freq_out); + ret = -EINVAL; + goto pll_out; + } + twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); switch (freq_in) { case 32768: @@ -388,56 +371,48 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, goto pll_out; } - if (twl6040->mclk != freq_in) { - hppllctl &= ~TWL6040_MCLK_MSK; - - switch (freq_in) { - case 12000000: - /* PLL enabled, active mode */ - hppllctl |= TWL6040_MCLK_12000KHZ | - TWL6040_HPLLENA; - break; - case 19200000: - /* - * PLL disabled - * (enable PLL if MCLK jitter quality - * doesn't meet specification) - */ - hppllctl |= TWL6040_MCLK_19200KHZ; - break; - case 26000000: - /* PLL enabled, active mode */ - hppllctl |= TWL6040_MCLK_26000KHZ | - TWL6040_HPLLENA; - break; - case 38400000: - /* PLL enabled, active mode */ - hppllctl |= TWL6040_MCLK_38400KHZ | - TWL6040_HPLLENA; - break; - default: - dev_err(twl6040->dev, - "freq_in %d not supported\n", freq_in); - ret = -EINVAL; - goto pll_out; - } + hppllctl &= ~TWL6040_MCLK_MSK; + switch (freq_in) { + case 12000000: + /* PLL enabled, active mode */ + hppllctl |= TWL6040_MCLK_12000KHZ | + TWL6040_HPLLENA; + break; + case 19200000: /* - * enable clock slicer to ensure input waveform is - * square + * PLL disabled + * (enable PLL if MCLK jitter quality + * doesn't meet specification) */ - hppllctl |= TWL6040_HPLLSQRENA; - - twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL, - hppllctl); - usleep_range(500, 700); - lppllctl |= TWL6040_HPLLSEL; - twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, - lppllctl); - lppllctl &= ~TWL6040_LPLLENA; - twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, - lppllctl); + hppllctl |= TWL6040_MCLK_19200KHZ; + break; + case 26000000: + /* PLL enabled, active mode */ + hppllctl |= TWL6040_MCLK_26000KHZ | + TWL6040_HPLLENA; + break; + case 38400000: + /* PLL enabled, active mode */ + hppllctl |= TWL6040_MCLK_38400KHZ | + TWL6040_HPLLENA; + break; + default: + dev_err(twl6040->dev, + "freq_in %d not supported\n", freq_in); + ret = -EINVAL; + goto pll_out; } + + /* enable clock slicer to ensure input waveform is square */ + hppllctl |= TWL6040_HPLLSQRENA; + + twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL, hppllctl); + usleep_range(500, 700); + lppllctl |= TWL6040_HPLLSEL; + twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); + lppllctl &= ~TWL6040_LPLLENA; + twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); break; default: dev_err(twl6040->dev, "unknown pll id %d\n", pll_id); @@ -446,7 +421,6 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, } twl6040->sysclk = freq_out; - twl6040->mclk = freq_in; twl6040->pll = pll_id; pll_out: diff --git a/trunk/drivers/mfd/ucb1x00-core.c b/trunk/drivers/mfd/ucb1x00-core.c index febc90cdef7e..91c4f25e0e55 100644 --- a/trunk/drivers/mfd/ucb1x00-core.c +++ b/trunk/drivers/mfd/ucb1x00-core.c @@ -36,6 +36,15 @@ static DEFINE_MUTEX(ucb1x00_mutex); static LIST_HEAD(ucb1x00_drivers); static LIST_HEAD(ucb1x00_devices); +static struct mcp_device_id ucb1x00_id[] = { + { "ucb1x00", 0 }, /* auto-detection */ + { "ucb1200", UCB_ID_1200 }, + { "ucb1300", UCB_ID_1300 }, + { "tc35143", UCB_ID_TC35143 }, + { } +}; +MODULE_DEVICE_TABLE(mcp, ucb1x00_id); + /** * ucb1x00_io_set_dir - set IO direction * @ucb: UCB1x00 structure describing chip @@ -148,22 +157,16 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset { struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio); unsigned long flags; - unsigned old, mask = 1 << offset; spin_lock_irqsave(&ucb->io_lock, flags); - old = ucb->io_out; + ucb->io_dir |= (1 << offset); + ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); + if (value) - ucb->io_out |= mask; + ucb->io_out |= 1 << offset; else - ucb->io_out &= ~mask; - - if (old != ucb->io_out) - ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); - - if (!(ucb->io_dir & mask)) { - ucb->io_dir |= mask; - ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); - } + ucb->io_out &= ~(1 << offset); + ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); spin_unlock_irqrestore(&ucb->io_lock, flags); return 0; @@ -533,17 +536,33 @@ static struct class ucb1x00_class = { static int ucb1x00_probe(struct mcp *mcp) { + const struct mcp_device_id *mid; struct ucb1x00 *ucb; struct ucb1x00_driver *drv; + struct ucb1x00_plat_data *pdata; unsigned int id; int ret = -ENODEV; int temp; mcp_enable(mcp); id = mcp_reg_read(mcp, UCB_ID); + mid = mcp_get_device_id(mcp); - if (id != UCB_ID_1200 && id != UCB_ID_1300 && id != UCB_ID_TC35143) { - printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id); + if (mid && mid->driver_data) { + if (id != mid->driver_data) { + printk(KERN_WARNING "%s wrong ID %04x found: %04x\n", + mid->name, (unsigned int) mid->driver_data, id); + goto err_disable; + } + } else { + mid = &ucb1x00_id[1]; + while (mid->driver_data) { + if (id == mid->driver_data) + break; + mid++; + } + printk(KERN_WARNING "%s ID not found: %04x\n", + ucb1x00_id[0].name, id); goto err_disable; } @@ -552,28 +571,28 @@ static int ucb1x00_probe(struct mcp *mcp) if (!ucb) goto err_disable; - + pdata = mcp->attached_device.platform_data; ucb->dev.class = &ucb1x00_class; ucb->dev.parent = &mcp->attached_device; - dev_set_name(&ucb->dev, "ucb1x00"); + dev_set_name(&ucb->dev, mid->name); spin_lock_init(&ucb->lock); spin_lock_init(&ucb->io_lock); sema_init(&ucb->adc_sem, 1); - ucb->id = id; + ucb->id = mid; ucb->mcp = mcp; ucb->irq = ucb1x00_detect_irq(ucb); if (ucb->irq == NO_IRQ) { - printk(KERN_ERR "UCB1x00: IRQ probe failed\n"); + printk(KERN_ERR "%s: IRQ probe failed\n", mid->name); ret = -ENODEV; goto err_free; } ucb->gpio.base = -1; - if (mcp->gpio_base != 0) { + if (pdata && (pdata->gpio_base >= 0)) { ucb->gpio.label = dev_name(&ucb->dev); - ucb->gpio.base = mcp->gpio_base; + ucb->gpio.base = pdata->gpio_base; ucb->gpio.ngpio = 10; ucb->gpio.set = ucb1x00_gpio_set; ucb->gpio.get = ucb1x00_gpio_get; @@ -586,10 +605,10 @@ static int ucb1x00_probe(struct mcp *mcp) dev_info(&ucb->dev, "gpio_base not set so no gpiolib support"); ret = request_irq(ucb->irq, ucb1x00_irq, IRQF_TRIGGER_RISING, - "UCB1x00", ucb); + mid->name, ucb); if (ret) { - printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n", - ucb->irq, ret); + printk(KERN_ERR "%s: unable to grab irq%d: %d\n", + mid->name, ucb->irq, ret); goto err_gpio; } @@ -693,7 +712,6 @@ static int ucb1x00_resume(struct mcp *mcp) struct ucb1x00 *ucb = mcp_get_drvdata(mcp); struct ucb1x00_dev *dev; - ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); mutex_lock(&ucb1x00_mutex); list_for_each_entry(dev, &ucb->devs, dev_node) { @@ -712,6 +730,7 @@ static struct mcp_driver ucb1x00_driver = { .remove = ucb1x00_remove, .suspend = ucb1x00_suspend, .resume = ucb1x00_resume, + .id_table = ucb1x00_id, }; static int __init ucb1x00_init(void) diff --git a/trunk/drivers/mfd/ucb1x00-ts.c b/trunk/drivers/mfd/ucb1x00-ts.c index 63a3cbdfa3f3..40ec3c118868 100644 --- a/trunk/drivers/mfd/ucb1x00-ts.c +++ b/trunk/drivers/mfd/ucb1x00-ts.c @@ -47,6 +47,7 @@ struct ucb1x00_ts { u16 x_res; u16 y_res; + unsigned int restart:1; unsigned int adcsync:1; }; @@ -206,17 +207,15 @@ static int ucb1x00_thread(void *_ts) { struct ucb1x00_ts *ts = _ts; DECLARE_WAITQUEUE(wait, current); - bool frozen, ignore = false; int valid = 0; set_freezable(); add_wait_queue(&ts->irq_wait, &wait); - while (!kthread_freezable_should_stop(&frozen)) { + while (!kthread_should_stop()) { unsigned int x, y, p; signed long timeout; - if (frozen) - ignore = true; + ts->restart = 0; ucb1x00_adc_enable(ts->ucb); @@ -259,7 +258,7 @@ static int ucb1x00_thread(void *_ts) * space. We therefore leave it to user space * to do any filtering they please. */ - if (!ignore) { + if (!ts->restart) { ucb1x00_ts_evt_add(ts, p, x, y); valid = 1; } @@ -268,6 +267,8 @@ static int ucb1x00_thread(void *_ts) timeout = HZ / 100; } + try_to_freeze(); + schedule_timeout(timeout); } @@ -339,6 +340,26 @@ static void ucb1x00_ts_close(struct input_dev *idev) ucb1x00_disable(ts->ucb); } +#ifdef CONFIG_PM +static int ucb1x00_ts_resume(struct ucb1x00_dev *dev) +{ + struct ucb1x00_ts *ts = dev->priv; + + if (ts->rtask != NULL) { + /* + * Restart the TS thread to ensure the + * TS interrupt mode is set up again + * after sleep. + */ + ts->restart = 1; + wake_up(&ts->irq_wait); + } + return 0; +} +#else +#define ucb1x00_ts_resume NULL +#endif + /* * Initialisation. @@ -361,7 +382,7 @@ static int ucb1x00_ts_add(struct ucb1x00_dev *dev) ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC; idev->name = "Touchscreen panel"; - idev->id.product = ts->ucb->id; + idev->id.product = ts->ucb->id->driver_data; idev->open = ucb1x00_ts_open; idev->close = ucb1x00_ts_close; @@ -404,6 +425,7 @@ static void ucb1x00_ts_remove(struct ucb1x00_dev *dev) static struct ucb1x00_driver ucb1x00_ts_driver = { .add = ucb1x00_ts_add, .remove = ucb1x00_ts_remove, + .resume = ucb1x00_ts_resume, }; static int __init ucb1x00_ts_init(void) 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/lkdtm.c b/trunk/drivers/misc/lkdtm.c index 28adefe70f96..150cd7061b80 100644 --- a/trunk/drivers/misc/lkdtm.c +++ b/trunk/drivers/misc/lkdtm.c @@ -354,7 +354,6 @@ static void lkdtm_do_action(enum ctype which) static void lkdtm_handler(void) { unsigned long flags; - bool do_it = false; spin_lock_irqsave(&count_lock, flags); count--; @@ -362,13 +361,10 @@ static void lkdtm_handler(void) cp_name_to_str(cpoint), cp_type_to_str(cptype), count); if (count == 0) { - do_it = true; + lkdtm_do_action(cptype); count = cpoint_count; } spin_unlock_irqrestore(&count_lock, flags); - - if (do_it) - lkdtm_do_action(cptype); } static int lkdtm_register_cpoint(enum cname which) 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/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/mtd/mtdcore.c b/trunk/drivers/mtd/mtdcore.c index 9a9ce71a71fc..6ae9ca01388b 100644 --- a/trunk/drivers/mtd/mtdcore.c +++ b/trunk/drivers/mtd/mtdcore.c @@ -119,7 +119,7 @@ static int mtd_cls_suspend(struct device *dev, pm_message_t state) { struct mtd_info *mtd = dev_get_drvdata(dev); - return mtd ? mtd_suspend(mtd) : 0; + return mtd_suspend(mtd); } static int mtd_cls_resume(struct device *dev) diff --git a/trunk/drivers/mtd/nand/atmel_nand.c b/trunk/drivers/mtd/nand/atmel_nand.c index 35b4fb55dbd6..4dd056e2e16a 100644 --- a/trunk/drivers/mtd/nand/atmel_nand.c +++ b/trunk/drivers/mtd/nand/atmel_nand.c @@ -161,37 +161,6 @@ static int atmel_nand_device_ready(struct mtd_info *mtd) !!host->board->rdy_pin_active_low; } -/* - * Minimal-overhead PIO for data access. - */ -static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len) -{ - struct nand_chip *nand_chip = mtd->priv; - - __raw_readsb(nand_chip->IO_ADDR_R, buf, len); -} - -static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len) -{ - struct nand_chip *nand_chip = mtd->priv; - - __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2); -} - -static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len) -{ - struct nand_chip *nand_chip = mtd->priv; - - __raw_writesb(nand_chip->IO_ADDR_W, buf, len); -} - -static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len) -{ - struct nand_chip *nand_chip = mtd->priv; - - __raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2); -} - static void dma_complete_func(void *completion) { complete(completion); @@ -266,33 +235,27 @@ static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len, static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len) { struct nand_chip *chip = mtd->priv; - struct atmel_nand_host *host = chip->priv; if (use_dma && len > mtd->oobsize) /* only use DMA for bigger than oob size: better performances */ if (atmel_nand_dma_op(mtd, buf, len, 1) == 0) return; - if (host->board->bus_width_16) - atmel_read_buf16(mtd, buf, len); - else - atmel_read_buf8(mtd, buf, len); + /* if no DMA operation possible, use PIO */ + memcpy_fromio(buf, chip->IO_ADDR_R, len); } static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len) { struct nand_chip *chip = mtd->priv; - struct atmel_nand_host *host = chip->priv; if (use_dma && len > mtd->oobsize) /* only use DMA for bigger than oob size: better performances */ if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0) return; - if (host->board->bus_width_16) - atmel_write_buf16(mtd, buf, len); - else - atmel_write_buf8(mtd, buf, len); + /* if no DMA operation possible, use PIO */ + memcpy_toio(chip->IO_ADDR_W, buf, len); } /* diff --git a/trunk/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/trunk/drivers/mtd/nand/gpmi-nand/gpmi-lib.c index 7db6555ed3ba..7f680420bfab 100644 --- a/trunk/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ b/trunk/drivers/mtd/nand/gpmi-nand/gpmi-lib.c @@ -69,19 +69,17 @@ static int clear_poll_bit(void __iomem *addr, u32 mask) * [1] enable the module. * [2] reset the module. * - * In most of the cases, it's ok. - * But in MX23, there is a hardware bug in the BCH block (see erratum #2847). + * In most of the cases, it's ok. But there is a hardware bug in the BCH block. * If you try to soft reset the BCH block, it becomes unusable until * the next hard reset. This case occurs in the NAND boot mode. When the board * boots by NAND, the ROM of the chip will initialize the BCH blocks itself. * So If the driver tries to reset the BCH again, the BCH will not work anymore. - * You will see a DMA timeout in this case. The bug has been fixed - * in the following chips, such as MX28. + * You will see a DMA timeout in this case. * * To avoid this bug, just add a new parameter `just_enable` for * the mxs_reset_block(), and rewrite it here. */ -static int gpmi_reset_block(void __iomem *reset_addr, bool just_enable) +int gpmi_reset_block(void __iomem *reset_addr, bool just_enable) { int ret; int timeout = 0x400; @@ -208,15 +206,7 @@ int bch_set_geometry(struct gpmi_nand_data *this) if (ret) goto err_out; - /* - * Due to erratum #2847 of the MX23, the BCH cannot be soft reset on this - * chip, otherwise it will lock up. So we skip resetting BCH on the MX23. - * On the other hand, the MX28 needs the reset, because one case has been - * seen where the BCH produced ECC errors constantly after 10000 - * consecutive reboots. The latter case has not been seen on the MX23 yet, - * still we don't know if it could happen there as well. - */ - ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MX23(this)); + ret = gpmi_reset_block(r->bch_regs, true); if (ret) goto err_out; diff --git a/trunk/drivers/mtd/nand/nand_base.c b/trunk/drivers/mtd/nand/nand_base.c index 8a393f9e6027..35b4565050f1 100644 --- a/trunk/drivers/mtd/nand/nand_base.c +++ b/trunk/drivers/mtd/nand/nand_base.c @@ -2588,7 +2588,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, instr->state = MTD_ERASING; while (len) { - /* Check if we have a bad block, we do not erase bad blocks! */ + /* Heck if we have a bad block, we do not erase bad blocks! */ if (nand_block_checkbad(mtd, ((loff_t) page) << chip->page_shift, 0, allowbbt)) { pr_warn("%s: attempt to erase a bad block at page 0x%08x\n", 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/3com/3c59x.c b/trunk/drivers/net/ethernet/3com/3c59x.c index f9b74c0a8492..8153a3e0a1a4 100644 --- a/trunk/drivers/net/ethernet/3com/3c59x.c +++ b/trunk/drivers/net/ethernet/3com/3c59x.c @@ -1842,7 +1842,7 @@ vortex_timer(unsigned long data) ok = 1; } - if (dev->flags & IFF_SLAVE || !netif_carrier_ok(dev)) + if (!netif_carrier_ok(dev)) next_tick = 5*HZ; if (vp->medialock) diff --git a/trunk/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/trunk/drivers/net/ethernet/broadcom/bcm63xx_enet.c index c7ca7ec065ee..986019b2c849 100644 --- a/trunk/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/trunk/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -797,7 +797,7 @@ static int bcm_enet_open(struct net_device *dev) if (priv->has_phy) { /* connect to PHY */ snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, - priv->mii_bus->id, priv->phy_id); + priv->mac_id ? "1" : "0", priv->phy_id); phydev = phy_connect(dev, phy_id, bcm_enet_adjust_phy_link, 0, PHY_INTERFACE_MODE_MII); 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/broadcom/bnx2x/bnx2x_main.c b/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 254521319150..1e3f978ee6da 100644 --- a/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -117,6 +117,10 @@ static int dropless_fc; module_param(dropless_fc, int, 0); MODULE_PARM_DESC(dropless_fc, " Pause on exhausted host ring"); +static int poll; +module_param(poll, int, 0); +MODULE_PARM_DESC(poll, " Use polling (for debug)"); + static int mrrs = -1; module_param(mrrs, int, 0); MODULE_PARM_DESC(mrrs, " Force Max Read Req Size (0..3) (for debug)"); @@ -4830,11 +4834,20 @@ void bnx2x_drv_pulse(struct bnx2x *bp) static void bnx2x_timer(unsigned long data) { + u8 cos; struct bnx2x *bp = (struct bnx2x *) data; if (!netif_running(bp->dev)) return; + if (poll) { + struct bnx2x_fastpath *fp = &bp->fp[0]; + + for_each_cos_in_tx_queue(fp, cos) + bnx2x_tx_int(bp, &fp->txdata[cos]); + bnx2x_rx_int(fp, 1000); + } + if (!BP_NOMCP(bp)) { int mb_idx = BP_FW_MB_IDX(bp); u32 drv_pulse; @@ -10050,6 +10063,7 @@ static void __devinit bnx2x_set_modes_bitmap(struct bnx2x *bp) static int __devinit bnx2x_init_bp(struct bnx2x *bp) { int func; + int timer_interval; int rc; mutex_init(&bp->port.phy_mutex); @@ -10125,7 +10139,8 @@ static int __devinit bnx2x_init_bp(struct bnx2x *bp) bp->tx_ticks = (50 / BNX2X_BTR) * BNX2X_BTR; bp->rx_ticks = (25 / BNX2X_BTR) * BNX2X_BTR; - bp->current_interval = CHIP_REV_IS_SLOW(bp) ? 5*HZ : HZ; + timer_interval = (CHIP_REV_IS_SLOW(bp) ? 5*HZ : HZ); + bp->current_interval = (poll ? poll : timer_interval); init_timer(&bp->timer); bp->timer.expires = jiffies + bp->current_interval; diff --git a/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c b/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c index 1adef266fcd5..bc0121ac291e 100644 --- a/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c +++ b/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c @@ -1081,17 +1081,17 @@ static int bnx2x_storm_stats_update(struct bnx2x *bp) estats->rx_stat_ifhcinbadoctets_lo); ADD_64(fstats->total_bytes_received_hi, - le32_to_cpu(tfunc->rcv_error_bytes.hi), + tfunc->rcv_error_bytes.hi, fstats->total_bytes_received_lo, - le32_to_cpu(tfunc->rcv_error_bytes.lo)); + tfunc->rcv_error_bytes.lo); memcpy(estats, &(fstats->total_bytes_received_hi), sizeof(struct host_func_stats) - 2*sizeof(u32)); ADD_64(estats->error_bytes_received_hi, - le32_to_cpu(tfunc->rcv_error_bytes.hi), + tfunc->rcv_error_bytes.hi, estats->error_bytes_received_lo, - le32_to_cpu(tfunc->rcv_error_bytes.lo)); + tfunc->rcv_error_bytes.lo); ADD_64(estats->etherstatsoverrsizepkts_hi, estats->rx_stat_dot3statsframestoolong_hi, 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 e92ef1bd732a..7b25e9cf13f6 100644 --- a/trunk/drivers/net/ethernet/freescale/fec.c +++ b/trunk/drivers/net/ethernet/freescale/fec.c @@ -986,11 +986,11 @@ static int fec_enet_mii_probe(struct net_device *ndev) printk(KERN_INFO "%s: no PHY, assuming direct connection to switch\n", ndev->name); - strncpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); + strncpy(mdio_bus_id, "0", MII_BUS_ID_SIZE); 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/eq.c b/trunk/drivers/net/ethernet/mellanox/mlx4/eq.c index 8fa41f3082cf..55d7bd4e210a 100644 --- a/trunk/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/trunk/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -815,9 +815,8 @@ int mlx4_init_eq_table(struct mlx4_dev *dev) int err; int i; - priv->eq_table.uar_map = kcalloc(mlx4_num_eq_uar(dev), - sizeof *priv->eq_table.uar_map, - GFP_KERNEL); + priv->eq_table.uar_map = kcalloc(sizeof *priv->eq_table.uar_map, + mlx4_num_eq_uar(dev), GFP_KERNEL); if (!priv->eq_table.uar_map) { err = -ENOMEM; goto err_out_free; 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/micrel/ks8851.c b/trunk/drivers/net/ethernet/micrel/ks8851.c index 0c3e4005224d..6b35e7da9a9c 100644 --- a/trunk/drivers/net/ethernet/micrel/ks8851.c +++ b/trunk/drivers/net/ethernet/micrel/ks8851.c @@ -583,7 +583,7 @@ static void ks8851_rx_pkts(struct ks8851_net *ks) ks8851_dbg_dumpkkt(ks, rxpkt); skb->protocol = eth_type_trans(skb, ks->netdev); - netif_rx_ni(skb); + netif_rx(skb); ks->netdev->stats.rx_packets++; ks->netdev->stats.rx_bytes += rxlen; diff --git a/trunk/drivers/net/ethernet/octeon/octeon_mgmt.c b/trunk/drivers/net/ethernet/octeon/octeon_mgmt.c index cd827ff4a021..212f43b308a3 100644 --- a/trunk/drivers/net/ethernet/octeon/octeon_mgmt.c +++ b/trunk/drivers/net/ethernet/octeon/octeon_mgmt.c @@ -670,7 +670,7 @@ static void octeon_mgmt_adjust_link(struct net_device *netdev) static int octeon_mgmt_init_phy(struct net_device *netdev) { struct octeon_mgmt *p = netdev_priv(netdev); - char phy_id[MII_BUS_ID_SIZE + 3]; + char phy_id[20]; if (octeon_is_simulation()) { /* No PHYs in the simulator. */ @@ -678,7 +678,7 @@ static int octeon_mgmt_init_phy(struct net_device *netdev) return 0; } - snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, "mdio-octeon-0", p->port); + snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, "0", p->port); p->phydev = phy_connect(netdev, phy_id, octeon_mgmt_adjust_link, 0, PHY_INTERFACE_MODE_MII); 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/cpmac.c b/trunk/drivers/net/ethernet/ti/cpmac.c index cbc8df78d84b..4d9a28ffd3c3 100644 --- a/trunk/drivers/net/ethernet/ti/cpmac.c +++ b/trunk/drivers/net/ethernet/ti/cpmac.c @@ -1122,7 +1122,7 @@ static int __devinit cpmac_probe(struct platform_device *pdev) pdata = pdev->dev.platform_data; if (external_switch || dumb_switch) { - strncpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); /* fixed phys bus */ + strncpy(mdio_bus_id, "0", MII_BUS_ID_SIZE); /* fixed phys bus */ phy_id = pdev->id; } else { for (phy_id = 0; phy_id < PHY_MAX_ADDR; phy_id++) { @@ -1138,7 +1138,7 @@ static int __devinit cpmac_probe(struct platform_device *pdev) if (phy_id == PHY_MAX_ADDR) { dev_err(&pdev->dev, "no PHY present, falling back " "to switch on MDIO bus 0\n"); - strncpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); /* fixed phys bus */ + strncpy(mdio_bus_id, "0", MII_BUS_ID_SIZE); /* fixed phys bus */ phy_id = pdev->id; } 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/ethernet/xscale/ixp4xx_eth.c b/trunk/drivers/net/ethernet/xscale/ixp4xx_eth.c index 41a8b5a9849e..72a854f05bb8 100644 --- a/trunk/drivers/net/ethernet/xscale/ixp4xx_eth.c +++ b/trunk/drivers/net/ethernet/xscale/ixp4xx_eth.c @@ -1416,8 +1416,7 @@ static int __devinit eth_init_one(struct platform_device *pdev) __raw_writel(DEFAULT_CORE_CNTRL, &port->regs->core_control); udelay(50); - snprintf(phy_id, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, - mdio_bus->id, plat->phy); + snprintf(phy_id, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, "0", plat->phy); port->phydev = phy_connect(dev, phy_id, &ixp4xx_adjust_link, 0, PHY_INTERFACE_MODE_MII); if (IS_ERR(port->phydev)) { 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/pcmcia/ds.c b/trunk/drivers/pcmcia/ds.c index 1932029de48d..749c2a16012c 100644 --- a/trunk/drivers/pcmcia/ds.c +++ b/trunk/drivers/pcmcia/ds.c @@ -1269,8 +1269,10 @@ static int pcmcia_bus_add(struct pcmcia_socket *skt) static int pcmcia_bus_early_resume(struct pcmcia_socket *skt) { - if (!verify_cis_cache(skt)) + if (!verify_cis_cache(skt)) { + pcmcia_put_socket(skt); return 0; + } dev_dbg(&skt->dev, "cis mismatch - different card\n"); diff --git a/trunk/drivers/pcmcia/sa1111_generic.c b/trunk/drivers/pcmcia/sa1111_generic.c index 27f2fe3b7fb4..59866905ea37 100644 --- a/trunk/drivers/pcmcia/sa1111_generic.c +++ b/trunk/drivers/pcmcia/sa1111_generic.c @@ -205,8 +205,7 @@ static int __devexit pcmcia_remove(struct sa1111_dev *dev) dev_set_drvdata(&dev->dev, NULL); - for (; s; s = next) { - next = s->next; + for (; next = s->next, s; s = next) { soc_pcmcia_remove_one(&s->soc); kfree(s); } diff --git a/trunk/drivers/pinctrl/core.c b/trunk/drivers/pinctrl/core.c index 8fe15cf15ac8..569bdb3ef104 100644 --- a/trunk/drivers/pinctrl/core.c +++ b/trunk/drivers/pinctrl/core.c @@ -510,12 +510,10 @@ static struct dentry *debugfs_root; static void pinctrl_init_device_debugfs(struct pinctrl_dev *pctldev) { - struct dentry *device_root; + static struct dentry *device_root; device_root = debugfs_create_dir(dev_name(pctldev->dev), debugfs_root); - pctldev->device_root = device_root; - if (IS_ERR(device_root) || !device_root) { pr_warn("failed to create debugfs directory for %s\n", dev_name(pctldev->dev)); @@ -531,11 +529,6 @@ static void pinctrl_init_device_debugfs(struct pinctrl_dev *pctldev) pinconf_init_device_debugfs(device_root, pctldev); } -static void pinctrl_remove_device_debugfs(struct pinctrl_dev *pctldev) -{ - debugfs_remove_recursive(pctldev->device_root); -} - static void pinctrl_init_debugfs(void) { debugfs_root = debugfs_create_dir("pinctrl", NULL); @@ -560,10 +553,6 @@ static void pinctrl_init_debugfs(void) { } -static void pinctrl_remove_device_debugfs(struct pinctrl_dev *pctldev) -{ -} - #endif /** @@ -583,40 +572,40 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, if (pctldesc->name == NULL) return NULL; - pctldev = kzalloc(sizeof(struct pinctrl_dev), GFP_KERNEL); - if (pctldev == NULL) - return NULL; - - /* Initialize pin control device struct */ - pctldev->owner = pctldesc->owner; - pctldev->desc = pctldesc; - pctldev->driver_data = driver_data; - INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL); - spin_lock_init(&pctldev->pin_desc_tree_lock); - INIT_LIST_HEAD(&pctldev->gpio_ranges); - mutex_init(&pctldev->gpio_ranges_lock); - pctldev->dev = dev; - /* If we're implementing pinmuxing, check the ops for sanity */ if (pctldesc->pmxops) { - ret = pinmux_check_ops(pctldev); + ret = pinmux_check_ops(pctldesc->pmxops); if (ret) { pr_err("%s pinmux ops lacks necessary functions\n", pctldesc->name); - goto out_err; + return NULL; } } /* If we're implementing pinconfig, check the ops for sanity */ if (pctldesc->confops) { - ret = pinconf_check_ops(pctldev); + ret = pinconf_check_ops(pctldesc->confops); if (ret) { pr_err("%s pin config ops lacks necessary functions\n", pctldesc->name); - goto out_err; + return NULL; } } + pctldev = kzalloc(sizeof(struct pinctrl_dev), GFP_KERNEL); + if (pctldev == NULL) + return NULL; + + /* Initialize pin control device struct */ + pctldev->owner = pctldesc->owner; + pctldev->desc = pctldesc; + pctldev->driver_data = driver_data; + INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL); + spin_lock_init(&pctldev->pin_desc_tree_lock); + INIT_LIST_HEAD(&pctldev->gpio_ranges); + mutex_init(&pctldev->gpio_ranges_lock); + pctldev->dev = dev; + /* Register all the pins */ pr_debug("try to register %d pins on %s...\n", pctldesc->npins, pctldesc->name); @@ -652,7 +641,6 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev) if (pctldev == NULL) return; - pinctrl_remove_device_debugfs(pctldev); pinmux_unhog_maps(pctldev); /* TODO: check that no pinmuxes are still active? */ mutex_lock(&pinctrldev_list_mutex); diff --git a/trunk/drivers/pinctrl/core.h b/trunk/drivers/pinctrl/core.h index cfa86da6b4b1..177a3310547f 100644 --- a/trunk/drivers/pinctrl/core.h +++ b/trunk/drivers/pinctrl/core.h @@ -41,9 +41,6 @@ struct pinctrl_dev { struct device *dev; struct module *owner; void *driver_data; -#ifdef CONFIG_DEBUG_FS - struct dentry *device_root; -#endif #ifdef CONFIG_PINMUX struct mutex pinmux_hogs_lock; struct list_head pinmux_hogs; diff --git a/trunk/drivers/pinctrl/pinconf.c b/trunk/drivers/pinctrl/pinconf.c index 9fb75456824c..1259872b0a1d 100644 --- a/trunk/drivers/pinctrl/pinconf.c +++ b/trunk/drivers/pinctrl/pinconf.c @@ -205,10 +205,8 @@ int pin_config_group_set(const char *dev_name, const char *pin_group, } EXPORT_SYMBOL(pin_config_group_set); -int pinconf_check_ops(struct pinctrl_dev *pctldev) +int pinconf_check_ops(const struct pinconf_ops *ops) { - const struct pinconf_ops *ops = pctldev->desc->confops; - /* We must be able to read out pin status */ if (!ops->pin_config_get && !ops->pin_config_group_get) return -EINVAL; @@ -238,7 +236,7 @@ static int pinconf_pins_show(struct seq_file *s, void *what) seq_puts(s, "Format: pin (name): pinmux setting array\n"); /* The pin number can be retrived from the pin controller descriptor */ - for (i = 0; i < pctldev->desc->npins; i++) { + for (i = 0; pin < pctldev->desc->npins; i++) { struct pin_desc *desc; pin = pctldev->desc->pins[i].number; diff --git a/trunk/drivers/pinctrl/pinconf.h b/trunk/drivers/pinctrl/pinconf.h index 006b77fa737e..e7dc6165032a 100644 --- a/trunk/drivers/pinctrl/pinconf.h +++ b/trunk/drivers/pinctrl/pinconf.h @@ -13,7 +13,7 @@ #ifdef CONFIG_PINCONF -int pinconf_check_ops(struct pinctrl_dev *pctldev); +int pinconf_check_ops(const struct pinconf_ops *ops); void pinconf_init_device_debugfs(struct dentry *devroot, struct pinctrl_dev *pctldev); int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned pin, @@ -23,7 +23,7 @@ int pin_config_set_for_pin(struct pinctrl_dev *pctldev, unsigned pin, #else -static inline int pinconf_check_ops(struct pinctrl_dev *pctldev) +static inline int pinconf_check_ops(const struct pinconf_ops *ops) { return 0; } diff --git a/trunk/drivers/pinctrl/pinmux.c b/trunk/drivers/pinctrl/pinmux.c index 7c3193f7a044..a76a348321bb 100644 --- a/trunk/drivers/pinctrl/pinmux.c +++ b/trunk/drivers/pinctrl/pinmux.c @@ -53,6 +53,11 @@ struct pinmux_group { * @dev: the device using this pinmux * @usecount: the number of active users of this mux setting, used to keep * track of nested use cases + * @pins: an array of discrete physical pins used in this mapping, taken + * from the global pin enumeration space (copied from pinmux map) + * @num_pins: the number of pins in this mapping array, i.e. the number of + * elements in .pins so we can iterate over that array (copied from + * pinmux map) * @pctldev: pin control device handling this pinmux * @func_selector: the function selector for the pinmux device handling * this pinmux @@ -147,7 +152,8 @@ static int pin_request(struct pinctrl_dev *pctldev, status = 0; if (status) - dev_err(pctldev->dev, "->request on device %s failed for pin %d\n", + dev_err(pctldev->dev, "->request on device %s failed " + "for pin %d\n", pctldev->desc->name, pin); out_free_pin: if (status) { @@ -349,20 +355,21 @@ int __init pinmux_register_mappings(struct pinmux_map const *maps, /* First sanity check the new mapping */ for (i = 0; i < num_maps; i++) { if (!maps[i].name) { - pr_err("failed to register map %d: no map name given\n", - i); + pr_err("failed to register map %d: " + "no map name given\n", i); return -EINVAL; } if (!maps[i].ctrl_dev && !maps[i].ctrl_dev_name) { - pr_err("failed to register map %s (%d): no pin control device given\n", + pr_err("failed to register map %s (%d): " + "no pin control device given\n", maps[i].name, i); return -EINVAL; } if (!maps[i].function) { - pr_err("failed to register map %s (%d): no function ID given\n", - maps[i].name, i); + pr_err("failed to register map %s (%d): " + "no function ID given\n", maps[i].name, i); return -EINVAL; } @@ -404,7 +411,7 @@ int __init pinmux_register_mappings(struct pinmux_map const *maps, } /** - * acquire_pins() - acquire all the pins for a certain function on a pinmux + * acquire_pins() - acquire all the pins for a certain funcion on a pinmux * @pctldev: the device to take the pins on * @func_selector: the function selector to acquire the pins for * @group_selector: the group selector containing the pins to acquire @@ -435,7 +442,8 @@ static int acquire_pins(struct pinctrl_dev *pctldev, ret = pin_request(pctldev, pins[i], func, NULL); if (ret) { dev_err(pctldev->dev, - "could not get pin %d for function %s on device %s - conflicting mux mappings?\n", + "could not get pin %d for function %s " + "on device %s - conflicting mux mappings?\n", pins[i], func ? : "(undefined)", pinctrl_dev_get_name(pctldev)); /* On error release all taken pins */ @@ -450,7 +458,7 @@ static int acquire_pins(struct pinctrl_dev *pctldev, /** * release_pins() - release pins taken by earlier acquirement - * @pctldev: the device to free the pins on + * @pctldev: the device to free the pinx on * @group_selector: the group selector containing the pins to free */ static void release_pins(struct pinctrl_dev *pctldev, @@ -465,7 +473,8 @@ static void release_pins(struct pinctrl_dev *pctldev, ret = pctlops->get_group_pins(pctldev, group_selector, &pins, &num_pins); if (ret) { - dev_err(pctldev->dev, "could not get pins to release for group selector %d\n", + dev_err(pctldev->dev, "could not get pins to release for " + "group selector %d\n", group_selector); return; } @@ -517,7 +526,8 @@ static int pinmux_check_pin_group(struct pinctrl_dev *pctldev, ret = pinctrl_get_group_selector(pctldev, groups[0]); if (ret < 0) { dev_err(pctldev->dev, - "function %s wants group %s but the pin controller does not seem to have that group\n", + "function %s wants group %s but the pin " + "controller does not seem to have that group\n", pmxops->get_function_name(pctldev, func_selector), groups[0]); return ret; @@ -525,7 +535,8 @@ static int pinmux_check_pin_group(struct pinctrl_dev *pctldev, if (num_groups > 1) dev_dbg(pctldev->dev, - "function %s support more than one group, default-selecting first group %s (%d)\n", + "function %s support more than one group, " + "default-selecting first group %s (%d)\n", pmxops->get_function_name(pctldev, func_selector), groups[0], ret); @@ -617,8 +628,10 @@ static int pinmux_enable_muxmap(struct pinctrl_dev *pctldev, if (pmx->pctldev && pmx->pctldev != pctldev) { dev_err(pctldev->dev, - "different pin control devices given for device %s, function %s\n", - devname, map->function); + "different pin control devices given for device %s, " + "function %s\n", + devname, + map->function); return -EINVAL; } pmx->dev = dev; @@ -682,6 +695,7 @@ static void pinmux_free_groups(struct pinmux *pmx) */ struct pinmux *pinmux_get(struct device *dev, const char *name) { + struct pinmux_map const *map = NULL; struct pinctrl_dev *pctldev = NULL; const char *devname = NULL; @@ -731,7 +745,8 @@ struct pinmux *pinmux_get(struct device *dev, const char *name) else if (map->ctrl_dev_name) devname = map->ctrl_dev_name; - pr_warning("could not find a pinctrl device for pinmux function %s, fishy, they shall all have one\n", + pr_warning("could not find a pinctrl device for pinmux " + "function %s, fishy, they shall all have one\n", map->function); pr_warning("given pinctrl device name: %s", devname ? devname : "UNDEFINED"); @@ -889,11 +904,8 @@ void pinmux_disable(struct pinmux *pmx) } EXPORT_SYMBOL_GPL(pinmux_disable); -int pinmux_check_ops(struct pinctrl_dev *pctldev) +int pinmux_check_ops(const struct pinmux_ops *ops) { - const struct pinmux_ops *ops = pctldev->desc->pmxops; - unsigned selector = 0; - /* Check that we implement required operations */ if (!ops->list_functions || !ops->get_function_name || @@ -902,18 +914,6 @@ int pinmux_check_ops(struct pinctrl_dev *pctldev) !ops->disable) return -EINVAL; - /* Check that all functions registered have names */ - while (ops->list_functions(pctldev, selector) >= 0) { - const char *fname = ops->get_function_name(pctldev, - selector); - if (!fname) { - pr_err("pinmux ops has no name for function%u\n", - selector); - return -EINVAL; - } - selector++; - } - return 0; } @@ -932,8 +932,8 @@ static int pinmux_hog_map(struct pinctrl_dev *pctldev, * without any problems, so then we can hog pinmuxes for * all devices that just want a static pin mux at this point. */ - dev_err(pctldev->dev, "map %s wants to hog a non-system pinmux, this is not going to work\n", - map->name); + dev_err(pctldev->dev, "map %s wants to hog a non-system " + "pinmux, this is not going to work\n", map->name); return -EINVAL; } @@ -993,12 +993,9 @@ int pinmux_hog_maps(struct pinctrl_dev *pctldev) for (i = 0; i < pinmux_maps_num; i++) { struct pinmux_map const *map = &pinmux_maps[i]; - if (!map->hog_on_boot) - continue; - - if ((map->ctrl_dev == dev) || - (map->ctrl_dev_name && - !strcmp(map->ctrl_dev_name, devname))) { + if (((map->ctrl_dev == dev) || + !strcmp(map->ctrl_dev_name, devname)) && + map->hog_on_boot) { /* OK time to hog! */ ret = pinmux_hog_map(pctldev, map); if (ret) @@ -1125,15 +1122,13 @@ static int pinmux_show(struct seq_file *s, void *what) seq_printf(s, "device: %s function: %s (%u),", pinctrl_dev_get_name(pmx->pctldev), - pmxops->get_function_name(pctldev, - pmx->func_selector), + pmxops->get_function_name(pctldev, pmx->func_selector), pmx->func_selector); seq_printf(s, " groups: ["); list_for_each_entry(grp, &pmx->groups, node) { seq_printf(s, " %s (%u)", - pctlops->get_group_name(pctldev, - grp->group_selector), + pctlops->get_group_name(pctldev, grp->group_selector), grp->group_selector); } seq_printf(s, " ]"); diff --git a/trunk/drivers/pinctrl/pinmux.h b/trunk/drivers/pinctrl/pinmux.h index 97f52223fbc2..844500b3331b 100644 --- a/trunk/drivers/pinctrl/pinmux.h +++ b/trunk/drivers/pinctrl/pinmux.h @@ -12,7 +12,7 @@ */ #ifdef CONFIG_PINMUX -int pinmux_check_ops(struct pinctrl_dev *pctldev); +int pinmux_check_ops(const struct pinmux_ops *ops); void pinmux_init_device_debugfs(struct dentry *devroot, struct pinctrl_dev *pctldev); void pinmux_init_debugfs(struct dentry *subsys_root); @@ -21,7 +21,7 @@ void pinmux_unhog_maps(struct pinctrl_dev *pctldev); #else -static inline int pinmux_check_ops(struct pinctrl_dev *pctldev) +static inline int pinmux_check_ops(const struct pinmux_ops *ops) { return 0; } diff --git a/trunk/drivers/regulator/of_regulator.c b/trunk/drivers/regulator/of_regulator.c index 679734d26a16..f1651eb69648 100644 --- a/trunk/drivers/regulator/of_regulator.c +++ b/trunk/drivers/regulator/of_regulator.c @@ -35,7 +35,7 @@ static void of_get_regulation_constraints(struct device_node *np, if (constraints->min_uV != constraints->max_uV) constraints->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE; /* Only one voltage? Then make sure it's set. */ - if (min_uV && max_uV && constraints->min_uV == constraints->max_uV) + if (constraints->min_uV == constraints->max_uV) constraints->apply_uV = true; uV_offset = of_get_property(np, "regulator-microvolt-offset", NULL); diff --git a/trunk/drivers/rtc/Kconfig b/trunk/drivers/rtc/Kconfig index 3a125b835546..e19a4031f45e 100644 --- a/trunk/drivers/rtc/Kconfig +++ b/trunk/drivers/rtc/Kconfig @@ -774,7 +774,7 @@ config RTC_DRV_EP93XX config RTC_DRV_SA1100 tristate "SA11x0/PXA2xx" - depends on ARCH_SA1100 || ARCH_PXA + depends on ARCH_SA1100 || ARCH_PXA || ARCH_MMP help If you say Y here you will get access to the real time clock built into your SA11x0 or PXA2xx CPU. diff --git a/trunk/drivers/rtc/rtc-sa1100.c b/trunk/drivers/rtc/rtc-sa1100.c index cb9a585312cc..4595d3e645a7 100644 --- a/trunk/drivers/rtc/rtc-sa1100.c +++ b/trunk/drivers/rtc/rtc-sa1100.c @@ -27,34 +27,42 @@ #include #include #include -#include #include -#include +#include +#include +#include #include #include -#ifdef CONFIG_ARCH_PXA -#include -#endif - #define RTC_DEF_DIVIDER (32768 - 1) #define RTC_DEF_TRIM 0 - -static const unsigned long RTC_FREQ = 1024; -static struct rtc_time rtc_alarm; -static DEFINE_SPINLOCK(sa1100_rtc_lock); - -static inline int rtc_periodic_alarm(struct rtc_time *tm) -{ - return (tm->tm_year == -1) || - ((unsigned)tm->tm_mon >= 12) || - ((unsigned)(tm->tm_mday - 1) >= 31) || - ((unsigned)tm->tm_hour > 23) || - ((unsigned)tm->tm_min > 59) || - ((unsigned)tm->tm_sec > 59); -} - +#define RTC_FREQ 1024 + +#define RCNR 0x00 /* RTC Count Register */ +#define RTAR 0x04 /* RTC Alarm Register */ +#define RTSR 0x08 /* RTC Status Register */ +#define RTTR 0x0c /* RTC Timer Trim Register */ + +#define RTSR_HZE (1 << 3) /* HZ interrupt enable */ +#define RTSR_ALE (1 << 2) /* RTC alarm interrupt enable */ +#define RTSR_HZ (1 << 1) /* HZ rising-edge detected */ +#define RTSR_AL (1 << 0) /* RTC alarm detected */ + +#define rtc_readl(sa1100_rtc, reg) \ + readl_relaxed((sa1100_rtc)->base + (reg)) +#define rtc_writel(sa1100_rtc, reg, value) \ + writel_relaxed((value), (sa1100_rtc)->base + (reg)) + +struct sa1100_rtc { + struct resource *ress; + void __iomem *base; + struct clk *clk; + int irq_1Hz; + int irq_Alrm; + struct rtc_device *rtc; + spinlock_t lock; /* Protects this structure */ +}; /* * Calculate the next alarm time given the requested alarm time mask * and the current time. @@ -82,46 +90,26 @@ static void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now, } } -static int rtc_update_alarm(struct rtc_time *alrm) -{ - struct rtc_time alarm_tm, now_tm; - unsigned long now, time; - int ret; - - do { - now = RCNR; - rtc_time_to_tm(now, &now_tm); - rtc_next_alarm_time(&alarm_tm, &now_tm, alrm); - ret = rtc_tm_to_time(&alarm_tm, &time); - if (ret != 0) - break; - - RTSR = RTSR & (RTSR_HZE|RTSR_ALE|RTSR_AL); - RTAR = time; - } while (now != RCNR); - - return ret; -} - static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id) { struct platform_device *pdev = to_platform_device(dev_id); - struct rtc_device *rtc = platform_get_drvdata(pdev); + struct sa1100_rtc *sa1100_rtc = platform_get_drvdata(pdev); unsigned int rtsr; unsigned long events = 0; - spin_lock(&sa1100_rtc_lock); + spin_lock(&sa1100_rtc->lock); - rtsr = RTSR; /* clear interrupt sources */ - RTSR = 0; + rtsr = rtc_readl(sa1100_rtc, RTSR); + rtc_writel(sa1100_rtc, RTSR, 0); + /* Fix for a nasty initialization problem the in SA11xx RTSR register. * See also the comments in sa1100_rtc_probe(). */ if (rtsr & (RTSR_ALE | RTSR_HZE)) { /* This is the original code, before there was the if test * above. This code does not clear interrupts that were not * enabled. */ - RTSR = (RTSR_AL | RTSR_HZ) & (rtsr >> 2); + rtc_writel(sa1100_rtc, RTSR, (RTSR_AL | RTSR_HZ) & (rtsr >> 2)); } else { /* For some reason, it is possible to enter this routine * without interruptions enabled, it has been tested with @@ -130,13 +118,13 @@ static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id) * This situation leads to an infinite "loop" of interrupt * routine calling and as a result the processor seems to * lock on its first call to open(). */ - RTSR = RTSR_AL | RTSR_HZ; + rtc_writel(sa1100_rtc, RTSR, (RTSR_AL | RTSR_HZ)); } /* clear alarm interrupt if it has occurred */ if (rtsr & RTSR_AL) rtsr &= ~RTSR_ALE; - RTSR = rtsr & (RTSR_ALE | RTSR_HZE); + rtc_writel(sa1100_rtc, RTSR, rtsr & (RTSR_ALE | RTSR_HZE)); /* update irq data & counter */ if (rtsr & RTSR_AL) @@ -144,89 +132,100 @@ static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id) if (rtsr & RTSR_HZ) events |= RTC_UF | RTC_IRQF; - rtc_update_irq(rtc, 1, events); + rtc_update_irq(sa1100_rtc->rtc, 1, events); - if (rtsr & RTSR_AL && rtc_periodic_alarm(&rtc_alarm)) - rtc_update_alarm(&rtc_alarm); - - spin_unlock(&sa1100_rtc_lock); + spin_unlock(&sa1100_rtc->lock); return IRQ_HANDLED; } static int sa1100_rtc_open(struct device *dev) { + struct sa1100_rtc *sa1100_rtc = dev_get_drvdata(dev); int ret; - struct platform_device *plat_dev = to_platform_device(dev); - struct rtc_device *rtc = platform_get_drvdata(plat_dev); - ret = request_irq(IRQ_RTC1Hz, sa1100_rtc_interrupt, IRQF_DISABLED, - "rtc 1Hz", dev); + ret = request_irq(sa1100_rtc->irq_1Hz, sa1100_rtc_interrupt, + IRQF_DISABLED, "rtc 1Hz", dev); if (ret) { - dev_err(dev, "IRQ %d already in use.\n", IRQ_RTC1Hz); + dev_err(dev, "IRQ %d already in use.\n", sa1100_rtc->irq_1Hz); goto fail_ui; } - ret = request_irq(IRQ_RTCAlrm, sa1100_rtc_interrupt, IRQF_DISABLED, - "rtc Alrm", dev); + ret = request_irq(sa1100_rtc->irq_Alrm, sa1100_rtc_interrupt, + IRQF_DISABLED, "rtc Alrm", dev); if (ret) { - dev_err(dev, "IRQ %d already in use.\n", IRQ_RTCAlrm); + dev_err(dev, "IRQ %d already in use.\n", sa1100_rtc->irq_Alrm); goto fail_ai; } - rtc->max_user_freq = RTC_FREQ; - rtc_irq_set_freq(rtc, NULL, RTC_FREQ); + sa1100_rtc->rtc->max_user_freq = RTC_FREQ; + rtc_irq_set_freq(sa1100_rtc->rtc, NULL, RTC_FREQ); return 0; fail_ai: - free_irq(IRQ_RTC1Hz, dev); + free_irq(sa1100_rtc->irq_1Hz, dev); fail_ui: return ret; } static void sa1100_rtc_release(struct device *dev) { - spin_lock_irq(&sa1100_rtc_lock); - RTSR = 0; - spin_unlock_irq(&sa1100_rtc_lock); + struct sa1100_rtc *sa1100_rtc = dev_get_drvdata(dev); + + spin_lock_irq(&sa1100_rtc->lock); + rtc_writel(sa1100_rtc, RTSR, 0); + spin_unlock_irq(&sa1100_rtc->lock); - free_irq(IRQ_RTCAlrm, dev); - free_irq(IRQ_RTC1Hz, dev); + free_irq(sa1100_rtc->irq_Alrm, dev); + free_irq(sa1100_rtc->irq_1Hz, dev); } static int sa1100_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { - spin_lock_irq(&sa1100_rtc_lock); + struct sa1100_rtc *sa1100_rtc = dev_get_drvdata(dev); + unsigned int rtsr; + + spin_lock_irq(&sa1100_rtc->lock); + + rtsr = rtc_readl(sa1100_rtc, RTSR); if (enabled) - RTSR |= RTSR_ALE; + rtsr |= RTSR_ALE; else - RTSR &= ~RTSR_ALE; - spin_unlock_irq(&sa1100_rtc_lock); + rtsr &= ~RTSR_ALE; + rtc_writel(sa1100_rtc, RTSR, rtsr); + + spin_unlock_irq(&sa1100_rtc->lock); return 0; } static int sa1100_rtc_read_time(struct device *dev, struct rtc_time *tm) { - rtc_time_to_tm(RCNR, tm); + struct sa1100_rtc *sa1100_rtc = dev_get_drvdata(dev); + + rtc_time_to_tm(rtc_readl(sa1100_rtc, RCNR), tm); return 0; } static int sa1100_rtc_set_time(struct device *dev, struct rtc_time *tm) { + struct sa1100_rtc *sa1100_rtc = dev_get_drvdata(dev); unsigned long time; int ret; ret = rtc_tm_to_time(tm, &time); if (ret == 0) - RCNR = time; + rtc_writel(sa1100_rtc, RCNR, time); return ret; } static int sa1100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) { - u32 rtsr; + struct sa1100_rtc *sa1100_rtc = dev_get_drvdata(dev); + unsigned long time; + unsigned int rtsr; - memcpy(&alrm->time, &rtc_alarm, sizeof(struct rtc_time)); - rtsr = RTSR; + time = rtc_readl(sa1100_rtc, RCNR); + rtc_time_to_tm(time, &alrm->time); + rtsr = rtc_readl(sa1100_rtc, RTSR); alrm->enabled = (rtsr & RTSR_ALE) ? 1 : 0; alrm->pending = (rtsr & RTSR_AL) ? 1 : 0; return 0; @@ -234,26 +233,39 @@ static int sa1100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) static int sa1100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) { - int ret; + struct sa1100_rtc *sa1100_rtc = dev_get_drvdata(dev); + struct rtc_time now_tm, alarm_tm; + unsigned long time, alarm; + unsigned int rtsr; - spin_lock_irq(&sa1100_rtc_lock); - ret = rtc_update_alarm(&alrm->time); - if (ret == 0) { - if (alrm->enabled) - RTSR |= RTSR_ALE; - else - RTSR &= ~RTSR_ALE; - } - spin_unlock_irq(&sa1100_rtc_lock); + spin_lock_irq(&sa1100_rtc->lock); - return ret; + time = rtc_readl(sa1100_rtc, RCNR); + rtc_time_to_tm(time, &now_tm); + rtc_next_alarm_time(&alarm_tm, &now_tm, &alrm->time); + rtc_tm_to_time(&alarm_tm, &alarm); + rtc_writel(sa1100_rtc, RTAR, alarm); + + rtsr = rtc_readl(sa1100_rtc, RTSR); + if (alrm->enabled) + rtsr |= RTSR_ALE; + else + rtsr &= ~RTSR_ALE; + rtc_writel(sa1100_rtc, RTSR, rtsr); + + spin_unlock_irq(&sa1100_rtc->lock); + + return 0; } static int sa1100_rtc_proc(struct device *dev, struct seq_file *seq) { - seq_printf(seq, "trim/divider\t\t: 0x%08x\n", (u32) RTTR); - seq_printf(seq, "RTSR\t\t\t: 0x%08x\n", (u32)RTSR); + struct sa1100_rtc *sa1100_rtc = dev_get_drvdata(dev); + seq_printf(seq, "trim/divider\t\t: 0x%08x\n", + rtc_readl(sa1100_rtc, RTTR)); + seq_printf(seq, "RTSR\t\t\t: 0x%08x\n", + rtc_readl(sa1100_rtc, RTSR)); return 0; } @@ -270,7 +282,51 @@ static const struct rtc_class_ops sa1100_rtc_ops = { static int sa1100_rtc_probe(struct platform_device *pdev) { - struct rtc_device *rtc; + struct sa1100_rtc *sa1100_rtc; + unsigned int rttr; + int ret; + + sa1100_rtc = kzalloc(sizeof(struct sa1100_rtc), GFP_KERNEL); + if (!sa1100_rtc) + return -ENOMEM; + + spin_lock_init(&sa1100_rtc->lock); + platform_set_drvdata(pdev, sa1100_rtc); + + ret = -ENXIO; + sa1100_rtc->ress = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!sa1100_rtc->ress) { + dev_err(&pdev->dev, "No I/O memory resource defined\n"); + goto err_ress; + } + + sa1100_rtc->irq_1Hz = platform_get_irq(pdev, 0); + if (sa1100_rtc->irq_1Hz < 0) { + dev_err(&pdev->dev, "No 1Hz IRQ resource defined\n"); + goto err_ress; + } + sa1100_rtc->irq_Alrm = platform_get_irq(pdev, 1); + if (sa1100_rtc->irq_Alrm < 0) { + dev_err(&pdev->dev, "No alarm IRQ resource defined\n"); + goto err_ress; + } + + ret = -ENOMEM; + sa1100_rtc->base = ioremap(sa1100_rtc->ress->start, + resource_size(sa1100_rtc->ress)); + if (!sa1100_rtc->base) { + dev_err(&pdev->dev, "Unable to map pxa RTC I/O memory\n"); + goto err_map; + } + + sa1100_rtc->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(sa1100_rtc->clk)) { + dev_err(&pdev->dev, "failed to find rtc clock source\n"); + ret = PTR_ERR(sa1100_rtc->clk); + goto err_clk; + } + clk_prepare(sa1100_rtc->clk); + clk_enable(sa1100_rtc->clk); /* * According to the manual we should be able to let RTTR be zero @@ -279,24 +335,24 @@ static int sa1100_rtc_probe(struct platform_device *pdev) * If the clock divider is uninitialized then reset it to the * default value to get the 1Hz clock. */ - if (RTTR == 0) { - RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16); - dev_warn(&pdev->dev, "warning: " - "initializing default clock divider/trim value\n"); + if (rtc_readl(sa1100_rtc, RTTR) == 0) { + rttr = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16); + rtc_writel(sa1100_rtc, RTTR, rttr); + dev_warn(&pdev->dev, "warning: initializing default clock" + " divider/trim value\n"); /* The current RTC value probably doesn't make sense either */ - RCNR = 0; + rtc_writel(sa1100_rtc, RCNR, 0); } device_init_wakeup(&pdev->dev, 1); - rtc = rtc_device_register(pdev->name, &pdev->dev, &sa1100_rtc_ops, - THIS_MODULE); - - if (IS_ERR(rtc)) - return PTR_ERR(rtc); - - platform_set_drvdata(pdev, rtc); - + sa1100_rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, + &sa1100_rtc_ops, THIS_MODULE); + if (IS_ERR(sa1100_rtc->rtc)) { + dev_err(&pdev->dev, "Failed to register RTC device -> %d\n", + ret); + goto err_rtc_reg; + } /* Fix for a nasty initialization problem the in SA11xx RTSR register. * See also the comments in sa1100_rtc_interrupt(). * @@ -319,33 +375,46 @@ static int sa1100_rtc_probe(struct platform_device *pdev) * * Notice that clearing bit 1 and 0 is accomplished by writting ONES to * the corresponding bits in RTSR. */ - RTSR = RTSR_AL | RTSR_HZ; + rtc_writel(sa1100_rtc, RTSR, (RTSR_AL | RTSR_HZ)); return 0; + +err_rtc_reg: +err_clk: + iounmap(sa1100_rtc->base); +err_ress: +err_map: + kfree(sa1100_rtc); + return ret; } static int sa1100_rtc_remove(struct platform_device *pdev) { - struct rtc_device *rtc = platform_get_drvdata(pdev); - - if (rtc) - rtc_device_unregister(rtc); + struct sa1100_rtc *sa1100_rtc = platform_get_drvdata(pdev); + rtc_device_unregister(sa1100_rtc->rtc); + clk_disable(sa1100_rtc->clk); + clk_unprepare(sa1100_rtc->clk); + iounmap(sa1100_rtc->base); return 0; } #ifdef CONFIG_PM static int sa1100_rtc_suspend(struct device *dev) { + struct sa1100_rtc *sa1100_rtc = dev_get_drvdata(dev); + if (device_may_wakeup(dev)) - enable_irq_wake(IRQ_RTCAlrm); + enable_irq_wake(sa1100_rtc->irq_Alrm); return 0; } static int sa1100_rtc_resume(struct device *dev) { + struct sa1100_rtc *sa1100_rtc = dev_get_drvdata(dev); + if (device_may_wakeup(dev)) - disable_irq_wake(IRQ_RTCAlrm); + disable_irq_wake(sa1100_rtc->irq_Alrm); return 0; } diff --git a/trunk/drivers/s390/block/dasd.c b/trunk/drivers/s390/block/dasd.c index 110137e7ec81..eef27a197c00 100644 --- a/trunk/drivers/s390/block/dasd.c +++ b/trunk/drivers/s390/block/dasd.c @@ -3261,12 +3261,6 @@ void dasd_generic_path_event(struct ccw_device *cdev, int *path_event) device->path_data.tbvpm |= eventlpm; dasd_schedule_device_bh(device); } - if (path_event[chp] & PE_PATHGROUP_ESTABLISHED) { - DBF_DEV_EVENT(DBF_WARNING, device, "%s", - "Pathgroup re-established\n"); - if (device->discipline->kick_validate) - device->discipline->kick_validate(device); - } } dasd_put_device(device); } diff --git a/trunk/drivers/s390/block/dasd_alias.c b/trunk/drivers/s390/block/dasd_alias.c index b3beed5434e4..553b3c5abb0a 100644 --- a/trunk/drivers/s390/block/dasd_alias.c +++ b/trunk/drivers/s390/block/dasd_alias.c @@ -189,12 +189,14 @@ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device) unsigned long flags; struct alias_server *server, *newserver; struct alias_lcu *lcu, *newlcu; + int is_lcu_known; struct dasd_uid uid; private = (struct dasd_eckd_private *) device->private; device->discipline->get_uid(device, &uid); spin_lock_irqsave(&aliastree.lock, flags); + is_lcu_known = 1; server = _find_server(&uid); if (!server) { spin_unlock_irqrestore(&aliastree.lock, flags); @@ -206,6 +208,7 @@ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device) if (!server) { list_add(&newserver->server, &aliastree.serverlist); server = newserver; + is_lcu_known = 0; } else { /* someone was faster */ _free_server(newserver); @@ -223,10 +226,12 @@ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device) if (!lcu) { list_add(&newlcu->lcu, &server->lculist); lcu = newlcu; + is_lcu_known = 0; } else { /* someone was faster */ _free_lcu(newlcu); } + is_lcu_known = 0; } spin_lock(&lcu->lock); list_add(&device->alias_list, &lcu->inactive_devices); @@ -234,7 +239,64 @@ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device) spin_unlock(&lcu->lock); spin_unlock_irqrestore(&aliastree.lock, flags); - return 0; + return is_lcu_known; +} + +/* + * The first device to be registered on an LCU will have to do + * some additional setup steps to configure that LCU on the + * storage server. All further devices should wait with their + * initialization until the first device is done. + * To synchronize this work, the first device will call + * dasd_alias_lcu_setup_complete when it is done, and all + * other devices will wait for it with dasd_alias_wait_for_lcu_setup. + */ +void dasd_alias_lcu_setup_complete(struct dasd_device *device) +{ + unsigned long flags; + struct alias_server *server; + struct alias_lcu *lcu; + struct dasd_uid uid; + + device->discipline->get_uid(device, &uid); + lcu = NULL; + spin_lock_irqsave(&aliastree.lock, flags); + server = _find_server(&uid); + if (server) + lcu = _find_lcu(server, &uid); + spin_unlock_irqrestore(&aliastree.lock, flags); + if (!lcu) { + DBF_EVENT_DEVID(DBF_ERR, device->cdev, + "could not find lcu for %04x %02x", + uid.ssid, uid.real_unit_addr); + WARN_ON(1); + return; + } + complete_all(&lcu->lcu_setup); +} + +void dasd_alias_wait_for_lcu_setup(struct dasd_device *device) +{ + unsigned long flags; + struct alias_server *server; + struct alias_lcu *lcu; + struct dasd_uid uid; + + device->discipline->get_uid(device, &uid); + lcu = NULL; + spin_lock_irqsave(&aliastree.lock, flags); + server = _find_server(&uid); + if (server) + lcu = _find_lcu(server, &uid); + spin_unlock_irqrestore(&aliastree.lock, flags); + if (!lcu) { + DBF_EVENT_DEVID(DBF_ERR, device->cdev, + "could not find lcu for %04x %02x", + uid.ssid, uid.real_unit_addr); + WARN_ON(1); + return; + } + wait_for_completion(&lcu->lcu_setup); } /* diff --git a/trunk/drivers/s390/block/dasd_eckd.c b/trunk/drivers/s390/block/dasd_eckd.c index 70880be26015..bbcd5e9206ee 100644 --- a/trunk/drivers/s390/block/dasd_eckd.c +++ b/trunk/drivers/s390/block/dasd_eckd.c @@ -1534,10 +1534,6 @@ static void dasd_eckd_validate_server(struct dasd_device *device) struct dasd_eckd_private *private; int enable_pav; - private = (struct dasd_eckd_private *) device->private; - if (private->uid.type == UA_BASE_PAV_ALIAS || - private->uid.type == UA_HYPER_PAV_ALIAS) - return; if (dasd_nopav || MACHINE_IS_VM) enable_pav = 0; else @@ -1546,28 +1542,11 @@ static void dasd_eckd_validate_server(struct dasd_device *device) /* may be requested feature is not available on server, * therefore just report error and go ahead */ + private = (struct dasd_eckd_private *) device->private; DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "PSF-SSC for SSID %04x " "returned rc=%d", private->uid.ssid, rc); } -/* - * worker to do a validate server in case of a lost pathgroup - */ -static void dasd_eckd_do_validate_server(struct work_struct *work) -{ - struct dasd_device *device = container_of(work, struct dasd_device, - kick_validate); - dasd_eckd_validate_server(device); - dasd_put_device(device); -} - -static void dasd_eckd_kick_validate_server(struct dasd_device *device) -{ - dasd_get_device(device); - /* queue call to do_validate_server to the kernel event daemon. */ - schedule_work(&device->kick_validate); -} - static u32 get_fcx_max_data(struct dasd_device *device) { #if defined(CONFIG_64BIT) @@ -1609,13 +1588,10 @@ dasd_eckd_check_characteristics(struct dasd_device *device) struct dasd_eckd_private *private; struct dasd_block *block; struct dasd_uid temp_uid; - int rc, i; + int is_known, rc, i; int readonly; unsigned long value; - /* setup work queue for validate server*/ - INIT_WORK(&device->kick_validate, dasd_eckd_do_validate_server); - if (!ccw_device_is_pathgroup(device->cdev)) { dev_warn(&device->cdev->dev, "A channel path group could not be established\n"); @@ -1675,12 +1651,22 @@ dasd_eckd_check_characteristics(struct dasd_device *device) block->base = device; } - /* register lcu with alias handling, enable PAV */ - rc = dasd_alias_make_device_known_to_lcu(device); - if (rc) + /* register lcu with alias handling, enable PAV if this is a new lcu */ + is_known = dasd_alias_make_device_known_to_lcu(device); + if (is_known < 0) { + rc = is_known; goto out_err2; - - dasd_eckd_validate_server(device); + } + /* + * dasd_eckd_validate_server is done on the first device that + * is found for an LCU. All later other devices have to wait + * for it, so they will read the correct feature codes. + */ + if (!is_known) { + dasd_eckd_validate_server(device); + dasd_alias_lcu_setup_complete(device); + } else + dasd_alias_wait_for_lcu_setup(device); /* device may report different configuration data after LCU setup */ rc = dasd_eckd_read_conf(device); @@ -4112,7 +4098,7 @@ static int dasd_eckd_restore_device(struct dasd_device *device) { struct dasd_eckd_private *private; struct dasd_eckd_characteristics temp_rdc_data; - int rc; + int is_known, rc; struct dasd_uid temp_uid; unsigned long flags; @@ -4135,10 +4121,14 @@ static int dasd_eckd_restore_device(struct dasd_device *device) goto out_err; /* register lcu with alias handling, enable PAV if this is a new lcu */ - rc = dasd_alias_make_device_known_to_lcu(device); - if (rc) - return rc; - dasd_eckd_validate_server(device); + is_known = dasd_alias_make_device_known_to_lcu(device); + if (is_known < 0) + return is_known; + if (!is_known) { + dasd_eckd_validate_server(device); + dasd_alias_lcu_setup_complete(device); + } else + dasd_alias_wait_for_lcu_setup(device); /* RE-Read Configuration Data */ rc = dasd_eckd_read_conf(device); @@ -4280,7 +4270,6 @@ static struct dasd_discipline dasd_eckd_discipline = { .restore = dasd_eckd_restore_device, .reload = dasd_eckd_reload_device, .get_uid = dasd_eckd_get_uid, - .kick_validate = dasd_eckd_kick_validate_server, }; static int __init diff --git a/trunk/drivers/s390/block/dasd_int.h b/trunk/drivers/s390/block/dasd_int.h index 33a6743ddc55..afe8c33422ed 100644 --- a/trunk/drivers/s390/block/dasd_int.h +++ b/trunk/drivers/s390/block/dasd_int.h @@ -355,7 +355,6 @@ struct dasd_discipline { int (*reload) (struct dasd_device *); int (*get_uid) (struct dasd_device *, struct dasd_uid *); - void (*kick_validate) (struct dasd_device *); }; extern struct dasd_discipline *dasd_diag_discipline_pointer; @@ -456,7 +455,6 @@ struct dasd_device { struct work_struct kick_work; struct work_struct restore_device; struct work_struct reload_device; - struct work_struct kick_validate; struct timer_list timer; debug_info_t *debug_area; diff --git a/trunk/drivers/scsi/mac_esp.c b/trunk/drivers/scsi/mac_esp.c index 70eb1f79b1ba..4ceeace80453 100644 --- a/trunk/drivers/scsi/mac_esp.c +++ b/trunk/drivers/scsi/mac_esp.c @@ -565,7 +565,8 @@ static int __devinit esp_mac_probe(struct platform_device *dev) esp_chips[dev->id] = esp; mb(); if (esp_chips[!dev->id] == NULL) { - err = request_irq(host->irq, mac_scsi_esp_intr, 0, "ESP", NULL); + err = request_irq(host->irq, mac_scsi_esp_intr, 0, + "Mac ESP", NULL); if (err < 0) { esp_chips[dev->id] = NULL; goto fail_free_priv; diff --git a/trunk/drivers/scsi/mac_scsi.c b/trunk/drivers/scsi/mac_scsi.c index 2bccfbe5661e..ea2bde206f7f 100644 --- a/trunk/drivers/scsi/mac_scsi.c +++ b/trunk/drivers/scsi/mac_scsi.c @@ -339,6 +339,9 @@ static void mac_scsi_reset_boot(struct Scsi_Host *instance) printk(KERN_INFO "Macintosh SCSI: resetting the SCSI bus..." ); + /* switch off SCSI IRQ - catch an interrupt without IRQ bit set else */ + disable_irq(IRQ_MAC_SCSI); + /* get in phase */ NCR5380_write( TARGET_COMMAND_REG, PHASE_SR_TO_TCR( NCR5380_read(STATUS_REG) )); @@ -354,6 +357,9 @@ static void mac_scsi_reset_boot(struct Scsi_Host *instance) for( end = jiffies + AFTER_RESET_DELAY; time_before(jiffies, end); ) barrier(); + /* switch on SCSI IRQ again */ + enable_irq(IRQ_MAC_SCSI); + printk(KERN_INFO " done\n" ); } #endif diff --git a/trunk/drivers/spi/Kconfig b/trunk/drivers/spi/Kconfig index 8293658e7cf9..3f9a47ec67dc 100644 --- a/trunk/drivers/spi/Kconfig +++ b/trunk/drivers/spi/Kconfig @@ -299,7 +299,7 @@ config SPI_S3C24XX_FIQ config SPI_S3C64XX tristate "Samsung S3C64XX series type SPI" - depends on (ARCH_S3C64XX || ARCH_S5P64X0 || ARCH_EXYNOS) + depends on (ARCH_S3C64XX || ARCH_S5P64X0) select S3C64XX_DMA if ARCH_S3C64XX help SPI driver for Samsung S3C64XX and newer SoCs. diff --git a/trunk/drivers/spi/spi-topcliff-pch.c b/trunk/drivers/spi/spi-topcliff-pch.c index 10182eb50068..2a6429d8c363 100644 --- a/trunk/drivers/spi/spi-topcliff-pch.c +++ b/trunk/drivers/spi/spi-topcliff-pch.c @@ -1720,7 +1720,7 @@ static int pch_spi_resume(struct pci_dev *pdev) #endif -static struct pci_driver pch_spi_pcidev_driver = { +static struct pci_driver pch_spi_pcidev = { .name = "pch_spi", .id_table = pch_spi_pcidev_id, .probe = pch_spi_probe, @@ -1736,7 +1736,7 @@ static int __init pch_spi_init(void) if (ret) return ret; - ret = pci_register_driver(&pch_spi_pcidev_driver); + ret = pci_register_driver(&pch_spi_pcidev); if (ret) return ret; @@ -1746,7 +1746,7 @@ module_init(pch_spi_init); static void __exit pch_spi_exit(void) { - pci_unregister_driver(&pch_spi_pcidev_driver); + pci_unregister_driver(&pch_spi_pcidev); platform_driver_unregister(&pch_spi_pd_driver); } module_exit(pch_spi_exit); 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/media/go7007/go7007-usb.c b/trunk/drivers/staging/media/go7007/go7007-usb.c index 5443e25086e9..70e006b50f29 100644 --- a/trunk/drivers/staging/media/go7007/go7007-usb.c +++ b/trunk/drivers/staging/media/go7007/go7007-usb.c @@ -1279,4 +1279,3 @@ static struct usb_driver go7007_usb_driver = { }; module_usb_driver(go7007_usb_driver); -MODULE_LICENSE("GPL v2"); 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/target/iscsi/iscsi_target.c b/trunk/drivers/target/iscsi/iscsi_target.c index 44262908def5..ac44af165b27 100644 --- a/trunk/drivers/target/iscsi/iscsi_target.c +++ b/trunk/drivers/target/iscsi/iscsi_target.c @@ -1061,7 +1061,7 @@ static int iscsit_handle_scsi_cmd( if (ret < 0) return iscsit_add_reject_from_cmd( ISCSI_REASON_BOOKMARK_NO_RESOURCES, - 1, 0, buf, cmd); + 1, 1, buf, cmd); /* * Check the CmdSN against ExpCmdSN/MaxCmdSN here if * the Immediate Bit is not set, and no Immediate @@ -3164,30 +3164,6 @@ static int iscsit_send_task_mgt_rsp( return 0; } -static bool iscsit_check_inaddr_any(struct iscsi_np *np) -{ - bool ret = false; - - if (np->np_sockaddr.ss_family == AF_INET6) { - const struct sockaddr_in6 sin6 = { - .sin6_addr = IN6ADDR_ANY_INIT }; - struct sockaddr_in6 *sock_in6 = - (struct sockaddr_in6 *)&np->np_sockaddr; - - if (!memcmp(sock_in6->sin6_addr.s6_addr, - sin6.sin6_addr.s6_addr, 16)) - ret = true; - } else { - struct sockaddr_in * sock_in = - (struct sockaddr_in *)&np->np_sockaddr; - - if (sock_in->sin_addr.s_addr == INADDR_ANY) - ret = true; - } - - return ret; -} - static int iscsit_build_sendtargets_response(struct iscsi_cmd *cmd) { char *payload = NULL; @@ -3237,17 +3213,12 @@ static int iscsit_build_sendtargets_response(struct iscsi_cmd *cmd) spin_lock(&tpg->tpg_np_lock); list_for_each_entry(tpg_np, &tpg->tpg_gnp_list, tpg_np_list) { - struct iscsi_np *np = tpg_np->tpg_np; - bool inaddr_any = iscsit_check_inaddr_any(np); - len = sprintf(buf, "TargetAddress=" "%s%s%s:%hu,%hu", - (np->np_sockaddr.ss_family == AF_INET6) ? - "[" : "", (inaddr_any == false) ? - np->np_ip : conn->local_ip, - (np->np_sockaddr.ss_family == AF_INET6) ? - "]" : "", (inaddr_any == false) ? - np->np_port : conn->local_port, + (tpg_np->tpg_np->np_sockaddr.ss_family == AF_INET6) ? + "[" : "", tpg_np->tpg_np->np_ip, + (tpg_np->tpg_np->np_sockaddr.ss_family == AF_INET6) ? + "]" : "", tpg_np->tpg_np->np_port, tpg->tpgt); len += 1; diff --git a/trunk/drivers/target/iscsi/iscsi_target_configfs.c b/trunk/drivers/target/iscsi/iscsi_target_configfs.c index 6b35b37988ed..3468caab47a2 100644 --- a/trunk/drivers/target/iscsi/iscsi_target_configfs.c +++ b/trunk/drivers/target/iscsi/iscsi_target_configfs.c @@ -21,7 +21,6 @@ #include #include -#include #include #include #include diff --git a/trunk/drivers/target/iscsi/iscsi_target_core.h b/trunk/drivers/target/iscsi/iscsi_target_core.h index 0ec3b77a0c27..f1a02dad05a0 100644 --- a/trunk/drivers/target/iscsi/iscsi_target_core.h +++ b/trunk/drivers/target/iscsi/iscsi_target_core.h @@ -508,7 +508,6 @@ struct iscsi_conn { u16 cid; /* Remote TCP Port */ u16 login_port; - u16 local_port; int net_size; u32 auth_id; #define CONNFLAG_SCTP_STRUCT_FILE 0x01 @@ -528,7 +527,6 @@ struct iscsi_conn { unsigned char bad_hdr[ISCSI_HDR_LEN]; #define IPV6_ADDRESS_SPACE 48 unsigned char login_ip[IPV6_ADDRESS_SPACE]; - unsigned char local_ip[IPV6_ADDRESS_SPACE]; int conn_usage_count; int conn_waiting_on_uc; atomic_t check_immediate_queue; @@ -563,8 +561,8 @@ struct iscsi_conn { struct hash_desc conn_tx_hash; /* Used for scheduling TX and RX connection kthreads */ cpumask_var_t conn_cpumask; - unsigned int conn_rx_reset_cpumask:1; - unsigned int conn_tx_reset_cpumask:1; + int conn_rx_reset_cpumask:1; + int conn_tx_reset_cpumask:1; /* list_head of struct iscsi_cmd for this connection */ struct list_head conn_cmd_list; struct list_head immed_queue_list; diff --git a/trunk/drivers/target/iscsi/iscsi_target_erl1.c b/trunk/drivers/target/iscsi/iscsi_target_erl1.c index 27901e37c125..255c0d67e898 100644 --- a/trunk/drivers/target/iscsi/iscsi_target_erl1.c +++ b/trunk/drivers/target/iscsi/iscsi_target_erl1.c @@ -1238,7 +1238,7 @@ void iscsit_mod_dataout_timer(struct iscsi_cmd *cmd) { struct iscsi_conn *conn = cmd->conn; struct iscsi_session *sess = conn->sess; - struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess); + struct iscsi_node_attrib *na = na = iscsit_tpg_get_node_attrib(sess); spin_lock_bh(&cmd->dataout_timeout_lock); if (!(cmd->dataout_timer_flags & ISCSI_TF_RUNNING)) { @@ -1261,7 +1261,7 @@ void iscsit_start_dataout_timer( struct iscsi_conn *conn) { struct iscsi_session *sess = conn->sess; - struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess); + struct iscsi_node_attrib *na = na = iscsit_tpg_get_node_attrib(sess); if (cmd->dataout_timer_flags & ISCSI_TF_RUNNING) return; diff --git a/trunk/drivers/target/iscsi/iscsi_target_login.c b/trunk/drivers/target/iscsi/iscsi_target_login.c index 38cb7ce8469e..373b0cc6abd8 100644 --- a/trunk/drivers/target/iscsi/iscsi_target_login.c +++ b/trunk/drivers/target/iscsi/iscsi_target_login.c @@ -615,8 +615,8 @@ static int iscsi_post_login_handler( } pr_debug("iSCSI Login successful on CID: %hu from %s to" - " %s:%hu,%hu\n", conn->cid, conn->login_ip, - conn->local_ip, conn->local_port, tpg->tpgt); + " %s:%hu,%hu\n", conn->cid, conn->login_ip, np->np_ip, + np->np_port, tpg->tpgt); list_add_tail(&conn->conn_list, &sess->sess_conn_list); atomic_inc(&sess->nconn); @@ -658,8 +658,7 @@ static int iscsi_post_login_handler( sess->session_state = TARG_SESS_STATE_LOGGED_IN; pr_debug("iSCSI Login successful on CID: %hu from %s to %s:%hu,%hu\n", - conn->cid, conn->login_ip, conn->local_ip, conn->local_port, - tpg->tpgt); + conn->cid, conn->login_ip, np->np_ip, np->np_port, tpg->tpgt); spin_lock_bh(&sess->conn_lock); list_add_tail(&conn->conn_list, &sess->sess_conn_list); @@ -842,14 +841,6 @@ int iscsi_target_setup_login_socket( goto fail; } - ret = kernel_setsockopt(sock, IPPROTO_IP, IP_FREEBIND, - (char *)&opt, sizeof(opt)); - if (ret < 0) { - pr_err("kernel_setsockopt() for IP_FREEBIND" - " failed\n"); - goto fail; - } - ret = kernel_bind(sock, (struct sockaddr *)&np->np_sockaddr, len); if (ret < 0) { pr_err("kernel_bind() failed: %d\n", ret); @@ -1029,18 +1020,6 @@ static int __iscsi_target_login_thread(struct iscsi_np *np) snprintf(conn->login_ip, sizeof(conn->login_ip), "%pI6c", &sock_in6.sin6_addr.in6_u); conn->login_port = ntohs(sock_in6.sin6_port); - - if (conn->sock->ops->getname(conn->sock, - (struct sockaddr *)&sock_in6, &err, 0) < 0) { - pr_err("sock_ops->getname() failed.\n"); - iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, - ISCSI_LOGIN_STATUS_TARGET_ERROR); - goto new_sess_out; - } - snprintf(conn->local_ip, sizeof(conn->local_ip), "%pI6c", - &sock_in6.sin6_addr.in6_u); - conn->local_port = ntohs(sock_in6.sin6_port); - } else { memset(&sock_in, 0, sizeof(struct sockaddr_in)); @@ -1053,16 +1032,6 @@ static int __iscsi_target_login_thread(struct iscsi_np *np) } sprintf(conn->login_ip, "%pI4", &sock_in.sin_addr.s_addr); conn->login_port = ntohs(sock_in.sin_port); - - if (conn->sock->ops->getname(conn->sock, - (struct sockaddr *)&sock_in, &err, 0) < 0) { - pr_err("sock_ops->getname() failed.\n"); - iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, - ISCSI_LOGIN_STATUS_TARGET_ERROR); - goto new_sess_out; - } - sprintf(conn->local_ip, "%pI4", &sock_in.sin_addr.s_addr); - conn->local_port = ntohs(sock_in.sin_port); } conn->network_transport = np->np_network_transport; @@ -1070,7 +1039,7 @@ static int __iscsi_target_login_thread(struct iscsi_np *np) pr_debug("Received iSCSI login request from %s on %s Network" " Portal %s:%hu\n", conn->login_ip, (conn->network_transport == ISCSI_TCP) ? "TCP" : "SCTP", - conn->local_ip, conn->local_port); + np->np_ip, np->np_port); pr_debug("Moving to TARG_CONN_STATE_IN_LOGIN.\n"); conn->conn_state = TARG_CONN_STATE_IN_LOGIN; diff --git a/trunk/drivers/target/iscsi/iscsi_target_util.c b/trunk/drivers/target/iscsi/iscsi_target_util.c index 11287e1ece13..a05ca1c4f01c 100644 --- a/trunk/drivers/target/iscsi/iscsi_target_util.c +++ b/trunk/drivers/target/iscsi/iscsi_target_util.c @@ -849,17 +849,6 @@ void iscsit_free_cmd(struct iscsi_cmd *cmd) case ISCSI_OP_SCSI_TMFUNC: transport_generic_free_cmd(&cmd->se_cmd, 1); break; - case ISCSI_OP_REJECT: - /* - * Handle special case for REJECT when iscsi_add_reject*() has - * overwritten the original iscsi_opcode assignment, and the - * associated cmd->se_cmd needs to be released. - */ - if (cmd->se_cmd.se_tfo != NULL) { - transport_generic_free_cmd(&cmd->se_cmd, 1); - break; - } - /* Fall-through */ default: iscsit_release_cmd(cmd); break; diff --git a/trunk/drivers/target/target_core_alua.c b/trunk/drivers/target/target_core_alua.c index 01a2691dfb47..1b1edd14f4bf 100644 --- a/trunk/drivers/target/target_core_alua.c +++ b/trunk/drivers/target/target_core_alua.c @@ -78,7 +78,7 @@ int target_emulate_report_target_port_groups(struct se_task *task) return -EINVAL; } - buf = transport_kmap_data_sg(cmd); + buf = transport_kmap_first_data_page(cmd); spin_lock(&su_dev->t10_alua.tg_pt_gps_lock); list_for_each_entry(tg_pt_gp, &su_dev->t10_alua.tg_pt_gps_list, @@ -163,7 +163,7 @@ int target_emulate_report_target_port_groups(struct se_task *task) buf[2] = ((rd_len >> 8) & 0xff); buf[3] = (rd_len & 0xff); - transport_kunmap_data_sg(cmd); + transport_kunmap_first_data_page(cmd); task->task_scsi_status = GOOD; transport_complete_task(task, 1); @@ -194,7 +194,7 @@ int target_emulate_set_target_port_groups(struct se_task *task) cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; return -EINVAL; } - buf = transport_kmap_data_sg(cmd); + buf = transport_kmap_first_data_page(cmd); /* * Determine if explict ALUA via SET_TARGET_PORT_GROUPS is allowed @@ -351,7 +351,7 @@ int target_emulate_set_target_port_groups(struct se_task *task) } out: - transport_kunmap_data_sg(cmd); + transport_kunmap_first_data_page(cmd); task->task_scsi_status = GOOD; transport_complete_task(task, 1); return 0; diff --git a/trunk/drivers/target/target_core_cdb.c b/trunk/drivers/target/target_core_cdb.c index f3d71fa88a28..2f2235edefff 100644 --- a/trunk/drivers/target/target_core_cdb.c +++ b/trunk/drivers/target/target_core_cdb.c @@ -83,7 +83,7 @@ target_emulate_inquiry_std(struct se_cmd *cmd) return -EINVAL; } - buf = transport_kmap_data_sg(cmd); + buf = transport_kmap_first_data_page(cmd); if (dev == tpg->tpg_virt_lun0.lun_se_dev) { buf[0] = 0x3f; /* Not connected */ @@ -134,7 +134,7 @@ target_emulate_inquiry_std(struct se_cmd *cmd) buf[4] = 31; /* Set additional length to 31 */ out: - transport_kunmap_data_sg(cmd); + transport_kunmap_first_data_page(cmd); return 0; } @@ -698,13 +698,6 @@ int target_emulate_inquiry(struct se_task *task) int p, ret; if (!(cdb[1] & 0x1)) { - if (cdb[2]) { - pr_err("INQUIRY with EVPD==0 but PAGE CODE=%02x\n", - cdb[2]); - cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD; - return -EINVAL; - } - ret = target_emulate_inquiry_std(cmd); goto out; } @@ -723,7 +716,7 @@ int target_emulate_inquiry(struct se_task *task) return -EINVAL; } - buf = transport_kmap_data_sg(cmd); + buf = transport_kmap_first_data_page(cmd); buf[0] = dev->transport->get_device_type(dev); @@ -736,11 +729,11 @@ int target_emulate_inquiry(struct se_task *task) } pr_err("Unknown VPD Code: 0x%02x\n", cdb[2]); - cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD; + cmd->scsi_sense_reason = TCM_UNSUPPORTED_SCSI_OPCODE; ret = -EINVAL; out_unmap: - transport_kunmap_data_sg(cmd); + transport_kunmap_first_data_page(cmd); out: if (!ret) { task->task_scsi_status = GOOD; @@ -762,7 +755,7 @@ int target_emulate_readcapacity(struct se_task *task) else blocks = (u32)blocks_long; - buf = transport_kmap_data_sg(cmd); + buf = transport_kmap_first_data_page(cmd); buf[0] = (blocks >> 24) & 0xff; buf[1] = (blocks >> 16) & 0xff; @@ -778,7 +771,7 @@ int target_emulate_readcapacity(struct se_task *task) if (dev->se_sub_dev->se_dev_attrib.emulate_tpu || dev->se_sub_dev->se_dev_attrib.emulate_tpws) put_unaligned_be32(0xFFFFFFFF, &buf[0]); - transport_kunmap_data_sg(cmd); + transport_kunmap_first_data_page(cmd); task->task_scsi_status = GOOD; transport_complete_task(task, 1); @@ -792,7 +785,7 @@ int target_emulate_readcapacity_16(struct se_task *task) unsigned char *buf; unsigned long long blocks = dev->transport->get_blocks(dev); - buf = transport_kmap_data_sg(cmd); + buf = transport_kmap_first_data_page(cmd); buf[0] = (blocks >> 56) & 0xff; buf[1] = (blocks >> 48) & 0xff; @@ -813,7 +806,7 @@ int target_emulate_readcapacity_16(struct se_task *task) if (dev->se_sub_dev->se_dev_attrib.emulate_tpu || dev->se_sub_dev->se_dev_attrib.emulate_tpws) buf[14] = 0x80; - transport_kunmap_data_sg(cmd); + transport_kunmap_first_data_page(cmd); task->task_scsi_status = GOOD; transport_complete_task(task, 1); @@ -1026,9 +1019,9 @@ int target_emulate_modesense(struct se_task *task) offset = cmd->data_length; } - rbuf = transport_kmap_data_sg(cmd); + rbuf = transport_kmap_first_data_page(cmd); memcpy(rbuf, buf, offset); - transport_kunmap_data_sg(cmd); + transport_kunmap_first_data_page(cmd); task->task_scsi_status = GOOD; transport_complete_task(task, 1); @@ -1050,7 +1043,7 @@ int target_emulate_request_sense(struct se_task *task) return -ENOSYS; } - buf = transport_kmap_data_sg(cmd); + buf = transport_kmap_first_data_page(cmd); if (!core_scsi3_ua_clear_for_request_sense(cmd, &ua_asc, &ua_ascq)) { /* @@ -1058,8 +1051,11 @@ int target_emulate_request_sense(struct se_task *task) */ buf[0] = 0x70; buf[SPC_SENSE_KEY_OFFSET] = UNIT_ATTENTION; - - if (cmd->data_length < 18) { + /* + * Make sure request data length is enough for additional + * sense data. + */ + if (cmd->data_length <= 18) { buf[7] = 0x00; err = -EINVAL; goto end; @@ -1076,8 +1072,11 @@ int target_emulate_request_sense(struct se_task *task) */ buf[0] = 0x70; buf[SPC_SENSE_KEY_OFFSET] = NO_SENSE; - - if (cmd->data_length < 18) { + /* + * Make sure request data length is enough for additional + * sense data. + */ + if (cmd->data_length <= 18) { buf[7] = 0x00; err = -EINVAL; goto end; @@ -1090,7 +1089,7 @@ int target_emulate_request_sense(struct se_task *task) } end: - transport_kunmap_data_sg(cmd); + transport_kunmap_first_data_page(cmd); task->task_scsi_status = GOOD; transport_complete_task(task, 1); return 0; @@ -1124,7 +1123,7 @@ int target_emulate_unmap(struct se_task *task) dl = get_unaligned_be16(&cdb[0]); bd_dl = get_unaligned_be16(&cdb[2]); - buf = transport_kmap_data_sg(cmd); + buf = transport_kmap_first_data_page(cmd); ptr = &buf[offset]; pr_debug("UNMAP: Sub: %s Using dl: %hu bd_dl: %hu size: %hu" @@ -1148,7 +1147,7 @@ int target_emulate_unmap(struct se_task *task) } err: - transport_kunmap_data_sg(cmd); + transport_kunmap_first_data_page(cmd); if (!ret) { task->task_scsi_status = GOOD; transport_complete_task(task, 1); diff --git a/trunk/drivers/target/target_core_configfs.c b/trunk/drivers/target/target_core_configfs.c index 6e043eeb1db9..0955bb8979fb 100644 --- a/trunk/drivers/target/target_core_configfs.c +++ b/trunk/drivers/target/target_core_configfs.c @@ -1704,15 +1704,13 @@ static ssize_t target_core_store_dev_alias( return -EINVAL; } + se_dev->su_dev_flags |= SDF_USING_ALIAS; read_bytes = snprintf(&se_dev->se_dev_alias[0], SE_DEV_ALIAS_LEN, "%s", page); - if (!read_bytes) - return -EINVAL; + if (se_dev->se_dev_alias[read_bytes - 1] == '\n') se_dev->se_dev_alias[read_bytes - 1] = '\0'; - se_dev->su_dev_flags |= SDF_USING_ALIAS; - pr_debug("Target_Core_ConfigFS: %s/%s set alias: %s\n", config_item_name(&hba->hba_group.cg_item), config_item_name(&se_dev->se_dev_group.cg_item), @@ -1755,15 +1753,13 @@ static ssize_t target_core_store_dev_udev_path( return -EINVAL; } + se_dev->su_dev_flags |= SDF_USING_UDEV_PATH; read_bytes = snprintf(&se_dev->se_dev_udev_path[0], SE_UDEV_PATH_LEN, "%s", page); - if (!read_bytes) - return -EINVAL; + if (se_dev->se_dev_udev_path[read_bytes - 1] == '\n') se_dev->se_dev_udev_path[read_bytes - 1] = '\0'; - se_dev->su_dev_flags |= SDF_USING_UDEV_PATH; - pr_debug("Target_Core_ConfigFS: %s/%s set udev_path: %s\n", config_item_name(&hba->hba_group.cg_item), config_item_name(&se_dev->se_dev_group.cg_item), diff --git a/trunk/drivers/target/target_core_device.c b/trunk/drivers/target/target_core_device.c index edbcabbf85f7..0c5992f0d946 100644 --- a/trunk/drivers/target/target_core_device.c +++ b/trunk/drivers/target/target_core_device.c @@ -320,12 +320,11 @@ int core_free_device_list_for_node( void core_dec_lacl_count(struct se_node_acl *se_nacl, struct se_cmd *se_cmd) { struct se_dev_entry *deve; - unsigned long flags; - spin_lock_irqsave(&se_nacl->device_list_lock, flags); + spin_lock_irq(&se_nacl->device_list_lock); deve = &se_nacl->device_list[se_cmd->orig_fe_lun]; deve->deve_cmds--; - spin_unlock_irqrestore(&se_nacl->device_list_lock, flags); + spin_unlock_irq(&se_nacl->device_list_lock); } void core_update_device_list_access( @@ -657,7 +656,7 @@ int target_report_luns(struct se_task *se_task) unsigned char *buf; u32 cdb_offset = 0, lun_count = 0, offset = 8, i; - buf = (unsigned char *) transport_kmap_data_sg(se_cmd); + buf = transport_kmap_first_data_page(se_cmd); /* * If no struct se_session pointer is present, this struct se_cmd is @@ -695,7 +694,7 @@ int target_report_luns(struct se_task *se_task) * See SPC3 r07, page 159. */ done: - transport_kunmap_data_sg(se_cmd); + transport_kunmap_first_data_page(se_cmd); lun_count *= 8; buf[0] = ((lun_count >> 24) & 0xff); buf[1] = ((lun_count >> 16) & 0xff); @@ -1295,26 +1294,24 @@ struct se_lun *core_dev_add_lun( { struct se_lun *lun_p; u32 lun_access = 0; - int rc; if (atomic_read(&dev->dev_access_obj.obj_access_count) != 0) { pr_err("Unable to export struct se_device while dev_access_obj: %d\n", atomic_read(&dev->dev_access_obj.obj_access_count)); - return ERR_PTR(-EACCES); + return NULL; } lun_p = core_tpg_pre_addlun(tpg, lun); - if (IS_ERR(lun_p)) - return lun_p; + if ((IS_ERR(lun_p)) || !lun_p) + return NULL; if (dev->dev_flags & DF_READ_ONLY) lun_access = TRANSPORT_LUNFLAGS_READ_ONLY; else lun_access = TRANSPORT_LUNFLAGS_READ_WRITE; - rc = core_tpg_post_addlun(tpg, lun_p, lun_access, dev); - if (rc < 0) - return ERR_PTR(rc); + if (core_tpg_post_addlun(tpg, lun_p, lun_access, dev) < 0) + return NULL; pr_debug("%s_TPG[%u]_LUN[%u] - Activated %s Logical Unit from" " CORE HBA: %u\n", tpg->se_tpg_tfo->get_fabric_name(), @@ -1351,10 +1348,11 @@ int core_dev_del_lun( u32 unpacked_lun) { struct se_lun *lun; + int ret = 0; - lun = core_tpg_pre_dellun(tpg, unpacked_lun); - if (IS_ERR(lun)) - return PTR_ERR(lun); + lun = core_tpg_pre_dellun(tpg, unpacked_lun, &ret); + if (!lun) + return ret; core_tpg_post_dellun(tpg, lun); diff --git a/trunk/drivers/target/target_core_fabric_configfs.c b/trunk/drivers/target/target_core_fabric_configfs.c index 9a2ce11e1a6e..4f77cce22646 100644 --- a/trunk/drivers/target/target_core_fabric_configfs.c +++ b/trunk/drivers/target/target_core_fabric_configfs.c @@ -766,9 +766,9 @@ static int target_fabric_port_link( lun_p = core_dev_add_lun(se_tpg, dev->se_hba, dev, lun->unpacked_lun); - if (IS_ERR(lun_p)) { + if (IS_ERR(lun_p) || !lun_p) { pr_err("core_dev_add_lun() failed\n"); - ret = PTR_ERR(lun_p); + ret = -EINVAL; goto out; } diff --git a/trunk/drivers/target/target_core_iblock.c b/trunk/drivers/target/target_core_iblock.c index 8572eae62da7..cc8e6b58ef20 100644 --- a/trunk/drivers/target/target_core_iblock.c +++ b/trunk/drivers/target/target_core_iblock.c @@ -129,7 +129,7 @@ static struct se_device *iblock_create_virtdevice( /* * These settings need to be made tunable.. */ - ib_dev->ibd_bio_set = bioset_create(32, 0); + ib_dev->ibd_bio_set = bioset_create(32, 64); if (!ib_dev->ibd_bio_set) { pr_err("IBLOCK: Unable to create bioset()\n"); return ERR_PTR(-ENOMEM); @@ -181,7 +181,7 @@ static struct se_device *iblock_create_virtdevice( */ dev->se_sub_dev->se_dev_attrib.max_unmap_block_desc_count = 1; dev->se_sub_dev->se_dev_attrib.unmap_granularity = - q->limits.discard_granularity >> 9; + q->limits.discard_granularity; dev->se_sub_dev->se_dev_attrib.unmap_granularity_alignment = q->limits.discard_alignment; @@ -488,13 +488,6 @@ iblock_get_bio(struct se_task *task, sector_t lba, u32 sg_num) struct iblock_req *ib_req = IBLOCK_REQ(task); struct bio *bio; - /* - * Only allocate as many vector entries as the bio code allows us to, - * we'll loop later on until we have handled the whole request. - */ - if (sg_num > BIO_MAX_PAGES) - sg_num = BIO_MAX_PAGES; - bio = bio_alloc_bioset(GFP_NOIO, sg_num, ib_dev->ibd_bio_set); if (!bio) { pr_err("Unable to allocate memory for bio\n"); diff --git a/trunk/drivers/target/target_core_internal.h b/trunk/drivers/target/target_core_internal.h index 45001364788a..26f135e94f6e 100644 --- a/trunk/drivers/target/target_core_internal.h +++ b/trunk/drivers/target/target_core_internal.h @@ -90,7 +90,7 @@ void core_tpg_wait_for_nacl_pr_ref(struct se_node_acl *); struct se_lun *core_tpg_pre_addlun(struct se_portal_group *, u32); int core_tpg_post_addlun(struct se_portal_group *, struct se_lun *, u32, void *); -struct se_lun *core_tpg_pre_dellun(struct se_portal_group *, u32 unpacked_lun); +struct se_lun *core_tpg_pre_dellun(struct se_portal_group *, u32, int *); int core_tpg_post_dellun(struct se_portal_group *, struct se_lun *); /* target_core_transport.c */ diff --git a/trunk/drivers/target/target_core_pr.c b/trunk/drivers/target/target_core_pr.c index b7c779389eea..429ad7291664 100644 --- a/trunk/drivers/target/target_core_pr.c +++ b/trunk/drivers/target/target_core_pr.c @@ -478,7 +478,6 @@ static int core_scsi3_pr_seq_non_holder( case READ_MEDIA_SERIAL_NUMBER: case REPORT_LUNS: case REQUEST_SENSE: - case PERSISTENT_RESERVE_IN: ret = 0; /*/ Allowed CDBs */ break; default: @@ -1535,7 +1534,7 @@ static int core_scsi3_decode_spec_i_port( tidh_new->dest_local_nexus = 1; list_add_tail(&tidh_new->dest_list, &tid_dest_list); - buf = transport_kmap_data_sg(cmd); + buf = transport_kmap_first_data_page(cmd); /* * For a PERSISTENT RESERVE OUT specify initiator ports payload, * first extract TransportID Parameter Data Length, and make sure @@ -1786,7 +1785,7 @@ static int core_scsi3_decode_spec_i_port( } - transport_kunmap_data_sg(cmd); + transport_kunmap_first_data_page(cmd); /* * Go ahead and create a registrations from tid_dest_list for the @@ -1834,7 +1833,7 @@ static int core_scsi3_decode_spec_i_port( return 0; out: - transport_kunmap_data_sg(cmd); + transport_kunmap_first_data_page(cmd); /* * For the failure case, release everything from tid_dest_list * including *dest_pr_reg and the configfs dependances.. @@ -3121,7 +3120,7 @@ static int core_scsi3_pro_preempt( if (!calling_it_nexus) core_scsi3_ua_allocate(pr_reg_nacl, pr_res_mapped_lun, 0x2A, - ASCQ_2AH_REGISTRATIONS_PREEMPTED); + ASCQ_2AH_RESERVATIONS_PREEMPTED); } spin_unlock(&pr_tmpl->registration_lock); /* @@ -3234,7 +3233,7 @@ static int core_scsi3_pro_preempt( * additional sense code set to REGISTRATIONS PREEMPTED; */ core_scsi3_ua_allocate(pr_reg_nacl, pr_res_mapped_lun, 0x2A, - ASCQ_2AH_REGISTRATIONS_PREEMPTED); + ASCQ_2AH_RESERVATIONS_PREEMPTED); } spin_unlock(&pr_tmpl->registration_lock); /* @@ -3411,14 +3410,14 @@ static int core_scsi3_emulate_pro_register_and_move( * will be moved to for the TransportID containing SCSI initiator WWN * information. */ - buf = transport_kmap_data_sg(cmd); + buf = transport_kmap_first_data_page(cmd); rtpi = (buf[18] & 0xff) << 8; rtpi |= buf[19] & 0xff; tid_len = (buf[20] & 0xff) << 24; tid_len |= (buf[21] & 0xff) << 16; tid_len |= (buf[22] & 0xff) << 8; tid_len |= buf[23] & 0xff; - transport_kunmap_data_sg(cmd); + transport_kunmap_first_data_page(cmd); buf = NULL; if ((tid_len + 24) != cmd->data_length) { @@ -3470,7 +3469,7 @@ static int core_scsi3_emulate_pro_register_and_move( return -EINVAL; } - buf = transport_kmap_data_sg(cmd); + buf = transport_kmap_first_data_page(cmd); proto_ident = (buf[24] & 0x0f); #if 0 pr_debug("SPC-3 PR REGISTER_AND_MOVE: Extracted Protocol Identifier:" @@ -3504,7 +3503,7 @@ static int core_scsi3_emulate_pro_register_and_move( goto out; } - transport_kunmap_data_sg(cmd); + transport_kunmap_first_data_page(cmd); buf = NULL; pr_debug("SPC-3 PR [%s] Extracted initiator %s identifier: %s" @@ -3769,13 +3768,13 @@ static int core_scsi3_emulate_pro_register_and_move( " REGISTER_AND_MOVE\n"); } - transport_kunmap_data_sg(cmd); + transport_kunmap_first_data_page(cmd); core_scsi3_put_pr_reg(dest_pr_reg); return 0; out: if (buf) - transport_kunmap_data_sg(cmd); + transport_kunmap_first_data_page(cmd); if (dest_se_deve) core_scsi3_lunacl_undepend_item(dest_se_deve); if (dest_node_acl) @@ -3849,7 +3848,7 @@ int target_scsi3_emulate_pr_out(struct se_task *task) scope = (cdb[2] & 0xf0); type = (cdb[2] & 0x0f); - buf = transport_kmap_data_sg(cmd); + buf = transport_kmap_first_data_page(cmd); /* * From PERSISTENT_RESERVE_OUT parameter list (payload) */ @@ -3867,7 +3866,7 @@ int target_scsi3_emulate_pr_out(struct se_task *task) aptpl = (buf[17] & 0x01); unreg = (buf[17] & 0x02); } - transport_kunmap_data_sg(cmd); + transport_kunmap_first_data_page(cmd); buf = NULL; /* @@ -3967,7 +3966,7 @@ static int core_scsi3_pri_read_keys(struct se_cmd *cmd) return -EINVAL; } - buf = transport_kmap_data_sg(cmd); + buf = transport_kmap_first_data_page(cmd); buf[0] = ((su_dev->t10_pr.pr_generation >> 24) & 0xff); buf[1] = ((su_dev->t10_pr.pr_generation >> 16) & 0xff); buf[2] = ((su_dev->t10_pr.pr_generation >> 8) & 0xff); @@ -4001,7 +4000,7 @@ static int core_scsi3_pri_read_keys(struct se_cmd *cmd) buf[6] = ((add_len >> 8) & 0xff); buf[7] = (add_len & 0xff); - transport_kunmap_data_sg(cmd); + transport_kunmap_first_data_page(cmd); return 0; } @@ -4027,7 +4026,7 @@ static int core_scsi3_pri_read_reservation(struct se_cmd *cmd) return -EINVAL; } - buf = transport_kmap_data_sg(cmd); + buf = transport_kmap_first_data_page(cmd); buf[0] = ((su_dev->t10_pr.pr_generation >> 24) & 0xff); buf[1] = ((su_dev->t10_pr.pr_generation >> 16) & 0xff); buf[2] = ((su_dev->t10_pr.pr_generation >> 8) & 0xff); @@ -4086,7 +4085,7 @@ static int core_scsi3_pri_read_reservation(struct se_cmd *cmd) err: spin_unlock(&se_dev->dev_reservation_lock); - transport_kunmap_data_sg(cmd); + transport_kunmap_first_data_page(cmd); return 0; } @@ -4110,7 +4109,7 @@ static int core_scsi3_pri_report_capabilities(struct se_cmd *cmd) return -EINVAL; } - buf = transport_kmap_data_sg(cmd); + buf = transport_kmap_first_data_page(cmd); buf[0] = ((add_len << 8) & 0xff); buf[1] = (add_len & 0xff); @@ -4142,7 +4141,7 @@ static int core_scsi3_pri_report_capabilities(struct se_cmd *cmd) buf[4] |= 0x02; /* PR_TYPE_WRITE_EXCLUSIVE */ buf[5] |= 0x01; /* PR_TYPE_EXCLUSIVE_ACCESS_ALLREG */ - transport_kunmap_data_sg(cmd); + transport_kunmap_first_data_page(cmd); return 0; } @@ -4172,7 +4171,7 @@ static int core_scsi3_pri_read_full_status(struct se_cmd *cmd) return -EINVAL; } - buf = transport_kmap_data_sg(cmd); + buf = transport_kmap_first_data_page(cmd); buf[0] = ((su_dev->t10_pr.pr_generation >> 24) & 0xff); buf[1] = ((su_dev->t10_pr.pr_generation >> 16) & 0xff); @@ -4293,7 +4292,7 @@ static int core_scsi3_pri_read_full_status(struct se_cmd *cmd) buf[6] = ((add_len >> 8) & 0xff); buf[7] = (add_len & 0xff); - transport_kunmap_data_sg(cmd); + transport_kunmap_first_data_page(cmd); return 0; } diff --git a/trunk/drivers/target/target_core_pscsi.c b/trunk/drivers/target/target_core_pscsi.c index 8d4def30e9e8..d35467d42e12 100644 --- a/trunk/drivers/target/target_core_pscsi.c +++ b/trunk/drivers/target/target_core_pscsi.c @@ -693,7 +693,7 @@ static int pscsi_transport_complete(struct se_task *task) if (task->task_se_cmd->se_deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY) { - unsigned char *buf = transport_kmap_data_sg(task->task_se_cmd); + unsigned char *buf = transport_kmap_first_data_page(task->task_se_cmd); if (cdb[0] == MODE_SENSE_10) { if (!(buf[3] & 0x80)) @@ -703,7 +703,7 @@ static int pscsi_transport_complete(struct se_task *task) buf[2] |= 0x80; } - transport_kunmap_data_sg(task->task_se_cmd); + transport_kunmap_first_data_page(task->task_se_cmd); } } after_mode_sense: diff --git a/trunk/drivers/target/target_core_tpg.c b/trunk/drivers/target/target_core_tpg.c index 06336ecd872d..b7668029bb31 100644 --- a/trunk/drivers/target/target_core_tpg.c +++ b/trunk/drivers/target/target_core_tpg.c @@ -807,7 +807,8 @@ static void core_tpg_shutdown_lun( struct se_lun *core_tpg_pre_dellun( struct se_portal_group *tpg, - u32 unpacked_lun) + u32 unpacked_lun, + int *ret) { struct se_lun *lun; diff --git a/trunk/drivers/target/target_core_transport.c b/trunk/drivers/target/target_core_transport.c index 58cea07b12fb..d3ddd1361949 100644 --- a/trunk/drivers/target/target_core_transport.c +++ b/trunk/drivers/target/target_core_transport.c @@ -1255,34 +1255,32 @@ static void core_setup_task_attr_emulation(struct se_device *dev) static void scsi_dump_inquiry(struct se_device *dev) { struct t10_wwn *wwn = &dev->se_sub_dev->t10_wwn; - char buf[17]; int i, device_type; /* * Print Linux/SCSI style INQUIRY formatting to the kernel ring buffer */ + pr_debug(" Vendor: "); for (i = 0; i < 8; i++) if (wwn->vendor[i] >= 0x20) - buf[i] = wwn->vendor[i]; + pr_debug("%c", wwn->vendor[i]); else - buf[i] = ' '; - buf[i] = '\0'; - pr_debug(" Vendor: %s\n", buf); + pr_debug(" "); + pr_debug(" Model: "); for (i = 0; i < 16; i++) if (wwn->model[i] >= 0x20) - buf[i] = wwn->model[i]; + pr_debug("%c", wwn->model[i]); else - buf[i] = ' '; - buf[i] = '\0'; - pr_debug(" Model: %s\n", buf); + pr_debug(" "); + pr_debug(" Revision: "); for (i = 0; i < 4; i++) if (wwn->revision[i] >= 0x20) - buf[i] = wwn->revision[i]; + pr_debug("%c", wwn->revision[i]); else - buf[i] = ' '; - buf[i] = '\0'; - pr_debug(" Revision: %s\n", buf); + pr_debug(" "); + + pr_debug("\n"); device_type = dev->transport->get_device_type(dev); pr_debug(" Type: %s ", scsi_device_type(device_type)); @@ -1657,7 +1655,7 @@ EXPORT_SYMBOL(transport_handle_cdb_direct); * This may only be called from process context, and also currently * assumes internal allocation of fabric payload buffer by target-core. **/ -void target_submit_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, +int target_submit_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, unsigned char *cdb, unsigned char *sense, u32 unpacked_lun, u32 data_length, int task_attr, int data_dir, int flags) { @@ -1690,21 +1688,15 @@ void target_submit_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, /* * Locate se_lun pointer and attach it to struct se_cmd */ - if (transport_lookup_cmd_lun(se_cmd, unpacked_lun) < 0) { - transport_send_check_condition_and_sense(se_cmd, - se_cmd->scsi_sense_reason, 0); - target_put_sess_cmd(se_sess, se_cmd); - return; - } + if (transport_lookup_cmd_lun(se_cmd, unpacked_lun) < 0) + goto out_check_cond; /* * Sanitize CDBs via transport_generic_cmd_sequencer() and * allocate the necessary tasks to complete the received CDB+data */ rc = transport_generic_allocate_tasks(se_cmd, cdb); - if (rc != 0) { - transport_generic_request_failure(se_cmd); - return; - } + if (rc != 0) + goto out_check_cond; /* * Dispatch se_cmd descriptor to se_lun->lun_se_dev backend * for immediate execution of READs, otherwise wait for @@ -1712,7 +1704,12 @@ void target_submit_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, * when fabric has filled the incoming buffer. */ transport_handle_cdb_direct(se_cmd); - return; + return 0; + +out_check_cond: + transport_send_check_condition_and_sense(se_cmd, + se_cmd->scsi_sense_reason, 0); + return 0; } EXPORT_SYMBOL(target_submit_cmd); @@ -2697,7 +2694,7 @@ static int transport_generic_cmd_sequencer( cmd->se_cmd_flags |= SCF_SCSI_CONTROL_SG_IO_CDB; if (target_check_write_same_discard(&cdb[10], dev) < 0) - goto out_unsupported_cdb; + goto out_invalid_cdb_field; if (!passthrough) cmd->execute_task = target_emulate_write_same; break; @@ -2980,7 +2977,7 @@ static int transport_generic_cmd_sequencer( cmd->se_cmd_flags |= SCF_SCSI_CONTROL_SG_IO_CDB; if (target_check_write_same_discard(&cdb[1], dev) < 0) - goto out_unsupported_cdb; + goto out_invalid_cdb_field; if (!passthrough) cmd->execute_task = target_emulate_write_same; break; @@ -3003,7 +3000,7 @@ static int transport_generic_cmd_sequencer( * of byte 1 bit 3 UNMAP instead of original reserved field */ if (target_check_write_same_discard(&cdb[1], dev) < 0) - goto out_unsupported_cdb; + goto out_invalid_cdb_field; if (!passthrough) cmd->execute_task = target_emulate_write_same; break; @@ -3085,6 +3082,11 @@ static int transport_generic_cmd_sequencer( (cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB))) goto out_unsupported_cdb; + /* Let's limit control cdbs to a page, for simplicity's sake. */ + if ((cmd->se_cmd_flags & SCF_SCSI_CONTROL_SG_IO_CDB) && + size > PAGE_SIZE) + goto out_invalid_cdb_field; + transport_set_supported_SAM_opcode(cmd); return ret; @@ -3488,11 +3490,9 @@ int transport_generic_map_mem_to_cmd( } EXPORT_SYMBOL(transport_generic_map_mem_to_cmd); -void *transport_kmap_data_sg(struct se_cmd *cmd) +void *transport_kmap_first_data_page(struct se_cmd *cmd) { struct scatterlist *sg = cmd->t_data_sg; - struct page **pages; - int i; BUG_ON(!sg); /* @@ -3500,41 +3500,15 @@ void *transport_kmap_data_sg(struct se_cmd *cmd) * tcm_loop who may be using a contig buffer from the SCSI midlayer for * control CDBs passed as SGLs via transport_generic_map_mem_to_cmd() */ - if (!cmd->t_data_nents) - return NULL; - else if (cmd->t_data_nents == 1) - return kmap(sg_page(sg)) + sg->offset; - - /* >1 page. use vmap */ - pages = kmalloc(sizeof(*pages) * cmd->t_data_nents, GFP_KERNEL); - if (!pages) - return NULL; - - /* convert sg[] to pages[] */ - for_each_sg(cmd->t_data_sg, sg, cmd->t_data_nents, i) { - pages[i] = sg_page(sg); - } - - cmd->t_data_vmap = vmap(pages, cmd->t_data_nents, VM_MAP, PAGE_KERNEL); - kfree(pages); - if (!cmd->t_data_vmap) - return NULL; - - return cmd->t_data_vmap + cmd->t_data_sg[0].offset; + return kmap(sg_page(sg)) + sg->offset; } -EXPORT_SYMBOL(transport_kmap_data_sg); +EXPORT_SYMBOL(transport_kmap_first_data_page); -void transport_kunmap_data_sg(struct se_cmd *cmd) +void transport_kunmap_first_data_page(struct se_cmd *cmd) { - if (!cmd->t_data_nents) - return; - else if (cmd->t_data_nents == 1) - kunmap(sg_page(cmd->t_data_sg)); - - vunmap(cmd->t_data_vmap); - cmd->t_data_vmap = NULL; + kunmap(sg_page(cmd->t_data_sg)); } -EXPORT_SYMBOL(transport_kunmap_data_sg); +EXPORT_SYMBOL(transport_kunmap_first_data_page); static int transport_generic_get_mem(struct se_cmd *cmd) @@ -3542,7 +3516,6 @@ transport_generic_get_mem(struct se_cmd *cmd) u32 length = cmd->data_length; unsigned int nents; struct page *page; - gfp_t zero_flag; int i = 0; nents = DIV_ROUND_UP(length, PAGE_SIZE); @@ -3553,11 +3526,9 @@ transport_generic_get_mem(struct se_cmd *cmd) cmd->t_data_nents = nents; sg_init_table(cmd->t_data_sg, nents); - zero_flag = cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB ? 0 : __GFP_ZERO; - while (length) { u32 page_len = min_t(u32, length, PAGE_SIZE); - page = alloc_page(GFP_KERNEL | zero_flag); + page = alloc_page(GFP_KERNEL | __GFP_ZERO); if (!page) goto out; @@ -3785,11 +3756,6 @@ transport_allocate_control_task(struct se_cmd *cmd) struct se_task *task; unsigned long flags; - /* Workaround for handling zero-length control CDBs */ - if ((cmd->se_cmd_flags & SCF_SCSI_CONTROL_SG_IO_CDB) && - !cmd->data_length) - return 0; - task = transport_generic_get_task(cmd, cmd->data_direction); if (!task) return -ENOMEM; @@ -3861,14 +3827,6 @@ int transport_generic_new_cmd(struct se_cmd *cmd) else if (!task_cdbs && (cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB)) { cmd->t_state = TRANSPORT_COMPLETE; atomic_set(&cmd->t_transport_active, 1); - - if (cmd->t_task_cdb[0] == REQUEST_SENSE) { - u8 ua_asc = 0, ua_ascq = 0; - - core_scsi3_ua_clear_for_request_sense(cmd, - &ua_asc, &ua_ascq); - } - INIT_WORK(&cmd->work, target_complete_ok_work); queue_work(target_completion_wq, &cmd->work); return 0; @@ -4490,8 +4448,8 @@ int transport_send_check_condition_and_sense( /* CURRENT ERROR */ buffer[offset] = 0x70; buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* ILLEGAL REQUEST */ - buffer[offset+SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; + /* ABORTED COMMAND */ + buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; /* INVALID FIELD IN CDB */ buffer[offset+SPC_ASC_KEY_OFFSET] = 0x24; break; @@ -4499,8 +4457,8 @@ int transport_send_check_condition_and_sense( /* CURRENT ERROR */ buffer[offset] = 0x70; buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* ILLEGAL REQUEST */ - buffer[offset+SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; + /* ABORTED COMMAND */ + buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; /* INVALID FIELD IN PARAMETER LIST */ buffer[offset+SPC_ASC_KEY_OFFSET] = 0x26; break; diff --git a/trunk/drivers/target/tcm_fc/tfc_cmd.c b/trunk/drivers/target/tcm_fc/tfc_cmd.c index 9e7e26c74c79..addc18f727ea 100644 --- a/trunk/drivers/target/tcm_fc/tfc_cmd.c +++ b/trunk/drivers/target/tcm_fc/tfc_cmd.c @@ -540,6 +540,7 @@ static void ft_send_work(struct work_struct *work) int data_dir = 0; u32 data_len; int task_attr; + int ret; fcp = fc_frame_payload_get(cmd->req_frame, sizeof(*fcp)); if (!fcp) @@ -602,10 +603,14 @@ static void ft_send_work(struct work_struct *work) * Use a single se_cmd->cmd_kref as we expect to release se_cmd * directly from ft_check_stop_free callback in response path. */ - target_submit_cmd(&cmd->se_cmd, cmd->sess->se_sess, cmd->cdb, + ret = target_submit_cmd(&cmd->se_cmd, cmd->sess->se_sess, cmd->cdb, &cmd->ft_sense_buffer[0], cmd->lun, data_len, task_attr, data_dir, 0); - pr_debug("r_ctl %x alloc target_submit_cmd\n", fh->fh_r_ctl); + pr_debug("r_ctl %x alloc target_submit_cmd %d\n", fh->fh_r_ctl, ret); + if (ret < 0) { + ft_dump_cmd(cmd, __func__); + return; + } return; err: diff --git a/trunk/drivers/thermal/thermal_sys.c b/trunk/drivers/thermal/thermal_sys.c index 220ce7e31cf5..dd9a5743fa99 100644 --- a/trunk/drivers/thermal/thermal_sys.c +++ b/trunk/drivers/thermal/thermal_sys.c @@ -1304,7 +1304,7 @@ static struct genl_multicast_group thermal_event_mcgrp = { .name = THERMAL_GENL_MCAST_GROUP_NAME, }; -int thermal_generate_netlink_event(u32 orig, enum events event) +int generate_netlink_event(u32 orig, enum events event) { struct sk_buff *skb; struct nlattr *attr; @@ -1363,7 +1363,7 @@ int thermal_generate_netlink_event(u32 orig, enum events event) return result; } -EXPORT_SYMBOL(thermal_generate_netlink_event); +EXPORT_SYMBOL(generate_netlink_event); static int genetlink_init(void) { diff --git a/trunk/drivers/tty/serial/8250/8250.c b/trunk/drivers/tty/serial/8250.c similarity index 99% rename from trunk/drivers/tty/serial/8250/8250.c rename to trunk/drivers/tty/serial/8250.c index 9b7336fcfbb3..9f50c4e3c2be 100644 --- a/trunk/drivers/tty/serial/8250/8250.c +++ b/trunk/drivers/tty/serial/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/8250/8250.h b/trunk/drivers/tty/serial/8250.h similarity index 100% rename from trunk/drivers/tty/serial/8250/8250.h rename to trunk/drivers/tty/serial/8250.h diff --git a/trunk/drivers/tty/serial/8250/Kconfig b/trunk/drivers/tty/serial/8250/Kconfig deleted file mode 100644 index 591f8018e7dd..000000000000 --- a/trunk/drivers/tty/serial/8250/Kconfig +++ /dev/null @@ -1,280 +0,0 @@ -# -# The 8250/16550 serial drivers. You shouldn't be in this list unless -# you somehow have an implicit or explicit dependency on SERIAL_8250. -# - -config SERIAL_8250 - tristate "8250/16550 and compatible serial support" - select SERIAL_CORE - ---help--- - This selects whether you want to include the driver for the standard - serial ports. The standard answer is Y. People who might say N - here are those that are setting up dedicated Ethernet WWW/FTP - servers, or users that have one of the various bus mice instead of a - serial mouse and don't intend to use their machine's standard serial - port for anything. (Note that the Cyclades and Stallion multi - serial port drivers do not need this driver built in for them to - work.) - - To compile this driver as a module, choose M here: the - module will be called 8250. - [WARNING: Do not compile this driver as a module if you are using - non-standard serial ports, since the configuration information will - be lost when the driver is unloaded. This limitation may be lifted - in the future.] - - BTW1: If you have a mouseman serial mouse which is not recognized by - the X window system, try running gpm first. - - BTW2: If you intend to use a software modem (also called Winmodem) - under Linux, forget it. These modems are crippled and require - proprietary drivers which are only available under Windows. - - Most people will say Y or M here, so that they can use serial mice, - modems and similar devices connecting to the standard serial ports. - -config SERIAL_8250_CONSOLE - bool "Console on 8250/16550 and compatible serial port" - depends on SERIAL_8250=y - select SERIAL_CORE_CONSOLE - ---help--- - If you say Y here, it will be possible to use a serial port as the - system console (the system console is the device which receives all - kernel messages and warnings and which allows logins in single user - mode). This could be useful if some terminal or printer is connected - to that serial port. - - Even if you say Y here, the currently visible virtual console - (/dev/tty0) will still be used as the system console by default, but - you can alter that using a kernel command line option such as - "console=ttyS1". (Try "man bootparam" or see the documentation of - your boot loader (grub or lilo or loadlin) about how to pass options - to the kernel at boot time.) - - If you don't have a VGA card installed and you say Y here, the - kernel will automatically use the first serial line, /dev/ttyS0, as - system console. - - You can set that using a kernel command line option such as - "console=uart8250,io,0x3f8,9600n8" - "console=uart8250,mmio,0xff5e0000,115200n8". - and it will switch to normal serial console when the corresponding - port is ready. - "earlycon=uart8250,io,0x3f8,9600n8" - "earlycon=uart8250,mmio,0xff5e0000,115200n8". - it will not only setup early console. - - If unsure, say N. - -config FIX_EARLYCON_MEM - bool - depends on X86 - default y - -config SERIAL_8250_GSC - tristate - depends on SERIAL_8250 && GSC - default SERIAL_8250 - -config SERIAL_8250_PCI - tristate "8250/16550 PCI device support" if EXPERT - depends on SERIAL_8250 && PCI - default SERIAL_8250 - help - This builds standard PCI serial support. You may be able to - disable this feature if you only need legacy serial support. - Saves about 9K. - -config SERIAL_8250_PNP - tristate "8250/16550 PNP device support" if EXPERT - depends on SERIAL_8250 && PNP - default SERIAL_8250 - help - This builds standard PNP serial support. You may be able to - disable this feature if you only need legacy serial support. - -config SERIAL_8250_HP300 - tristate - depends on SERIAL_8250 && HP300 - default SERIAL_8250 - -config SERIAL_8250_CS - tristate "8250/16550 PCMCIA device support" - depends on PCMCIA && SERIAL_8250 - ---help--- - Say Y here to enable support for 16-bit PCMCIA serial devices, - including serial port cards, modems, and the modem functions of - multi-function Ethernet/modem cards. (PCMCIA- or PC-cards are - credit-card size devices often used with laptops.) - - To compile this driver as a module, choose M here: the - module will be called serial_cs. - - If unsure, say N. - -config SERIAL_8250_NR_UARTS - int "Maximum number of 8250/16550 serial ports" - depends on SERIAL_8250 - default "4" - help - Set this to the number of serial ports you want the driver - to support. This includes any ports discovered via ACPI or - PCI enumeration and any ports that may be added at run-time - via hot-plug, or any ISA multi-port serial cards. - -config SERIAL_8250_RUNTIME_UARTS - int "Number of 8250/16550 serial ports to register at runtime" - depends on SERIAL_8250 - range 0 SERIAL_8250_NR_UARTS - default "4" - help - Set this to the maximum number of serial ports you want - the kernel to register at boot time. This can be overridden - with the module parameter "nr_uarts", or boot-time parameter - 8250.nr_uarts - -config SERIAL_8250_EXTENDED - bool "Extended 8250/16550 serial driver options" - depends on SERIAL_8250 - help - If you wish to use any non-standard features of the standard "dumb" - driver, say Y here. This includes HUB6 support, shared serial - interrupts, special multiport support, support for more than the - four COM 1/2/3/4 boards, etc. - - Note that the answer to this question won't directly affect the - kernel: saying N will just cause the configurator to skip all - the questions about serial driver options. If unsure, say N. - -config SERIAL_8250_MANY_PORTS - bool "Support more than 4 legacy serial ports" - depends on SERIAL_8250_EXTENDED && !IA64 - help - Say Y here if you have dumb serial boards other than the four - standard COM 1/2/3/4 ports. This may happen if you have an AST - FourPort, Accent Async, Boca (read the Boca mini-HOWTO, available - from ), or other custom - serial port hardware which acts similar to standard serial port - hardware. If you only use the standard COM 1/2/3/4 ports, you can - say N here to save some memory. You can also say Y if you have an - "intelligent" multiport card such as Cyclades, Digiboards, etc. - -# -# Multi-port serial cards -# - -config SERIAL_8250_FOURPORT - tristate "Support Fourport cards" - depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS - help - Say Y here if you have an AST FourPort serial board. - - To compile this driver as a module, choose M here: the module - will be called 8250_fourport. - -config SERIAL_8250_ACCENT - tristate "Support Accent cards" - depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS - help - Say Y here if you have an Accent Async serial board. - - To compile this driver as a module, choose M here: the module - will be called 8250_accent. - -config SERIAL_8250_BOCA - tristate "Support Boca cards" - depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS - help - Say Y here if you have a Boca serial board. Please read the Boca - mini-HOWTO, available from - - To compile this driver as a module, choose M here: the module - will be called 8250_boca. - -config SERIAL_8250_EXAR_ST16C554 - tristate "Support Exar ST16C554/554D Quad UART" - depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS - help - The Uplogix Envoy TU301 uses this Exar Quad UART. If you are - tinkering with your Envoy TU301, or have a machine with this UART, - say Y here. - - To compile this driver as a module, choose M here: the module - will be called 8250_exar_st16c554. - -config SERIAL_8250_HUB6 - tristate "Support Hub6 cards" - depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS - help - Say Y here if you have a HUB6 serial board. - - To compile this driver as a module, choose M here: the module - will be called 8250_hub6. - -# -# Misc. options/drivers. -# - -config SERIAL_8250_SHARE_IRQ - bool "Support for sharing serial interrupts" - depends on SERIAL_8250_EXTENDED - help - Some serial boards have hardware support which allows multiple dumb - serial ports on the same board to share a single IRQ. To enable - support for this in the serial driver, say Y here. - -config SERIAL_8250_DETECT_IRQ - bool "Autodetect IRQ on standard ports (unsafe)" - depends on SERIAL_8250_EXTENDED - help - Say Y here if you want the kernel to try to guess which IRQ - to use for your serial port. - - This is considered unsafe; it is far better to configure the IRQ in - a boot script using the setserial command. - - If unsure, say N. - -config SERIAL_8250_RSA - bool "Support RSA serial ports" - depends on SERIAL_8250_EXTENDED - help - ::: To be written ::: - -config SERIAL_8250_MCA - tristate "Support 8250-type ports on MCA buses" - depends on SERIAL_8250 != n && MCA - help - Say Y here if you have a MCA serial ports. - - To compile this driver as a module, choose M here: the module - will be called 8250_mca. - -config SERIAL_8250_ACORN - tristate "Acorn expansion card serial port support" - depends on ARCH_ACORN && SERIAL_8250 - help - If you have an Atomwide Serial card or Serial Port card for an Acorn - system, say Y to this option. The driver can handle 1, 2, or 3 port - cards. If unsure, say N. - -config SERIAL_8250_RM9K - bool "Support for MIPS RM9xxx integrated serial port" - depends on SERIAL_8250 != n && SERIAL_RM9000 - select SERIAL_8250_SHARE_IRQ - help - Selecting this option will add support for the integrated serial - port hardware found on MIPS RM9122 and similar processors. - If unsure, say N. - -config SERIAL_8250_FSL - bool - depends on SERIAL_8250_CONSOLE && PPC_UDBG_16550 - default PPC - -config SERIAL_8250_DW - tristate "Support for Synopsys DesignWare 8250 quirks" - depends on SERIAL_8250 && OF - help - Selecting this option will enable handling of the extra features - present in the Synopsys DesignWare APB UART. diff --git a/trunk/drivers/tty/serial/8250/Makefile b/trunk/drivers/tty/serial/8250/Makefile deleted file mode 100644 index 867bba738908..000000000000 --- a/trunk/drivers/tty/serial/8250/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# -# Makefile for the 8250 serial device drivers. -# - -obj-$(CONFIG_SERIAL_8250) += 8250.o -obj-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o -obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o -obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o -obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o -obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o -obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o -obj-$(CONFIG_SERIAL_8250_CONSOLE) += 8250_early.o -obj-$(CONFIG_SERIAL_8250_FOURPORT) += 8250_fourport.o -obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o -obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o -obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o -obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o -obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o -obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o -obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o diff --git a/trunk/drivers/tty/serial/8250/8250_accent.c b/trunk/drivers/tty/serial/8250_accent.c similarity index 100% rename from trunk/drivers/tty/serial/8250/8250_accent.c rename to trunk/drivers/tty/serial/8250_accent.c diff --git a/trunk/drivers/tty/serial/8250/8250_acorn.c b/trunk/drivers/tty/serial/8250_acorn.c similarity index 100% rename from trunk/drivers/tty/serial/8250/8250_acorn.c rename to trunk/drivers/tty/serial/8250_acorn.c diff --git a/trunk/drivers/tty/serial/8250/8250_boca.c b/trunk/drivers/tty/serial/8250_boca.c similarity index 100% rename from trunk/drivers/tty/serial/8250/8250_boca.c rename to trunk/drivers/tty/serial/8250_boca.c diff --git a/trunk/drivers/tty/serial/8250/8250_dw.c b/trunk/drivers/tty/serial/8250_dw.c similarity index 100% rename from trunk/drivers/tty/serial/8250/8250_dw.c rename to trunk/drivers/tty/serial/8250_dw.c diff --git a/trunk/drivers/tty/serial/8250/8250_early.c b/trunk/drivers/tty/serial/8250_early.c similarity index 100% rename from trunk/drivers/tty/serial/8250/8250_early.c rename to trunk/drivers/tty/serial/8250_early.c diff --git a/trunk/drivers/tty/serial/8250/8250_exar_st16c554.c b/trunk/drivers/tty/serial/8250_exar_st16c554.c similarity index 100% rename from trunk/drivers/tty/serial/8250/8250_exar_st16c554.c rename to trunk/drivers/tty/serial/8250_exar_st16c554.c diff --git a/trunk/drivers/tty/serial/8250/8250_fourport.c b/trunk/drivers/tty/serial/8250_fourport.c similarity index 100% rename from trunk/drivers/tty/serial/8250/8250_fourport.c rename to trunk/drivers/tty/serial/8250_fourport.c diff --git a/trunk/drivers/tty/serial/8250/8250_fsl.c b/trunk/drivers/tty/serial/8250_fsl.c similarity index 100% rename from trunk/drivers/tty/serial/8250/8250_fsl.c rename to trunk/drivers/tty/serial/8250_fsl.c diff --git a/trunk/drivers/tty/serial/8250/8250_gsc.c b/trunk/drivers/tty/serial/8250_gsc.c similarity index 100% rename from trunk/drivers/tty/serial/8250/8250_gsc.c rename to trunk/drivers/tty/serial/8250_gsc.c diff --git a/trunk/drivers/tty/serial/8250/8250_hp300.c b/trunk/drivers/tty/serial/8250_hp300.c similarity index 100% rename from trunk/drivers/tty/serial/8250/8250_hp300.c rename to trunk/drivers/tty/serial/8250_hp300.c diff --git a/trunk/drivers/tty/serial/8250/8250_hub6.c b/trunk/drivers/tty/serial/8250_hub6.c similarity index 100% rename from trunk/drivers/tty/serial/8250/8250_hub6.c rename to trunk/drivers/tty/serial/8250_hub6.c diff --git a/trunk/drivers/tty/serial/8250/8250_mca.c b/trunk/drivers/tty/serial/8250_mca.c similarity index 100% rename from trunk/drivers/tty/serial/8250/8250_mca.c rename to trunk/drivers/tty/serial/8250_mca.c diff --git a/trunk/drivers/tty/serial/8250/8250_pci.c b/trunk/drivers/tty/serial/8250_pci.c similarity index 100% rename from trunk/drivers/tty/serial/8250/8250_pci.c rename to trunk/drivers/tty/serial/8250_pci.c diff --git a/trunk/drivers/tty/serial/8250/8250_pnp.c b/trunk/drivers/tty/serial/8250_pnp.c similarity index 100% rename from trunk/drivers/tty/serial/8250/8250_pnp.c rename to trunk/drivers/tty/serial/8250_pnp.c diff --git a/trunk/drivers/tty/serial/Kconfig b/trunk/drivers/tty/serial/Kconfig index 2de99248dfae..aca2386c5ef1 100644 --- a/trunk/drivers/tty/serial/Kconfig +++ b/trunk/drivers/tty/serial/Kconfig @@ -5,7 +5,279 @@ menu "Serial drivers" depends on HAS_IOMEM -source "drivers/tty/serial/8250/Kconfig" +# +# The new 8250/16550 serial drivers +config SERIAL_8250 + tristate "8250/16550 and compatible serial support" + select SERIAL_CORE + ---help--- + This selects whether you want to include the driver for the standard + serial ports. The standard answer is Y. People who might say N + here are those that are setting up dedicated Ethernet WWW/FTP + servers, or users that have one of the various bus mice instead of a + serial mouse and don't intend to use their machine's standard serial + port for anything. (Note that the Cyclades and Stallion multi + serial port drivers do not need this driver built in for them to + work.) + + To compile this driver as a module, choose M here: the + module will be called 8250. + [WARNING: Do not compile this driver as a module if you are using + non-standard serial ports, since the configuration information will + be lost when the driver is unloaded. This limitation may be lifted + in the future.] + + BTW1: If you have a mouseman serial mouse which is not recognized by + the X window system, try running gpm first. + + BTW2: If you intend to use a software modem (also called Winmodem) + under Linux, forget it. These modems are crippled and require + proprietary drivers which are only available under Windows. + + Most people will say Y or M here, so that they can use serial mice, + modems and similar devices connecting to the standard serial ports. + +config SERIAL_8250_CONSOLE + bool "Console on 8250/16550 and compatible serial port" + depends on SERIAL_8250=y + select SERIAL_CORE_CONSOLE + ---help--- + If you say Y here, it will be possible to use a serial port as the + system console (the system console is the device which receives all + kernel messages and warnings and which allows logins in single user + mode). This could be useful if some terminal or printer is connected + to that serial port. + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyS1". (Try "man bootparam" or see the documentation of + your boot loader (grub or lilo or loadlin) about how to pass options + to the kernel at boot time.) + + If you don't have a VGA card installed and you say Y here, the + kernel will automatically use the first serial line, /dev/ttyS0, as + system console. + + You can set that using a kernel command line option such as + "console=uart8250,io,0x3f8,9600n8" + "console=uart8250,mmio,0xff5e0000,115200n8". + and it will switch to normal serial console when the corresponding + port is ready. + "earlycon=uart8250,io,0x3f8,9600n8" + "earlycon=uart8250,mmio,0xff5e0000,115200n8". + it will not only setup early console. + + If unsure, say N. + +config FIX_EARLYCON_MEM + bool + depends on X86 + default y + +config SERIAL_8250_GSC + tristate + depends on SERIAL_8250 && GSC + default SERIAL_8250 + +config SERIAL_8250_PCI + tristate "8250/16550 PCI device support" if EXPERT + depends on SERIAL_8250 && PCI + default SERIAL_8250 + help + This builds standard PCI serial support. You may be able to + disable this feature if you only need legacy serial support. + Saves about 9K. + +config SERIAL_8250_PNP + tristate "8250/16550 PNP device support" if EXPERT + depends on SERIAL_8250 && PNP + default SERIAL_8250 + help + This builds standard PNP serial support. You may be able to + disable this feature if you only need legacy serial support. + +config SERIAL_8250_FSL + bool + depends on SERIAL_8250_CONSOLE && PPC_UDBG_16550 + default PPC + +config SERIAL_8250_HP300 + tristate + depends on SERIAL_8250 && HP300 + default SERIAL_8250 + +config SERIAL_8250_CS + tristate "8250/16550 PCMCIA device support" + depends on PCMCIA && SERIAL_8250 + ---help--- + Say Y here to enable support for 16-bit PCMCIA serial devices, + including serial port cards, modems, and the modem functions of + multi-function Ethernet/modem cards. (PCMCIA- or PC-cards are + credit-card size devices often used with laptops.) + + To compile this driver as a module, choose M here: the + module will be called serial_cs. + + If unsure, say N. + +config SERIAL_8250_NR_UARTS + int "Maximum number of 8250/16550 serial ports" + depends on SERIAL_8250 + default "4" + help + Set this to the number of serial ports you want the driver + to support. This includes any ports discovered via ACPI or + PCI enumeration and any ports that may be added at run-time + via hot-plug, or any ISA multi-port serial cards. + +config SERIAL_8250_RUNTIME_UARTS + int "Number of 8250/16550 serial ports to register at runtime" + depends on SERIAL_8250 + range 0 SERIAL_8250_NR_UARTS + default "4" + help + Set this to the maximum number of serial ports you want + the kernel to register at boot time. This can be overridden + with the module parameter "nr_uarts", or boot-time parameter + 8250.nr_uarts + +config SERIAL_8250_EXTENDED + bool "Extended 8250/16550 serial driver options" + depends on SERIAL_8250 + help + If you wish to use any non-standard features of the standard "dumb" + driver, say Y here. This includes HUB6 support, shared serial + interrupts, special multiport support, support for more than the + four COM 1/2/3/4 boards, etc. + + Note that the answer to this question won't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about serial driver options. If unsure, say N. + +config SERIAL_8250_MANY_PORTS + bool "Support more than 4 legacy serial ports" + depends on SERIAL_8250_EXTENDED && !IA64 + help + Say Y here if you have dumb serial boards other than the four + standard COM 1/2/3/4 ports. This may happen if you have an AST + FourPort, Accent Async, Boca (read the Boca mini-HOWTO, available + from ), or other custom + serial port hardware which acts similar to standard serial port + hardware. If you only use the standard COM 1/2/3/4 ports, you can + say N here to save some memory. You can also say Y if you have an + "intelligent" multiport card such as Cyclades, Digiboards, etc. + +# +# Multi-port serial cards +# + +config SERIAL_8250_FOURPORT + tristate "Support Fourport cards" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + Say Y here if you have an AST FourPort serial board. + + To compile this driver as a module, choose M here: the module + will be called 8250_fourport. + +config SERIAL_8250_ACCENT + tristate "Support Accent cards" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + Say Y here if you have an Accent Async serial board. + + To compile this driver as a module, choose M here: the module + will be called 8250_accent. + +config SERIAL_8250_BOCA + tristate "Support Boca cards" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + Say Y here if you have a Boca serial board. Please read the Boca + mini-HOWTO, available from + + To compile this driver as a module, choose M here: the module + will be called 8250_boca. + +config SERIAL_8250_EXAR_ST16C554 + tristate "Support Exar ST16C554/554D Quad UART" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + The Uplogix Envoy TU301 uses this Exar Quad UART. If you are + tinkering with your Envoy TU301, or have a machine with this UART, + say Y here. + + To compile this driver as a module, choose M here: the module + will be called 8250_exar_st16c554. + +config SERIAL_8250_HUB6 + tristate "Support Hub6 cards" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + Say Y here if you have a HUB6 serial board. + + To compile this driver as a module, choose M here: the module + will be called 8250_hub6. + +config SERIAL_8250_SHARE_IRQ + bool "Support for sharing serial interrupts" + depends on SERIAL_8250_EXTENDED + help + Some serial boards have hardware support which allows multiple dumb + serial ports on the same board to share a single IRQ. To enable + support for this in the serial driver, say Y here. + +config SERIAL_8250_DETECT_IRQ + bool "Autodetect IRQ on standard ports (unsafe)" + depends on SERIAL_8250_EXTENDED + help + Say Y here if you want the kernel to try to guess which IRQ + to use for your serial port. + + This is considered unsafe; it is far better to configure the IRQ in + a boot script using the setserial command. + + If unsure, say N. + +config SERIAL_8250_RSA + bool "Support RSA serial ports" + depends on SERIAL_8250_EXTENDED + help + ::: To be written ::: + +config SERIAL_8250_MCA + tristate "Support 8250-type ports on MCA buses" + depends on SERIAL_8250 != n && MCA + help + Say Y here if you have a MCA serial ports. + + To compile this driver as a module, choose M here: the module + will be called 8250_mca. + +config SERIAL_8250_ACORN + tristate "Acorn expansion card serial port support" + depends on ARCH_ACORN && SERIAL_8250 + help + If you have an Atomwide Serial card or Serial Port card for an Acorn + system, say Y to this option. The driver can handle 1, 2, or 3 port + cards. If unsure, say N. + +config SERIAL_8250_RM9K + bool "Support for MIPS RM9xxx integrated serial port" + depends on SERIAL_8250 != n && SERIAL_RM9000 + select SERIAL_8250_SHARE_IRQ + help + Selecting this option will add support for the integrated serial + port hardware found on MIPS RM9122 and similar processors. + If unsure, say N. + +config SERIAL_8250_DW + tristate "Support for Synopsys DesignWare 8250 quirks" + depends on SERIAL_8250 && OF + help + Selecting this option will enable handling of the extra features + present in the Synopsys DesignWare APB UART. comment "Non-8250 serial port support" @@ -264,6 +536,15 @@ config SERIAL_MAX3107 help MAX3107 chip support +config SERIAL_MAX3107_AAVA + tristate "MAX3107 AAVA platform support" + depends on X86_MRST && SERIAL_MAX3107 && GPIOLIB + select SERIAL_CORE + help + Support for the MAX3107 chip configuration found on the AAVA + platform. Includes the extra initialisation and GPIO support + neded for this device. + config SERIAL_DZ bool "DECstation DZ serial driver" depends on MACH_DECSTATION && 32BIT diff --git a/trunk/drivers/tty/serial/Makefile b/trunk/drivers/tty/serial/Makefile index fef32e10c851..f5b01f2ce525 100644 --- a/trunk/drivers/tty/serial/Makefile +++ b/trunk/drivers/tty/serial/Makefile @@ -14,9 +14,22 @@ obj-$(CONFIG_SERIAL_SUNZILOG) += sunzilog.o obj-$(CONFIG_SERIAL_SUNSU) += sunsu.o obj-$(CONFIG_SERIAL_SUNSAB) += sunsab.o -# Now bring in any enabled 8250/16450/16550 type drivers. -obj-$(CONFIG_SERIAL_8250) += 8250/ - +obj-$(CONFIG_SERIAL_8250) += 8250.o +obj-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o +obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o +obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o +obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o +obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o +obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o +obj-$(CONFIG_SERIAL_8250_CONSOLE) += 8250_early.o +obj-$(CONFIG_SERIAL_8250_FOURPORT) += 8250_fourport.o +obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o +obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o +obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o +obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o +obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o +obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o +obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o @@ -29,6 +42,7 @@ obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o obj-$(CONFIG_SERIAL_MAX3100) += max3100.o obj-$(CONFIG_SERIAL_MAX3107) += max3107.o +obj-$(CONFIG_SERIAL_MAX3107_AAVA) += max3107-aava.o obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o obj-$(CONFIG_SERIAL_MUX) += mux.o obj-$(CONFIG_SERIAL_68328) += 68328serial.o diff --git a/trunk/drivers/tty/serial/amba-pl011.c b/trunk/drivers/tty/serial/amba-pl011.c index 6800f5f26241..9ae024025ff3 100644 --- a/trunk/drivers/tty/serial/amba-pl011.c +++ b/trunk/drivers/tty/serial/amba-pl011.c @@ -159,7 +159,6 @@ struct uart_amba_port { unsigned int fifosize; /* vendor-specific */ unsigned int lcrh_tx; /* vendor-specific */ unsigned int lcrh_rx; /* vendor-specific */ - unsigned int old_cr; /* state during shutdown */ bool autorts; char type[12]; bool interrupt_may_hang; /* vendor-specific */ @@ -1412,9 +1411,7 @@ static int pl011_startup(struct uart_port *port) while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY) barrier(); - /* restore RTS and DTR */ - cr = uap->old_cr & (UART011_CR_RTS | UART011_CR_DTR); - cr |= UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE; + cr = UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE; writew(cr, uap->port.membase + UART011_CR); /* Clear pending error interrupts */ @@ -1472,7 +1469,6 @@ static void pl011_shutdown_channel(struct uart_amba_port *uap, static void pl011_shutdown(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned int cr; /* * disable all interrupts @@ -1492,16 +1488,9 @@ static void pl011_shutdown(struct uart_port *port) /* * disable the port - * disable the port. It should not disable RTS and DTR. - * Also RTS and DTR state should be preserved to restore - * it during startup(). */ uap->autorts = false; - cr = readw(uap->port.membase + UART011_CR); - uap->old_cr = cr; - cr &= UART011_CR_RTS | UART011_CR_DTR; - cr |= UART01x_CR_UARTEN | UART011_CR_TXE; - writew(cr, uap->port.membase + UART011_CR); + writew(UART01x_CR_UARTEN | UART011_CR_TXE, uap->port.membase + UART011_CR); /* * disable break condition and fifos @@ -1751,19 +1740,9 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) { struct uart_amba_port *uap = amba_ports[co->index]; unsigned int status, old_cr, new_cr; - unsigned long flags; - int locked = 1; clk_enable(uap->clk); - local_irq_save(flags); - if (uap->port.sysrq) - locked = 0; - else if (oops_in_progress) - locked = spin_trylock(&uap->port.lock); - else - spin_lock(&uap->port.lock); - /* * First save the CR then disable the interrupts */ @@ -1783,10 +1762,6 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) } while (status & UART01x_FR_BUSY); writew(old_cr, uap->port.membase + UART011_CR); - if (locked) - spin_unlock(&uap->port.lock); - local_irq_restore(flags); - clk_disable(uap->clk); } @@ -1930,7 +1905,6 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) uap->vendor = vendor; uap->lcrh_rx = vendor->lcrh_rx; uap->lcrh_tx = vendor->lcrh_tx; - uap->old_cr = 0; uap->fifosize = vendor->fifosize; uap->interrupt_may_hang = vendor->interrupt_may_hang; uap->port.dev = &dev->dev; diff --git a/trunk/drivers/tty/serial/jsm/jsm_driver.c b/trunk/drivers/tty/serial/jsm/jsm_driver.c index 7545fe1b9925..7c867a046c97 100644 --- a/trunk/drivers/tty/serial/jsm/jsm_driver.c +++ b/trunk/drivers/tty/serial/jsm/jsm_driver.c @@ -251,7 +251,6 @@ static void jsm_io_resume(struct pci_dev *pdev) struct jsm_board *brd = pci_get_drvdata(pdev); pci_restore_state(pdev); - pci_save_state(pdev); jsm_uart_port_init(brd); } diff --git a/trunk/drivers/tty/serial/max3107-aava.c b/trunk/drivers/tty/serial/max3107-aava.c new file mode 100644 index 000000000000..aae772a71de6 --- /dev/null +++ b/trunk/drivers/tty/serial/max3107-aava.c @@ -0,0 +1,344 @@ +/* + * max3107.c - spi uart protocol driver for Maxim 3107 + * Based on max3100.c + * by Christian Pellegrin + * and max3110.c + * by Feng Tang + * + * Copyright (C) Aavamobile 2009 + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "max3107.h" + +/* GPIO direction to input function */ +static int max3107_gpio_direction_in(struct gpio_chip *chip, unsigned offset) +{ + struct max3107_port *s = container_of(chip, struct max3107_port, chip); + u16 buf[1]; /* Buffer for SPI transfer */ + + if (offset >= MAX3107_GPIO_COUNT) { + dev_err(&s->spi->dev, "Invalid GPIO\n"); + return -EINVAL; + } + + /* Read current GPIO configuration register */ + buf[0] = MAX3107_GPIOCFG_REG; + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { + dev_err(&s->spi->dev, "SPI transfer GPIO read failed\n"); + return -EIO; + } + buf[0] &= MAX3107_SPI_RX_DATA_MASK; + + /* Set GPIO to input */ + buf[0] &= ~(0x0001 << offset); + + /* Write new GPIO configuration register value */ + buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIOCFG_REG); + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, NULL, 2)) { + dev_err(&s->spi->dev, "SPI transfer GPIO write failed\n"); + return -EIO; + } + return 0; +} + +/* GPIO direction to output function */ +static int max3107_gpio_direction_out(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct max3107_port *s = container_of(chip, struct max3107_port, chip); + u16 buf[2]; /* Buffer for SPI transfers */ + + if (offset >= MAX3107_GPIO_COUNT) { + dev_err(&s->spi->dev, "Invalid GPIO\n"); + return -EINVAL; + } + + /* Read current GPIO configuration and data registers */ + buf[0] = MAX3107_GPIOCFG_REG; + buf[1] = MAX3107_GPIODATA_REG; + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 4)) { + dev_err(&s->spi->dev, "SPI transfer gpio failed\n"); + return -EIO; + } + buf[0] &= MAX3107_SPI_RX_DATA_MASK; + buf[1] &= MAX3107_SPI_RX_DATA_MASK; + + /* Set GPIO to output */ + buf[0] |= (0x0001 << offset); + /* Set value */ + if (value) + buf[1] |= (0x0001 << offset); + else + buf[1] &= ~(0x0001 << offset); + + /* Write new GPIO configuration and data register values */ + buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIOCFG_REG); + buf[1] |= (MAX3107_WRITE_BIT | MAX3107_GPIODATA_REG); + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, NULL, 4)) { + dev_err(&s->spi->dev, + "SPI transfer for GPIO conf data w failed\n"); + return -EIO; + } + return 0; +} + +/* GPIO value query function */ +static int max3107_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct max3107_port *s = container_of(chip, struct max3107_port, chip); + u16 buf[1]; /* Buffer for SPI transfer */ + + if (offset >= MAX3107_GPIO_COUNT) { + dev_err(&s->spi->dev, "Invalid GPIO\n"); + return -EINVAL; + } + + /* Read current GPIO data register */ + buf[0] = MAX3107_GPIODATA_REG; + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { + dev_err(&s->spi->dev, "SPI transfer GPIO data r failed\n"); + return -EIO; + } + buf[0] &= MAX3107_SPI_RX_DATA_MASK; + + /* Return value */ + return buf[0] & (0x0001 << offset); +} + +/* GPIO value set function */ +static void max3107_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct max3107_port *s = container_of(chip, struct max3107_port, chip); + u16 buf[2]; /* Buffer for SPI transfers */ + + if (offset >= MAX3107_GPIO_COUNT) { + dev_err(&s->spi->dev, "Invalid GPIO\n"); + return; + } + + /* Read current GPIO configuration registers*/ + buf[0] = MAX3107_GPIODATA_REG; + buf[1] = MAX3107_GPIOCFG_REG; + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 4)) { + dev_err(&s->spi->dev, + "SPI transfer for GPIO data and config read failed\n"); + return; + } + buf[0] &= MAX3107_SPI_RX_DATA_MASK; + buf[1] &= MAX3107_SPI_RX_DATA_MASK; + + if (!(buf[1] & (0x0001 << offset))) { + /* Configured as input, can't set value */ + dev_warn(&s->spi->dev, + "Trying to set value for input GPIO\n"); + return; + } + + /* Set value */ + if (value) + buf[0] |= (0x0001 << offset); + else + buf[0] &= ~(0x0001 << offset); + + /* Write new GPIO data register value */ + buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIODATA_REG); + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, NULL, 2)) + dev_err(&s->spi->dev, "SPI transfer GPIO data w failed\n"); +} + +/* GPIO chip data */ +static struct gpio_chip max3107_gpio_chip = { + .owner = THIS_MODULE, + .direction_input = max3107_gpio_direction_in, + .direction_output = max3107_gpio_direction_out, + .get = max3107_gpio_get, + .set = max3107_gpio_set, + .can_sleep = 1, + .base = MAX3107_GPIO_BASE, + .ngpio = MAX3107_GPIO_COUNT, +}; + +/** + * max3107_aava_reset - reset on AAVA systems + * @spi: The SPI device we are probing + * + * Reset the device ready for probing. + */ + +static int max3107_aava_reset(struct spi_device *spi) +{ + /* Reset the chip */ + if (gpio_request(MAX3107_RESET_GPIO, "max3107")) { + pr_err("Requesting RESET GPIO failed\n"); + return -EIO; + } + if (gpio_direction_output(MAX3107_RESET_GPIO, 0)) { + pr_err("Setting RESET GPIO to 0 failed\n"); + gpio_free(MAX3107_RESET_GPIO); + return -EIO; + } + msleep(MAX3107_RESET_DELAY); + if (gpio_direction_output(MAX3107_RESET_GPIO, 1)) { + pr_err("Setting RESET GPIO to 1 failed\n"); + gpio_free(MAX3107_RESET_GPIO); + return -EIO; + } + gpio_free(MAX3107_RESET_GPIO); + msleep(MAX3107_WAKEUP_DELAY); + return 0; +} + +static int max3107_aava_configure(struct max3107_port *s) +{ + int retval; + + /* Initialize GPIO chip data */ + s->chip = max3107_gpio_chip; + s->chip.label = s->spi->modalias; + s->chip.dev = &s->spi->dev; + + /* Add GPIO chip */ + retval = gpiochip_add(&s->chip); + if (retval) { + dev_err(&s->spi->dev, "Adding GPIO chip failed\n"); + return retval; + } + + /* Temporary fix for EV2 boot problems, set modem reset to 0 */ + max3107_gpio_direction_out(&s->chip, 3, 0); + return 0; +} + +#if 0 +/* This will get enabled once we have the board stuff merged for this + specific case */ + +static const struct baud_table brg13_ext[] = { + { 300, MAX3107_BRG13_B300 }, + { 600, MAX3107_BRG13_B600 }, + { 1200, MAX3107_BRG13_B1200 }, + { 2400, MAX3107_BRG13_B2400 }, + { 4800, MAX3107_BRG13_B4800 }, + { 9600, MAX3107_BRG13_B9600 }, + { 19200, MAX3107_BRG13_B19200 }, + { 57600, MAX3107_BRG13_B57600 }, + { 115200, MAX3107_BRG13_B115200 }, + { 230400, MAX3107_BRG13_B230400 }, + { 460800, MAX3107_BRG13_B460800 }, + { 921600, MAX3107_BRG13_B921600 }, + { 0, 0 } +}; + +static void max3107_aava_init(struct max3107_port *s) +{ + /*override for AAVA SC specific*/ + if (mrst_platform_id() == MRST_PLATFORM_AAVA_SC) { + if (get_koski_build_id() <= KOSKI_EV2) + if (s->ext_clk) { + s->brg_cfg = MAX3107_BRG13_B9600; + s->baud_tbl = (struct baud_table *)brg13_ext; + } + } +} +#endif + +static int __devexit max3107_aava_remove(struct spi_device *spi) +{ + struct max3107_port *s = dev_get_drvdata(&spi->dev); + + /* Remove GPIO chip */ + if (gpiochip_remove(&s->chip)) + dev_warn(&spi->dev, "Removing GPIO chip failed\n"); + + /* Then do the default remove */ + return max3107_remove(spi); +} + +/* Platform data */ +static struct max3107_plat aava_plat_data = { + .loopback = 0, + .ext_clk = 1, +/* .init = max3107_aava_init, */ + .configure = max3107_aava_configure, + .hw_suspend = max3107_hw_susp, + .polled_mode = 0, + .poll_time = 0, +}; + + +static int __devinit max3107_probe_aava(struct spi_device *spi) +{ + int err = max3107_aava_reset(spi); + if (err < 0) + return err; + return max3107_probe(spi, &aava_plat_data); +} + +/* Spi driver data */ +static struct spi_driver max3107_driver = { + .driver = { + .name = "aava-max3107", + .owner = THIS_MODULE, + }, + .probe = max3107_probe_aava, + .remove = __devexit_p(max3107_aava_remove), + .suspend = max3107_suspend, + .resume = max3107_resume, +}; + +/* Driver init function */ +static int __init max3107_init(void) +{ + return spi_register_driver(&max3107_driver); +} + +/* Driver exit function */ +static void __exit max3107_exit(void) +{ + spi_unregister_driver(&max3107_driver); +} + +module_init(max3107_init); +module_exit(max3107_exit); + +MODULE_DESCRIPTION("MAX3107 driver"); +MODULE_AUTHOR("Aavamobile"); +MODULE_ALIAS("spi:aava-max3107"); +MODULE_LICENSE("GPL v2"); diff --git a/trunk/drivers/tty/serial/omap-serial.c b/trunk/drivers/tty/serial/omap-serial.c index f80904145fd4..d192dcbb82f5 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); @@ -1180,7 +1160,7 @@ static struct uart_driver serial_omap_reg = { .cons = OMAP_CONSOLE, }; -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_SUSPEND static int serial_omap_suspend(struct device *dev) { struct uart_omap_port *up = dev_get_drvdata(dev); @@ -1541,7 +1521,6 @@ static void serial_omap_mdr1_errataset(struct uart_omap_port *up, u8 mdr1) } } -#ifdef CONFIG_PM_RUNTIME static void serial_omap_restore_context(struct uart_omap_port *up) { if (up->errata & UART_ERRATA_i202_MDR1_ACCESS) @@ -1571,6 +1550,7 @@ static void serial_omap_restore_context(struct uart_omap_port *up) serial_out(up, UART_OMAP_MDR1, up->mdr1); } +#ifdef CONFIG_PM_RUNTIME static int serial_omap_runtime_suspend(struct device *dev) { struct uart_omap_port *up = dev_get_drvdata(dev); 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/tty/serial/serial_core.c b/trunk/drivers/tty/serial/serial_core.c index 13056180adf5..c7bf31a6a7e7 100644 --- a/trunk/drivers/tty/serial/serial_core.c +++ b/trunk/drivers/tty/serial/serial_core.c @@ -2348,11 +2348,11 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) */ tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev); if (likely(!IS_ERR(tty_dev))) { - device_set_wakeup_capable(tty_dev, 1); - } else { + device_init_wakeup(tty_dev, 1); + device_set_wakeup_enable(tty_dev, 0); + } else printk(KERN_ERR "Cannot register tty device on line %d\n", uport->line); - } /* * Ensure UPF_DEAD is not set. diff --git a/trunk/drivers/tty/serial/8250/serial_cs.c b/trunk/drivers/tty/serial/serial_cs.c similarity index 100% rename from trunk/drivers/tty/serial/8250/serial_cs.c rename to trunk/drivers/tty/serial/serial_cs.c diff --git a/trunk/drivers/tty/tty_port.c b/trunk/drivers/tty/tty_port.c index bf6e238146ae..ef9dd628ba0b 100644 --- a/trunk/drivers/tty/tty_port.c +++ b/trunk/drivers/tty/tty_port.c @@ -227,6 +227,7 @@ int tty_port_block_til_ready(struct tty_port *port, int do_clocal = 0, retval; unsigned long flags; DEFINE_WAIT(wait); + int cd; /* block if port is in the process of being closed */ if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { @@ -283,14 +284,11 @@ int tty_port_block_til_ready(struct tty_port *port, retval = -ERESTARTSYS; break; } - /* - * Probe the carrier. For devices with no carrier detect - * tty_port_carrier_raised will always return true. - * Never ask drivers if CLOCAL is set, this causes troubles - * on some hardware. - */ + /* Probe the carrier. For devices with no carrier detect this + will always return true */ + cd = tty_port_carrier_raised(port); if (!(port->flags & ASYNC_CLOSING) && - (do_clocal || tty_port_carrier_raised(port))) + (do_clocal || cd)) break; if (signal_pending(current)) { retval = -ERESTARTSYS; diff --git a/trunk/drivers/tty/vt/vt_ioctl.c b/trunk/drivers/tty/vt/vt_ioctl.c index 65447c5f91d7..5e096f43bcea 100644 --- a/trunk/drivers/tty/vt/vt_ioctl.c +++ b/trunk/drivers/tty/vt/vt_ioctl.c @@ -1463,6 +1463,7 @@ compat_kdfontop_ioctl(struct compat_console_font_op __user *fontop, if (!perm && op->op != KD_FONT_OP_GET) return -EPERM; op->data = compat_ptr(((struct compat_console_font_op *)op)->data); + op->flags |= KD_FONT_FLAG_OLD; i = con_font_op(vc, op); if (i) return i; diff --git a/trunk/drivers/usb/class/cdc-wdm.c b/trunk/drivers/usb/class/cdc-wdm.c index d2b3cffca3f7..1c50baff7725 100644 --- a/trunk/drivers/usb/class/cdc-wdm.c +++ b/trunk/drivers/usb/class/cdc-wdm.c @@ -57,8 +57,6 @@ MODULE_DEVICE_TABLE (usb, wdm_ids); #define WDM_MAX 16 -/* CDC-WMC r1.1 requires wMaxCommand to be "at least 256 decimal (0x100)" */ -#define WDM_DEFAULT_BUFSIZE 256 static DEFINE_MUTEX(wdm_mutex); @@ -90,8 +88,7 @@ struct wdm_device { int count; dma_addr_t shandle; dma_addr_t ihandle; - struct mutex wlock; - struct mutex rlock; + struct mutex lock; wait_queue_head_t wait; struct work_struct rxwork; int werr; @@ -326,7 +323,7 @@ static ssize_t wdm_write } /* concurrent writes and disconnect */ - r = mutex_lock_interruptible(&desc->wlock); + r = mutex_lock_interruptible(&desc->lock); rv = -ERESTARTSYS; if (r) { kfree(buf); @@ -389,7 +386,7 @@ static ssize_t wdm_write out: usb_autopm_put_interface(desc->intf); outnp: - mutex_unlock(&desc->wlock); + mutex_unlock(&desc->lock); outnl: return rv < 0 ? rv : count; } @@ -402,7 +399,7 @@ static ssize_t wdm_read struct wdm_device *desc = file->private_data; - rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */ + rv = mutex_lock_interruptible(&desc->lock); /*concurrent reads */ if (rv < 0) return -ERESTARTSYS; @@ -470,16 +467,14 @@ static ssize_t wdm_read for (i = 0; i < desc->length - cntr; i++) desc->ubuf[i] = desc->ubuf[i + cntr]; - spin_lock_irq(&desc->iuspin); desc->length -= cntr; - spin_unlock_irq(&desc->iuspin); /* in case we had outstanding data */ if (!desc->length) clear_bit(WDM_READ, &desc->flags); rv = cntr; err: - mutex_unlock(&desc->rlock); + mutex_unlock(&desc->lock); return rv; } @@ -545,8 +540,7 @@ static int wdm_open(struct inode *inode, struct file *file) } intf->needs_remote_wakeup = 1; - /* using write lock to protect desc->count */ - mutex_lock(&desc->wlock); + mutex_lock(&desc->lock); if (!desc->count++) { desc->werr = 0; desc->rerr = 0; @@ -559,7 +553,7 @@ static int wdm_open(struct inode *inode, struct file *file) } else { rv = 0; } - mutex_unlock(&desc->wlock); + mutex_unlock(&desc->lock); usb_autopm_put_interface(desc->intf); out: mutex_unlock(&wdm_mutex); @@ -571,11 +565,9 @@ static int wdm_release(struct inode *inode, struct file *file) struct wdm_device *desc = file->private_data; mutex_lock(&wdm_mutex); - - /* using write lock to protect desc->count */ - mutex_lock(&desc->wlock); + mutex_lock(&desc->lock); desc->count--; - mutex_unlock(&desc->wlock); + mutex_unlock(&desc->lock); if (!desc->count) { dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); @@ -638,7 +630,7 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) struct usb_cdc_dmm_desc *dmhd; u8 *buffer = intf->altsetting->extra; int buflen = intf->altsetting->extralen; - u16 maxcom = WDM_DEFAULT_BUFSIZE; + u16 maxcom = 0; if (!buffer) goto out; @@ -673,8 +665,7 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL); if (!desc) goto out; - mutex_init(&desc->rlock); - mutex_init(&desc->wlock); + mutex_init(&desc->lock); spin_lock_init(&desc->iuspin); init_waitqueue_head(&desc->wait); desc->wMaxCommand = maxcom; @@ -725,7 +716,7 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) goto err; desc->inbuf = usb_alloc_coherent(interface_to_usbdev(intf), - desc->wMaxCommand, + desc->bMaxPacketSize0, GFP_KERNEL, &desc->response->transfer_dma); if (!desc->inbuf) @@ -788,13 +779,11 @@ static void wdm_disconnect(struct usb_interface *intf) /* to terminate pending flushes */ clear_bit(WDM_IN_USE, &desc->flags); spin_unlock_irqrestore(&desc->iuspin, flags); - wake_up_all(&desc->wait); - mutex_lock(&desc->rlock); - mutex_lock(&desc->wlock); + mutex_lock(&desc->lock); kill_urbs(desc); cancel_work_sync(&desc->rxwork); - mutex_unlock(&desc->wlock); - mutex_unlock(&desc->rlock); + mutex_unlock(&desc->lock); + wake_up_all(&desc->wait); if (!desc->count) cleanup(desc); mutex_unlock(&wdm_mutex); @@ -809,10 +798,8 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message) dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor); /* if this is an autosuspend the caller does the locking */ - if (!PMSG_IS_AUTO(message)) { - mutex_lock(&desc->rlock); - mutex_lock(&desc->wlock); - } + if (!PMSG_IS_AUTO(message)) + mutex_lock(&desc->lock); spin_lock_irq(&desc->iuspin); if (PMSG_IS_AUTO(message) && @@ -828,10 +815,8 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message) kill_urbs(desc); cancel_work_sync(&desc->rxwork); } - if (!PMSG_IS_AUTO(message)) { - mutex_unlock(&desc->wlock); - mutex_unlock(&desc->rlock); - } + if (!PMSG_IS_AUTO(message)) + mutex_unlock(&desc->lock); return rv; } @@ -869,8 +854,7 @@ static int wdm_pre_reset(struct usb_interface *intf) { struct wdm_device *desc = usb_get_intfdata(intf); - mutex_lock(&desc->rlock); - mutex_lock(&desc->wlock); + mutex_lock(&desc->lock); kill_urbs(desc); /* @@ -892,8 +876,7 @@ static int wdm_post_reset(struct usb_interface *intf) int rv; rv = recover_from_urb_loss(desc); - mutex_unlock(&desc->wlock); - mutex_unlock(&desc->rlock); + mutex_unlock(&desc->lock); return 0; } diff --git a/trunk/drivers/usb/dwc3/ep0.c b/trunk/drivers/usb/dwc3/ep0.c index c8df1dd967ef..2f51de57593a 100644 --- a/trunk/drivers/usb/dwc3/ep0.c +++ b/trunk/drivers/usb/dwc3/ep0.c @@ -126,6 +126,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, struct dwc3_request *req) { struct dwc3 *dwc = dep->dwc; + u32 type; int ret = 0; req->request.actual = 0; @@ -148,14 +149,20 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, direction = !!(dep->flags & DWC3_EP0_DIR_IN); - if (dwc->ep0state != EP0_DATA_PHASE) { - dev_WARN(dwc->dev, "Unexpected pending request\n"); + if (dwc->ep0state == EP0_STATUS_PHASE) { + type = dwc->three_stage_setup + ? DWC3_TRBCTL_CONTROL_STATUS3 + : DWC3_TRBCTL_CONTROL_STATUS2; + } else if (dwc->ep0state == EP0_DATA_PHASE) { + type = DWC3_TRBCTL_CONTROL_DATA; + } else { + /* should never happen */ + WARN_ON(1); return 0; } ret = dwc3_ep0_start_trans(dwc, direction, - req->request.dma, req->request.length, - DWC3_TRBCTL_CONTROL_DATA); + req->request.dma, req->request.length, type); dep->flags &= ~(DWC3_EP_PENDING_REQUEST | DWC3_EP0_DIR_IN); } else if (dwc->delayed_status) { diff --git a/trunk/drivers/usb/dwc3/gadget.c b/trunk/drivers/usb/dwc3/gadget.c index 064b6e2cd411..a696bde53222 100644 --- a/trunk/drivers/usb/dwc3/gadget.c +++ b/trunk/drivers/usb/dwc3/gadget.c @@ -101,7 +101,7 @@ void dwc3_unmap_buffer_from_dma(struct dwc3_request *req) if (req->request.num_mapped_sgs) { req->request.dma = DMA_ADDR_INVALID; dma_unmap_sg(dwc->dev, req->request.sg, - req->request.num_mapped_sgs, + req->request.num_sgs, req->direction ? DMA_TO_DEVICE : DMA_FROM_DEVICE); diff --git a/trunk/drivers/usb/gadget/composite.c b/trunk/drivers/usb/gadget/composite.c index baaebf2830fc..a95de6a4a134 100644 --- a/trunk/drivers/usb/gadget/composite.c +++ b/trunk/drivers/usb/gadget/composite.c @@ -175,13 +175,14 @@ int config_ep_by_speed(struct usb_gadget *g, _ep->comp_desc = comp_desc; if (g->speed == USB_SPEED_SUPER) { switch (usb_endpoint_type(_ep->desc)) { - case USB_ENDPOINT_XFER_ISOC: - /* mult: bits 1:0 of bmAttributes */ - _ep->mult = comp_desc->bmAttributes & 0x3; case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_INT: _ep->maxburst = comp_desc->bMaxBurst; break; + case USB_ENDPOINT_XFER_ISOC: + /* mult: bits 1:0 of bmAttributes */ + _ep->mult = comp_desc->bmAttributes & 0x3; + break; default: /* Do nothing for control endpoints */ break; diff --git a/trunk/drivers/usb/gadget/epautoconf.c b/trunk/drivers/usb/gadget/epautoconf.c index e0e6375ef5dd..753aa0683ac1 100644 --- a/trunk/drivers/usb/gadget/epautoconf.c +++ b/trunk/drivers/usb/gadget/epautoconf.c @@ -126,7 +126,7 @@ ep_matches ( * descriptor and see if the EP matches it */ if (usb_endpoint_xfer_bulk(desc)) { - if (ep_comp && gadget->max_speed >= USB_SPEED_SUPER) { + if (ep_comp) { num_req_streams = ep_comp->bmAttributes & 0x1f; if (num_req_streams > ep->max_streams) return 0; 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/gadget/f_mass_storage.c b/trunk/drivers/usb/gadget/f_mass_storage.c index ee8ceec01560..6353eca1e852 100644 --- a/trunk/drivers/usb/gadget/f_mass_storage.c +++ b/trunk/drivers/usb/gadget/f_mass_storage.c @@ -3123,15 +3123,15 @@ fsg_add(struct usb_composite_dev *cdev, struct usb_configuration *c, struct fsg_module_parameters { char *file[FSG_MAX_LUNS]; - bool ro[FSG_MAX_LUNS]; - bool removable[FSG_MAX_LUNS]; - bool cdrom[FSG_MAX_LUNS]; - bool nofua[FSG_MAX_LUNS]; + int ro[FSG_MAX_LUNS]; + int removable[FSG_MAX_LUNS]; + int cdrom[FSG_MAX_LUNS]; + int nofua[FSG_MAX_LUNS]; unsigned int file_count, ro_count, removable_count, cdrom_count; unsigned int nofua_count; unsigned int luns; /* nluns */ - bool stall; /* can_stall */ + int stall; /* can_stall */ }; #define _FSG_MODULE_PARAM_ARRAY(prefix, params, name, type, desc) \ diff --git a/trunk/drivers/usb/gadget/fsl_udc_core.c b/trunk/drivers/usb/gadget/fsl_udc_core.c index b04712f19f1e..d7ea6c076ce9 100644 --- a/trunk/drivers/usb/gadget/fsl_udc_core.c +++ b/trunk/drivers/usb/gadget/fsl_udc_core.c @@ -1430,7 +1430,7 @@ static void setup_received_irq(struct fsl_udc *udc, int pipe = get_pipe_by_windex(wIndex); struct fsl_ep *ep; - if (wValue != 0 || wLength != 0 || pipe >= udc->max_ep) + if (wValue != 0 || wLength != 0 || pipe > udc->max_ep) break; ep = get_ep_by_pipe(udc, pipe); @@ -1673,7 +1673,7 @@ static void dtd_complete_irq(struct fsl_udc *udc) if (!bit_pos) return; - for (i = 0; i < udc->max_ep; i++) { + for (i = 0; i < udc->max_ep * 2; i++) { ep_num = i >> 1; direction = i % 2; diff --git a/trunk/drivers/usb/gadget/langwell_udc.c b/trunk/drivers/usb/gadget/langwell_udc.c index e2293c1588ee..fa0fcc11263f 100644 --- a/trunk/drivers/usb/gadget/langwell_udc.c +++ b/trunk/drivers/usb/gadget/langwell_udc.c @@ -11,6 +11,11 @@ /* #undef DEBUG */ /* #undef VERBOSE_DEBUG */ +#if defined(CONFIG_USB_LANGWELL_OTG) +#define OTG_TRANSCEIVER +#endif + + #include #include #include @@ -1517,7 +1522,8 @@ static void langwell_udc_stop(struct langwell_udc *dev) /* stop all USB activities */ -static void stop_activity(struct langwell_udc *dev) +static void stop_activity(struct langwell_udc *dev, + struct usb_gadget_driver *driver) { struct langwell_ep *ep; dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); @@ -1529,9 +1535,9 @@ static void stop_activity(struct langwell_udc *dev) } /* report disconnect; the driver is already quiesced */ - if (dev->driver) { + if (driver) { spin_unlock(&dev->lock); - dev->driver->disconnect(&dev->gadget); + driver->disconnect(&dev->gadget); spin_lock(&dev->lock); } @@ -1919,10 +1925,11 @@ static int langwell_stop(struct usb_gadget *g, /* stop all usb activities */ dev->gadget.speed = USB_SPEED_UNKNOWN; + stop_activity(dev, driver); + spin_unlock_irqrestore(&dev->lock, flags); + dev->gadget.dev.driver = NULL; dev->driver = NULL; - stop_activity(dev); - spin_unlock_irqrestore(&dev->lock, flags); device_remove_file(&dev->pdev->dev, &dev_attr_function); @@ -2308,9 +2315,13 @@ static void handle_setup_packet(struct langwell_udc *dev, if (!gadget_is_otg(&dev->gadget)) break; - else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE) + else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE) { dev->gadget.b_hnp_enable = 1; - else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT) +#ifdef OTG_TRANSCEIVER + if (!dev->lotg->otg.default_a) + dev->lotg->hsm.b_hnp_enable = 1; +#endif + } else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT) dev->gadget.a_hnp_support = 1; else if (setup->bRequest == USB_DEVICE_A_ALT_HNP_SUPPORT) @@ -2722,7 +2733,7 @@ static void handle_usb_reset(struct langwell_udc *dev) dev->bus_reset = 1; /* reset all the queues, stop all USB activities */ - stop_activity(dev); + stop_activity(dev, dev->driver); dev->usb_state = USB_STATE_DEFAULT; } else { dev_vdbg(&dev->pdev->dev, "device controller reset\n"); @@ -2730,7 +2741,7 @@ static void handle_usb_reset(struct langwell_udc *dev) langwell_udc_reset(dev); /* reset all the queues, stop all USB activities */ - stop_activity(dev); + stop_activity(dev, dev->driver); /* reset ep0 dQH and endptctrl */ ep0_reset(dev); @@ -2741,6 +2752,12 @@ static void handle_usb_reset(struct langwell_udc *dev) dev->usb_state = USB_STATE_ATTACHED; } +#ifdef OTG_TRANSCEIVER + /* refer to USB OTG 6.6.2.3 b_hnp_en is cleared */ + if (!dev->lotg->otg.default_a) + dev->lotg->hsm.b_hnp_enable = 0; +#endif + dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__); } @@ -2753,6 +2770,29 @@ static void handle_bus_suspend(struct langwell_udc *dev) dev->resume_state = dev->usb_state; dev->usb_state = USB_STATE_SUSPENDED; +#ifdef OTG_TRANSCEIVER + if (dev->lotg->otg.default_a) { + if (dev->lotg->hsm.b_bus_suspend_vld == 1) { + dev->lotg->hsm.b_bus_suspend = 1; + /* notify transceiver the state changes */ + if (spin_trylock(&dev->lotg->wq_lock)) { + langwell_update_transceiver(); + spin_unlock(&dev->lotg->wq_lock); + } + } + dev->lotg->hsm.b_bus_suspend_vld++; + } else { + if (!dev->lotg->hsm.a_bus_suspend) { + dev->lotg->hsm.a_bus_suspend = 1; + /* notify transceiver the state changes */ + if (spin_trylock(&dev->lotg->wq_lock)) { + langwell_update_transceiver(); + spin_unlock(&dev->lotg->wq_lock); + } + } + } +#endif + /* report suspend to the driver */ if (dev->driver) { if (dev->driver->suspend) { @@ -2783,6 +2823,11 @@ static void handle_bus_resume(struct langwell_udc *dev) if (dev->pdev->device != 0x0829) langwell_phy_low_power(dev, 0); +#ifdef OTG_TRANSCEIVER + if (dev->lotg->otg.default_a == 0) + dev->lotg->hsm.a_bus_suspend = 0; +#endif + /* report resume to the driver */ if (dev->driver) { if (dev->driver->resume) { @@ -2975,6 +3020,7 @@ static void langwell_udc_remove(struct pci_dev *pdev) dev->done = &done; +#ifndef OTG_TRANSCEIVER /* free dTD dma_pool and dQH */ if (dev->dtd_pool) dma_pool_destroy(dev->dtd_pool); @@ -2986,6 +3032,7 @@ static void langwell_udc_remove(struct pci_dev *pdev) /* release SRAM caching */ if (dev->has_sram && dev->got_sram) sram_deinit(dev); +#endif if (dev->status_req) { kfree(dev->status_req->req.buf); @@ -2998,6 +3045,7 @@ static void langwell_udc_remove(struct pci_dev *pdev) if (dev->got_irq) free_irq(pdev->irq, dev); +#ifndef OTG_TRANSCEIVER if (dev->cap_regs) iounmap(dev->cap_regs); @@ -3007,6 +3055,13 @@ static void langwell_udc_remove(struct pci_dev *pdev) if (dev->enabled) pci_disable_device(pdev); +#else + if (dev->transceiver) { + otg_put_transceiver(dev->transceiver); + dev->transceiver = NULL; + dev->lotg = NULL; + } +#endif dev->cap_regs = NULL; @@ -3017,7 +3072,9 @@ static void langwell_udc_remove(struct pci_dev *pdev) device_remove_file(&pdev->dev, &dev_attr_langwell_udc); device_remove_file(&pdev->dev, &dev_attr_remote_wakeup); +#ifndef OTG_TRANSCEIVER pci_set_drvdata(pdev, NULL); +#endif /* free dev, wait for the release() finished */ wait_for_completion(&done); @@ -3032,7 +3089,9 @@ static int langwell_udc_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct langwell_udc *dev; +#ifndef OTG_TRANSCEIVER unsigned long resource, len; +#endif void __iomem *base = NULL; size_t size; int retval; @@ -3050,6 +3109,16 @@ static int langwell_udc_probe(struct pci_dev *pdev, dev->pdev = pdev; dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); +#ifdef OTG_TRANSCEIVER + /* PCI device is already enabled by otg_transceiver driver */ + dev->enabled = 1; + + /* mem region and register base */ + dev->region = 1; + dev->transceiver = otg_get_transceiver(); + dev->lotg = otg_to_langwell(dev->transceiver); + base = dev->lotg->regs; +#else pci_set_drvdata(pdev, dev); /* now all the pci goodies ... */ @@ -3070,6 +3139,7 @@ static int langwell_udc_probe(struct pci_dev *pdev, dev->region = 1; base = ioremap_nocache(resource, len); +#endif if (base == NULL) { dev_err(&dev->pdev->dev, "can't map memory\n"); retval = -EFAULT; @@ -3093,6 +3163,7 @@ static int langwell_udc_probe(struct pci_dev *pdev, dev->got_sram = 0; dev_vdbg(&dev->pdev->dev, "dev->has_sram: %d\n", dev->has_sram); +#ifndef OTG_TRANSCEIVER /* enable SRAM caching if detected */ if (dev->has_sram && !dev->got_sram) sram_init(dev); @@ -3111,6 +3182,7 @@ static int langwell_udc_probe(struct pci_dev *pdev, goto error; } dev->got_irq = 1; +#endif /* set stopped bit */ dev->stopped = 1; @@ -3185,8 +3257,10 @@ static int langwell_udc_probe(struct pci_dev *pdev, dev->remote_wakeup = 0; dev->dev_status = 1 << USB_DEVICE_SELF_POWERED; +#ifndef OTG_TRANSCEIVER /* reset device controller */ langwell_udc_reset(dev); +#endif /* initialize gadget structure */ dev->gadget.ops = &langwell_ops; /* usb_gadget_ops */ @@ -3194,6 +3268,9 @@ static int langwell_udc_probe(struct pci_dev *pdev, INIT_LIST_HEAD(&dev->gadget.ep_list); /* ep_list */ dev->gadget.speed = USB_SPEED_UNKNOWN; /* speed */ dev->gadget.max_speed = USB_SPEED_HIGH; /* support dual speed */ +#ifdef OTG_TRANSCEIVER + dev->gadget.is_otg = 1; /* support otg mode */ +#endif /* the "gadget" abstracts/virtualizes the controller */ dev_set_name(&dev->gadget.dev, "gadget"); @@ -3205,8 +3282,10 @@ static int langwell_udc_probe(struct pci_dev *pdev, /* controller endpoints reinit */ eps_reinit(dev); +#ifndef OTG_TRANSCEIVER /* reset ep0 dQH and endptctrl */ ep0_reset(dev); +#endif /* create dTD dma_pool resource */ dev->dtd_pool = dma_pool_create("langwell_dtd", @@ -3288,7 +3367,7 @@ static int langwell_udc_suspend(struct pci_dev *pdev, pm_message_t state) spin_lock_irq(&dev->lock); /* stop all usb activities */ - stop_activity(dev); + stop_activity(dev, dev->driver); spin_unlock_irq(&dev->lock); /* free dTD dma_pool and dQH */ @@ -3446,14 +3525,22 @@ static struct pci_driver langwell_pci_driver = { static int __init init(void) { +#ifdef OTG_TRANSCEIVER + return langwell_register_peripheral(&langwell_pci_driver); +#else return pci_register_driver(&langwell_pci_driver); +#endif } module_init(init); static void __exit cleanup(void) { +#ifdef OTG_TRANSCEIVER + return langwell_unregister_peripheral(&langwell_pci_driver); +#else pci_unregister_driver(&langwell_pci_driver); +#endif } module_exit(cleanup); diff --git a/trunk/drivers/usb/gadget/langwell_udc.h b/trunk/drivers/usb/gadget/langwell_udc.h index d6e78accaffe..ef79e242b7b0 100644 --- a/trunk/drivers/usb/gadget/langwell_udc.h +++ b/trunk/drivers/usb/gadget/langwell_udc.h @@ -8,6 +8,7 @@ */ #include +#include /*-------------------------------------------------------------------------*/ diff --git a/trunk/drivers/usb/gadget/storage_common.c b/trunk/drivers/usb/gadget/storage_common.c index 85ea14e2545e..c7f291a331df 100644 --- a/trunk/drivers/usb/gadget/storage_common.c +++ b/trunk/drivers/usb/gadget/storage_common.c @@ -598,16 +598,16 @@ static __maybe_unused struct usb_ss_cap_descriptor fsg_ss_cap_desc = { | USB_5GBPS_OPERATION), .bFunctionalitySupport = USB_LOW_SPEED_OPERATION, .bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT, - .bU2DevExitLat = cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT), + .bU2DevExitLat = USB_DEFAULT_U2_DEV_EXIT_LAT, }; static __maybe_unused struct usb_bos_descriptor fsg_bos_desc = { .bLength = USB_DT_BOS_SIZE, .bDescriptorType = USB_DT_BOS, - .wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE + .wTotalLength = USB_DT_BOS_SIZE + USB_DT_USB_EXT_CAP_SIZE - + USB_DT_USB_SS_CAP_SIZE), + + USB_DT_USB_SS_CAP_SIZE, .bNumDeviceCaps = 2, }; 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..e90344a17631 100644 --- a/trunk/drivers/usb/host/ehci-fsl.c +++ b/trunk/drivers/usb/host/ehci-fsl.c @@ -125,7 +125,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, */ if (pdata->init && pdata->init(pdev)) { retval = -ENODEV; - goto err4; + goto err3; } /* Enable USB controller, 83xx or 8536 */ @@ -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/ehci-pci.c b/trunk/drivers/usb/host/ehci-pci.c index 01bb7241d6ef..f4b627d343ac 100644 --- a/trunk/drivers/usb/host/ehci-pci.c +++ b/trunk/drivers/usb/host/ehci-pci.c @@ -276,9 +276,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd) /* Serial Bus Release Number is at PCI 0x60 offset */ pci_read_config_byte(pdev, 0x60, &ehci->sbrn); - if (pdev->vendor == PCI_VENDOR_ID_STMICRO - && pdev->device == PCI_DEVICE_ID_STMICRO_USB_HOST) - ehci->sbrn = 0x20; /* ConneXT has no sbrn register */ /* Keep this around for a while just in case some EHCI * implementation uses legacy PCI PM support. This test @@ -529,9 +526,6 @@ static const struct pci_device_id pci_ids [] = { { /* handle any USB 2.0 EHCI controller */ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_EHCI, ~0), .driver_data = (unsigned long) &ehci_pci_hc_driver, - }, { - PCI_VDEVICE(STMICRO, PCI_DEVICE_ID_STMICRO_USB_HOST), - .driver_data = (unsigned long) &ehci_pci_hc_driver, }, { /* end: all zeroes */ } }; diff --git a/trunk/drivers/usb/host/ohci-at91.c b/trunk/drivers/usb/host/ohci-at91.c index 77afabc77f9b..5df0b0e3392b 100644 --- a/trunk/drivers/usb/host/ohci-at91.c +++ b/trunk/drivers/usb/host/ohci-at91.c @@ -139,23 +139,8 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, } iclk = clk_get(&pdev->dev, "ohci_clk"); - if (IS_ERR(iclk)) { - dev_err(&pdev->dev, "failed to get ohci_clk\n"); - retval = PTR_ERR(iclk); - goto err3; - } fclk = clk_get(&pdev->dev, "uhpck"); - if (IS_ERR(fclk)) { - dev_err(&pdev->dev, "failed to get uhpck\n"); - retval = PTR_ERR(fclk); - goto err4; - } hclk = clk_get(&pdev->dev, "hclk"); - if (IS_ERR(hclk)) { - dev_err(&pdev->dev, "failed to get hclk\n"); - retval = PTR_ERR(hclk); - goto err5; - } at91_start_hc(pdev); ohci_hcd_init(hcd_to_ohci(hcd)); @@ -168,12 +153,9 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, at91_stop_hc(pdev); clk_put(hclk); - err5: clk_put(fclk); - err4: clk_put(iclk); - err3: iounmap(hcd->regs); err2: @@ -244,8 +226,7 @@ static void ohci_at91_usb_set_power(struct at91_usbh_data *pdata, int port, int if (!gpio_is_valid(pdata->vbus_pin[port])) return; - gpio_set_value(pdata->vbus_pin[port], - !pdata->vbus_pin_active_low[port] ^ enable); + gpio_set_value(pdata->vbus_pin[port], !pdata->vbus_pin_inverted ^ enable); } static int ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port) @@ -256,8 +237,7 @@ static int ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port) if (!gpio_is_valid(pdata->vbus_pin[port])) return -EINVAL; - return gpio_get_value(pdata->vbus_pin[port]) ^ - !pdata->vbus_pin_active_low[port]; + return gpio_get_value(pdata->vbus_pin[port]) ^ !pdata->vbus_pin_inverted; } /* diff --git a/trunk/drivers/usb/host/ohci-dbg.c b/trunk/drivers/usb/host/ohci-dbg.c index e4bcb62b930a..5179fcd73d8a 100644 --- a/trunk/drivers/usb/host/ohci-dbg.c +++ b/trunk/drivers/usb/host/ohci-dbg.c @@ -82,14 +82,6 @@ urb_print(struct urb * urb, char * str, int small, int status) ohci_dbg(ohci,format, ## arg ); \ } while (0); -/* Version for use where "next" is the address of a local variable */ -#define ohci_dbg_nosw(ohci, next, size, format, arg...) \ - do { \ - unsigned s_len; \ - s_len = scnprintf(*next, *size, format, ## arg); \ - *size -= s_len; *next += s_len; \ - } while (0); - static void ohci_dump_intr_mask ( struct ohci_hcd *ohci, @@ -661,7 +653,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) /* dump driver info, then registers in spec order */ - ohci_dbg_nosw(ohci, &next, &size, + ohci_dbg_sw (ohci, &next, &size, "bus %s, device %s\n" "%s\n" "%s\n", @@ -680,7 +672,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) /* hcca */ if (ohci->hcca) - ohci_dbg_nosw(ohci, &next, &size, + ohci_dbg_sw (ohci, &next, &size, "hcca frame 0x%04x\n", ohci_frame_no(ohci)); /* other registers mostly affect frame timings */ diff --git a/trunk/drivers/usb/host/ohci-pci.c b/trunk/drivers/usb/host/ohci-pci.c index 1843bb68ac7c..6109810cc2d3 100644 --- a/trunk/drivers/usb/host/ohci-pci.c +++ b/trunk/drivers/usb/host/ohci-pci.c @@ -397,10 +397,6 @@ static const struct pci_device_id pci_ids [] = { { /* handle any USB OHCI controller */ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_OHCI, ~0), .driver_data = (unsigned long) &ohci_pci_hc_driver, - }, { - /* The device in the ConneXT I/O hub has no class reg */ - PCI_VDEVICE(STMICRO, PCI_DEVICE_ID_STMICRO_USB_OHCI), - .driver_data = (unsigned long) &ohci_pci_hc_driver, }, { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE (pci, pci_ids); 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/host/xhci-ring.c b/trunk/drivers/usb/host/xhci-ring.c index b62037bff688..b90e1386418b 100644 --- a/trunk/drivers/usb/host/xhci-ring.c +++ b/trunk/drivers/usb/host/xhci-ring.c @@ -1204,7 +1204,6 @@ static void handle_vendor_event(struct xhci_hcd *xhci, * * Returns a zero-based port number, which is suitable for indexing into each of * the split roothubs' port arrays and bus state arrays. - * Add one to it in order to call xhci_find_slot_id_by_port. */ static unsigned int find_faked_portnum_from_hw_portnum(struct usb_hcd *hcd, struct xhci_hcd *xhci, u32 port_id) @@ -1325,7 +1324,7 @@ static void handle_port_status(struct xhci_hcd *xhci, xhci_set_link_state(xhci, port_array, faked_port_index, XDEV_U0); slot_id = xhci_find_slot_id_by_port(hcd, xhci, - faked_port_index + 1); + faked_port_index); if (!slot_id) { xhci_dbg(xhci, "slot_id is zero\n"); goto cleanup; @@ -3324,8 +3323,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, /* Check TD length */ if (running_total != td_len) { xhci_err(xhci, "ISOC TD length unmatch\n"); - ret = -EINVAL; - goto cleanup; + return -EINVAL; } } diff --git a/trunk/drivers/usb/misc/emi26.c b/trunk/drivers/usb/misc/emi26.c index da97dcec1f32..d9b6a0355443 100644 --- a/trunk/drivers/usb/misc/emi26.c +++ b/trunk/drivers/usb/misc/emi26.c @@ -37,6 +37,9 @@ static int emi26_set_reset(struct usb_device *dev, unsigned char reset_bit); static int emi26_load_firmware (struct usb_device *dev); static int emi26_probe(struct usb_interface *intf, const struct usb_device_id *id); static void emi26_disconnect(struct usb_interface *intf); +static int __init emi26_init (void); +static void __exit emi26_exit (void); + /* thanks to drivers/usb/serial/keyspan_pda.c code */ static int emi26_writememory (struct usb_device *dev, int address, diff --git a/trunk/drivers/usb/misc/emi62.c b/trunk/drivers/usb/misc/emi62.c index 4e0f167a6c4e..9f39062ebb08 100644 --- a/trunk/drivers/usb/misc/emi62.c +++ b/trunk/drivers/usb/misc/emi62.c @@ -46,6 +46,9 @@ static int emi62_set_reset(struct usb_device *dev, unsigned char reset_bit); static int emi62_load_firmware (struct usb_device *dev); static int emi62_probe(struct usb_interface *intf, const struct usb_device_id *id); static void emi62_disconnect(struct usb_interface *intf); +static int __init emi62_init (void); +static void __exit emi62_exit (void); + /* thanks to drivers/usb/serial/keyspan_pda.c code */ static int emi62_writememory(struct usb_device *dev, int address, diff --git a/trunk/drivers/usb/misc/usbsevseg.c b/trunk/drivers/usb/misc/usbsevseg.c index b2d82b937392..107bf13b1cf1 100644 --- a/trunk/drivers/usb/misc/usbsevseg.c +++ b/trunk/drivers/usb/misc/usbsevseg.c @@ -24,7 +24,7 @@ #define VENDOR_ID 0x0fc5 #define PRODUCT_ID 0x1227 -#define MAXLEN 8 +#define MAXLEN 6 /* table of devices that work with this driver */ static const struct usb_device_id id_table[] = { diff --git a/trunk/drivers/usb/musb/davinci.c b/trunk/drivers/usb/musb/davinci.c index 7c569f51212a..f9a3f62a83b5 100644 --- a/trunk/drivers/usb/musb/davinci.c +++ b/trunk/drivers/usb/musb/davinci.c @@ -33,6 +33,9 @@ #include #include +#include +#include +#include #include #include diff --git a/trunk/drivers/usb/musb/musb_core.c b/trunk/drivers/usb/musb/musb_core.c index 3d11cf64ebd1..56cf0243979e 100644 --- a/trunk/drivers/usb/musb/musb_core.c +++ b/trunk/drivers/usb/musb/musb_core.c @@ -981,9 +981,6 @@ static void musb_shutdown(struct platform_device *pdev) unsigned long flags; pm_runtime_get_sync(musb->controller); - - musb_gadget_cleanup(musb); - spin_lock_irqsave(&musb->lock, flags); musb_platform_disable(musb); musb_generic_disable(musb); @@ -1830,6 +1827,8 @@ static void musb_free(struct musb *musb) sysfs_remove_group(&musb->controller->kobj, &musb_attr_group); #endif + musb_gadget_cleanup(musb); + if (musb->nIrq >= 0) { if (musb->irq_wake) disable_irq_wake(musb->nIrq); 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/musb/omap2430.c b/trunk/drivers/usb/musb/omap2430.c index df719eae3b03..c27bbbf32b52 100644 --- a/trunk/drivers/usb/musb/omap2430.c +++ b/trunk/drivers/usb/musb/omap2430.c @@ -222,6 +222,7 @@ static inline void omap2430_low_level_init(struct musb *musb) musb_writel(musb->mregs, OTG_FORCESTDBY, l); } +/* blocking notifier support */ static int musb_otg_notifications(struct notifier_block *nb, unsigned long event, void *unused) { @@ -230,7 +231,7 @@ static int musb_otg_notifications(struct notifier_block *nb, musb->xceiv_event = event; schedule_work(&musb->otg_notifier_work); - return NOTIFY_OK; + return 0; } static void musb_otg_notifier_work(struct work_struct *data_notifier_work) @@ -385,7 +386,6 @@ static void omap2430_musb_disable(struct musb *musb) static int omap2430_musb_exit(struct musb *musb) { del_timer_sync(&musb_idle_timer); - cancel_work_sync(&musb->otg_notifier_work); omap2430_low_level_exit(musb); otg_put_transceiver(musb->xceiv); diff --git a/trunk/drivers/usb/otg/Kconfig b/trunk/drivers/usb/otg/Kconfig index 735ef4c2339a..2a25955881fc 100644 --- a/trunk/drivers/usb/otg/Kconfig +++ b/trunk/drivers/usb/otg/Kconfig @@ -86,6 +86,20 @@ config NOP_USB_XCEIV built-in with usb ip or which are autonomous and doesn't require any phy programming such as ISP1x04 etc. +config USB_LANGWELL_OTG + tristate "Intel Langwell USB OTG dual-role support" + depends on USB && PCI && INTEL_SCU_IPC + select USB_OTG + select USB_OTG_UTILS + help + Say Y here if you want to build Intel Langwell USB OTG + transciever driver in kernel. This driver implements role + switch between EHCI host driver and Langwell USB OTG + client driver. + + To compile this driver as a module, choose M here: the + module will be called langwell_otg. + config USB_MSM_OTG tristate "OTG support for Qualcomm on-chip USB controller" depends on (USB || USB_GADGET) && ARCH_MSM @@ -110,7 +124,7 @@ config AB8500_USB config FSL_USB2_OTG bool "Freescale USB OTG Transceiver Driver" - depends on USB_EHCI_FSL && USB_GADGET_FSL_USB2 && USB_SUSPEND + depends on USB_EHCI_FSL && USB_GADGET_FSL_USB2 select USB_OTG select USB_OTG_UTILS help @@ -118,7 +132,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 select USB_OTG select USB_OTG_UTILS help diff --git a/trunk/drivers/usb/otg/Makefile b/trunk/drivers/usb/otg/Makefile index 41aa5098b139..b2c5a9598637 100644 --- a/trunk/drivers/usb/otg/Makefile +++ b/trunk/drivers/usb/otg/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o obj-$(CONFIG_TWL6030_USB) += twl6030-usb.o +obj-$(CONFIG_USB_LANGWELL_OTG) += langwell_otg.o obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o obj-$(CONFIG_USB_ULPI) += ulpi.o obj-$(CONFIG_USB_ULPI_VIEWPORT) += ulpi_viewport.o diff --git a/trunk/drivers/usb/otg/langwell_otg.c b/trunk/drivers/usb/otg/langwell_otg.c new file mode 100644 index 000000000000..f08f784086f7 --- /dev/null +++ b/trunk/drivers/usb/otg/langwell_otg.c @@ -0,0 +1,2347 @@ +/* + * Intel Langwell USB OTG transceiver driver + * Copyright (C) 2008 - 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, + * 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. + * + */ +/* This driver helps to switch Langwell OTG controller function between host + * and peripheral. It works with EHCI driver and Langwell client controller + * driver together. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRIVER_DESC "Intel Langwell USB OTG transceiver driver" +#define DRIVER_VERSION "July 10, 2010" + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Henry Yuan , Hao Wu "); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +static const char driver_name[] = "langwell_otg"; + +static int langwell_otg_probe(struct pci_dev *pdev, + const struct pci_device_id *id); +static void langwell_otg_remove(struct pci_dev *pdev); +static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message); +static int langwell_otg_resume(struct pci_dev *pdev); + +static int langwell_otg_set_host(struct otg_transceiver *otg, + struct usb_bus *host); +static int langwell_otg_set_peripheral(struct otg_transceiver *otg, + struct usb_gadget *gadget); +static int langwell_otg_start_srp(struct otg_transceiver *otg); + +static const struct pci_device_id pci_ids[] = {{ + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), + .class_mask = ~0, + .vendor = 0x8086, + .device = 0x0811, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, +}, { /* end: all zeroes */ } +}; + +static struct pci_driver otg_pci_driver = { + .name = (char *) driver_name, + .id_table = pci_ids, + + .probe = langwell_otg_probe, + .remove = langwell_otg_remove, + + .suspend = langwell_otg_suspend, + .resume = langwell_otg_resume, +}; + +/* HSM timers */ +static inline struct langwell_otg_timer *otg_timer_initializer +(void (*function)(unsigned long), unsigned long expires, unsigned long data) +{ + struct langwell_otg_timer *timer; + timer = kmalloc(sizeof(struct langwell_otg_timer), GFP_KERNEL); + if (timer == NULL) + return timer; + + timer->function = function; + timer->expires = expires; + timer->data = data; + return timer; +} + +static struct langwell_otg_timer *a_wait_vrise_tmr, *a_aidl_bdis_tmr, + *b_se0_srp_tmr, *b_srp_init_tmr; + +static struct list_head active_timers; + +static struct langwell_otg *the_transceiver; + +/* host/client notify transceiver when event affects HNP state */ +void langwell_update_transceiver(void) +{ + struct langwell_otg *lnw = the_transceiver; + + dev_dbg(lnw->dev, "transceiver is updated\n"); + + if (!lnw->qwork) + return ; + + queue_work(lnw->qwork, &lnw->work); +} +EXPORT_SYMBOL(langwell_update_transceiver); + +static int langwell_otg_set_host(struct otg_transceiver *otg, + struct usb_bus *host) +{ + otg->host = host; + + return 0; +} + +static int langwell_otg_set_peripheral(struct otg_transceiver *otg, + struct usb_gadget *gadget) +{ + otg->gadget = gadget; + + return 0; +} + +static int langwell_otg_set_power(struct otg_transceiver *otg, + unsigned mA) +{ + return 0; +} + +/* A-device drives vbus, controlled through IPC commands */ +static int langwell_otg_set_vbus(struct otg_transceiver *otg, bool enabled) +{ + struct langwell_otg *lnw = the_transceiver; + u8 sub_id; + + dev_dbg(lnw->dev, "%s <--- %s\n", __func__, enabled ? "on" : "off"); + + if (enabled) + sub_id = 0x8; /* Turn on the VBus */ + else + sub_id = 0x9; /* Turn off the VBus */ + + if (intel_scu_ipc_simple_command(0xef, sub_id)) { + dev_dbg(lnw->dev, "Failed to set Vbus via IPC commands\n"); + return -EBUSY; + } + + dev_dbg(lnw->dev, "%s --->\n", __func__); + + return 0; +} + +/* charge vbus or discharge vbus through a resistor to ground */ +static void langwell_otg_chrg_vbus(int on) +{ + struct langwell_otg *lnw = the_transceiver; + u32 val; + + val = readl(lnw->iotg.base + CI_OTGSC); + + if (on) + writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VC, + lnw->iotg.base + CI_OTGSC); + else + writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VD, + lnw->iotg.base + CI_OTGSC); +} + +/* Start SRP */ +static int langwell_otg_start_srp(struct otg_transceiver *otg) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + u32 val; + + dev_dbg(lnw->dev, "%s --->\n", __func__); + + val = readl(iotg->base + CI_OTGSC); + + writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HADP, + iotg->base + CI_OTGSC); + + /* Check if the data plus is finished or not */ + msleep(8); + val = readl(iotg->base + CI_OTGSC); + if (val & (OTGSC_HADP | OTGSC_DP)) + dev_dbg(lnw->dev, "DataLine SRP Error\n"); + + /* Disable interrupt - b_sess_vld */ + val = readl(iotg->base + CI_OTGSC); + val &= (~(OTGSC_BSVIE | OTGSC_BSEIE)); + writel(val, iotg->base + CI_OTGSC); + + /* Start VBus SRP, drive vbus to generate VBus pulse */ + iotg->otg.set_vbus(&iotg->otg, true); + msleep(15); + iotg->otg.set_vbus(&iotg->otg, false); + + /* Enable interrupt - b_sess_vld*/ + val = readl(iotg->base + CI_OTGSC); + dev_dbg(lnw->dev, "after VBUS pulse otgsc = %x\n", val); + + val |= (OTGSC_BSVIE | OTGSC_BSEIE); + writel(val, iotg->base + CI_OTGSC); + + /* If Vbus is valid, then update the hsm */ + if (val & OTGSC_BSV) { + dev_dbg(lnw->dev, "no b_sess_vld interrupt\n"); + + lnw->iotg.hsm.b_sess_vld = 1; + langwell_update_transceiver(); + } + + dev_dbg(lnw->dev, "%s <---\n", __func__); + return 0; +} + +/* stop SOF via bus_suspend */ +static void langwell_otg_loc_sof(int on) +{ + struct langwell_otg *lnw = the_transceiver; + struct usb_hcd *hcd; + int err; + + dev_dbg(lnw->dev, "%s ---> %s\n", __func__, on ? "suspend" : "resume"); + + hcd = bus_to_hcd(lnw->iotg.otg.host); + if (on) + err = hcd->driver->bus_resume(hcd); + else + err = hcd->driver->bus_suspend(hcd); + + if (err) + dev_dbg(lnw->dev, "Fail to resume/suspend USB bus - %d\n", err); + + dev_dbg(lnw->dev, "%s <---\n", __func__); +} + +static int langwell_otg_check_otgsc(void) +{ + struct langwell_otg *lnw = the_transceiver; + u32 otgsc, usbcfg; + + dev_dbg(lnw->dev, "check sync OTGSC and USBCFG registers\n"); + + otgsc = readl(lnw->iotg.base + CI_OTGSC); + usbcfg = readl(lnw->usbcfg); + + dev_dbg(lnw->dev, "OTGSC = %08x, USBCFG = %08x\n", + otgsc, usbcfg); + dev_dbg(lnw->dev, "OTGSC_AVV = %d\n", !!(otgsc & OTGSC_AVV)); + dev_dbg(lnw->dev, "USBCFG.VBUSVAL = %d\n", + !!(usbcfg & USBCFG_VBUSVAL)); + dev_dbg(lnw->dev, "OTGSC_ASV = %d\n", !!(otgsc & OTGSC_ASV)); + dev_dbg(lnw->dev, "USBCFG.AVALID = %d\n", + !!(usbcfg & USBCFG_AVALID)); + dev_dbg(lnw->dev, "OTGSC_BSV = %d\n", !!(otgsc & OTGSC_BSV)); + dev_dbg(lnw->dev, "USBCFG.BVALID = %d\n", + !!(usbcfg & USBCFG_BVALID)); + dev_dbg(lnw->dev, "OTGSC_BSE = %d\n", !!(otgsc & OTGSC_BSE)); + dev_dbg(lnw->dev, "USBCFG.SESEND = %d\n", + !!(usbcfg & USBCFG_SESEND)); + + /* Check USBCFG VBusValid/AValid/BValid/SessEnd */ + if (!!(otgsc & OTGSC_AVV) ^ !!(usbcfg & USBCFG_VBUSVAL)) { + dev_dbg(lnw->dev, "OTGSC.AVV != USBCFG.VBUSVAL\n"); + goto err; + } + if (!!(otgsc & OTGSC_ASV) ^ !!(usbcfg & USBCFG_AVALID)) { + dev_dbg(lnw->dev, "OTGSC.ASV != USBCFG.AVALID\n"); + goto err; + } + if (!!(otgsc & OTGSC_BSV) ^ !!(usbcfg & USBCFG_BVALID)) { + dev_dbg(lnw->dev, "OTGSC.BSV != USBCFG.BVALID\n"); + goto err; + } + if (!!(otgsc & OTGSC_BSE) ^ !!(usbcfg & USBCFG_SESEND)) { + dev_dbg(lnw->dev, "OTGSC.BSE != USBCFG.SESSEN\n"); + goto err; + } + + dev_dbg(lnw->dev, "OTGSC and USBCFG are synced\n"); + + return 0; + +err: + dev_warn(lnw->dev, "OTGSC isn't equal to USBCFG\n"); + return -EPIPE; +} + + +static void langwell_otg_phy_low_power(int on) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + u8 val, phcd; + int retval; + + dev_dbg(lnw->dev, "%s ---> %s mode\n", + __func__, on ? "Low power" : "Normal"); + + phcd = 0x40; + + val = readb(iotg->base + CI_HOSTPC1 + 2); + + if (on) { + /* Due to hardware issue, after set PHCD, sync will failed + * between USBCFG and OTGSC, so before set PHCD, check if + * sync is in process now. If the answer is "yes", then do + * not touch PHCD bit */ + retval = langwell_otg_check_otgsc(); + if (retval) { + dev_dbg(lnw->dev, "Skip PHCD programming..\n"); + return ; + } + + writeb(val | phcd, iotg->base + CI_HOSTPC1 + 2); + } else + writeb(val & ~phcd, iotg->base + CI_HOSTPC1 + 2); + + dev_dbg(lnw->dev, "%s <--- done\n", __func__); +} + +/* After drv vbus, add 5 ms delay to set PHCD */ +static void langwell_otg_phy_low_power_wait(int on) +{ + struct langwell_otg *lnw = the_transceiver; + + dev_dbg(lnw->dev, "add 5ms delay before programing PHCD\n"); + + mdelay(5); + langwell_otg_phy_low_power(on); +} + +/* Enable/Disable OTG interrupt */ +static void langwell_otg_intr(int on) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + u32 val; + + dev_dbg(lnw->dev, "%s ---> %s\n", __func__, on ? "on" : "off"); + + val = readl(iotg->base + CI_OTGSC); + + /* OTGSC_INT_MASK doesn't contains 1msInt */ + if (on) { + val = val | (OTGSC_INT_MASK); + writel(val, iotg->base + CI_OTGSC); + } else { + val = val & ~(OTGSC_INT_MASK); + writel(val, iotg->base + CI_OTGSC); + } + + dev_dbg(lnw->dev, "%s <---\n", __func__); +} + +/* set HAAR: Hardware Assist Auto-Reset */ +static void langwell_otg_HAAR(int on) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + u32 val; + + dev_dbg(lnw->dev, "%s ---> %s\n", __func__, on ? "on" : "off"); + + val = readl(iotg->base + CI_OTGSC); + if (on) + writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HAAR, + iotg->base + CI_OTGSC); + else + writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HAAR, + iotg->base + CI_OTGSC); + + dev_dbg(lnw->dev, "%s <---\n", __func__); +} + +/* set HABA: Hardware Assist B-Disconnect to A-Connect */ +static void langwell_otg_HABA(int on) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + u32 val; + + dev_dbg(lnw->dev, "%s ---> %s\n", __func__, on ? "on" : "off"); + + val = readl(iotg->base + CI_OTGSC); + if (on) + writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HABA, + iotg->base + CI_OTGSC); + else + writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HABA, + iotg->base + CI_OTGSC); + + dev_dbg(lnw->dev, "%s <---\n", __func__); +} + +static int langwell_otg_check_se0_srp(int on) +{ + struct langwell_otg *lnw = the_transceiver; + int delay_time = TB_SE0_SRP * 10; + u32 val; + + dev_dbg(lnw->dev, "%s --->\n", __func__); + + do { + udelay(100); + if (!delay_time--) + break; + val = readl(lnw->iotg.base + CI_PORTSC1); + val &= PORTSC_LS; + } while (!val); + + dev_dbg(lnw->dev, "%s <---\n", __func__); + return val; +} + +/* The timeout callback function to set time out bit */ +static void set_tmout(unsigned long indicator) +{ + *(int *)indicator = 1; +} + +void langwell_otg_nsf_msg(unsigned long indicator) +{ + struct langwell_otg *lnw = the_transceiver; + + switch (indicator) { + case 2: + case 4: + case 6: + case 7: + dev_warn(lnw->dev, + "OTG:NSF-%lu - deivce not responding\n", indicator); + break; + case 3: + dev_warn(lnw->dev, + "OTG:NSF-%lu - deivce not supported\n", indicator); + break; + default: + dev_warn(lnw->dev, "Do not have this kind of NSF\n"); + break; + } +} + +/* Initialize timers */ +static int langwell_otg_init_timers(struct otg_hsm *hsm) +{ + /* HSM used timers */ + a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE, + (unsigned long)&hsm->a_wait_vrise_tmout); + if (a_wait_vrise_tmr == NULL) + return -ENOMEM; + a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS, + (unsigned long)&hsm->a_aidl_bdis_tmout); + if (a_aidl_bdis_tmr == NULL) + return -ENOMEM; + b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP, + (unsigned long)&hsm->b_se0_srp); + if (b_se0_srp_tmr == NULL) + return -ENOMEM; + b_srp_init_tmr = otg_timer_initializer(&set_tmout, TB_SRP_INIT, + (unsigned long)&hsm->b_srp_init_tmout); + if (b_srp_init_tmr == NULL) + return -ENOMEM; + + return 0; +} + +/* Free timers */ +static void langwell_otg_free_timers(void) +{ + kfree(a_wait_vrise_tmr); + kfree(a_aidl_bdis_tmr); + kfree(b_se0_srp_tmr); + kfree(b_srp_init_tmr); +} + +/* The timeout callback function to set time out bit */ +static void langwell_otg_timer_fn(unsigned long indicator) +{ + struct langwell_otg *lnw = the_transceiver; + + *(int *)indicator = 1; + + dev_dbg(lnw->dev, "kernel timer - timeout\n"); + + langwell_update_transceiver(); +} + +/* kernel timer used instead of HW based interrupt */ +static void langwell_otg_add_ktimer(enum langwell_otg_timer_type timers) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + unsigned long j = jiffies; + unsigned long data, time; + + switch (timers) { + case TA_WAIT_VRISE_TMR: + iotg->hsm.a_wait_vrise_tmout = 0; + data = (unsigned long)&iotg->hsm.a_wait_vrise_tmout; + time = TA_WAIT_VRISE; + break; + case TA_WAIT_BCON_TMR: + iotg->hsm.a_wait_bcon_tmout = 0; + data = (unsigned long)&iotg->hsm.a_wait_bcon_tmout; + time = TA_WAIT_BCON; + break; + case TA_AIDL_BDIS_TMR: + iotg->hsm.a_aidl_bdis_tmout = 0; + data = (unsigned long)&iotg->hsm.a_aidl_bdis_tmout; + time = TA_AIDL_BDIS; + break; + case TB_ASE0_BRST_TMR: + iotg->hsm.b_ase0_brst_tmout = 0; + data = (unsigned long)&iotg->hsm.b_ase0_brst_tmout; + time = TB_ASE0_BRST; + break; + case TB_SRP_INIT_TMR: + iotg->hsm.b_srp_init_tmout = 0; + data = (unsigned long)&iotg->hsm.b_srp_init_tmout; + time = TB_SRP_INIT; + break; + case TB_SRP_FAIL_TMR: + iotg->hsm.b_srp_fail_tmout = 0; + data = (unsigned long)&iotg->hsm.b_srp_fail_tmout; + time = TB_SRP_FAIL; + break; + case TB_BUS_SUSPEND_TMR: + iotg->hsm.b_bus_suspend_tmout = 0; + data = (unsigned long)&iotg->hsm.b_bus_suspend_tmout; + time = TB_BUS_SUSPEND; + break; + default: + dev_dbg(lnw->dev, "unknown timer, cannot enable it\n"); + return; + } + + lnw->hsm_timer.data = data; + lnw->hsm_timer.function = langwell_otg_timer_fn; + lnw->hsm_timer.expires = j + time * HZ / 1000; /* milliseconds */ + + add_timer(&lnw->hsm_timer); + + dev_dbg(lnw->dev, "add timer successfully\n"); +} + +/* Add timer to timer list */ +static void langwell_otg_add_timer(void *gtimer) +{ + struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer; + struct langwell_otg_timer *tmp_timer; + struct intel_mid_otg_xceiv *iotg = &the_transceiver->iotg; + u32 val32; + + /* Check if the timer is already in the active list, + * if so update timer count + */ + list_for_each_entry(tmp_timer, &active_timers, list) + if (tmp_timer == timer) { + timer->count = timer->expires; + return; + } + timer->count = timer->expires; + + if (list_empty(&active_timers)) { + val32 = readl(iotg->base + CI_OTGSC); + writel(val32 | OTGSC_1MSE, iotg->base + CI_OTGSC); + } + + list_add_tail(&timer->list, &active_timers); +} + +/* Remove timer from the timer list; clear timeout status */ +static void langwell_otg_del_timer(void *gtimer) +{ + struct langwell_otg *lnw = the_transceiver; + struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer; + struct langwell_otg_timer *tmp_timer, *del_tmp; + u32 val32; + + list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) + if (tmp_timer == timer) + list_del(&timer->list); + + if (list_empty(&active_timers)) { + val32 = readl(lnw->iotg.base + CI_OTGSC); + writel(val32 & ~OTGSC_1MSE, lnw->iotg.base + CI_OTGSC); + } +} + +/* Reduce timer count by 1, and find timeout conditions.*/ +static int langwell_otg_tick_timer(u32 *int_sts) +{ + struct langwell_otg *lnw = the_transceiver; + struct langwell_otg_timer *tmp_timer, *del_tmp; + int expired = 0; + + list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) { + tmp_timer->count--; + /* check if timer expires */ + if (!tmp_timer->count) { + list_del(&tmp_timer->list); + tmp_timer->function(tmp_timer->data); + expired = 1; + } + } + + if (list_empty(&active_timers)) { + dev_dbg(lnw->dev, "tick timer: disable 1ms int\n"); + *int_sts = *int_sts & ~OTGSC_1MSE; + } + return expired; +} + +static void reset_otg(void) +{ + struct langwell_otg *lnw = the_transceiver; + int delay_time = 1000; + u32 val; + + dev_dbg(lnw->dev, "reseting OTG controller ...\n"); + val = readl(lnw->iotg.base + CI_USBCMD); + writel(val | USBCMD_RST, lnw->iotg.base + CI_USBCMD); + do { + udelay(100); + if (!delay_time--) + dev_dbg(lnw->dev, "reset timeout\n"); + val = readl(lnw->iotg.base + CI_USBCMD); + val &= USBCMD_RST; + } while (val != 0); + dev_dbg(lnw->dev, "reset done.\n"); +} + +static void set_host_mode(void) +{ + struct langwell_otg *lnw = the_transceiver; + u32 val; + + reset_otg(); + val = readl(lnw->iotg.base + CI_USBMODE); + val = (val & (~USBMODE_CM)) | USBMODE_HOST; + writel(val, lnw->iotg.base + CI_USBMODE); +} + +static void set_client_mode(void) +{ + struct langwell_otg *lnw = the_transceiver; + u32 val; + + reset_otg(); + val = readl(lnw->iotg.base + CI_USBMODE); + val = (val & (~USBMODE_CM)) | USBMODE_DEVICE; + writel(val, lnw->iotg.base + CI_USBMODE); +} + +static void init_hsm(void) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + u32 val32; + + /* read OTGSC after reset */ + val32 = readl(lnw->iotg.base + CI_OTGSC); + dev_dbg(lnw->dev, "%s: OTGSC init value = 0x%x\n", __func__, val32); + + /* set init state */ + if (val32 & OTGSC_ID) { + iotg->hsm.id = 1; + iotg->otg.default_a = 0; + set_client_mode(); + iotg->otg.state = OTG_STATE_B_IDLE; + } else { + iotg->hsm.id = 0; + iotg->otg.default_a = 1; + set_host_mode(); + iotg->otg.state = OTG_STATE_A_IDLE; + } + + /* set session indicator */ + if (val32 & OTGSC_BSE) + iotg->hsm.b_sess_end = 1; + if (val32 & OTGSC_BSV) + iotg->hsm.b_sess_vld = 1; + if (val32 & OTGSC_ASV) + iotg->hsm.a_sess_vld = 1; + if (val32 & OTGSC_AVV) + iotg->hsm.a_vbus_vld = 1; + + /* defautly power the bus */ + iotg->hsm.a_bus_req = 1; + iotg->hsm.a_bus_drop = 0; + /* defautly don't request bus as B device */ + iotg->hsm.b_bus_req = 0; + /* no system error */ + iotg->hsm.a_clr_err = 0; + + langwell_otg_phy_low_power_wait(1); +} + +static void update_hsm(void) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + u32 val32; + + /* read OTGSC */ + val32 = readl(lnw->iotg.base + CI_OTGSC); + dev_dbg(lnw->dev, "%s: OTGSC value = 0x%x\n", __func__, val32); + + iotg->hsm.id = !!(val32 & OTGSC_ID); + iotg->hsm.b_sess_end = !!(val32 & OTGSC_BSE); + iotg->hsm.b_sess_vld = !!(val32 & OTGSC_BSV); + iotg->hsm.a_sess_vld = !!(val32 & OTGSC_ASV); + iotg->hsm.a_vbus_vld = !!(val32 & OTGSC_AVV); +} + +static irqreturn_t otg_dummy_irq(int irq, void *_dev) +{ + struct langwell_otg *lnw = the_transceiver; + void __iomem *reg_base = _dev; + u32 val; + u32 int_mask = 0; + + val = readl(reg_base + CI_USBMODE); + if ((val & USBMODE_CM) != USBMODE_DEVICE) + return IRQ_NONE; + + val = readl(reg_base + CI_USBSTS); + int_mask = val & INTR_DUMMY_MASK; + + if (int_mask == 0) + return IRQ_NONE; + + /* clear hsm.b_conn here since host driver can't detect it + * otg_dummy_irq called means B-disconnect happened. + */ + if (lnw->iotg.hsm.b_conn) { + lnw->iotg.hsm.b_conn = 0; + if (spin_trylock(&lnw->wq_lock)) { + langwell_update_transceiver(); + spin_unlock(&lnw->wq_lock); + } + } + + /* Clear interrupts */ + writel(int_mask, reg_base + CI_USBSTS); + return IRQ_HANDLED; +} + +static irqreturn_t otg_irq(int irq, void *_dev) +{ + struct langwell_otg *lnw = _dev; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + u32 int_sts, int_en; + u32 int_mask = 0; + int flag = 0; + + int_sts = readl(lnw->iotg.base + CI_OTGSC); + int_en = (int_sts & OTGSC_INTEN_MASK) >> 8; + int_mask = int_sts & int_en; + if (int_mask == 0) + return IRQ_NONE; + + if (int_mask & OTGSC_IDIS) { + dev_dbg(lnw->dev, "%s: id change int\n", __func__); + iotg->hsm.id = (int_sts & OTGSC_ID) ? 1 : 0; + dev_dbg(lnw->dev, "id = %d\n", iotg->hsm.id); + flag = 1; + } + if (int_mask & OTGSC_DPIS) { + dev_dbg(lnw->dev, "%s: data pulse int\n", __func__); + iotg->hsm.a_srp_det = (int_sts & OTGSC_DPS) ? 1 : 0; + dev_dbg(lnw->dev, "data pulse = %d\n", iotg->hsm.a_srp_det); + flag = 1; + } + if (int_mask & OTGSC_BSEIS) { + dev_dbg(lnw->dev, "%s: b session end int\n", __func__); + iotg->hsm.b_sess_end = (int_sts & OTGSC_BSE) ? 1 : 0; + dev_dbg(lnw->dev, "b_sess_end = %d\n", iotg->hsm.b_sess_end); + flag = 1; + } + if (int_mask & OTGSC_BSVIS) { + dev_dbg(lnw->dev, "%s: b session valid int\n", __func__); + iotg->hsm.b_sess_vld = (int_sts & OTGSC_BSV) ? 1 : 0; + dev_dbg(lnw->dev, "b_sess_vld = %d\n", iotg->hsm.b_sess_end); + flag = 1; + } + if (int_mask & OTGSC_ASVIS) { + dev_dbg(lnw->dev, "%s: a session valid int\n", __func__); + iotg->hsm.a_sess_vld = (int_sts & OTGSC_ASV) ? 1 : 0; + dev_dbg(lnw->dev, "a_sess_vld = %d\n", iotg->hsm.a_sess_vld); + flag = 1; + } + if (int_mask & OTGSC_AVVIS) { + dev_dbg(lnw->dev, "%s: a vbus valid int\n", __func__); + iotg->hsm.a_vbus_vld = (int_sts & OTGSC_AVV) ? 1 : 0; + dev_dbg(lnw->dev, "a_vbus_vld = %d\n", iotg->hsm.a_vbus_vld); + flag = 1; + } + + if (int_mask & OTGSC_1MSS) { + /* need to schedule otg_work if any timer is expired */ + if (langwell_otg_tick_timer(&int_sts)) + flag = 1; + } + + writel((int_sts & ~OTGSC_INTSTS_MASK) | int_mask, + lnw->iotg.base + CI_OTGSC); + if (flag) + langwell_update_transceiver(); + + return IRQ_HANDLED; +} + +static int langwell_otg_iotg_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = data; + int flag = 0; + + if (iotg == NULL) + return NOTIFY_BAD; + + if (lnw == NULL) + return NOTIFY_BAD; + + switch (action) { + case MID_OTG_NOTIFY_CONNECT: + dev_dbg(lnw->dev, "Lnw OTG Notify Connect Event\n"); + if (iotg->otg.default_a == 1) + iotg->hsm.b_conn = 1; + else + iotg->hsm.a_conn = 1; + flag = 1; + break; + case MID_OTG_NOTIFY_DISCONN: + dev_dbg(lnw->dev, "Lnw OTG Notify Disconnect Event\n"); + if (iotg->otg.default_a == 1) + iotg->hsm.b_conn = 0; + else + iotg->hsm.a_conn = 0; + flag = 1; + break; + case MID_OTG_NOTIFY_HSUSPEND: + dev_dbg(lnw->dev, "Lnw OTG Notify Host Bus suspend Event\n"); + if (iotg->otg.default_a == 1) + iotg->hsm.a_suspend_req = 1; + else + iotg->hsm.b_bus_req = 0; + flag = 1; + break; + case MID_OTG_NOTIFY_HRESUME: + dev_dbg(lnw->dev, "Lnw OTG Notify Host Bus resume Event\n"); + if (iotg->otg.default_a == 1) + iotg->hsm.b_bus_resume = 1; + flag = 1; + break; + case MID_OTG_NOTIFY_CSUSPEND: + dev_dbg(lnw->dev, "Lnw OTG Notify Client Bus suspend Event\n"); + if (iotg->otg.default_a == 1) { + if (iotg->hsm.b_bus_suspend_vld == 2) { + iotg->hsm.b_bus_suspend = 1; + iotg->hsm.b_bus_suspend_vld = 0; + flag = 1; + } else { + iotg->hsm.b_bus_suspend_vld++; + flag = 0; + } + } else { + if (iotg->hsm.a_bus_suspend == 0) { + iotg->hsm.a_bus_suspend = 1; + flag = 1; + } + } + break; + case MID_OTG_NOTIFY_CRESUME: + dev_dbg(lnw->dev, "Lnw OTG Notify Client Bus resume Event\n"); + if (iotg->otg.default_a == 0) + iotg->hsm.a_bus_suspend = 0; + flag = 0; + break; + case MID_OTG_NOTIFY_HOSTADD: + dev_dbg(lnw->dev, "Lnw OTG Nofity Host Driver Add\n"); + flag = 1; + break; + case MID_OTG_NOTIFY_HOSTREMOVE: + dev_dbg(lnw->dev, "Lnw OTG Nofity Host Driver remove\n"); + flag = 1; + break; + case MID_OTG_NOTIFY_CLIENTADD: + dev_dbg(lnw->dev, "Lnw OTG Nofity Client Driver Add\n"); + flag = 1; + break; + case MID_OTG_NOTIFY_CLIENTREMOVE: + dev_dbg(lnw->dev, "Lnw OTG Nofity Client Driver remove\n"); + flag = 1; + break; + default: + dev_dbg(lnw->dev, "Lnw OTG Nofity unknown notify message\n"); + return NOTIFY_DONE; + } + + if (flag) + langwell_update_transceiver(); + + return NOTIFY_OK; +} + +static void langwell_otg_work(struct work_struct *work) +{ + struct langwell_otg *lnw; + struct intel_mid_otg_xceiv *iotg; + int retval; + struct pci_dev *pdev; + + lnw = container_of(work, struct langwell_otg, work); + iotg = &lnw->iotg; + pdev = to_pci_dev(lnw->dev); + + dev_dbg(lnw->dev, "%s: old state = %s\n", __func__, + otg_state_string(iotg->otg.state)); + + switch (iotg->otg.state) { + case OTG_STATE_UNDEFINED: + case OTG_STATE_B_IDLE: + if (!iotg->hsm.id) { + langwell_otg_del_timer(b_srp_init_tmr); + del_timer_sync(&lnw->hsm_timer); + + iotg->otg.default_a = 1; + iotg->hsm.a_srp_det = 0; + + langwell_otg_chrg_vbus(0); + set_host_mode(); + langwell_otg_phy_low_power(1); + + iotg->otg.state = OTG_STATE_A_IDLE; + langwell_update_transceiver(); + } else if (iotg->hsm.b_sess_vld) { + langwell_otg_del_timer(b_srp_init_tmr); + del_timer_sync(&lnw->hsm_timer); + iotg->hsm.b_sess_end = 0; + iotg->hsm.a_bus_suspend = 0; + langwell_otg_chrg_vbus(0); + + if (lnw->iotg.start_peripheral) { + lnw->iotg.start_peripheral(&lnw->iotg); + iotg->otg.state = OTG_STATE_B_PERIPHERAL; + } else + dev_dbg(lnw->dev, "client driver not loaded\n"); + + } else if (iotg->hsm.b_srp_init_tmout) { + iotg->hsm.b_srp_init_tmout = 0; + dev_warn(lnw->dev, "SRP init timeout\n"); + } else if (iotg->hsm.b_srp_fail_tmout) { + iotg->hsm.b_srp_fail_tmout = 0; + iotg->hsm.b_bus_req = 0; + + /* No silence failure */ + langwell_otg_nsf_msg(6); + } else if (iotg->hsm.b_bus_req && iotg->hsm.b_sess_end) { + del_timer_sync(&lnw->hsm_timer); + /* workaround for b_se0_srp detection */ + retval = langwell_otg_check_se0_srp(0); + if (retval) { + iotg->hsm.b_bus_req = 0; + dev_dbg(lnw->dev, "LS isn't SE0, try later\n"); + } else { + /* clear the PHCD before start srp */ + langwell_otg_phy_low_power(0); + + /* Start SRP */ + langwell_otg_add_timer(b_srp_init_tmr); + iotg->otg.start_srp(&iotg->otg); + langwell_otg_del_timer(b_srp_init_tmr); + langwell_otg_add_ktimer(TB_SRP_FAIL_TMR); + + /* reset PHY low power mode here */ + langwell_otg_phy_low_power_wait(1); + } + } + break; + case OTG_STATE_B_SRP_INIT: + if (!iotg->hsm.id) { + iotg->otg.default_a = 1; + iotg->hsm.a_srp_det = 0; + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + langwell_otg_chrg_vbus(0); + set_host_mode(); + langwell_otg_phy_low_power(1); + iotg->otg.state = OTG_STATE_A_IDLE; + langwell_update_transceiver(); + } else if (iotg->hsm.b_sess_vld) { + langwell_otg_chrg_vbus(0); + if (lnw->iotg.start_peripheral) { + lnw->iotg.start_peripheral(&lnw->iotg); + iotg->otg.state = OTG_STATE_B_PERIPHERAL; + } else + dev_dbg(lnw->dev, "client driver not loaded\n"); + } + break; + case OTG_STATE_B_PERIPHERAL: + if (!iotg->hsm.id) { + iotg->otg.default_a = 1; + iotg->hsm.a_srp_det = 0; + + langwell_otg_chrg_vbus(0); + + if (lnw->iotg.stop_peripheral) + lnw->iotg.stop_peripheral(&lnw->iotg); + else + dev_dbg(lnw->dev, + "client driver has been removed.\n"); + + set_host_mode(); + langwell_otg_phy_low_power(1); + iotg->otg.state = OTG_STATE_A_IDLE; + langwell_update_transceiver(); + } else if (!iotg->hsm.b_sess_vld) { + iotg->hsm.b_hnp_enable = 0; + + if (lnw->iotg.stop_peripheral) + lnw->iotg.stop_peripheral(&lnw->iotg); + else + dev_dbg(lnw->dev, + "client driver has been removed.\n"); + + iotg->otg.state = OTG_STATE_B_IDLE; + } else if (iotg->hsm.b_bus_req && iotg->otg.gadget && + iotg->otg.gadget->b_hnp_enable && + iotg->hsm.a_bus_suspend) { + + if (lnw->iotg.stop_peripheral) + lnw->iotg.stop_peripheral(&lnw->iotg); + else + dev_dbg(lnw->dev, + "client driver has been removed.\n"); + + langwell_otg_HAAR(1); + iotg->hsm.a_conn = 0; + + if (lnw->iotg.start_host) { + lnw->iotg.start_host(&lnw->iotg); + iotg->otg.state = OTG_STATE_B_WAIT_ACON; + } else + dev_dbg(lnw->dev, + "host driver not loaded.\n"); + + iotg->hsm.a_bus_resume = 0; + langwell_otg_add_ktimer(TB_ASE0_BRST_TMR); + } + break; + + case OTG_STATE_B_WAIT_ACON: + if (!iotg->hsm.id) { + /* delete hsm timer for b_ase0_brst_tmr */ + del_timer_sync(&lnw->hsm_timer); + + iotg->otg.default_a = 1; + iotg->hsm.a_srp_det = 0; + + langwell_otg_chrg_vbus(0); + + langwell_otg_HAAR(0); + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + set_host_mode(); + langwell_otg_phy_low_power(1); + iotg->otg.state = OTG_STATE_A_IDLE; + langwell_update_transceiver(); + } else if (!iotg->hsm.b_sess_vld) { + /* delete hsm timer for b_ase0_brst_tmr */ + del_timer_sync(&lnw->hsm_timer); + + iotg->hsm.b_hnp_enable = 0; + iotg->hsm.b_bus_req = 0; + + langwell_otg_chrg_vbus(0); + langwell_otg_HAAR(0); + + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + set_client_mode(); + langwell_otg_phy_low_power(1); + iotg->otg.state = OTG_STATE_B_IDLE; + } else if (iotg->hsm.a_conn) { + /* delete hsm timer for b_ase0_brst_tmr */ + del_timer_sync(&lnw->hsm_timer); + + langwell_otg_HAAR(0); + iotg->otg.state = OTG_STATE_B_HOST; + langwell_update_transceiver(); + } else if (iotg->hsm.a_bus_resume || + iotg->hsm.b_ase0_brst_tmout) { + /* delete hsm timer for b_ase0_brst_tmr */ + del_timer_sync(&lnw->hsm_timer); + + langwell_otg_HAAR(0); + langwell_otg_nsf_msg(7); + + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + iotg->hsm.a_bus_suspend = 0; + iotg->hsm.b_bus_req = 0; + + if (lnw->iotg.start_peripheral) + lnw->iotg.start_peripheral(&lnw->iotg); + else + dev_dbg(lnw->dev, + "client driver not loaded.\n"); + + iotg->otg.state = OTG_STATE_B_PERIPHERAL; + } + break; + + case OTG_STATE_B_HOST: + if (!iotg->hsm.id) { + iotg->otg.default_a = 1; + iotg->hsm.a_srp_det = 0; + + langwell_otg_chrg_vbus(0); + + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + set_host_mode(); + langwell_otg_phy_low_power(1); + iotg->otg.state = OTG_STATE_A_IDLE; + langwell_update_transceiver(); + } else if (!iotg->hsm.b_sess_vld) { + iotg->hsm.b_hnp_enable = 0; + iotg->hsm.b_bus_req = 0; + + langwell_otg_chrg_vbus(0); + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + set_client_mode(); + langwell_otg_phy_low_power(1); + iotg->otg.state = OTG_STATE_B_IDLE; + } else if ((!iotg->hsm.b_bus_req) || + (!iotg->hsm.a_conn)) { + iotg->hsm.b_bus_req = 0; + langwell_otg_loc_sof(0); + + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + iotg->hsm.a_bus_suspend = 0; + + if (lnw->iotg.start_peripheral) + lnw->iotg.start_peripheral(&lnw->iotg); + else + dev_dbg(lnw->dev, + "client driver not loaded.\n"); + + iotg->otg.state = OTG_STATE_B_PERIPHERAL; + } + break; + + case OTG_STATE_A_IDLE: + iotg->otg.default_a = 1; + if (iotg->hsm.id) { + iotg->otg.default_a = 0; + iotg->hsm.b_bus_req = 0; + iotg->hsm.vbus_srp_up = 0; + + langwell_otg_chrg_vbus(0); + set_client_mode(); + langwell_otg_phy_low_power(1); + iotg->otg.state = OTG_STATE_B_IDLE; + langwell_update_transceiver(); + } else if (!iotg->hsm.a_bus_drop && + (iotg->hsm.a_srp_det || iotg->hsm.a_bus_req)) { + langwell_otg_phy_low_power(0); + + /* Turn on VBus */ + iotg->otg.set_vbus(&iotg->otg, true); + + iotg->hsm.vbus_srp_up = 0; + iotg->hsm.a_wait_vrise_tmout = 0; + langwell_otg_add_timer(a_wait_vrise_tmr); + iotg->otg.state = OTG_STATE_A_WAIT_VRISE; + langwell_update_transceiver(); + } else if (!iotg->hsm.a_bus_drop && iotg->hsm.a_sess_vld) { + iotg->hsm.vbus_srp_up = 1; + } else if (!iotg->hsm.a_sess_vld && iotg->hsm.vbus_srp_up) { + msleep(10); + langwell_otg_phy_low_power(0); + + /* Turn on VBus */ + iotg->otg.set_vbus(&iotg->otg, true); + iotg->hsm.a_srp_det = 1; + iotg->hsm.vbus_srp_up = 0; + iotg->hsm.a_wait_vrise_tmout = 0; + langwell_otg_add_timer(a_wait_vrise_tmr); + iotg->otg.state = OTG_STATE_A_WAIT_VRISE; + langwell_update_transceiver(); + } else if (!iotg->hsm.a_sess_vld && + !iotg->hsm.vbus_srp_up) { + langwell_otg_phy_low_power(1); + } + break; + case OTG_STATE_A_WAIT_VRISE: + if (iotg->hsm.id) { + langwell_otg_del_timer(a_wait_vrise_tmr); + iotg->hsm.b_bus_req = 0; + iotg->otg.default_a = 0; + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + set_client_mode(); + langwell_otg_phy_low_power_wait(1); + iotg->otg.state = OTG_STATE_B_IDLE; + } else if (iotg->hsm.a_vbus_vld) { + langwell_otg_del_timer(a_wait_vrise_tmr); + iotg->hsm.b_conn = 0; + if (lnw->iotg.start_host) + lnw->iotg.start_host(&lnw->iotg); + else { + dev_dbg(lnw->dev, "host driver not loaded.\n"); + break; + } + + langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); + iotg->otg.state = OTG_STATE_A_WAIT_BCON; + } else if (iotg->hsm.a_wait_vrise_tmout) { + iotg->hsm.b_conn = 0; + if (iotg->hsm.a_vbus_vld) { + if (lnw->iotg.start_host) + lnw->iotg.start_host(&lnw->iotg); + else { + dev_dbg(lnw->dev, + "host driver not loaded.\n"); + break; + } + langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); + iotg->otg.state = OTG_STATE_A_WAIT_BCON; + } else { + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + langwell_otg_phy_low_power_wait(1); + iotg->otg.state = OTG_STATE_A_VBUS_ERR; + } + } + break; + case OTG_STATE_A_WAIT_BCON: + if (iotg->hsm.id) { + /* delete hsm timer for a_wait_bcon_tmr */ + del_timer_sync(&lnw->hsm_timer); + + iotg->otg.default_a = 0; + iotg->hsm.b_bus_req = 0; + + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + set_client_mode(); + langwell_otg_phy_low_power_wait(1); + iotg->otg.state = OTG_STATE_B_IDLE; + langwell_update_transceiver(); + } else if (!iotg->hsm.a_vbus_vld) { + /* delete hsm timer for a_wait_bcon_tmr */ + del_timer_sync(&lnw->hsm_timer); + + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + langwell_otg_phy_low_power_wait(1); + iotg->otg.state = OTG_STATE_A_VBUS_ERR; + } else if (iotg->hsm.a_bus_drop || + (iotg->hsm.a_wait_bcon_tmout && + !iotg->hsm.a_bus_req)) { + /* delete hsm timer for a_wait_bcon_tmr */ + del_timer_sync(&lnw->hsm_timer); + + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + iotg->otg.state = OTG_STATE_A_WAIT_VFALL; + } else if (iotg->hsm.b_conn) { + /* delete hsm timer for a_wait_bcon_tmr */ + del_timer_sync(&lnw->hsm_timer); + + iotg->hsm.a_suspend_req = 0; + iotg->otg.state = OTG_STATE_A_HOST; + if (iotg->hsm.a_srp_det && iotg->otg.host && + !iotg->otg.host->b_hnp_enable) { + /* SRP capable peripheral-only device */ + iotg->hsm.a_bus_req = 1; + iotg->hsm.a_srp_det = 0; + } else if (!iotg->hsm.a_bus_req && iotg->otg.host && + iotg->otg.host->b_hnp_enable) { + /* It is not safe enough to do a fast + * transition from A_WAIT_BCON to + * A_SUSPEND */ + msleep(10000); + if (iotg->hsm.a_bus_req) + break; + + if (request_irq(pdev->irq, + otg_dummy_irq, IRQF_SHARED, + driver_name, iotg->base) != 0) { + dev_dbg(lnw->dev, + "request interrupt %d fail\n", + pdev->irq); + } + + langwell_otg_HABA(1); + iotg->hsm.b_bus_resume = 0; + iotg->hsm.a_aidl_bdis_tmout = 0; + + langwell_otg_loc_sof(0); + /* clear PHCD to enable HW timer */ + langwell_otg_phy_low_power(0); + langwell_otg_add_timer(a_aidl_bdis_tmr); + iotg->otg.state = OTG_STATE_A_SUSPEND; + } else if (!iotg->hsm.a_bus_req && iotg->otg.host && + !iotg->otg.host->b_hnp_enable) { + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + iotg->otg.state = OTG_STATE_A_WAIT_VFALL; + } + } + break; + case OTG_STATE_A_HOST: + if (iotg->hsm.id) { + iotg->otg.default_a = 0; + iotg->hsm.b_bus_req = 0; + + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + set_client_mode(); + langwell_otg_phy_low_power_wait(1); + iotg->otg.state = OTG_STATE_B_IDLE; + langwell_update_transceiver(); + } else if (iotg->hsm.a_bus_drop || + (iotg->otg.host && + !iotg->otg.host->b_hnp_enable && + !iotg->hsm.a_bus_req)) { + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + iotg->otg.state = OTG_STATE_A_WAIT_VFALL; + } else if (!iotg->hsm.a_vbus_vld) { + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + langwell_otg_phy_low_power_wait(1); + iotg->otg.state = OTG_STATE_A_VBUS_ERR; + } else if (iotg->otg.host && + iotg->otg.host->b_hnp_enable && + !iotg->hsm.a_bus_req) { + /* Set HABA to enable hardware assistance to signal + * A-connect after receiver B-disconnect. Hardware + * will then set client mode and enable URE, SLE and + * PCE after the assistance. otg_dummy_irq is used to + * clean these ints when client driver is not resumed. + */ + if (request_irq(pdev->irq, otg_dummy_irq, IRQF_SHARED, + driver_name, iotg->base) != 0) { + dev_dbg(lnw->dev, + "request interrupt %d failed\n", + pdev->irq); + } + + /* set HABA */ + langwell_otg_HABA(1); + iotg->hsm.b_bus_resume = 0; + iotg->hsm.a_aidl_bdis_tmout = 0; + langwell_otg_loc_sof(0); + /* clear PHCD to enable HW timer */ + langwell_otg_phy_low_power(0); + langwell_otg_add_timer(a_aidl_bdis_tmr); + iotg->otg.state = OTG_STATE_A_SUSPEND; + } else if (!iotg->hsm.b_conn || !iotg->hsm.a_bus_req) { + langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); + iotg->otg.state = OTG_STATE_A_WAIT_BCON; + } + break; + case OTG_STATE_A_SUSPEND: + if (iotg->hsm.id) { + langwell_otg_del_timer(a_aidl_bdis_tmr); + langwell_otg_HABA(0); + free_irq(pdev->irq, iotg->base); + iotg->otg.default_a = 0; + iotg->hsm.b_bus_req = 0; + + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + set_client_mode(); + langwell_otg_phy_low_power(1); + iotg->otg.state = OTG_STATE_B_IDLE; + langwell_update_transceiver(); + } else if (iotg->hsm.a_bus_req || + iotg->hsm.b_bus_resume) { + langwell_otg_del_timer(a_aidl_bdis_tmr); + langwell_otg_HABA(0); + free_irq(pdev->irq, iotg->base); + iotg->hsm.a_suspend_req = 0; + langwell_otg_loc_sof(1); + iotg->otg.state = OTG_STATE_A_HOST; + } else if (iotg->hsm.a_aidl_bdis_tmout || + iotg->hsm.a_bus_drop) { + langwell_otg_del_timer(a_aidl_bdis_tmr); + langwell_otg_HABA(0); + free_irq(pdev->irq, iotg->base); + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + iotg->otg.state = OTG_STATE_A_WAIT_VFALL; + } else if (!iotg->hsm.b_conn && iotg->otg.host && + iotg->otg.host->b_hnp_enable) { + langwell_otg_del_timer(a_aidl_bdis_tmr); + langwell_otg_HABA(0); + free_irq(pdev->irq, iotg->base); + + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + iotg->hsm.b_bus_suspend = 0; + iotg->hsm.b_bus_suspend_vld = 0; + + /* msleep(200); */ + if (lnw->iotg.start_peripheral) + lnw->iotg.start_peripheral(&lnw->iotg); + else + dev_dbg(lnw->dev, + "client driver not loaded.\n"); + + langwell_otg_add_ktimer(TB_BUS_SUSPEND_TMR); + iotg->otg.state = OTG_STATE_A_PERIPHERAL; + break; + } else if (!iotg->hsm.a_vbus_vld) { + langwell_otg_del_timer(a_aidl_bdis_tmr); + langwell_otg_HABA(0); + free_irq(pdev->irq, iotg->base); + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + langwell_otg_phy_low_power_wait(1); + iotg->otg.state = OTG_STATE_A_VBUS_ERR; + } + break; + case OTG_STATE_A_PERIPHERAL: + if (iotg->hsm.id) { + /* delete hsm timer for b_bus_suspend_tmr */ + del_timer_sync(&lnw->hsm_timer); + iotg->otg.default_a = 0; + iotg->hsm.b_bus_req = 0; + if (lnw->iotg.stop_peripheral) + lnw->iotg.stop_peripheral(&lnw->iotg); + else + dev_dbg(lnw->dev, + "client driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + set_client_mode(); + langwell_otg_phy_low_power_wait(1); + iotg->otg.state = OTG_STATE_B_IDLE; + langwell_update_transceiver(); + } else if (!iotg->hsm.a_vbus_vld) { + /* delete hsm timer for b_bus_suspend_tmr */ + del_timer_sync(&lnw->hsm_timer); + + if (lnw->iotg.stop_peripheral) + lnw->iotg.stop_peripheral(&lnw->iotg); + else + dev_dbg(lnw->dev, + "client driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + langwell_otg_phy_low_power_wait(1); + iotg->otg.state = OTG_STATE_A_VBUS_ERR; + } else if (iotg->hsm.a_bus_drop) { + /* delete hsm timer for b_bus_suspend_tmr */ + del_timer_sync(&lnw->hsm_timer); + + if (lnw->iotg.stop_peripheral) + lnw->iotg.stop_peripheral(&lnw->iotg); + else + dev_dbg(lnw->dev, + "client driver has been removed.\n"); + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + iotg->otg.state = OTG_STATE_A_WAIT_VFALL; + } else if (iotg->hsm.b_bus_suspend) { + /* delete hsm timer for b_bus_suspend_tmr */ + del_timer_sync(&lnw->hsm_timer); + + if (lnw->iotg.stop_peripheral) + lnw->iotg.stop_peripheral(&lnw->iotg); + else + dev_dbg(lnw->dev, + "client driver has been removed.\n"); + + if (lnw->iotg.start_host) + lnw->iotg.start_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver not loaded.\n"); + langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); + iotg->otg.state = OTG_STATE_A_WAIT_BCON; + } else if (iotg->hsm.b_bus_suspend_tmout) { + u32 val; + val = readl(lnw->iotg.base + CI_PORTSC1); + if (!(val & PORTSC_SUSP)) + break; + + if (lnw->iotg.stop_peripheral) + lnw->iotg.stop_peripheral(&lnw->iotg); + else + dev_dbg(lnw->dev, + "client driver has been removed.\n"); + + if (lnw->iotg.start_host) + lnw->iotg.start_host(&lnw->iotg); + else + dev_dbg(lnw->dev, + "host driver not loaded.\n"); + langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); + iotg->otg.state = OTG_STATE_A_WAIT_BCON; + } + break; + case OTG_STATE_A_VBUS_ERR: + if (iotg->hsm.id) { + iotg->otg.default_a = 0; + iotg->hsm.a_clr_err = 0; + iotg->hsm.a_srp_det = 0; + set_client_mode(); + langwell_otg_phy_low_power(1); + iotg->otg.state = OTG_STATE_B_IDLE; + langwell_update_transceiver(); + } else if (iotg->hsm.a_clr_err) { + iotg->hsm.a_clr_err = 0; + iotg->hsm.a_srp_det = 0; + reset_otg(); + init_hsm(); + if (iotg->otg.state == OTG_STATE_A_IDLE) + langwell_update_transceiver(); + } else { + /* FW will clear PHCD bit when any VBus + * event detected. Reset PHCD to 1 again */ + langwell_otg_phy_low_power(1); + } + break; + case OTG_STATE_A_WAIT_VFALL: + if (iotg->hsm.id) { + iotg->otg.default_a = 0; + set_client_mode(); + langwell_otg_phy_low_power(1); + iotg->otg.state = OTG_STATE_B_IDLE; + langwell_update_transceiver(); + } else if (iotg->hsm.a_bus_req) { + + /* Turn on VBus */ + iotg->otg.set_vbus(&iotg->otg, true); + iotg->hsm.a_wait_vrise_tmout = 0; + langwell_otg_add_timer(a_wait_vrise_tmr); + iotg->otg.state = OTG_STATE_A_WAIT_VRISE; + } else if (!iotg->hsm.a_sess_vld) { + iotg->hsm.a_srp_det = 0; + set_host_mode(); + langwell_otg_phy_low_power(1); + iotg->otg.state = OTG_STATE_A_IDLE; + } + break; + default: + ; + } + + dev_dbg(lnw->dev, "%s: new state = %s\n", __func__, + otg_state_string(iotg->otg.state)); +} + +static ssize_t +show_registers(struct device *_dev, struct device_attribute *attr, char *buf) +{ + struct langwell_otg *lnw = the_transceiver; + char *next; + unsigned size, t; + + next = buf; + size = PAGE_SIZE; + + t = scnprintf(next, size, + "\n" + "USBCMD = 0x%08x\n" + "USBSTS = 0x%08x\n" + "USBINTR = 0x%08x\n" + "ASYNCLISTADDR = 0x%08x\n" + "PORTSC1 = 0x%08x\n" + "HOSTPC1 = 0x%08x\n" + "OTGSC = 0x%08x\n" + "USBMODE = 0x%08x\n", + readl(lnw->iotg.base + 0x30), + readl(lnw->iotg.base + 0x34), + readl(lnw->iotg.base + 0x38), + readl(lnw->iotg.base + 0x48), + readl(lnw->iotg.base + 0x74), + readl(lnw->iotg.base + 0xb4), + readl(lnw->iotg.base + 0xf4), + readl(lnw->iotg.base + 0xf8) + ); + size -= t; + next += t; + + return PAGE_SIZE - size; +} +static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL); + +static ssize_t +show_hsm(struct device *_dev, struct device_attribute *attr, char *buf) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + char *next; + unsigned size, t; + + next = buf; + size = PAGE_SIZE; + + if (iotg->otg.host) + iotg->hsm.a_set_b_hnp_en = iotg->otg.host->b_hnp_enable; + + if (iotg->otg.gadget) + iotg->hsm.b_hnp_enable = iotg->otg.gadget->b_hnp_enable; + + t = scnprintf(next, size, + "\n" + "current state = %s\n" + "a_bus_resume = \t%d\n" + "a_bus_suspend = \t%d\n" + "a_conn = \t%d\n" + "a_sess_vld = \t%d\n" + "a_srp_det = \t%d\n" + "a_vbus_vld = \t%d\n" + "b_bus_resume = \t%d\n" + "b_bus_suspend = \t%d\n" + "b_conn = \t%d\n" + "b_se0_srp = \t%d\n" + "b_sess_end = \t%d\n" + "b_sess_vld = \t%d\n" + "id = \t%d\n" + "a_set_b_hnp_en = \t%d\n" + "b_srp_done = \t%d\n" + "b_hnp_enable = \t%d\n" + "a_wait_vrise_tmout = \t%d\n" + "a_wait_bcon_tmout = \t%d\n" + "a_aidl_bdis_tmout = \t%d\n" + "b_ase0_brst_tmout = \t%d\n" + "a_bus_drop = \t%d\n" + "a_bus_req = \t%d\n" + "a_clr_err = \t%d\n" + "a_suspend_req = \t%d\n" + "b_bus_req = \t%d\n" + "b_bus_suspend_tmout = \t%d\n" + "b_bus_suspend_vld = \t%d\n", + otg_state_string(iotg->otg.state), + iotg->hsm.a_bus_resume, + iotg->hsm.a_bus_suspend, + iotg->hsm.a_conn, + iotg->hsm.a_sess_vld, + iotg->hsm.a_srp_det, + iotg->hsm.a_vbus_vld, + iotg->hsm.b_bus_resume, + iotg->hsm.b_bus_suspend, + iotg->hsm.b_conn, + iotg->hsm.b_se0_srp, + iotg->hsm.b_sess_end, + iotg->hsm.b_sess_vld, + iotg->hsm.id, + iotg->hsm.a_set_b_hnp_en, + iotg->hsm.b_srp_done, + iotg->hsm.b_hnp_enable, + iotg->hsm.a_wait_vrise_tmout, + iotg->hsm.a_wait_bcon_tmout, + iotg->hsm.a_aidl_bdis_tmout, + iotg->hsm.b_ase0_brst_tmout, + iotg->hsm.a_bus_drop, + iotg->hsm.a_bus_req, + iotg->hsm.a_clr_err, + iotg->hsm.a_suspend_req, + iotg->hsm.b_bus_req, + iotg->hsm.b_bus_suspend_tmout, + iotg->hsm.b_bus_suspend_vld + ); + size -= t; + next += t; + + return PAGE_SIZE - size; +} +static DEVICE_ATTR(hsm, S_IRUGO, show_hsm, NULL); + +static ssize_t +get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct langwell_otg *lnw = the_transceiver; + char *next; + unsigned size, t; + + next = buf; + size = PAGE_SIZE; + + t = scnprintf(next, size, "%d", lnw->iotg.hsm.a_bus_req); + size -= t; + next += t; + + return PAGE_SIZE - size; +} + +static ssize_t +set_a_bus_req(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + + if (!iotg->otg.default_a) + return -1; + if (count > 2) + return -1; + + if (buf[0] == '0') { + iotg->hsm.a_bus_req = 0; + dev_dbg(lnw->dev, "User request: a_bus_req = 0\n"); + } else if (buf[0] == '1') { + /* If a_bus_drop is TRUE, a_bus_req can't be set */ + if (iotg->hsm.a_bus_drop) + return -1; + iotg->hsm.a_bus_req = 1; + dev_dbg(lnw->dev, "User request: a_bus_req = 1\n"); + } + if (spin_trylock(&lnw->wq_lock)) { + langwell_update_transceiver(); + spin_unlock(&lnw->wq_lock); + } + return count; +} +static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUSR, get_a_bus_req, set_a_bus_req); + +static ssize_t +get_a_bus_drop(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct langwell_otg *lnw = the_transceiver; + char *next; + unsigned size, t; + + next = buf; + size = PAGE_SIZE; + + t = scnprintf(next, size, "%d", lnw->iotg.hsm.a_bus_drop); + size -= t; + next += t; + + return PAGE_SIZE - size; +} + +static ssize_t +set_a_bus_drop(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + + if (!iotg->otg.default_a) + return -1; + if (count > 2) + return -1; + + if (buf[0] == '0') { + iotg->hsm.a_bus_drop = 0; + dev_dbg(lnw->dev, "User request: a_bus_drop = 0\n"); + } else if (buf[0] == '1') { + iotg->hsm.a_bus_drop = 1; + iotg->hsm.a_bus_req = 0; + dev_dbg(lnw->dev, "User request: a_bus_drop = 1\n"); + dev_dbg(lnw->dev, "User request: and a_bus_req = 0\n"); + } + if (spin_trylock(&lnw->wq_lock)) { + langwell_update_transceiver(); + spin_unlock(&lnw->wq_lock); + } + return count; +} +static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUSR, get_a_bus_drop, set_a_bus_drop); + +static ssize_t +get_b_bus_req(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct langwell_otg *lnw = the_transceiver; + char *next; + unsigned size, t; + + next = buf; + size = PAGE_SIZE; + + t = scnprintf(next, size, "%d", lnw->iotg.hsm.b_bus_req); + size -= t; + next += t; + + return PAGE_SIZE - size; +} + +static ssize_t +set_b_bus_req(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + + if (iotg->otg.default_a) + return -1; + + if (count > 2) + return -1; + + if (buf[0] == '0') { + iotg->hsm.b_bus_req = 0; + dev_dbg(lnw->dev, "User request: b_bus_req = 0\n"); + } else if (buf[0] == '1') { + iotg->hsm.b_bus_req = 1; + dev_dbg(lnw->dev, "User request: b_bus_req = 1\n"); + } + if (spin_trylock(&lnw->wq_lock)) { + langwell_update_transceiver(); + spin_unlock(&lnw->wq_lock); + } + return count; +} +static DEVICE_ATTR(b_bus_req, S_IRUGO | S_IWUSR, get_b_bus_req, set_b_bus_req); + +static ssize_t +set_a_clr_err(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + + if (!iotg->otg.default_a) + return -1; + if (count > 2) + return -1; + + if (buf[0] == '1') { + iotg->hsm.a_clr_err = 1; + dev_dbg(lnw->dev, "User request: a_clr_err = 1\n"); + } + if (spin_trylock(&lnw->wq_lock)) { + langwell_update_transceiver(); + spin_unlock(&lnw->wq_lock); + } + return count; +} +static DEVICE_ATTR(a_clr_err, S_IWUSR, NULL, set_a_clr_err); + +static struct attribute *inputs_attrs[] = { + &dev_attr_a_bus_req.attr, + &dev_attr_a_bus_drop.attr, + &dev_attr_b_bus_req.attr, + &dev_attr_a_clr_err.attr, + NULL, +}; + +static struct attribute_group debug_dev_attr_group = { + .name = "inputs", + .attrs = inputs_attrs, +}; + +static int langwell_otg_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + unsigned long resource, len; + void __iomem *base = NULL; + int retval; + u32 val32; + struct langwell_otg *lnw; + char qname[] = "langwell_otg_queue"; + + retval = 0; + dev_dbg(&pdev->dev, "\notg controller is detected.\n"); + if (pci_enable_device(pdev) < 0) { + retval = -ENODEV; + goto done; + } + + lnw = kzalloc(sizeof *lnw, GFP_KERNEL); + if (lnw == NULL) { + retval = -ENOMEM; + goto done; + } + the_transceiver = lnw; + + /* control register: BAR 0 */ + resource = pci_resource_start(pdev, 0); + len = pci_resource_len(pdev, 0); + if (!request_mem_region(resource, len, driver_name)) { + retval = -EBUSY; + goto err; + } + lnw->region = 1; + + base = ioremap_nocache(resource, len); + if (base == NULL) { + retval = -EFAULT; + goto err; + } + lnw->iotg.base = base; + + if (!request_mem_region(USBCFG_ADDR, USBCFG_LEN, driver_name)) { + retval = -EBUSY; + goto err; + } + lnw->cfg_region = 1; + + /* For the SCCB.USBCFG register */ + base = ioremap_nocache(USBCFG_ADDR, USBCFG_LEN); + if (base == NULL) { + retval = -EFAULT; + goto err; + } + lnw->usbcfg = base; + + if (!pdev->irq) { + dev_dbg(&pdev->dev, "No IRQ.\n"); + retval = -ENODEV; + goto err; + } + + lnw->qwork = create_singlethread_workqueue(qname); + if (!lnw->qwork) { + dev_dbg(&pdev->dev, "cannot create workqueue %s\n", qname); + retval = -ENOMEM; + goto err; + } + INIT_WORK(&lnw->work, langwell_otg_work); + + /* OTG common part */ + lnw->dev = &pdev->dev; + lnw->iotg.otg.dev = lnw->dev; + lnw->iotg.otg.label = driver_name; + lnw->iotg.otg.set_host = langwell_otg_set_host; + lnw->iotg.otg.set_peripheral = langwell_otg_set_peripheral; + lnw->iotg.otg.set_power = langwell_otg_set_power; + lnw->iotg.otg.set_vbus = langwell_otg_set_vbus; + lnw->iotg.otg.start_srp = langwell_otg_start_srp; + lnw->iotg.otg.state = OTG_STATE_UNDEFINED; + + if (otg_set_transceiver(&lnw->iotg.otg)) { + dev_dbg(lnw->dev, "can't set transceiver\n"); + retval = -EBUSY; + goto err; + } + + reset_otg(); + init_hsm(); + + spin_lock_init(&lnw->lock); + spin_lock_init(&lnw->wq_lock); + INIT_LIST_HEAD(&active_timers); + retval = langwell_otg_init_timers(&lnw->iotg.hsm); + if (retval) { + dev_dbg(&pdev->dev, "Failed to init timers\n"); + goto err; + } + + init_timer(&lnw->hsm_timer); + ATOMIC_INIT_NOTIFIER_HEAD(&lnw->iotg.iotg_notifier); + + lnw->iotg_notifier.notifier_call = langwell_otg_iotg_notify; + + retval = intel_mid_otg_register_notifier(&lnw->iotg, + &lnw->iotg_notifier); + if (retval) { + dev_dbg(lnw->dev, "Failed to register notifier\n"); + goto err; + } + + if (request_irq(pdev->irq, otg_irq, IRQF_SHARED, + driver_name, lnw) != 0) { + dev_dbg(lnw->dev, "request interrupt %d failed\n", pdev->irq); + retval = -EBUSY; + goto err; + } + + /* enable OTGSC int */ + val32 = OTGSC_DPIE | OTGSC_BSEIE | OTGSC_BSVIE | + OTGSC_ASVIE | OTGSC_AVVIE | OTGSC_IDIE | OTGSC_IDPU; + writel(val32, lnw->iotg.base + CI_OTGSC); + + retval = device_create_file(&pdev->dev, &dev_attr_registers); + if (retval < 0) { + dev_dbg(lnw->dev, + "Can't register sysfs attribute: %d\n", retval); + goto err; + } + + retval = device_create_file(&pdev->dev, &dev_attr_hsm); + if (retval < 0) { + dev_dbg(lnw->dev, "Can't hsm sysfs attribute: %d\n", retval); + goto err; + } + + retval = sysfs_create_group(&pdev->dev.kobj, &debug_dev_attr_group); + if (retval < 0) { + dev_dbg(lnw->dev, + "Can't register sysfs attr group: %d\n", retval); + goto err; + } + + if (lnw->iotg.otg.state == OTG_STATE_A_IDLE) + langwell_update_transceiver(); + + return 0; + +err: + if (the_transceiver) + langwell_otg_remove(pdev); +done: + return retval; +} + +static void langwell_otg_remove(struct pci_dev *pdev) +{ + struct langwell_otg *lnw = the_transceiver; + + if (lnw->qwork) { + flush_workqueue(lnw->qwork); + destroy_workqueue(lnw->qwork); + } + intel_mid_otg_unregister_notifier(&lnw->iotg, &lnw->iotg_notifier); + langwell_otg_free_timers(); + + /* disable OTGSC interrupt as OTGSC doesn't change in reset */ + writel(0, lnw->iotg.base + CI_OTGSC); + + if (pdev->irq) + free_irq(pdev->irq, lnw); + if (lnw->usbcfg) + iounmap(lnw->usbcfg); + if (lnw->cfg_region) + release_mem_region(USBCFG_ADDR, USBCFG_LEN); + if (lnw->iotg.base) + iounmap(lnw->iotg.base); + if (lnw->region) + release_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + + otg_set_transceiver(NULL); + pci_disable_device(pdev); + sysfs_remove_group(&pdev->dev.kobj, &debug_dev_attr_group); + device_remove_file(&pdev->dev, &dev_attr_hsm); + device_remove_file(&pdev->dev, &dev_attr_registers); + kfree(lnw); + lnw = NULL; +} + +static void transceiver_suspend(struct pci_dev *pdev) +{ + pci_save_state(pdev); + pci_set_power_state(pdev, PCI_D3hot); + langwell_otg_phy_low_power(1); +} + +static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message) +{ + struct langwell_otg *lnw = the_transceiver; + struct intel_mid_otg_xceiv *iotg = &lnw->iotg; + int ret = 0; + + /* Disbale OTG interrupts */ + langwell_otg_intr(0); + + if (pdev->irq) + free_irq(pdev->irq, lnw); + + /* Prevent more otg_work */ + flush_workqueue(lnw->qwork); + destroy_workqueue(lnw->qwork); + lnw->qwork = NULL; + + /* start actions */ + switch (iotg->otg.state) { + case OTG_STATE_A_WAIT_VFALL: + iotg->otg.state = OTG_STATE_A_IDLE; + case OTG_STATE_A_IDLE: + case OTG_STATE_B_IDLE: + case OTG_STATE_A_VBUS_ERR: + transceiver_suspend(pdev); + break; + case OTG_STATE_A_WAIT_VRISE: + langwell_otg_del_timer(a_wait_vrise_tmr); + iotg->hsm.a_srp_det = 0; + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + iotg->otg.state = OTG_STATE_A_IDLE; + transceiver_suspend(pdev); + break; + case OTG_STATE_A_WAIT_BCON: + del_timer_sync(&lnw->hsm_timer); + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(&pdev->dev, "host driver has been removed.\n"); + + iotg->hsm.a_srp_det = 0; + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + iotg->otg.state = OTG_STATE_A_IDLE; + transceiver_suspend(pdev); + break; + case OTG_STATE_A_HOST: + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(&pdev->dev, "host driver has been removed.\n"); + + iotg->hsm.a_srp_det = 0; + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + + iotg->otg.state = OTG_STATE_A_IDLE; + transceiver_suspend(pdev); + break; + case OTG_STATE_A_SUSPEND: + langwell_otg_del_timer(a_aidl_bdis_tmr); + langwell_otg_HABA(0); + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(lnw->dev, "host driver has been removed.\n"); + iotg->hsm.a_srp_det = 0; + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + iotg->otg.state = OTG_STATE_A_IDLE; + transceiver_suspend(pdev); + break; + case OTG_STATE_A_PERIPHERAL: + del_timer_sync(&lnw->hsm_timer); + + if (lnw->iotg.stop_peripheral) + lnw->iotg.stop_peripheral(&lnw->iotg); + else + dev_dbg(&pdev->dev, + "client driver has been removed.\n"); + iotg->hsm.a_srp_det = 0; + + /* Turn off VBus */ + iotg->otg.set_vbus(&iotg->otg, false); + iotg->otg.state = OTG_STATE_A_IDLE; + transceiver_suspend(pdev); + break; + case OTG_STATE_B_HOST: + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(&pdev->dev, "host driver has been removed.\n"); + iotg->hsm.b_bus_req = 0; + iotg->otg.state = OTG_STATE_B_IDLE; + transceiver_suspend(pdev); + break; + case OTG_STATE_B_PERIPHERAL: + if (lnw->iotg.stop_peripheral) + lnw->iotg.stop_peripheral(&lnw->iotg); + else + dev_dbg(&pdev->dev, + "client driver has been removed.\n"); + iotg->otg.state = OTG_STATE_B_IDLE; + transceiver_suspend(pdev); + break; + case OTG_STATE_B_WAIT_ACON: + /* delete hsm timer for b_ase0_brst_tmr */ + del_timer_sync(&lnw->hsm_timer); + + langwell_otg_HAAR(0); + + if (lnw->iotg.stop_host) + lnw->iotg.stop_host(&lnw->iotg); + else + dev_dbg(&pdev->dev, "host driver has been removed.\n"); + iotg->hsm.b_bus_req = 0; + iotg->otg.state = OTG_STATE_B_IDLE; + transceiver_suspend(pdev); + break; + default: + dev_dbg(lnw->dev, "error state before suspend\n"); + break; + } + + return ret; +} + +static void transceiver_resume(struct pci_dev *pdev) +{ + pci_restore_state(pdev); + pci_set_power_state(pdev, PCI_D0); +} + +static int langwell_otg_resume(struct pci_dev *pdev) +{ + struct langwell_otg *lnw = the_transceiver; + int ret = 0; + + transceiver_resume(pdev); + + lnw->qwork = create_singlethread_workqueue("langwell_otg_queue"); + if (!lnw->qwork) { + dev_dbg(&pdev->dev, "cannot create langwell otg workqueuen"); + ret = -ENOMEM; + goto error; + } + + if (request_irq(pdev->irq, otg_irq, IRQF_SHARED, + driver_name, lnw) != 0) { + dev_dbg(&pdev->dev, "request interrupt %d failed\n", pdev->irq); + ret = -EBUSY; + goto error; + } + + /* enable OTG interrupts */ + langwell_otg_intr(1); + + update_hsm(); + + langwell_update_transceiver(); + + return ret; +error: + langwell_otg_intr(0); + transceiver_suspend(pdev); + return ret; +} + +static int __init langwell_otg_init(void) +{ + return pci_register_driver(&otg_pci_driver); +} +module_init(langwell_otg_init); + +static void __exit langwell_otg_cleanup(void) +{ + pci_unregister_driver(&otg_pci_driver); +} +module_exit(langwell_otg_cleanup); diff --git a/trunk/drivers/usb/otg/mv_otg.c b/trunk/drivers/usb/otg/mv_otg.c index b5fbe1452ab0..db0d4fcdc8e2 100644 --- a/trunk/drivers/usb/otg/mv_otg.c +++ b/trunk/drivers/usb/otg/mv_otg.c @@ -202,7 +202,6 @@ static void mv_otg_init_irq(struct mv_otg *mvotg) static void mv_otg_start_host(struct mv_otg *mvotg, int on) { -#ifdef CONFIG_USB struct otg_transceiver *otg = &mvotg->otg; struct usb_hcd *hcd; @@ -217,7 +216,6 @@ static void mv_otg_start_host(struct mv_otg *mvotg, int on) usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); else usb_remove_hcd(hcd); -#endif /* CONFIG_USB */ } static void mv_otg_start_periphrals(struct mv_otg *mvotg, int on) diff --git a/trunk/drivers/usb/renesas_usbhs/mod_gadget.c b/trunk/drivers/usb/renesas_usbhs/mod_gadget.c index 7542aa94a462..528691d5f3e2 100644 --- a/trunk/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/trunk/drivers/usb/renesas_usbhs/mod_gadget.c @@ -425,7 +425,7 @@ static int usbhsg_recip_run_handle(struct usbhs_priv *priv, struct usbhs_pipe *pipe; int recip = ctrl->bRequestType & USB_RECIP_MASK; int nth = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK; - int ret = 0; + int ret; int (*func)(struct usbhs_priv *priv, struct usbhsg_uep *uep, struct usb_ctrlrequest *ctrl); char *msg; diff --git a/trunk/drivers/usb/serial/cp210x.c b/trunk/drivers/usb/serial/cp210x.c index 8dbf51a43c45..fba1147ed916 100644 --- a/trunk/drivers/usb/serial/cp210x.c +++ b/trunk/drivers/usb/serial/cp210x.c @@ -39,8 +39,6 @@ static void cp210x_get_termios(struct tty_struct *, struct usb_serial_port *port); static void cp210x_get_termios_port(struct usb_serial_port *port, unsigned int *cflagp, unsigned int *baudp); -static void cp210x_change_speed(struct tty_struct *, struct usb_serial_port *, - struct ktermios *); static void cp210x_set_termios(struct tty_struct *, struct usb_serial_port *, struct ktermios*); static int cp210x_tiocmget(struct tty_struct *); @@ -140,7 +138,6 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */ { USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */ { USB_DEVICE(0x1BE3, 0x07A6) }, /* WAGO 750-923 USB Service Cable */ - { USB_DEVICE(0x3195, 0xF190) }, /* Link Instruments MSO-19 */ { USB_DEVICE(0x413C, 0x9500) }, /* DW700 GPS USB interface */ { } /* Terminating Entry */ }; @@ -204,8 +201,6 @@ static struct usb_serial_driver cp210x_device = { #define CP210X_EMBED_EVENTS 0x15 #define CP210X_GET_EVENTSTATE 0x16 #define CP210X_SET_CHARS 0x19 -#define CP210X_GET_BAUDRATE 0x1D -#define CP210X_SET_BAUDRATE 0x1E /* CP210X_IFC_ENABLE */ #define UART_ENABLE 0x0001 @@ -365,8 +360,8 @@ static inline int cp210x_set_config_single(struct usb_serial_port *port, * Quantises the baud rate as per AN205 Table 1 */ static unsigned int cp210x_quantise_baudrate(unsigned int baud) { - if (baud <= 300) - baud = 300; + if (baud <= 56) baud = 0; + else if (baud <= 300) baud = 300; else if (baud <= 600) baud = 600; else if (baud <= 1200) baud = 1200; else if (baud <= 1800) baud = 1800; @@ -394,10 +389,10 @@ static unsigned int cp210x_quantise_baudrate(unsigned int baud) { else if (baud <= 491520) baud = 460800; else if (baud <= 567138) baud = 500000; else if (baud <= 670254) baud = 576000; - else if (baud < 1000000) - baud = 921600; - else if (baud > 2000000) - baud = 2000000; + else if (baud <= 1053257) baud = 921600; + else if (baud <= 1474560) baud = 1228800; + else if (baud <= 2457600) baud = 1843200; + else baud = 3686400; return baud; } @@ -414,14 +409,13 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port) return result; } + result = usb_serial_generic_open(tty, port); + if (result) + return result; + /* Configure the termios structure */ cp210x_get_termios(tty, port); - - /* The baud rate must be initialised on cp2104 */ - if (tty) - cp210x_change_speed(tty, port, NULL); - - return usb_serial_generic_open(tty, port); + return 0; } static void cp210x_close(struct usb_serial_port *port) @@ -473,7 +467,10 @@ static void cp210x_get_termios_port(struct usb_serial_port *port, dbg("%s - port %d", __func__, port->number); - cp210x_get_config(port, CP210X_GET_BAUDRATE, &baud, 4); + cp210x_get_config(port, CP210X_GET_BAUDDIV, &baud, 2); + /* Convert to baudrate */ + if (baud) + baud = cp210x_quantise_baudrate((BAUD_RATE_GEN_FREQ + baud/2)/ baud); dbg("%s - baud rate = %d", __func__, baud); *baudp = baud; @@ -582,64 +579,11 @@ static void cp210x_get_termios_port(struct usb_serial_port *port, *cflagp = cflag; } -/* - * CP2101 supports the following baud rates: - * - * 300, 600, 1200, 1800, 2400, 4800, 7200, 9600, 14400, 19200, 28800, - * 38400, 56000, 57600, 115200, 128000, 230400, 460800, 921600 - * - * CP2102 and CP2103 support the following additional rates: - * - * 4000, 16000, 51200, 64000, 76800, 153600, 250000, 256000, 500000, - * 576000 - * - * The device will map a requested rate to a supported one, but the result - * of requests for rates greater than 1053257 is undefined (see AN205). - * - * CP2104, CP2105 and CP2110 support most rates up to 2M, 921k and 1M baud, - * respectively, with an error less than 1%. The actual rates are determined - * by - * - * div = round(freq / (2 x prescale x request)) - * actual = freq / (2 x prescale x div) - * - * For CP2104 and CP2105 freq is 48Mhz and prescale is 4 for request <= 365bps - * or 1 otherwise. - * For CP2110 freq is 24Mhz and prescale is 4 for request <= 300bps or 1 - * otherwise. - */ -static void cp210x_change_speed(struct tty_struct *tty, - struct usb_serial_port *port, struct ktermios *old_termios) -{ - u32 baud; - - baud = tty->termios->c_ospeed; - - /* This maps the requested rate to a rate valid on cp2102 or cp2103, - * or to an arbitrary rate in [1M,2M]. - * - * NOTE: B0 is not implemented. - */ - baud = cp210x_quantise_baudrate(baud); - - dbg("%s - setting baud rate to %u", __func__, baud); - if (cp210x_set_config(port, CP210X_SET_BAUDRATE, &baud, - sizeof(baud))) { - dev_warn(&port->dev, "failed to set baud rate to %u\n", baud); - if (old_termios) - baud = old_termios->c_ospeed; - else - baud = 9600; - } - - tty_encode_baud_rate(tty, baud, baud); -} - static void cp210x_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { unsigned int cflag, old_cflag; - unsigned int bits; + unsigned int baud = 0, bits; unsigned int modem_ctl[4]; dbg("%s - port %d", __func__, port->number); @@ -649,9 +593,20 @@ static void cp210x_set_termios(struct tty_struct *tty, cflag = tty->termios->c_cflag; old_cflag = old_termios->c_cflag; - - if (tty->termios->c_ospeed != old_termios->c_ospeed) - cp210x_change_speed(tty, port, old_termios); + baud = cp210x_quantise_baudrate(tty_get_baud_rate(tty)); + + /* If the baud rate is to be updated*/ + if (baud != tty_termios_baud_rate(old_termios) && baud != 0) { + dbg("%s - Setting baud rate to %d baud", __func__, + baud); + if (cp210x_set_config_single(port, CP210X_SET_BAUDDIV, + ((BAUD_RATE_GEN_FREQ + baud/2) / baud))) { + dbg("Baud rate requested not supported by device"); + baud = tty_termios_baud_rate(old_termios); + } + } + /* Report back the resulting baud rate */ + tty_encode_baud_rate(tty, baud, baud); /* If the number of data bits is to be updated */ if ((cflag & CSIZE) != (old_cflag & CSIZE)) { diff --git a/trunk/drivers/usb/serial/ftdi_sio.c b/trunk/drivers/usb/serial/ftdi_sio.c index f770415305f8..01b6404df395 100644 --- a/trunk/drivers/usb/serial/ftdi_sio.c +++ b/trunk/drivers/usb/serial/ftdi_sio.c @@ -797,7 +797,6 @@ static struct usb_device_id id_table_combined [] = { .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(ADI_VID, ADI_GNICEPLUS_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(HORNBY_VID, HORNBY_ELITE_PID) }, { USB_DEVICE(JETI_VID, JETI_SPC1201_PID) }, { USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, @@ -806,8 +805,6 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(BAYER_VID, BAYER_CONTOUR_CABLE_PID) }, { USB_DEVICE(FTDI_VID, MARVELL_OPENRD_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, TI_XDS100V2_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(FTDI_VID, HAMEG_HO820_PID) }, { USB_DEVICE(FTDI_VID, HAMEG_HO720_PID) }, { USB_DEVICE(FTDI_VID, HAMEG_HO730_PID) }, @@ -839,13 +836,11 @@ 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 }, { USB_DEVICE(ST_VID, ST_STMCLT1030_PID), .driver_info = (kernel_ulong_t)&ftdi_stmclite_quirk }, - { USB_DEVICE(FTDI_VID, FTDI_RF_R106) }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; @@ -1338,7 +1333,8 @@ static int set_serial_info(struct tty_struct *tty, goto check_and_exit; } - if (new_serial.baud_base != priv->baud_base) { + if ((new_serial.baud_base != priv->baud_base) && + (new_serial.baud_base < 9600)) { mutex_unlock(&priv->cfg_lock); return -EINVAL; } @@ -1828,7 +1824,6 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port) static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port) { - struct ktermios dummy; struct usb_device *dev = port->serial->dev; struct ftdi_private *priv = usb_get_serial_port_data(port); int result; @@ -1847,10 +1842,8 @@ static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port) This is same behaviour as serial.c/rs_open() - Kuba */ /* ftdi_set_termios will send usb control messages */ - if (tty) { - memset(&dummy, 0, sizeof(dummy)); - ftdi_set_termios(tty, port, &dummy); - } + if (tty) + ftdi_set_termios(tty, port, tty->termios); /* Start reading from the device */ result = usb_serial_generic_open(tty, port); diff --git a/trunk/drivers/usb/serial/ftdi_sio_ids.h b/trunk/drivers/usb/serial/ftdi_sio_ids.h index 6f6058f0db1b..df1d7da933ec 100644 --- a/trunk/drivers/usb/serial/ftdi_sio_ids.h +++ b/trunk/drivers/usb/serial/ftdi_sio_ids.h @@ -39,13 +39,6 @@ /* www.candapter.com Ewert Energy Systems CANdapter device */ #define FTDI_CANDAPTER_PID 0x9F80 /* Product Id */ -/* - * Texas Instruments XDS100v2 JTAG / BeagleBone A3 - * http://processors.wiki.ti.com/index.php/XDS100 - * http://beagleboard.org/bone - */ -#define TI_XDS100V2_PID 0xa6d0 - #define FTDI_NXTCAM_PID 0xABB8 /* NXTCam for Mindstorms NXT */ /* US Interface Navigator (http://www.usinterface.com/) */ @@ -531,12 +524,6 @@ #define ADI_GNICE_PID 0xF000 #define ADI_GNICEPLUS_PID 0xF001 -/* - * Hornby Elite - */ -#define HORNBY_VID 0x04D8 -#define HORNBY_ELITE_PID 0x000A - /* * RATOC REX-USB60F */ @@ -1181,16 +1168,3 @@ */ /* TagTracer MIFARE*/ #define FTDI_ZEITCONTROL_TAGTRACE_MIFARE_PID 0xF7C0 - -/* - * Rainforest Automation - */ -/* 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/io_ti.c b/trunk/drivers/usb/serial/io_ti.c index 5818bfc3261e..65bf06aa591a 100644 --- a/trunk/drivers/usb/serial/io_ti.c +++ b/trunk/drivers/usb/serial/io_ti.c @@ -2657,7 +2657,15 @@ static int edge_startup(struct usb_serial *serial) static void edge_disconnect(struct usb_serial *serial) { + int i; + struct edgeport_port *edge_port; + dbg("%s", __func__); + + for (i = 0; i < serial->num_ports; ++i) { + edge_port = usb_get_serial_port_data(serial->port[i]); + edge_remove_sysfs_attrs(edge_port->port); + } } static void edge_release(struct usb_serial *serial) @@ -2736,7 +2744,6 @@ static struct usb_serial_driver edgeport_1port_device = { .disconnect = edge_disconnect, .release = edge_release, .port_probe = edge_create_sysfs_attrs, - .port_remove = edge_remove_sysfs_attrs, .ioctl = edge_ioctl, .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, @@ -2768,7 +2775,6 @@ static struct usb_serial_driver edgeport_2port_device = { .disconnect = edge_disconnect, .release = edge_release, .port_probe = edge_create_sysfs_attrs, - .port_remove = edge_remove_sysfs_attrs, .ioctl = edge_ioctl, .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, diff --git a/trunk/drivers/usb/serial/kobil_sct.c b/trunk/drivers/usb/serial/kobil_sct.c index a92a3efb507b..5d3beeeb5fd9 100644 --- a/trunk/drivers/usb/serial/kobil_sct.c +++ b/trunk/drivers/usb/serial/kobil_sct.c @@ -38,7 +38,7 @@ #include #include "kobil_sct.h" -static bool debug; +static int debug; /* Version Information */ #define DRIVER_VERSION "21/05/2004" diff --git a/trunk/drivers/usb/serial/option.c b/trunk/drivers/usb/serial/option.c index 39ed1f46cec0..420d9857394a 100644 --- a/trunk/drivers/usb/serial/option.c +++ b/trunk/drivers/usb/serial/option.c @@ -480,10 +480,6 @@ static void option_instat_callback(struct urb *urb); #define ZD_VENDOR_ID 0x0685 #define ZD_PRODUCT_7000 0x7000 -/* LG products */ -#define LG_VENDOR_ID 0x1004 -#define LG_PRODUCT_L02C 0x618f - /* some devices interfaces need special handling due to a number of reasons */ enum option_blacklist_reason { OPTION_BLACKLIST_NONE = 0, @@ -855,18 +851,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 +879,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 +888,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 +1062,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) }, @@ -1314,7 +1183,6 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLU526) }, { USB_DEVICE_AND_INTERFACE_INFO(VIETTEL_VENDOR_ID, VIETTEL_PRODUCT_VT1000, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZD_VENDOR_ID, ZD_PRODUCT_7000, 0xff, 0xff, 0xff) }, - { USB_DEVICE(LG_VENDOR_ID, LG_PRODUCT_L02C) }, /* docomo L-02C modem */ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); diff --git a/trunk/drivers/usb/serial/qcaux.c b/trunk/drivers/usb/serial/qcaux.c index a34819884c1a..30b73e68a904 100644 --- a/trunk/drivers/usb/serial/qcaux.c +++ b/trunk/drivers/usb/serial/qcaux.c @@ -36,7 +36,6 @@ #define UTSTARCOM_PRODUCT_UM175_V1 0x3712 #define UTSTARCOM_PRODUCT_UM175_V2 0x3714 #define UTSTARCOM_PRODUCT_UM175_ALLTEL 0x3715 -#define PANTECH_PRODUCT_UML190_VZW 0x3716 #define PANTECH_PRODUCT_UML290_VZW 0x3718 /* CMOTECH devices */ @@ -68,11 +67,7 @@ static struct usb_device_id id_table[] = { { USB_DEVICE_AND_INTERFACE_INFO(LG_VENDOR_ID, LG_PRODUCT_VX4400_6000, 0xff, 0xff, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(SANYO_VENDOR_ID, SANYO_PRODUCT_KATANA_LX, 0xff, 0xff, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_U520, 0xff, 0x00, 0x00) }, - { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML190_VZW, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML190_VZW, 0xff, 0xfe, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML290_VZW, 0xff, 0xfd, 0xff) }, /* NMEA */ - { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML290_VZW, 0xff, 0xfe, 0xff) }, /* WMC */ - { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML290_VZW, 0xff, 0xff, 0xff) }, /* DIAG */ + { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML290_VZW, 0xff, 0xff, 0xff) }, { }, }; MODULE_DEVICE_TABLE(usb, id_table); 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/usb/storage/realtek_cr.c b/trunk/drivers/usb/storage/realtek_cr.c index d32f72061c09..1f62723ef1a8 100644 --- a/trunk/drivers/usb/storage/realtek_cr.c +++ b/trunk/drivers/usb/storage/realtek_cr.c @@ -789,7 +789,7 @@ static void rts51x_suspend_timer_fn(unsigned long data) rts51x_set_stat(chip, RTS51X_STAT_SS); /* ignore mass storage interface's children */ pm_suspend_ignore_children(&us->pusb_intf->dev, true); - usb_autopm_put_interface_async(us->pusb_intf); + usb_autopm_put_interface(us->pusb_intf); US_DEBUGP("%s: RTS51X_STAT_SS 01," "intf->pm_usage_cnt:%d, power.usage:%d\n", __func__, diff --git a/trunk/drivers/usb/usb-skeleton.c b/trunk/drivers/usb/usb-skeleton.c index b4a71679c933..8efeae24764f 100644 --- a/trunk/drivers/usb/usb-skeleton.c +++ b/trunk/drivers/usb/usb-skeleton.c @@ -27,6 +27,8 @@ #define USB_SKEL_VENDOR_ID 0xfff0 #define USB_SKEL_PRODUCT_ID 0xfff0 +static DEFINE_MUTEX(skel_mutex); + /* table of devices that work with this driver */ static const struct usb_device_id skel_table[] = { { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) }, @@ -99,18 +101,25 @@ static int skel_open(struct inode *inode, struct file *file) goto exit; } + mutex_lock(&skel_mutex); dev = usb_get_intfdata(interface); if (!dev) { + mutex_unlock(&skel_mutex); retval = -ENODEV; goto exit; } /* increment our usage count for the device */ kref_get(&dev->kref); + mutex_unlock(&skel_mutex); /* lock the device to allow correctly handling errors * in resumption */ mutex_lock(&dev->io_mutex); + if (!dev->interface) { + retval = -ENODEV; + goto out_err; + } retval = usb_autopm_get_interface(interface); if (retval) @@ -118,7 +127,11 @@ static int skel_open(struct inode *inode, struct file *file) /* save our object in the file's private structure */ file->private_data = dev; + +out_err: mutex_unlock(&dev->io_mutex); + if (retval) + kref_put(&dev->kref, skel_delete); exit: return retval; @@ -598,7 +611,6 @@ static void skel_disconnect(struct usb_interface *interface) int minor = interface->minor; dev = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); /* give back our minor */ usb_deregister_dev(interface, &skel_class); @@ -610,8 +622,12 @@ static void skel_disconnect(struct usb_interface *interface) usb_kill_anchored_urbs(&dev->submitted); + mutex_lock(&skel_mutex); + usb_set_intfdata(interface, NULL); + /* decrement our usage count */ kref_put(&dev->kref, skel_delete); + mutex_unlock(&skel_mutex); dev_info(&interface->dev, "USB Skeleton #%d now disconnected", minor); } diff --git a/trunk/drivers/usb/wusbcore/Kconfig b/trunk/drivers/usb/wusbcore/Kconfig index f29fdd7f6d75..0ead8826ec79 100644 --- a/trunk/drivers/usb/wusbcore/Kconfig +++ b/trunk/drivers/usb/wusbcore/Kconfig @@ -6,7 +6,7 @@ config USB_WUSB depends on EXPERIMENTAL depends on USB depends on PCI - depends on UWB + select UWB select CRYPTO select CRYPTO_BLKCIPHER select CRYPTO_CBC diff --git a/trunk/drivers/video/atmel_lcdfb.c b/trunk/drivers/video/atmel_lcdfb.c index e40c00f2c2ba..0d7b20d4285d 100644 --- a/trunk/drivers/video/atmel_lcdfb.c +++ b/trunk/drivers/video/atmel_lcdfb.c @@ -1108,7 +1108,7 @@ static int atmel_lcdfb_suspend(struct platform_device *pdev, pm_message_t mesg) */ lcdc_writel(sinfo, ATMEL_LCDC_IDR, ~0UL); - sinfo->saved_lcdcon = lcdc_readl(sinfo, ATMEL_LCDC_CONTRAST_CTR); + sinfo->saved_lcdcon = lcdc_readl(sinfo, ATMEL_LCDC_CONTRAST_VAL); lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_CTR, 0); if (sinfo->atmel_lcdfb_power_control) sinfo->atmel_lcdfb_power_control(0); diff --git a/trunk/drivers/video/fsl-diu-fb.c b/trunk/drivers/video/fsl-diu-fb.c index 6af3f16754f0..acf292bfba02 100644 --- a/trunk/drivers/video/fsl-diu-fb.c +++ b/trunk/drivers/video/fsl-diu-fb.c @@ -1432,7 +1432,7 @@ static int fsl_diu_suspend(struct platform_device *ofdev, pm_message_t state) struct fsl_diu_data *data; data = dev_get_drvdata(&ofdev->dev); - disable_lcdc(data->fsl_diu_info); + disable_lcdc(data->fsl_diu_info[0]); return 0; } @@ -1442,7 +1442,7 @@ static int fsl_diu_resume(struct platform_device *ofdev) struct fsl_diu_data *data; data = dev_get_drvdata(&ofdev->dev); - enable_lcdc(data->fsl_diu_info); + enable_lcdc(data->fsl_diu_info[0]); return 0; } diff --git a/trunk/drivers/video/intelfb/intelfbdrv.c b/trunk/drivers/video/intelfb/intelfbdrv.c index 02fd2263610c..c6afa33a4532 100644 --- a/trunk/drivers/video/intelfb/intelfbdrv.c +++ b/trunk/drivers/video/intelfb/intelfbdrv.c @@ -529,6 +529,7 @@ static int __devinit intelfb_pci_register(struct pci_dev *pdev, if (fb_alloc_cmap(&info->cmap, 256, 1) < 0) { ERR_MSG("Could not allocate cmap for intelfb_info.\n"); goto err_out_cmap; + return -ENODEV; } dinfo = info->par; diff --git a/trunk/drivers/video/macfb.c b/trunk/drivers/video/macfb.c index fe01add3700e..43207cc6cc19 100644 --- a/trunk/drivers/video/macfb.c +++ b/trunk/drivers/video/macfb.c @@ -592,12 +592,12 @@ static int __init macfb_init(void) if (!fb_info.screen_base) return -ENODEV; - pr_info("macfb: framebuffer at 0x%08lx, mapped to 0x%p, size %dk\n", - macfb_fix.smem_start, fb_info.screen_base, - macfb_fix.smem_len / 1024); - pr_info("macfb: mode is %dx%dx%d, linelength=%d\n", - macfb_defined.xres, macfb_defined.yres, - macfb_defined.bits_per_pixel, macfb_fix.line_length); + printk("macfb: framebuffer at 0x%08lx, mapped to 0x%p, size %dk\n", + macfb_fix.smem_start, fb_info.screen_base, + macfb_fix.smem_len / 1024); + printk("macfb: mode is %dx%dx%d, linelength=%d\n", + macfb_defined.xres, macfb_defined.yres, + macfb_defined.bits_per_pixel, macfb_fix.line_length); /* Fill in the available video resolution */ macfb_defined.xres_virtual = macfb_defined.xres; @@ -613,10 +613,14 @@ static int __init macfb_init(void) switch (macfb_defined.bits_per_pixel) { case 1: + /* + * XXX: I think this will catch any program that tries + * to do FBIO_PUTCMAP when the visual is monochrome. + */ macfb_defined.red.length = macfb_defined.bits_per_pixel; macfb_defined.green.length = macfb_defined.bits_per_pixel; macfb_defined.blue.length = macfb_defined.bits_per_pixel; - video_cmap_len = 2; + video_cmap_len = 0; macfb_fix.visual = FB_VISUAL_MONO01; break; case 2: @@ -656,10 +660,11 @@ static int __init macfb_init(void) macfb_fix.visual = FB_VISUAL_TRUECOLOR; break; default: - pr_err("macfb: unknown or unsupported bit depth: %d\n", + video_cmap_len = 0; + macfb_fix.visual = FB_VISUAL_MONO01; + printk("macfb: unknown or unsupported bit depth: %d\n", macfb_defined.bits_per_pixel); - err = -EINVAL; - goto fail_unmap; + break; } /* @@ -729,8 +734,8 @@ static int __init macfb_init(void) case MAC_MODEL_Q950: strcpy(macfb_fix.id, "DAFB"); macfb_setpalette = dafb_setpalette; - dafb_cmap_regs = ioremap(DAFB_BASE, 0x1000); macfb_defined.activate = FB_ACTIVATE_NOW; + dafb_cmap_regs = ioremap(DAFB_BASE, 0x1000); break; /* @@ -739,8 +744,8 @@ static int __init macfb_init(void) case MAC_MODEL_LCII: strcpy(macfb_fix.id, "V8"); macfb_setpalette = v8_brazil_setpalette; - v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000); macfb_defined.activate = FB_ACTIVATE_NOW; + v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000); break; /* @@ -753,8 +758,8 @@ static int __init macfb_init(void) case MAC_MODEL_P600: strcpy(macfb_fix.id, "Brazil"); macfb_setpalette = v8_brazil_setpalette; - v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000); macfb_defined.activate = FB_ACTIVATE_NOW; + v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000); break; /* @@ -768,10 +773,10 @@ static int __init macfb_init(void) case MAC_MODEL_P520: case MAC_MODEL_P550: case MAC_MODEL_P460: - strcpy(macfb_fix.id, "Sonora"); macfb_setpalette = v8_brazil_setpalette; - v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000); macfb_defined.activate = FB_ACTIVATE_NOW; + strcpy(macfb_fix.id, "Sonora"); + v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000); break; /* @@ -781,10 +786,10 @@ static int __init macfb_init(void) */ case MAC_MODEL_IICI: case MAC_MODEL_IISI: - strcpy(macfb_fix.id, "RBV"); macfb_setpalette = rbv_setpalette; - rbv_cmap_regs = ioremap(DAC_BASE, 0x1000); macfb_defined.activate = FB_ACTIVATE_NOW; + strcpy(macfb_fix.id, "RBV"); + rbv_cmap_regs = ioremap(DAC_BASE, 0x1000); break; /* @@ -792,10 +797,10 @@ static int __init macfb_init(void) */ case MAC_MODEL_Q840: case MAC_MODEL_C660: - strcpy(macfb_fix.id, "Civic"); macfb_setpalette = civic_setpalette; - civic_cmap_regs = ioremap(CIVIC_BASE, 0x1000); macfb_defined.activate = FB_ACTIVATE_NOW; + strcpy(macfb_fix.id, "Civic"); + civic_cmap_regs = ioremap(CIVIC_BASE, 0x1000); break; @@ -804,26 +809,26 @@ static int __init macfb_init(void) * We think this may be like the LC II */ case MAC_MODEL_LC: - strcpy(macfb_fix.id, "LC"); if (vidtest) { macfb_setpalette = v8_brazil_setpalette; + macfb_defined.activate = FB_ACTIVATE_NOW; v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000); - macfb_defined.activate = FB_ACTIVATE_NOW; } + strcpy(macfb_fix.id, "LC"); break; /* * We think this may be like the LC II */ case MAC_MODEL_CCL: - strcpy(macfb_fix.id, "Color Classic"); if (vidtest) { macfb_setpalette = v8_brazil_setpalette; + macfb_defined.activate = FB_ACTIVATE_NOW; v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000); - macfb_defined.activate = FB_ACTIVATE_NOW; } + strcpy(macfb_fix.id, "Color Classic"); break; /* @@ -888,10 +893,10 @@ static int __init macfb_init(void) case MAC_MODEL_PB270C: case MAC_MODEL_PB280: case MAC_MODEL_PB280C: - strcpy(macfb_fix.id, "CSC"); macfb_setpalette = csc_setpalette; - csc_cmap_regs = ioremap(CSC_BASE, 0x1000); macfb_defined.activate = FB_ACTIVATE_NOW; + strcpy(macfb_fix.id, "CSC"); + csc_cmap_regs = ioremap(CSC_BASE, 0x1000); break; default: @@ -913,9 +918,8 @@ static int __init macfb_init(void) if (err) goto fail_dealloc; - pr_info("fb%d: %s frame buffer device\n", - fb_info.node, fb_info.fix.id); - + printk("fb%d: %s frame buffer device\n", + fb_info.node, fb_info.fix.id); return 0; fail_dealloc: diff --git a/trunk/drivers/video/omap2/dss/dispc.c b/trunk/drivers/video/omap2/dss/dispc.c index e1626a1d5c45..a5ec7f37c185 100644 --- a/trunk/drivers/video/omap2/dss/dispc.c +++ b/trunk/drivers/video/omap2/dss/dispc.c @@ -401,7 +401,7 @@ void dispc_runtime_put(void) DSSDBG("dispc_runtime_put\n"); - r = pm_runtime_put_sync(&dispc.pdev->dev); + r = pm_runtime_put(&dispc.pdev->dev); WARN_ON(r < 0); } diff --git a/trunk/drivers/video/omap2/dss/dsi.c b/trunk/drivers/video/omap2/dss/dsi.c index 52f36ec1c8bb..d4d676c82c12 100644 --- a/trunk/drivers/video/omap2/dss/dsi.c +++ b/trunk/drivers/video/omap2/dss/dsi.c @@ -1079,7 +1079,7 @@ void dsi_runtime_put(struct platform_device *dsidev) DSSDBG("dsi_runtime_put\n"); - r = pm_runtime_put_sync(&dsi->pdev->dev); + r = pm_runtime_put(&dsi->pdev->dev); WARN_ON(r < 0); } diff --git a/trunk/drivers/video/omap2/dss/dss.c b/trunk/drivers/video/omap2/dss/dss.c index 77c2b5a32b5d..17033457ee89 100644 --- a/trunk/drivers/video/omap2/dss/dss.c +++ b/trunk/drivers/video/omap2/dss/dss.c @@ -720,7 +720,7 @@ void dss_runtime_put(void) DSSDBG("dss_runtime_put\n"); - r = pm_runtime_put_sync(&dss.pdev->dev); + r = pm_runtime_put(&dss.pdev->dev); WARN_ON(r < 0); } diff --git a/trunk/drivers/video/omap2/dss/hdmi.c b/trunk/drivers/video/omap2/dss/hdmi.c index d7aa3b056529..b4c270edb915 100644 --- a/trunk/drivers/video/omap2/dss/hdmi.c +++ b/trunk/drivers/video/omap2/dss/hdmi.c @@ -176,7 +176,7 @@ static void hdmi_runtime_put(void) DSSDBG("hdmi_runtime_put\n"); - r = pm_runtime_put_sync(&hdmi.pdev->dev); + r = pm_runtime_put(&hdmi.pdev->dev); WARN_ON(r < 0); } @@ -497,7 +497,6 @@ bool omapdss_hdmi_detect(void) int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev) { - struct omap_dss_hdmi_data *priv = dssdev->data; int r = 0; DSSDBG("ENTER hdmi_display_enable\n"); @@ -510,8 +509,6 @@ int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev) goto err0; } - hdmi.ip_data.hpd_gpio = priv->hpd_gpio; - r = omap_dss_start_device(dssdev); if (r) { DSSERR("failed to start device\n"); diff --git a/trunk/drivers/video/omap2/dss/rfbi.c b/trunk/drivers/video/omap2/dss/rfbi.c index 55f398014f33..814bb9500dca 100644 --- a/trunk/drivers/video/omap2/dss/rfbi.c +++ b/trunk/drivers/video/omap2/dss/rfbi.c @@ -140,7 +140,7 @@ static void rfbi_runtime_put(void) DSSDBG("rfbi_runtime_put\n"); - r = pm_runtime_put_sync(&rfbi.pdev->dev); + r = pm_runtime_put(&rfbi.pdev->dev); WARN_ON(r < 0); } diff --git a/trunk/drivers/video/omap2/dss/ti_hdmi.h b/trunk/drivers/video/omap2/dss/ti_hdmi.h index 50dadba5070a..7503f7f619a7 100644 --- a/trunk/drivers/video/omap2/dss/ti_hdmi.h +++ b/trunk/drivers/video/omap2/dss/ti_hdmi.h @@ -126,10 +126,6 @@ struct hdmi_ip_data { const struct ti_hdmi_ip_ops *ops; struct hdmi_config cfg; struct hdmi_pll_info pll_data; - - /* ti_hdmi_4xxx_ip private data. These should be in a separate struct */ - int hpd_gpio; - bool phy_tx_enabled; }; int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data); diff --git a/trunk/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/trunk/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c index 2d72334ca3da..9af81f18f163 100644 --- a/trunk/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c +++ b/trunk/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c @@ -28,7 +28,6 @@ #include #include #include -#include #include "ti_hdmi_4xxx_ip.h" #include "dss.h" @@ -224,49 +223,6 @@ void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data) hdmi_set_pll_pwr(ip_data, HDMI_PLLPWRCMD_ALLOFF); } -static int hdmi_check_hpd_state(struct hdmi_ip_data *ip_data) -{ - unsigned long flags; - bool hpd; - int r; - /* this should be in ti_hdmi_4xxx_ip private data */ - static DEFINE_SPINLOCK(phy_tx_lock); - - spin_lock_irqsave(&phy_tx_lock, flags); - - hpd = gpio_get_value(ip_data->hpd_gpio); - - if (hpd == ip_data->phy_tx_enabled) { - spin_unlock_irqrestore(&phy_tx_lock, flags); - return 0; - } - - if (hpd) - r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON); - else - r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON); - - if (r) { - DSSERR("Failed to %s PHY TX power\n", - hpd ? "enable" : "disable"); - goto err; - } - - ip_data->phy_tx_enabled = hpd; -err: - spin_unlock_irqrestore(&phy_tx_lock, flags); - return r; -} - -static irqreturn_t hpd_irq_handler(int irq, void *data) -{ - struct hdmi_ip_data *ip_data = data; - - hdmi_check_hpd_state(ip_data); - - return IRQ_HANDLED; -} - int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data) { u16 r = 0; @@ -276,6 +232,10 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data) if (r) return r; + r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON); + if (r) + return r; + /* * Read address 0 in order to get the SCP reset done completed * Dummy access performed to make sure reset is done @@ -297,32 +257,12 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data) /* Write to phy address 3 to change the polarity control */ REG_FLD_MOD(phy_base, HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27); - r = request_threaded_irq(gpio_to_irq(ip_data->hpd_gpio), - NULL, hpd_irq_handler, - IRQF_DISABLED | IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING, "hpd", ip_data); - if (r) { - DSSERR("HPD IRQ request failed\n"); - hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF); - return r; - } - - r = hdmi_check_hpd_state(ip_data); - if (r) { - free_irq(gpio_to_irq(ip_data->hpd_gpio), ip_data); - hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF); - return r; - } - return 0; } void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data) { - free_irq(gpio_to_irq(ip_data->hpd_gpio), ip_data); - hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF); - ip_data->phy_tx_enabled = false; } static int hdmi_core_ddc_init(struct hdmi_ip_data *ip_data) diff --git a/trunk/drivers/video/omap2/dss/venc.c b/trunk/drivers/video/omap2/dss/venc.c index 5c3d0f901510..b3e9f9091581 100644 --- a/trunk/drivers/video/omap2/dss/venc.c +++ b/trunk/drivers/video/omap2/dss/venc.c @@ -401,7 +401,7 @@ static void venc_runtime_put(void) DSSDBG("venc_runtime_put\n"); - r = pm_runtime_put_sync(&venc.pdev->dev); + r = pm_runtime_put(&venc.pdev->dev); WARN_ON(r < 0); } diff --git a/trunk/drivers/virtio/virtio_ring.c b/trunk/drivers/virtio/virtio_ring.c index 5aa43c3392a2..79e1b292c030 100644 --- a/trunk/drivers/virtio/virtio_ring.c +++ b/trunk/drivers/virtio/virtio_ring.c @@ -35,7 +35,7 @@ #define virtio_rmb(vq) \ do { if ((vq)->weak_barriers) smp_rmb(); else rmb(); } while(0) #define virtio_wmb(vq) \ - do { if ((vq)->weak_barriers) smp_wmb(); else wmb(); } while(0) + do { if ((vq)->weak_barriers) smp_rmb(); else rmb(); } while(0) #else /* We must force memory ordering even if guest is UP since host could be * running on another CPU, but SMP barriers are defined to barrier() in that @@ -308,9 +308,9 @@ bool virtqueue_kick_prepare(struct virtqueue *_vq) bool needs_kick; START_USE(vq); - /* We need to expose available array entries before checking avail - * event. */ - virtio_mb(vq); + /* Descriptors and available array need to be set before we expose the + * new available array entries. */ + virtio_wmb(vq); old = vq->vring.avail->idx - vq->num_added; new = vq->vring.avail->idx; diff --git a/trunk/drivers/watchdog/dw_wdt.c b/trunk/drivers/watchdog/dw_wdt.c index 63d7b58f1c7d..1b0e3dd81c1a 100644 --- a/trunk/drivers/watchdog/dw_wdt.c +++ b/trunk/drivers/watchdog/dw_wdt.c @@ -300,7 +300,11 @@ static int __devinit dw_wdt_drv_probe(struct platform_device *pdev) if (!mem) return -EINVAL; - dw_wdt.regs = devm_request_and_ioremap(&pdev->dev, mem); + if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem), + "dw_wdt")) + return -ENOMEM; + + dw_wdt.regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); if (!dw_wdt.regs) return -ENOMEM; diff --git a/trunk/drivers/watchdog/iTCO_wdt.c b/trunk/drivers/watchdog/iTCO_wdt.c index bdf401b240b5..99796c5d913d 100644 --- a/trunk/drivers/watchdog/iTCO_wdt.c +++ b/trunk/drivers/watchdog/iTCO_wdt.c @@ -36,7 +36,6 @@ * document number TBD : Patsburg (PBG) * document number TBD : DH89xxCC * document number TBD : Panther Point - * document number TBD : Lynx Point */ /* @@ -127,7 +126,6 @@ enum iTCO_chipsets { TCO_PBG, /* Patsburg */ TCO_DH89XXCC, /* DH89xxCC */ TCO_PPT, /* Panther Point */ - TCO_LPT, /* Lynx Point */ }; static struct { @@ -191,7 +189,6 @@ static struct { {"Patsburg", 2}, {"DH89xxCC", 2}, {"Panther Point", 2}, - {"Lynx Point", 2}, {NULL, 0} }; @@ -334,38 +331,6 @@ static DEFINE_PCI_DEVICE_TABLE(iTCO_wdt_pci_tbl) = { { PCI_VDEVICE(INTEL, 0x1e5d), TCO_PPT}, { PCI_VDEVICE(INTEL, 0x1e5e), TCO_PPT}, { PCI_VDEVICE(INTEL, 0x1e5f), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x8c40), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c41), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c42), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c43), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c44), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c45), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c46), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c47), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c48), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c49), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4a), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4b), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4c), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4d), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4e), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4f), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c50), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c51), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c52), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c53), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c54), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c55), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c56), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c57), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c58), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c59), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5a), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5b), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5c), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5d), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5e), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5f), TCO_LPT}, { 0, }, /* End of list */ }; MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl); diff --git a/trunk/drivers/watchdog/imx2_wdt.c b/trunk/drivers/watchdog/imx2_wdt.c index c44c3334003a..b8ef2c6dca7c 100644 --- a/trunk/drivers/watchdog/imx2_wdt.c +++ b/trunk/drivers/watchdog/imx2_wdt.c @@ -247,6 +247,7 @@ static struct miscdevice imx2_wdt_miscdev = { static int __init imx2_wdt_probe(struct platform_device *pdev) { int ret; + int res_size; struct resource *res; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -255,7 +256,15 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) return -ENODEV; } - imx2_wdt.base = devm_request_and_ioremap(&pdev->dev, res); + res_size = resource_size(res); + if (!devm_request_mem_region(&pdev->dev, res->start, res_size, + res->name)) { + dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n", + res_size, res->start); + return -ENOMEM; + } + + imx2_wdt.base = devm_ioremap_nocache(&pdev->dev, res->start, res_size); if (!imx2_wdt.base) { dev_err(&pdev->dev, "ioremap failed\n"); return -ENOMEM; diff --git a/trunk/drivers/watchdog/nuc900_wdt.c b/trunk/drivers/watchdog/nuc900_wdt.c index 529085b8b8fb..50359bad9177 100644 --- a/trunk/drivers/watchdog/nuc900_wdt.c +++ b/trunk/drivers/watchdog/nuc900_wdt.c @@ -72,7 +72,7 @@ struct nuc900_wdt { }; static unsigned long nuc900wdt_busy; -static struct nuc900_wdt *nuc900_wdt; +struct nuc900_wdt *nuc900_wdt; static inline void nuc900_wdt_keepalive(void) { @@ -287,8 +287,7 @@ static int __devinit nuc900wdt_probe(struct platform_device *pdev) setup_timer(&nuc900_wdt->timer, nuc900_wdt_timer_ping, 0); - ret = misc_register(&nuc900wdt_miscdev); - if (ret) { + if (misc_register(&nuc900wdt_miscdev)) { dev_err(&pdev->dev, "err register miscdev on minor=%d (%d)\n", WATCHDOG_MINOR, ret); goto err_clk; diff --git a/trunk/drivers/watchdog/omap_wdt.c b/trunk/drivers/watchdog/omap_wdt.c index d19ff5145e82..4b33e3fd726b 100644 --- a/trunk/drivers/watchdog/omap_wdt.c +++ b/trunk/drivers/watchdog/omap_wdt.c @@ -339,7 +339,6 @@ static int __devinit omap_wdt_probe(struct platform_device *pdev) return 0; err_misc: - pm_runtime_disable(wdev->dev); platform_set_drvdata(pdev, NULL); iounmap(wdev->base); @@ -372,7 +371,6 @@ static int __devexit omap_wdt_remove(struct platform_device *pdev) struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pm_runtime_disable(wdev->dev); if (!res) return -ENOENT; diff --git a/trunk/drivers/watchdog/pnx4008_wdt.c b/trunk/drivers/watchdog/pnx4008_wdt.c index 8e210aafdfd0..bd143c9dd3e6 100644 --- a/trunk/drivers/watchdog/pnx4008_wdt.c +++ b/trunk/drivers/watchdog/pnx4008_wdt.c @@ -226,7 +226,7 @@ static long pnx4008_wdt_ioctl(struct file *file, unsigned int cmd, static int pnx4008_wdt_release(struct inode *inode, struct file *file) { if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status)) - printk(KERN_WARNING "WATCHDOG: Device closed unexpectedly\n"); + printk(KERN_WARNING "WATCHDOG: Device closed unexpectdly\n"); wdt_disable(); clk_disable(wdt_clk); diff --git a/trunk/drivers/watchdog/stmp3xxx_wdt.c b/trunk/drivers/watchdog/stmp3xxx_wdt.c index e37d81178b9e..4c2a4e8698f9 100644 --- a/trunk/drivers/watchdog/stmp3xxx_wdt.c +++ b/trunk/drivers/watchdog/stmp3xxx_wdt.c @@ -174,7 +174,7 @@ static int stmp3xxx_wdt_release(struct inode *inode, struct file *file) if (!nowayout) { if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status)) { wdt_ping(); - pr_debug("%s: Device closed unexpectedly\n", __func__); + pr_debug("%s: Device closed unexpectdly\n", __func__); ret = -EINVAL; } else { wdt_disable(); diff --git a/trunk/drivers/watchdog/via_wdt.c b/trunk/drivers/watchdog/via_wdt.c index 8f07dd4bd94a..026b4bbfa0aa 100644 --- a/trunk/drivers/watchdog/via_wdt.c +++ b/trunk/drivers/watchdog/via_wdt.c @@ -124,6 +124,8 @@ static int wdt_stop(struct watchdog_device *wdd) static int wdt_set_timeout(struct watchdog_device *wdd, unsigned int new_timeout) { + if (new_timeout < 1 || new_timeout > WDT_TIMEOUT_MAX) + return -EINVAL; writel(new_timeout, wdt_mem + VIA_WDT_COUNT); timeout = new_timeout; return 0; @@ -148,8 +150,6 @@ static const struct watchdog_ops wdt_ops = { static struct watchdog_device wdt_dev = { .info = &wdt_info, .ops = &wdt_ops, - .min_timeout = 1, - .max_timeout = WDT_TIMEOUT_MAX, }; static int __devinit wdt_probe(struct pci_dev *pdev, @@ -233,7 +233,7 @@ static void __devexit wdt_remove(struct pci_dev *pdev) pci_disable_device(pdev); } -static DEFINE_PCI_DEVICE_TABLE(wdt_pci_table) = { +DEFINE_PCI_DEVICE_TABLE(wdt_pci_table) = { { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_CX700) }, { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX800) }, { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855) }, diff --git a/trunk/drivers/watchdog/wafer5823wdt.c b/trunk/drivers/watchdog/wafer5823wdt.c index c3c3188c34d7..42e940c23891 100644 --- a/trunk/drivers/watchdog/wafer5823wdt.c +++ b/trunk/drivers/watchdog/wafer5823wdt.c @@ -152,12 +152,12 @@ static long wafwdt_ioctl(struct file *file, unsigned int cmd, return -EFAULT; if (options & WDIOS_DISABLECARD) { - wafwdt_stop(); + wafwdt_start(); retval = 0; } if (options & WDIOS_ENABLECARD) { - wafwdt_start(); + wafwdt_stop(); retval = 0; } diff --git a/trunk/drivers/watchdog/wm8350_wdt.c b/trunk/drivers/watchdog/wm8350_wdt.c index 5d7113c7e501..909c78650d3e 100644 --- a/trunk/drivers/watchdog/wm8350_wdt.c +++ b/trunk/drivers/watchdog/wm8350_wdt.c @@ -212,10 +212,10 @@ static long wm8350_wdt_ioctl(struct file *file, unsigned int cmd, /* Setting both simultaneously means at least one must fail */ if (options == WDIOS_DISABLECARD) - ret = wm8350_wdt_stop(wm8350); + ret = wm8350_wdt_start(wm8350); if (options == WDIOS_ENABLECARD) - ret = wm8350_wdt_start(wm8350); + ret = wm8350_wdt_stop(wm8350); break; } diff --git a/trunk/drivers/xen/grant-table.c b/trunk/drivers/xen/grant-table.c index b4d4eac761db..1cd94daa71db 100644 --- a/trunk/drivers/xen/grant-table.c +++ b/trunk/drivers/xen/grant-table.c @@ -948,12 +948,9 @@ static void gnttab_request_version(void) int rc; struct gnttab_set_version gsv; - if (xen_hvm_domain()) - gsv.version = 1; - else - gsv.version = 2; + gsv.version = 2; rc = HYPERVISOR_grant_table_op(GNTTABOP_set_version, &gsv, 1); - if (rc == 0 && gsv.version == 2) { + if (rc == 0) { grant_table_version = 2; gnttab_interface = &gnttab_v2_ops; } else if (grant_table_version == 2) { diff --git a/trunk/fs/btrfs/backref.c b/trunk/fs/btrfs/backref.c index 633c701a287d..b9a843226de8 100644 --- a/trunk/fs/btrfs/backref.c +++ b/trunk/fs/btrfs/backref.c @@ -297,7 +297,7 @@ static int __add_delayed_refs(struct btrfs_delayed_ref_head *head, u64 seq, struct btrfs_delayed_extent_op *extent_op = head->extent_op; struct rb_node *n = &head->node.rb_node; int sgn; - int ret = 0; + int ret; if (extent_op && extent_op->update_key) btrfs_disk_key_to_cpu(info_key, &extent_op->key); @@ -392,7 +392,7 @@ static int __add_inline_refs(struct btrfs_fs_info *fs_info, struct btrfs_key *info_key, int *info_level, struct list_head *prefs) { - int ret = 0; + int ret; int slot; struct extent_buffer *leaf; struct btrfs_key key; diff --git a/trunk/fs/btrfs/check-integrity.c b/trunk/fs/btrfs/check-integrity.c index b669a7d8e499..ad0b3ba735b7 100644 --- a/trunk/fs/btrfs/check-integrity.c +++ b/trunk/fs/btrfs/check-integrity.c @@ -1662,7 +1662,7 @@ static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state, block = btrfsic_block_hashtable_lookup(bdev, dev_bytenr, &state->block_hashtable); if (NULL != block) { - u64 bytenr = 0; + u64 bytenr; struct list_head *elem_ref_to; struct list_head *tmp_ref_to; @@ -2777,10 +2777,9 @@ int btrfsic_submit_bh(int rw, struct buffer_head *bh) printk(KERN_INFO "submit_bh(rw=0x%x, blocknr=%lu (bytenr %llu)," " size=%lu, data=%p, bdev=%p)\n", - rw, (unsigned long)bh->b_blocknr, - (unsigned long long)dev_bytenr, - (unsigned long)bh->b_size, bh->b_data, - bh->b_bdev); + rw, bh->b_blocknr, + (unsigned long long)dev_bytenr, bh->b_size, + bh->b_data, bh->b_bdev); btrfsic_process_written_block(dev_state, dev_bytenr, bh->b_data, bh->b_size, NULL, NULL, bh, rw); @@ -2845,7 +2844,7 @@ void btrfsic_submit_bio(int rw, struct bio *bio) printk(KERN_INFO "submit_bio(rw=0x%x, bi_vcnt=%u," " bi_sector=%lu (bytenr %llu), bi_bdev=%p)\n", - rw, bio->bi_vcnt, (unsigned long)bio->bi_sector, + rw, bio->bi_vcnt, bio->bi_sector, (unsigned long long)dev_bytenr, bio->bi_bdev); diff --git a/trunk/fs/btrfs/disk-io.c b/trunk/fs/btrfs/disk-io.c index 811d9f918b1c..7aa9cd36bf1b 100644 --- a/trunk/fs/btrfs/disk-io.c +++ b/trunk/fs/btrfs/disk-io.c @@ -962,13 +962,6 @@ static int btree_releasepage(struct page *page, gfp_t gfp_flags) tree = &BTRFS_I(page->mapping->host)->io_tree; map = &BTRFS_I(page->mapping->host)->extent_tree; - /* - * We need to mask out eg. __GFP_HIGHMEM and __GFP_DMA32 as we're doing - * slab allocation from alloc_extent_state down the callchain where - * it'd hit a BUG_ON as those flags are not allowed. - */ - gfp_flags &= ~GFP_SLAB_BUG_MASK; - ret = try_release_extent_state(map, tree, page, gfp_flags); if (!ret) return 0; diff --git a/trunk/fs/btrfs/extent-tree.c b/trunk/fs/btrfs/extent-tree.c index 283af7a676a3..700879ed64cf 100644 --- a/trunk/fs/btrfs/extent-tree.c +++ b/trunk/fs/btrfs/extent-tree.c @@ -34,24 +34,23 @@ #include "locking.h" #include "free-space-cache.h" -/* - * control flags for do_chunk_alloc's force field +/* control flags for do_chunk_alloc's force field * CHUNK_ALLOC_NO_FORCE means to only allocate a chunk * if we really need one. * + * CHUNK_ALLOC_FORCE means it must try to allocate one + * * CHUNK_ALLOC_LIMITED means to only try and allocate one * if we have very few chunks already allocated. This is * used as part of the clustering code to help make sure * we have a good pool of storage to cluster in, without * filling the FS with empty chunks * - * CHUNK_ALLOC_FORCE means it must try to allocate one - * */ enum { CHUNK_ALLOC_NO_FORCE = 0, - CHUNK_ALLOC_LIMITED = 1, - CHUNK_ALLOC_FORCE = 2, + CHUNK_ALLOC_FORCE = 1, + CHUNK_ALLOC_LIMITED = 2, }; /* @@ -3415,7 +3414,7 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans, again: spin_lock(&space_info->lock); - if (force < space_info->force_alloc) + if (space_info->force_alloc) force = space_info->force_alloc; if (space_info->full) { spin_unlock(&space_info->lock); @@ -5795,7 +5794,6 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans, u64 search_end, struct btrfs_key *ins, u64 data) { - bool final_tried = false; int ret; u64 search_start = 0; @@ -5815,25 +5813,22 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans, search_start, search_end, hint_byte, ins, data); - if (ret == -ENOSPC) { - if (!final_tried) { - num_bytes = num_bytes >> 1; - num_bytes = num_bytes & ~(root->sectorsize - 1); - num_bytes = max(num_bytes, min_alloc_size); - do_chunk_alloc(trans, root->fs_info->extent_root, - num_bytes, data, CHUNK_ALLOC_FORCE); - if (num_bytes == min_alloc_size) - final_tried = true; - goto again; - } else if (btrfs_test_opt(root, ENOSPC_DEBUG)) { - struct btrfs_space_info *sinfo; - - sinfo = __find_space_info(root->fs_info, data); - printk(KERN_ERR "btrfs allocation failed flags %llu, " - "wanted %llu\n", (unsigned long long)data, - (unsigned long long)num_bytes); - dump_space_info(sinfo, num_bytes, 1); - } + if (ret == -ENOSPC && num_bytes > min_alloc_size) { + num_bytes = num_bytes >> 1; + num_bytes = num_bytes & ~(root->sectorsize - 1); + num_bytes = max(num_bytes, min_alloc_size); + do_chunk_alloc(trans, root->fs_info->extent_root, + num_bytes, data, CHUNK_ALLOC_FORCE); + goto again; + } + if (ret == -ENOSPC && btrfs_test_opt(root, ENOSPC_DEBUG)) { + struct btrfs_space_info *sinfo; + + sinfo = __find_space_info(root->fs_info, data); + printk(KERN_ERR "btrfs allocation failed flags %llu, " + "wanted %llu\n", (unsigned long long)data, + (unsigned long long)num_bytes); + dump_space_info(sinfo, num_bytes, 1); } trace_btrfs_reserved_extent_alloc(root, ins->objectid, ins->offset); diff --git a/trunk/fs/btrfs/extent_io.c b/trunk/fs/btrfs/extent_io.c index fcf77e1ded40..9d09a4f81875 100644 --- a/trunk/fs/btrfs/extent_io.c +++ b/trunk/fs/btrfs/extent_io.c @@ -3909,8 +3909,6 @@ int extent_range_uptodate(struct extent_io_tree *tree, while (start <= end) { index = start >> PAGE_CACHE_SHIFT; page = find_get_page(tree->mapping, index); - if (!page) - return 1; uptodate = PageUptodate(page); page_cache_release(page); if (!uptodate) { diff --git a/trunk/fs/btrfs/free-space-cache.c b/trunk/fs/btrfs/free-space-cache.c index c2f20594c9f7..d20ff87ca603 100644 --- a/trunk/fs/btrfs/free-space-cache.c +++ b/trunk/fs/btrfs/free-space-cache.c @@ -2242,7 +2242,7 @@ u64 btrfs_alloc_from_cluster(struct btrfs_block_group_cache *block_group, if (entry->bitmap) { ret = btrfs_alloc_from_bitmap(block_group, cluster, entry, bytes, - cluster->window_start); + min_start); if (ret == 0) { node = rb_next(&entry->offset_index); if (!node) @@ -2251,7 +2251,6 @@ u64 btrfs_alloc_from_cluster(struct btrfs_block_group_cache *block_group, offset_index); continue; } - cluster->window_start += bytes; } else { ret = entry->offset; @@ -2476,7 +2475,7 @@ setup_cluster_bitmap(struct btrfs_block_group_cache *block_group, } list_for_each_entry(entry, bitmaps, list) { - if (entry->bytes < bytes) + if (entry->bytes < min_bytes) continue; ret = btrfs_bitmap_cluster(block_group, entry, cluster, offset, bytes, cont1_bytes, min_bytes); diff --git a/trunk/fs/btrfs/inode.c b/trunk/fs/btrfs/inode.c index 32214fe0f7e3..0da19a0ea00d 100644 --- a/trunk/fs/btrfs/inode.c +++ b/trunk/fs/btrfs/inode.c @@ -6401,23 +6401,18 @@ int btrfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) unsigned long zero_start; loff_t size; int ret; - int reserved = 0; u64 page_start; u64 page_end; ret = btrfs_delalloc_reserve_space(inode, PAGE_CACHE_SIZE); - if (!ret) { + if (!ret) ret = btrfs_update_time(vma->vm_file); - reserved = 1; - } if (ret) { if (ret == -ENOMEM) ret = VM_FAULT_OOM; else /* -ENOSPC, -EIO, etc */ ret = VM_FAULT_SIGBUS; - if (reserved) - goto out; - goto out_noreserve; + goto out; } ret = VM_FAULT_NOPAGE; /* make the VM retry the fault */ @@ -6500,7 +6495,6 @@ int btrfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) unlock_page(page); out: btrfs_delalloc_release_space(inode, PAGE_CACHE_SIZE); -out_noreserve: return ret; } diff --git a/trunk/fs/btrfs/ioctl.c b/trunk/fs/btrfs/ioctl.c index 03bb62a9ee24..ab620014bcc3 100644 --- a/trunk/fs/btrfs/ioctl.c +++ b/trunk/fs/btrfs/ioctl.c @@ -1065,7 +1065,7 @@ int btrfs_defrag_file(struct inode *inode, struct file *file, i = range->start >> PAGE_CACHE_SHIFT; } if (!max_to_defrag) - max_to_defrag = last_index + 1; + max_to_defrag = last_index; /* * make writeback starts from i, so the defrag range can be diff --git a/trunk/fs/btrfs/tree-log.c b/trunk/fs/btrfs/tree-log.c index 966cc74f5d6c..cb877e0886a7 100644 --- a/trunk/fs/btrfs/tree-log.c +++ b/trunk/fs/btrfs/tree-log.c @@ -1957,8 +1957,7 @@ static int wait_log_commit(struct btrfs_trans_handle *trans, finish_wait(&root->log_commit_wait[index], &wait); mutex_lock(&root->log_mutex); - } while (root->fs_info->last_trans_log_full_commit != - trans->transid && root->log_transid < transid + 2 && + } while (root->log_transid < transid + 2 && atomic_read(&root->log_commit[index])); return 0; } @@ -1967,8 +1966,7 @@ static int wait_for_writer(struct btrfs_trans_handle *trans, struct btrfs_root *root) { DEFINE_WAIT(wait); - while (root->fs_info->last_trans_log_full_commit != - trans->transid && atomic_read(&root->log_writers)) { + while (atomic_read(&root->log_writers)) { prepare_to_wait(&root->log_writer_wait, &wait, TASK_UNINTERRUPTIBLE); mutex_unlock(&root->log_mutex); diff --git a/trunk/fs/ceph/caps.c b/trunk/fs/ceph/caps.c index 620daad201db..b60fc8bfb3e9 100644 --- a/trunk/fs/ceph/caps.c +++ b/trunk/fs/ceph/caps.c @@ -641,10 +641,10 @@ static int __cap_is_valid(struct ceph_cap *cap) unsigned long ttl; u32 gen; - spin_lock(&cap->session->s_gen_ttl_lock); + spin_lock(&cap->session->s_cap_lock); gen = cap->session->s_cap_gen; ttl = cap->session->s_cap_ttl; - spin_unlock(&cap->session->s_gen_ttl_lock); + spin_unlock(&cap->session->s_cap_lock); if (cap->cap_gen < gen || time_after_eq(jiffies, ttl)) { dout("__cap_is_valid %p cap %p issued %s " diff --git a/trunk/fs/ceph/dir.c b/trunk/fs/ceph/dir.c index 3e8094be4604..618246bc2196 100644 --- a/trunk/fs/ceph/dir.c +++ b/trunk/fs/ceph/dir.c @@ -975,10 +975,10 @@ static int dentry_lease_is_valid(struct dentry *dentry) di = ceph_dentry(dentry); if (di->lease_session) { s = di->lease_session; - spin_lock(&s->s_gen_ttl_lock); + spin_lock(&s->s_cap_lock); gen = s->s_cap_gen; ttl = s->s_cap_ttl; - spin_unlock(&s->s_gen_ttl_lock); + spin_unlock(&s->s_cap_lock); if (di->lease_gen == gen && time_before(jiffies, dentry->d_time) && diff --git a/trunk/fs/ceph/mds_client.c b/trunk/fs/ceph/mds_client.c index 866e8d7ca37d..23ab6a3f1825 100644 --- a/trunk/fs/ceph/mds_client.c +++ b/trunk/fs/ceph/mds_client.c @@ -262,7 +262,6 @@ static int parse_reply_info(struct ceph_msg *msg, /* trace */ ceph_decode_32_safe(&p, end, len, bad); if (len > 0) { - ceph_decode_need(&p, end, len, bad); err = parse_reply_info_trace(&p, p+len, info, features); if (err < 0) goto out_bad; @@ -271,7 +270,6 @@ static int parse_reply_info(struct ceph_msg *msg, /* extra */ ceph_decode_32_safe(&p, end, len, bad); if (len > 0) { - ceph_decode_need(&p, end, len, bad); err = parse_reply_info_extra(&p, p+len, info, features); if (err < 0) goto out_bad; @@ -400,11 +398,9 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc, s->s_con.peer_name.type = CEPH_ENTITY_TYPE_MDS; s->s_con.peer_name.num = cpu_to_le64(mds); - spin_lock_init(&s->s_gen_ttl_lock); + spin_lock_init(&s->s_cap_lock); s->s_cap_gen = 0; s->s_cap_ttl = 0; - - spin_lock_init(&s->s_cap_lock); s->s_renew_requested = 0; s->s_renew_seq = 0; INIT_LIST_HEAD(&s->s_caps); @@ -2330,10 +2326,10 @@ static void handle_session(struct ceph_mds_session *session, case CEPH_SESSION_STALE: pr_info("mds%d caps went stale, renewing\n", session->s_mds); - spin_lock(&session->s_gen_ttl_lock); + spin_lock(&session->s_cap_lock); session->s_cap_gen++; session->s_cap_ttl = 0; - spin_unlock(&session->s_gen_ttl_lock); + spin_unlock(&session->s_cap_lock); send_renew_caps(mdsc, session); break; diff --git a/trunk/fs/ceph/mds_client.h b/trunk/fs/ceph/mds_client.h index 8c7c04ebb595..a50ca0e39475 100644 --- a/trunk/fs/ceph/mds_client.h +++ b/trunk/fs/ceph/mds_client.h @@ -117,13 +117,10 @@ struct ceph_mds_session { void *s_authorizer_buf, *s_authorizer_reply_buf; size_t s_authorizer_buf_len, s_authorizer_reply_buf_len; - /* protected by s_gen_ttl_lock */ - spinlock_t s_gen_ttl_lock; - u32 s_cap_gen; /* inc each time we get mds stale msg */ - unsigned long s_cap_ttl; /* when session caps expire */ - /* protected by s_cap_lock */ spinlock_t s_cap_lock; + u32 s_cap_gen; /* inc each time we get mds stale msg */ + unsigned long s_cap_ttl; /* when session caps expire */ struct list_head s_caps; /* all caps issued by this session */ int s_nr_caps, s_trim_caps; int s_num_cap_releases; diff --git a/trunk/fs/ceph/xattr.c b/trunk/fs/ceph/xattr.c index a76f697303d9..857214ae8c08 100644 --- a/trunk/fs/ceph/xattr.c +++ b/trunk/fs/ceph/xattr.c @@ -111,10 +111,8 @@ static size_t ceph_vxattrcb_layout(struct ceph_inode_info *ci, char *val, } static struct ceph_vxattr_cb ceph_file_vxattrs[] = { - { true, "ceph.file.layout", ceph_vxattrcb_layout}, - /* The following extended attribute name is deprecated */ { true, "ceph.layout", ceph_vxattrcb_layout}, - { true, NULL, NULL } + { NULL, NULL } }; static struct ceph_vxattr_cb *ceph_inode_vxattrs(struct inode *inode) diff --git a/trunk/fs/cifs/Kconfig b/trunk/fs/cifs/Kconfig index 2b243af70aa3..0554b00a7b33 100644 --- a/trunk/fs/cifs/Kconfig +++ b/trunk/fs/cifs/Kconfig @@ -139,7 +139,7 @@ config CIFS_DFS_UPCALL points. If unsure, say N. config CIFS_FSCACHE - bool "Provide CIFS client caching support" + bool "Provide CIFS client caching support (EXPERIMENTAL)" depends on CIFS=m && FSCACHE || CIFS=y && FSCACHE=y help Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data @@ -147,7 +147,7 @@ config CIFS_FSCACHE manager. If unsure, say N. config CIFS_ACL - bool "Provide CIFS ACL support" + bool "Provide CIFS ACL support (EXPERIMENTAL)" depends on CIFS_XATTR && KEYS help Allows to fetch CIFS/NTFS ACL from the server. The DACL blob diff --git a/trunk/fs/cifs/connect.c b/trunk/fs/cifs/connect.c index 9c288653e6d6..986709a8d903 100644 --- a/trunk/fs/cifs/connect.c +++ b/trunk/fs/cifs/connect.c @@ -2142,14 +2142,14 @@ cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses) len = delim - payload; if (len > MAX_USERNAME_SIZE || len <= 0) { - cFYI(1, "Bad value from username search (len=%zd)", len); + cFYI(1, "Bad value from username search (len=%ld)", len); rc = -EINVAL; goto out_key_put; } vol->username = kstrndup(payload, len, GFP_KERNEL); if (!vol->username) { - cFYI(1, "Unable to allocate %zd bytes for username", len); + cFYI(1, "Unable to allocate %ld bytes for username", len); rc = -ENOMEM; goto out_key_put; } @@ -2157,7 +2157,7 @@ cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses) len = key->datalen - (len + 1); if (len > MAX_PASSWORD_SIZE || len <= 0) { - cFYI(1, "Bad len for password search (len=%zd)", len); + cFYI(1, "Bad len for password search (len=%ld)", len); rc = -EINVAL; kfree(vol->username); vol->username = NULL; @@ -2167,7 +2167,7 @@ cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses) ++delim; vol->password = kstrndup(delim, len, GFP_KERNEL); if (!vol->password) { - cFYI(1, "Unable to allocate %zd bytes for password", len); + cFYI(1, "Unable to allocate %ld bytes for password", len); rc = -ENOMEM; kfree(vol->username); vol->username = NULL; @@ -3857,8 +3857,10 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) struct smb_vol *vol_info; vol_info = kzalloc(sizeof(*vol_info), GFP_KERNEL); - if (vol_info == NULL) - return ERR_PTR(-ENOMEM); + if (vol_info == NULL) { + tcon = ERR_PTR(-ENOMEM); + goto out; + } vol_info->local_nls = cifs_sb->local_nls; vol_info->linux_uid = fsuid; diff --git a/trunk/fs/cifs/sess.c b/trunk/fs/cifs/sess.c index 551d0c2b9736..d85efad5765f 100644 --- a/trunk/fs/cifs/sess.c +++ b/trunk/fs/cifs/sess.c @@ -246,15 +246,16 @@ static void ascii_ssetup_strings(char **pbcc_area, struct cifs_ses *ses, /* copy user */ /* BB what about null user mounts - check that we do this BB */ /* copy user */ - if (ses->user_name != NULL) { + if (ses->user_name != NULL) strncpy(bcc_ptr, ses->user_name, MAX_USERNAME_SIZE); - bcc_ptr += strnlen(ses->user_name, MAX_USERNAME_SIZE); - } /* else null user mount */ + + bcc_ptr += strnlen(ses->user_name, MAX_USERNAME_SIZE); *bcc_ptr = 0; bcc_ptr++; /* account for null termination */ /* copy domain */ + if (ses->domainName != NULL) { strncpy(bcc_ptr, ses->domainName, 256); bcc_ptr += strnlen(ses->domainName, 256); @@ -394,10 +395,6 @@ static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, ses->ntlmssp->server_flags = le32_to_cpu(pblob->NegotiateFlags); tioffset = le32_to_cpu(pblob->TargetInfoArray.BufferOffset); tilen = le16_to_cpu(pblob->TargetInfoArray.Length); - if (tioffset > blob_len || tioffset + tilen > blob_len) { - cERROR(1, "tioffset + tilen too high %u + %u", tioffset, tilen); - return -EINVAL; - } if (tilen) { ses->auth_key.response = kmalloc(tilen, GFP_KERNEL); if (!ses->auth_key.response) { diff --git a/trunk/fs/ecryptfs/crypto.c b/trunk/fs/ecryptfs/crypto.c index 63ab24510649..2a834255c75d 100644 --- a/trunk/fs/ecryptfs/crypto.c +++ b/trunk/fs/ecryptfs/crypto.c @@ -417,6 +417,17 @@ static int ecryptfs_encrypt_extent(struct page *enc_extent_page, (unsigned long long)(extent_base + extent_offset), rc); goto out; } + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "Encrypting extent " + "with iv:\n"); + ecryptfs_dump_hex(extent_iv, crypt_stat->iv_bytes); + ecryptfs_printk(KERN_DEBUG, "First 8 bytes before " + "encryption:\n"); + ecryptfs_dump_hex((char *) + (page_address(page) + + (extent_offset * crypt_stat->extent_size)), + 8); + } rc = ecryptfs_encrypt_page_offset(crypt_stat, enc_extent_page, 0, page, (extent_offset * crypt_stat->extent_size), @@ -429,6 +440,14 @@ static int ecryptfs_encrypt_extent(struct page *enc_extent_page, goto out; } rc = 0; + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "Encrypt extent [0x%.16llx]; " + "rc = [%d]\n", + (unsigned long long)(extent_base + extent_offset), rc); + ecryptfs_printk(KERN_DEBUG, "First 8 bytes after " + "encryption:\n"); + ecryptfs_dump_hex((char *)(page_address(enc_extent_page)), 8); + } out: return rc; } @@ -524,6 +543,17 @@ static int ecryptfs_decrypt_extent(struct page *page, (unsigned long long)(extent_base + extent_offset), rc); goto out; } + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "Decrypting extent " + "with iv:\n"); + ecryptfs_dump_hex(extent_iv, crypt_stat->iv_bytes); + ecryptfs_printk(KERN_DEBUG, "First 8 bytes before " + "decryption:\n"); + ecryptfs_dump_hex((char *) + (page_address(enc_extent_page) + + (extent_offset * crypt_stat->extent_size)), + 8); + } rc = ecryptfs_decrypt_page_offset(crypt_stat, page, (extent_offset * crypt_stat->extent_size), @@ -537,6 +567,16 @@ static int ecryptfs_decrypt_extent(struct page *page, goto out; } rc = 0; + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "Decrypt extent [0x%.16llx]; " + "rc = [%d]\n", + (unsigned long long)(extent_base + extent_offset), rc); + ecryptfs_printk(KERN_DEBUG, "First 8 bytes after " + "decryption:\n"); + ecryptfs_dump_hex((char *)(page_address(page) + + (extent_offset + * crypt_stat->extent_size)), 8); + } out: return rc; } @@ -1550,8 +1590,8 @@ int ecryptfs_read_and_validate_xattr_region(struct dentry *dentry, */ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry) { - int rc; - char *page_virt; + int rc = 0; + char *page_virt = NULL; struct inode *ecryptfs_inode = ecryptfs_dentry->d_inode; struct ecryptfs_crypt_stat *crypt_stat = &ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat; @@ -1576,13 +1616,11 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry) ecryptfs_dentry, ECRYPTFS_VALIDATE_HEADER_SIZE); if (rc) { - /* metadata is not in the file header, so try xattrs */ memset(page_virt, 0, PAGE_CACHE_SIZE); rc = ecryptfs_read_xattr_region(page_virt, ecryptfs_inode); if (rc) { printk(KERN_DEBUG "Valid eCryptfs headers not found in " - "file header region or xattr region, inode %lu\n", - ecryptfs_inode->i_ino); + "file header region or xattr region\n"); rc = -EINVAL; goto out; } @@ -1591,8 +1629,7 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry) ECRYPTFS_DONT_VALIDATE_HEADER_SIZE); if (rc) { printk(KERN_DEBUG "Valid eCryptfs headers not found in " - "file xattr region either, inode %lu\n", - ecryptfs_inode->i_ino); + "file xattr region either\n"); rc = -EINVAL; } if (crypt_stat->mount_crypt_stat->flags @@ -1603,8 +1640,7 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry) "crypto metadata only in the extended attribute " "region, but eCryptfs was mounted without " "xattr support enabled. eCryptfs will not treat " - "this like an encrypted file, inode %lu\n", - ecryptfs_inode->i_ino); + "this like an encrypted file.\n"); rc = -EINVAL; } } diff --git a/trunk/fs/ecryptfs/ecryptfs_kernel.h b/trunk/fs/ecryptfs/ecryptfs_kernel.h index a2362df58ae8..a9f29b12fbf2 100644 --- a/trunk/fs/ecryptfs/ecryptfs_kernel.h +++ b/trunk/fs/ecryptfs/ecryptfs_kernel.h @@ -151,11 +151,6 @@ ecryptfs_get_key_payload_data(struct key *key) * dentry name */ #define ECRYPTFS_TAG_73_PACKET_TYPE 0x49 /* FEK-encrypted filename as * metadata */ -#define ECRYPTFS_MIN_PKT_LEN_SIZE 1 /* Min size to specify packet length */ -#define ECRYPTFS_MAX_PKT_LEN_SIZE 2 /* Pass at least this many bytes to - * ecryptfs_parse_packet_length() and - * ecryptfs_write_packet_length() - */ /* Constraint: ECRYPTFS_FILENAME_MIN_RANDOM_PREPEND_BYTES >= * ECRYPTFS_MAX_IV_BYTES */ #define ECRYPTFS_FILENAME_MIN_RANDOM_PREPEND_BYTES 16 diff --git a/trunk/fs/ecryptfs/inode.c b/trunk/fs/ecryptfs/inode.c index 19892d7d2ed1..19a8ca4ab1dd 100644 --- a/trunk/fs/ecryptfs/inode.c +++ b/trunk/fs/ecryptfs/inode.c @@ -822,6 +822,18 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia, size_t num_zeros = (PAGE_CACHE_SIZE - (ia->ia_size & ~PAGE_CACHE_MASK)); + + /* + * XXX(truncate) this should really happen at the begginning + * of ->setattr. But the code is too messy to that as part + * of a larger patch. ecryptfs is also totally missing out + * on the inode_change_ok check at the beginning of + * ->setattr while would include this. + */ + rc = inode_newsize_ok(inode, ia->ia_size); + if (rc) + goto out; + if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) { truncate_setsize(inode, ia->ia_size); lower_ia->ia_size = ia->ia_size; @@ -871,28 +883,6 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia, return rc; } -static int ecryptfs_inode_newsize_ok(struct inode *inode, loff_t offset) -{ - struct ecryptfs_crypt_stat *crypt_stat; - loff_t lower_oldsize, lower_newsize; - - crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat; - lower_oldsize = upper_size_to_lower_size(crypt_stat, - i_size_read(inode)); - lower_newsize = upper_size_to_lower_size(crypt_stat, offset); - if (lower_newsize > lower_oldsize) { - /* - * The eCryptfs inode and the new *lower* size are mixed here - * because we may not have the lower i_mutex held and/or it may - * not be appropriate to call inode_newsize_ok() with inodes - * from other filesystems. - */ - return inode_newsize_ok(inode, lower_newsize); - } - - return 0; -} - /** * ecryptfs_truncate * @dentry: The ecryptfs layer dentry @@ -909,10 +899,6 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length) struct iattr lower_ia = { .ia_valid = 0 }; int rc; - rc = ecryptfs_inode_newsize_ok(dentry->d_inode, new_length); - if (rc) - return rc; - rc = truncate_upper(dentry, &ia, &lower_ia); if (!rc && lower_ia.ia_valid & ATTR_SIZE) { struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); @@ -992,16 +978,6 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia) } } mutex_unlock(&crypt_stat->cs_mutex); - - rc = inode_change_ok(inode, ia); - if (rc) - goto out; - if (ia->ia_valid & ATTR_SIZE) { - rc = ecryptfs_inode_newsize_ok(inode, ia->ia_size); - if (rc) - goto out; - } - if (S_ISREG(inode->i_mode)) { rc = filemap_write_and_wait(inode->i_mapping); if (rc) diff --git a/trunk/fs/ecryptfs/keystore.c b/trunk/fs/ecryptfs/keystore.c index 8e3b943e330f..ac1ad48c2376 100644 --- a/trunk/fs/ecryptfs/keystore.c +++ b/trunk/fs/ecryptfs/keystore.c @@ -109,7 +109,7 @@ int ecryptfs_parse_packet_length(unsigned char *data, size_t *size, (*size) += ((unsigned char)(data[1]) + 192); (*length_size) = 2; } else if (data[0] == 255) { - /* If support is added, adjust ECRYPTFS_MAX_PKT_LEN_SIZE */ + /* Five-byte length; we're not supposed to see this */ ecryptfs_printk(KERN_ERR, "Five-byte packet length not " "supported\n"); rc = -EINVAL; @@ -126,7 +126,7 @@ int ecryptfs_parse_packet_length(unsigned char *data, size_t *size, /** * ecryptfs_write_packet_length * @dest: The byte array target into which to write the length. Must - * have at least ECRYPTFS_MAX_PKT_LEN_SIZE bytes allocated. + * have at least 5 bytes allocated. * @size: The length to write. * @packet_size_length: The number of bytes used to encode the packet * length is written to this address. @@ -146,7 +146,6 @@ int ecryptfs_write_packet_length(char *dest, size_t size, dest[1] = ((size - 192) % 256); (*packet_size_length) = 2; } else { - /* If support is added, adjust ECRYPTFS_MAX_PKT_LEN_SIZE */ rc = -EINVAL; ecryptfs_printk(KERN_WARNING, "Unsupported packet size: [%zd]\n", size); diff --git a/trunk/fs/ecryptfs/miscdev.c b/trunk/fs/ecryptfs/miscdev.c index 349209dc6a91..940a82e63dc3 100644 --- a/trunk/fs/ecryptfs/miscdev.c +++ b/trunk/fs/ecryptfs/miscdev.c @@ -218,29 +218,6 @@ int ecryptfs_send_miscdev(char *data, size_t data_size, return rc; } -/* - * miscdevfs packet format: - * Octet 0: Type - * Octets 1-4: network byte order msg_ctx->counter - * Octets 5-N0: Size of struct ecryptfs_message to follow - * Octets N0-N1: struct ecryptfs_message (including data) - * - * Octets 5-N1 not written if the packet type does not include a message - */ -#define PKT_TYPE_SIZE 1 -#define PKT_CTR_SIZE 4 -#define MIN_NON_MSG_PKT_SIZE (PKT_TYPE_SIZE + PKT_CTR_SIZE) -#define MIN_MSG_PKT_SIZE (PKT_TYPE_SIZE + PKT_CTR_SIZE \ - + ECRYPTFS_MIN_PKT_LEN_SIZE) -/* 4 + ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES comes from tag 65 packet format */ -#define MAX_MSG_PKT_SIZE (PKT_TYPE_SIZE + PKT_CTR_SIZE \ - + ECRYPTFS_MAX_PKT_LEN_SIZE \ - + sizeof(struct ecryptfs_message) \ - + 4 + ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES) -#define PKT_TYPE_OFFSET 0 -#define PKT_CTR_OFFSET PKT_TYPE_SIZE -#define PKT_LEN_OFFSET (PKT_TYPE_SIZE + PKT_CTR_SIZE) - /** * ecryptfs_miscdev_read - format and send message from queue * @file: fs/ecryptfs/euid miscdevfs handle (ignored) @@ -260,7 +237,7 @@ ecryptfs_miscdev_read(struct file *file, char __user *buf, size_t count, struct ecryptfs_daemon *daemon; struct ecryptfs_msg_ctx *msg_ctx; size_t packet_length_size; - char packet_length[ECRYPTFS_MAX_PKT_LEN_SIZE]; + char packet_length[3]; size_t i; size_t total_length; uid_t euid = current_euid(); @@ -328,8 +305,15 @@ ecryptfs_miscdev_read(struct file *file, char __user *buf, size_t count, packet_length_size = 0; msg_ctx->msg_size = 0; } - total_length = (PKT_TYPE_SIZE + PKT_CTR_SIZE + packet_length_size - + msg_ctx->msg_size); + /* miscdevfs packet format: + * Octet 0: Type + * Octets 1-4: network byte order msg_ctx->counter + * Octets 5-N0: Size of struct ecryptfs_message to follow + * Octets N0-N1: struct ecryptfs_message (including data) + * + * Octets 5-N1 not written if the packet type does not + * include a message */ + total_length = (1 + 4 + packet_length_size + msg_ctx->msg_size); if (count < total_length) { rc = 0; printk(KERN_WARNING "%s: Only given user buffer of " @@ -340,10 +324,9 @@ ecryptfs_miscdev_read(struct file *file, char __user *buf, size_t count, rc = -EFAULT; if (put_user(msg_ctx->type, buf)) goto out_unlock_msg_ctx; - if (put_user(cpu_to_be32(msg_ctx->counter), - (__be32 __user *)(&buf[PKT_CTR_OFFSET]))) + if (put_user(cpu_to_be32(msg_ctx->counter), (__be32 __user *)(buf + 1))) goto out_unlock_msg_ctx; - i = PKT_TYPE_SIZE + PKT_CTR_SIZE; + i = 5; if (msg_ctx->msg) { if (copy_to_user(&buf[i], packet_length, packet_length_size)) goto out_unlock_msg_ctx; @@ -408,6 +391,12 @@ static int ecryptfs_miscdev_response(char *data, size_t data_size, * @count: Amount of data in @buf * @ppos: Pointer to offset in file (ignored) * + * miscdevfs packet format: + * Octet 0: Type + * Octets 1-4: network byte order msg_ctx->counter (0's for non-response) + * Octets 5-N0: Size of struct ecryptfs_message to follow + * Octets N0-N1: struct ecryptfs_message (including data) + * * Returns the number of bytes read from @buf */ static ssize_t @@ -416,78 +405,60 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf, { __be32 counter_nbo; u32 seq; - size_t packet_size, packet_size_length; + size_t packet_size, packet_size_length, i; + ssize_t sz = 0; char *data; uid_t euid = current_euid(); - unsigned char packet_size_peek[ECRYPTFS_MAX_PKT_LEN_SIZE]; - ssize_t rc; - - if (count == 0) { - return 0; - } else if (count == MIN_NON_MSG_PKT_SIZE) { - /* Likely a harmless MSG_HELO or MSG_QUIT - no packet length */ - goto memdup; - } else if (count < MIN_MSG_PKT_SIZE || count > MAX_MSG_PKT_SIZE) { - printk(KERN_WARNING "%s: Acceptable packet size range is " - "[%d-%lu], but amount of data written is [%zu].", - __func__, MIN_MSG_PKT_SIZE, MAX_MSG_PKT_SIZE, count); - return -EINVAL; - } - - if (copy_from_user(packet_size_peek, &buf[PKT_LEN_OFFSET], - sizeof(packet_size_peek))) { - printk(KERN_WARNING "%s: Error while inspecting packet size\n", - __func__); - return -EFAULT; - } - - rc = ecryptfs_parse_packet_length(packet_size_peek, &packet_size, - &packet_size_length); - if (rc) { - printk(KERN_WARNING "%s: Error parsing packet length; " - "rc = [%zd]\n", __func__, rc); - return rc; - } + int rc; - if ((PKT_TYPE_SIZE + PKT_CTR_SIZE + packet_size_length + packet_size) - != count) { - printk(KERN_WARNING "%s: Invalid packet size [%zu]\n", __func__, - packet_size); - return -EINVAL; - } + if (count == 0) + goto out; -memdup: data = memdup_user(buf, count); if (IS_ERR(data)) { printk(KERN_ERR "%s: memdup_user returned error [%ld]\n", __func__, PTR_ERR(data)); - return PTR_ERR(data); + goto out; } - switch (data[PKT_TYPE_OFFSET]) { + sz = count; + i = 0; + switch (data[i++]) { case ECRYPTFS_MSG_RESPONSE: - if (count < (MIN_MSG_PKT_SIZE - + sizeof(struct ecryptfs_message))) { + if (count < (1 + 4 + 1 + sizeof(struct ecryptfs_message))) { printk(KERN_WARNING "%s: Minimum acceptable packet " "size is [%zd], but amount of data written is " "only [%zd]. Discarding response packet.\n", __func__, - (MIN_MSG_PKT_SIZE - + sizeof(struct ecryptfs_message)), count); - rc = -EINVAL; + (1 + 4 + 1 + sizeof(struct ecryptfs_message)), + count); goto out_free; } - memcpy(&counter_nbo, &data[PKT_CTR_OFFSET], PKT_CTR_SIZE); + memcpy(&counter_nbo, &data[i], 4); seq = be32_to_cpu(counter_nbo); - rc = ecryptfs_miscdev_response( - &data[PKT_LEN_OFFSET + packet_size_length], - packet_size, euid, current_user_ns(), - task_pid(current), seq); + i += 4; + rc = ecryptfs_parse_packet_length(&data[i], &packet_size, + &packet_size_length); if (rc) { - printk(KERN_WARNING "%s: Failed to deliver miscdev " - "response to requesting operation; rc = [%zd]\n", - __func__, rc); + printk(KERN_WARNING "%s: Error parsing packet length; " + "rc = [%d]\n", __func__, rc); goto out_free; } + i += packet_size_length; + if ((1 + 4 + packet_size_length + packet_size) != count) { + printk(KERN_WARNING "%s: (1 + packet_size_length([%zd])" + " + packet_size([%zd]))([%zd]) != " + "count([%zd]). Invalid packet format.\n", + __func__, packet_size_length, packet_size, + (1 + packet_size_length + packet_size), count); + goto out_free; + } + rc = ecryptfs_miscdev_response(&data[i], packet_size, + euid, current_user_ns(), + task_pid(current), seq); + if (rc) + printk(KERN_WARNING "%s: Failed to deliver miscdev " + "response to requesting operation; rc = [%d]\n", + __func__, rc); break; case ECRYPTFS_MSG_HELO: case ECRYPTFS_MSG_QUIT: @@ -496,13 +467,12 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf, ecryptfs_printk(KERN_WARNING, "Dropping miscdev " "message of unrecognized type [%d]\n", data[0]); - rc = -EINVAL; - goto out_free; + break; } - rc = count; out_free: kfree(data); - return rc; +out: + return sz; } diff --git a/trunk/fs/ecryptfs/mmap.c b/trunk/fs/ecryptfs/mmap.c index 10ec695ccd68..6a44148c5fb9 100644 --- a/trunk/fs/ecryptfs/mmap.c +++ b/trunk/fs/ecryptfs/mmap.c @@ -57,10 +57,6 @@ struct page *ecryptfs_get_locked_page(struct inode *inode, loff_t index) * @page: Page that is locked before this call is made * * Returns zero on success; non-zero otherwise - * - * This is where we encrypt the data and pass the encrypted data to - * the lower filesystem. In OpenPGP-compatible mode, we operate on - * entire underlying packets. */ static int ecryptfs_writepage(struct page *page, struct writeback_control *wbc) { @@ -485,6 +481,10 @@ int ecryptfs_write_inode_size_to_metadata(struct inode *ecryptfs_inode) * @copied: The amount of data copied * @page: The eCryptfs page * @fsdata: The fsdata (unused) + * + * This is where we encrypt the data and pass the encrypted data to + * the lower filesystem. In OpenPGP-compatible mode, we operate on + * entire underlying packets. */ static int ecryptfs_write_end(struct file *file, struct address_space *mapping, diff --git a/trunk/fs/ecryptfs/read_write.c b/trunk/fs/ecryptfs/read_write.c index 5c0106f75775..3745f7c2b9c2 100644 --- a/trunk/fs/ecryptfs/read_write.c +++ b/trunk/fs/ecryptfs/read_write.c @@ -130,18 +130,13 @@ int ecryptfs_write(struct inode *ecryptfs_inode, char *data, loff_t offset, pgoff_t ecryptfs_page_idx = (pos >> PAGE_CACHE_SHIFT); size_t start_offset_in_page = (pos & ~PAGE_CACHE_MASK); size_t num_bytes = (PAGE_CACHE_SIZE - start_offset_in_page); - loff_t total_remaining_bytes = ((offset + size) - pos); - - if (fatal_signal_pending(current)) { - rc = -EINTR; - break; - } + size_t total_remaining_bytes = ((offset + size) - pos); if (num_bytes > total_remaining_bytes) num_bytes = total_remaining_bytes; if (pos < offset) { /* remaining zeros to write, up to destination offset */ - loff_t total_remaining_zeros = (offset - pos); + size_t total_remaining_zeros = (offset - pos); if (num_bytes > total_remaining_zeros) num_bytes = total_remaining_zeros; @@ -198,19 +193,15 @@ int ecryptfs_write(struct inode *ecryptfs_inode, char *data, loff_t offset, } pos += num_bytes; } - if (pos > ecryptfs_file_size) { - i_size_write(ecryptfs_inode, pos); + if ((offset + size) > ecryptfs_file_size) { + i_size_write(ecryptfs_inode, (offset + size)); if (crypt_stat->flags & ECRYPTFS_ENCRYPTED) { - int rc2; - - rc2 = ecryptfs_write_inode_size_to_metadata( + rc = ecryptfs_write_inode_size_to_metadata( ecryptfs_inode); - if (rc2) { + if (rc) { printk(KERN_ERR "Problem with " "ecryptfs_write_inode_size_to_metadata; " - "rc = [%d]\n", rc2); - if (!rc) - rc = rc2; + "rc = [%d]\n", rc); goto out; } } @@ -282,3 +273,76 @@ int ecryptfs_read_lower_page_segment(struct page *page_for_ecryptfs, flush_dcache_page(page_for_ecryptfs); return rc; } + +#if 0 +/** + * ecryptfs_read + * @data: The virtual address into which to write the data read (and + * possibly decrypted) from the lower file + * @offset: The offset in the decrypted view of the file from which to + * read into @data + * @size: The number of bytes to read into @data + * @ecryptfs_file: The eCryptfs file from which to read + * + * Read an arbitrary amount of data from an arbitrary location in the + * eCryptfs page cache. This is done on an extent-by-extent basis; + * individual extents are decrypted and read from the lower page + * cache (via VFS reads). This function takes care of all the + * address translation to locations in the lower filesystem. + * + * Returns zero on success; non-zero otherwise + */ +int ecryptfs_read(char *data, loff_t offset, size_t size, + struct file *ecryptfs_file) +{ + struct inode *ecryptfs_inode = ecryptfs_file->f_dentry->d_inode; + struct page *ecryptfs_page; + char *ecryptfs_page_virt; + loff_t ecryptfs_file_size = i_size_read(ecryptfs_inode); + loff_t data_offset = 0; + loff_t pos; + int rc = 0; + + if ((offset + size) > ecryptfs_file_size) { + rc = -EINVAL; + printk(KERN_ERR "%s: Attempt to read data past the end of the " + "file; offset = [%lld]; size = [%td]; " + "ecryptfs_file_size = [%lld]\n", + __func__, offset, size, ecryptfs_file_size); + goto out; + } + pos = offset; + while (pos < (offset + size)) { + pgoff_t ecryptfs_page_idx = (pos >> PAGE_CACHE_SHIFT); + size_t start_offset_in_page = (pos & ~PAGE_CACHE_MASK); + size_t num_bytes = (PAGE_CACHE_SIZE - start_offset_in_page); + size_t total_remaining_bytes = ((offset + size) - pos); + + if (num_bytes > total_remaining_bytes) + num_bytes = total_remaining_bytes; + ecryptfs_page = ecryptfs_get_locked_page(ecryptfs_inode, + ecryptfs_page_idx); + if (IS_ERR(ecryptfs_page)) { + rc = PTR_ERR(ecryptfs_page); + printk(KERN_ERR "%s: Error getting page at " + "index [%ld] from eCryptfs inode " + "mapping; rc = [%d]\n", __func__, + ecryptfs_page_idx, rc); + goto out; + } + ecryptfs_page_virt = kmap_atomic(ecryptfs_page, KM_USER0); + memcpy((data + data_offset), + ((char *)ecryptfs_page_virt + start_offset_in_page), + num_bytes); + kunmap_atomic(ecryptfs_page_virt, KM_USER0); + flush_dcache_page(ecryptfs_page); + SetPageUptodate(ecryptfs_page); + unlock_page(ecryptfs_page); + page_cache_release(ecryptfs_page); + pos += num_bytes; + data_offset += num_bytes; + } +out: + return rc; +} +#endif /* 0 */ diff --git a/trunk/fs/exec.c b/trunk/fs/exec.c index 92ce83a11e90..aeb135c7ff5c 100644 --- a/trunk/fs/exec.c +++ b/trunk/fs/exec.c @@ -1071,21 +1071,6 @@ void set_task_comm(struct task_struct *tsk, char *buf) perf_event_comm(tsk); } -static void filename_to_taskname(char *tcomm, const char *fn, unsigned int len) -{ - int i, ch; - - /* Copies the binary name from after last slash */ - for (i = 0; (ch = *(fn++)) != '\0';) { - if (ch == '/') - i = 0; /* overwrite what we wrote */ - else - if (i < len - 1) - tcomm[i++] = ch; - } - tcomm[i] = '\0'; -} - int flush_old_exec(struct linux_binprm * bprm) { int retval; @@ -1100,7 +1085,6 @@ int flush_old_exec(struct linux_binprm * bprm) set_mm_exe_file(bprm->mm, bprm->file); - filename_to_taskname(bprm->tcomm, bprm->filename, sizeof(bprm->tcomm)); /* * Release all of the old mmap stuff */ @@ -1132,6 +1116,10 @@ EXPORT_SYMBOL(would_dump); void setup_new_exec(struct linux_binprm * bprm) { + int i, ch; + const char *name; + char tcomm[sizeof(current->comm)]; + arch_pick_mmap_layout(current->mm); /* This is the point of no return */ @@ -1142,7 +1130,18 @@ void setup_new_exec(struct linux_binprm * bprm) else set_dumpable(current->mm, suid_dumpable); - set_task_comm(current, bprm->tcomm); + name = bprm->filename; + + /* Copies the binary name from after last slash */ + for (i=0; (ch = *(name++)) != '\0';) { + if (ch == '/') + i = 0; /* overwrite what we wrote */ + else + if (i < (sizeof(tcomm) - 1)) + tcomm[i++] = ch; + } + tcomm[i] = '\0'; + set_task_comm(current, tcomm); /* Set the new mm task size. We have to do that late because it may * depend on TIF_32BIT which is only updated in flush_thread() on 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/jffs2/erase.c b/trunk/fs/jffs2/erase.c index eafb8d37a6fb..a01cdad6aad1 100644 --- a/trunk/fs/jffs2/erase.c +++ b/trunk/fs/jffs2/erase.c @@ -335,7 +335,7 @@ static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_erasebl void *ebuf; uint32_t ofs; size_t retlen; - int ret; + int ret = -EIO; unsigned long *wordebuf; ret = mtd_point(c->mtd, jeb->offset, c->sector_size, &retlen, diff --git a/trunk/fs/logfs/dev_mtd.c b/trunk/fs/logfs/dev_mtd.c index 9c501449450d..e97404d611e0 100644 --- a/trunk/fs/logfs/dev_mtd.c +++ b/trunk/fs/logfs/dev_mtd.c @@ -152,6 +152,9 @@ static struct page *logfs_mtd_find_first_sb(struct super_block *sb, u64 *ofs) filler_t *filler = logfs_mtd_readpage; struct mtd_info *mtd = super->s_mtd; + if (!mtd_can_have_bb(mtd)) + return NULL; + *ofs = 0; while (mtd_block_isbad(mtd, *ofs)) { *ofs += mtd->erasesize; @@ -169,6 +172,9 @@ static struct page *logfs_mtd_find_last_sb(struct super_block *sb, u64 *ofs) filler_t *filler = logfs_mtd_readpage; struct mtd_info *mtd = super->s_mtd; + if (!mtd_can_have_bb(mtd)) + return NULL; + *ofs = mtd->size - mtd->erasesize; while (mtd_block_isbad(mtd, *ofs)) { *ofs -= mtd->erasesize; diff --git a/trunk/fs/logfs/dir.c b/trunk/fs/logfs/dir.c index 3de7a32cadbe..501043e8966c 100644 --- a/trunk/fs/logfs/dir.c +++ b/trunk/fs/logfs/dir.c @@ -71,7 +71,7 @@ static int write_dir(struct inode *dir, struct logfs_disk_dentry *dd, static int write_inode(struct inode *inode) { - return __logfs_write_inode(inode, NULL, WF_LOCK); + return __logfs_write_inode(inode, WF_LOCK); } static s64 dir_seek_data(struct inode *inode, s64 pos) diff --git a/trunk/fs/logfs/file.c b/trunk/fs/logfs/file.c index 3886cded283c..b548c87a86f1 100644 --- a/trunk/fs/logfs/file.c +++ b/trunk/fs/logfs/file.c @@ -230,9 +230,7 @@ int logfs_fsync(struct file *file, loff_t start, loff_t end, int datasync) return ret; mutex_lock(&inode->i_mutex); - logfs_get_wblocks(sb, NULL, WF_LOCK); logfs_write_anchor(sb); - logfs_put_wblocks(sb, NULL, WF_LOCK); mutex_unlock(&inode->i_mutex); return 0; diff --git a/trunk/fs/logfs/gc.c b/trunk/fs/logfs/gc.c index d4efb061bdc5..caa4419285dc 100644 --- a/trunk/fs/logfs/gc.c +++ b/trunk/fs/logfs/gc.c @@ -367,7 +367,7 @@ static struct gc_candidate *get_candidate(struct super_block *sb) int i, max_dist; struct gc_candidate *cand = NULL, *this; - max_dist = min(no_free_segments(sb), LOGFS_NO_AREAS - 1); + max_dist = min(no_free_segments(sb), LOGFS_NO_AREAS); for (i = max_dist; i >= 0; i--) { this = first_in_list(&super->s_low_list[i]); diff --git a/trunk/fs/logfs/inode.c b/trunk/fs/logfs/inode.c index a422f42238b2..388df1aa35e5 100644 --- a/trunk/fs/logfs/inode.c +++ b/trunk/fs/logfs/inode.c @@ -286,7 +286,7 @@ static int logfs_write_inode(struct inode *inode, struct writeback_control *wbc) if (logfs_inode(inode)->li_flags & LOGFS_IF_STILLBORN) return 0; - ret = __logfs_write_inode(inode, NULL, flags); + ret = __logfs_write_inode(inode, flags); LOGFS_BUG_ON(ret, inode->i_sb); return ret; } @@ -363,9 +363,7 @@ static void logfs_init_once(void *_li) static int logfs_sync_fs(struct super_block *sb, int wait) { - logfs_get_wblocks(sb, NULL, WF_LOCK); logfs_write_anchor(sb); - logfs_put_wblocks(sb, NULL, WF_LOCK); return 0; } diff --git a/trunk/fs/logfs/journal.c b/trunk/fs/logfs/journal.c index 1e1c369df22b..9da29706f91c 100644 --- a/trunk/fs/logfs/journal.c +++ b/trunk/fs/logfs/journal.c @@ -612,6 +612,7 @@ static size_t __logfs_write_je(struct super_block *sb, void *buf, u16 type, if (len == 0) return logfs_write_header(super, header, 0, type); + BUG_ON(len > sb->s_blocksize); compr_len = logfs_compress(buf, data, len, sb->s_blocksize); if (compr_len < 0 || type == JE_ANCHOR) { memcpy(data, buf, len); diff --git a/trunk/fs/logfs/logfs.h b/trunk/fs/logfs/logfs.h index 5f0937609465..926373866a55 100644 --- a/trunk/fs/logfs/logfs.h +++ b/trunk/fs/logfs/logfs.h @@ -528,7 +528,7 @@ void logfs_destroy_inode_cache(void); void logfs_set_blocks(struct inode *inode, u64 no); /* these logically belong into inode.c but actually reside in readwrite.c */ int logfs_read_inode(struct inode *inode); -int __logfs_write_inode(struct inode *inode, struct page *, long flags); +int __logfs_write_inode(struct inode *inode, long flags); void logfs_evict_inode(struct inode *inode); /* journal.c */ @@ -577,8 +577,6 @@ void initialize_block_counters(struct page *page, struct logfs_block *block, __be64 *array, int page_is_empty); int logfs_exist_block(struct inode *inode, u64 bix); int get_page_reserve(struct inode *inode, struct page *page); -void logfs_get_wblocks(struct super_block *sb, struct page *page, int lock); -void logfs_put_wblocks(struct super_block *sb, struct page *page, int lock); extern struct logfs_block_ops indirect_block_ops; /* segment.c */ @@ -596,7 +594,6 @@ int logfs_init_mapping(struct super_block *sb); void logfs_sync_area(struct logfs_area *area); void logfs_sync_segments(struct super_block *sb); void freeseg(struct super_block *sb, u32 segno); -void free_areas(struct super_block *sb); /* area handling */ int logfs_init_areas(struct super_block *sb); diff --git a/trunk/fs/logfs/readwrite.c b/trunk/fs/logfs/readwrite.c index 4153e65b0148..2ac4217b7901 100644 --- a/trunk/fs/logfs/readwrite.c +++ b/trunk/fs/logfs/readwrite.c @@ -244,7 +244,8 @@ static void preunlock_page(struct super_block *sb, struct page *page, int lock) * is waiting for s_write_mutex. We annotate this fact by setting PG_pre_locked * in addition to PG_locked. */ -void logfs_get_wblocks(struct super_block *sb, struct page *page, int lock) +static void logfs_get_wblocks(struct super_block *sb, struct page *page, + int lock) { struct logfs_super *super = logfs_super(sb); @@ -259,7 +260,8 @@ void logfs_get_wblocks(struct super_block *sb, struct page *page, int lock) } } -void logfs_put_wblocks(struct super_block *sb, struct page *page, int lock) +static void logfs_put_wblocks(struct super_block *sb, struct page *page, + int lock) { struct logfs_super *super = logfs_super(sb); @@ -422,7 +424,7 @@ static void inode_write_block(struct logfs_block *block) if (inode->i_ino == LOGFS_INO_MASTER) logfs_write_anchor(inode->i_sb); else { - ret = __logfs_write_inode(inode, NULL, 0); + ret = __logfs_write_inode(inode, 0); /* see indirect_write_block comment */ BUG_ON(ret); } @@ -558,13 +560,8 @@ static void inode_free_block(struct super_block *sb, struct logfs_block *block) static void indirect_free_block(struct super_block *sb, struct logfs_block *block) { - struct page *page = block->page; - - if (PagePrivate(page)) { - ClearPagePrivate(page); - page_cache_release(page); - set_page_private(page, 0); - } + ClearPagePrivate(block->page); + block->page->private = 0; __free_block(sb, block); } @@ -653,11 +650,8 @@ static void alloc_data_block(struct inode *inode, struct page *page) logfs_unpack_index(page->index, &bix, &level); block = __alloc_block(inode->i_sb, inode->i_ino, bix, level); block->page = page; - SetPagePrivate(page); - page_cache_get(page); - set_page_private(page, (unsigned long) block); - + page->private = (unsigned long)block; block->ops = &indirect_block_ops; } @@ -1576,15 +1570,11 @@ int logfs_write_buf(struct inode *inode, struct page *page, long flags) static int __logfs_delete(struct inode *inode, struct page *page) { long flags = WF_DELETE; - int err; inode->i_ctime = inode->i_mtime = CURRENT_TIME; if (page->index < I0_BLOCKS) return logfs_write_direct(inode, page, flags); - err = grow_inode(inode, page->index, 0); - if (err) - return err; return logfs_write_rec(inode, page, page->index, 0, flags); } @@ -1633,7 +1623,7 @@ int logfs_rewrite_block(struct inode *inode, u64 bix, u64 ofs, if (inode->i_ino == LOGFS_INO_MASTER) logfs_write_anchor(inode->i_sb); else { - err = __logfs_write_inode(inode, page, flags); + err = __logfs_write_inode(inode, flags); } } } @@ -1883,7 +1873,7 @@ int logfs_truncate(struct inode *inode, u64 target) logfs_get_wblocks(sb, NULL, 1); err = __logfs_truncate(inode, size); if (!err) - err = __logfs_write_inode(inode, NULL, 0); + err = __logfs_write_inode(inode, 0); logfs_put_wblocks(sb, NULL, 1); } @@ -1911,11 +1901,8 @@ static void move_page_to_inode(struct inode *inode, struct page *page) li->li_block = block; block->page = NULL; - if (PagePrivate(page)) { - ClearPagePrivate(page); - page_cache_release(page); - set_page_private(page, 0); - } + page->private = 0; + ClearPagePrivate(page); } static void move_inode_to_page(struct page *page, struct inode *inode) @@ -1931,12 +1918,8 @@ static void move_inode_to_page(struct page *page, struct inode *inode) BUG_ON(PagePrivate(page)); block->ops = &indirect_block_ops; block->page = page; - - if (!PagePrivate(page)) { - SetPagePrivate(page); - page_cache_get(page); - set_page_private(page, (unsigned long) block); - } + page->private = (unsigned long)block; + SetPagePrivate(page); block->inode = NULL; li->li_block = NULL; @@ -2123,14 +2106,14 @@ void logfs_set_segment_unreserved(struct super_block *sb, u32 segno, u32 ec) ec_level); } -int __logfs_write_inode(struct inode *inode, struct page *page, long flags) +int __logfs_write_inode(struct inode *inode, long flags) { struct super_block *sb = inode->i_sb; int ret; - logfs_get_wblocks(sb, page, flags & WF_LOCK); + logfs_get_wblocks(sb, NULL, flags & WF_LOCK); ret = do_write_inode(inode); - logfs_put_wblocks(sb, page, flags & WF_LOCK); + logfs_put_wblocks(sb, NULL, flags & WF_LOCK); return ret; } diff --git a/trunk/fs/logfs/segment.c b/trunk/fs/logfs/segment.c index ab798ed1cc88..9d5187353255 100644 --- a/trunk/fs/logfs/segment.c +++ b/trunk/fs/logfs/segment.c @@ -86,11 +86,7 @@ int __logfs_buf_write(struct logfs_area *area, u64 ofs, void *buf, size_t len, BUG_ON(!page); /* FIXME: reserve a pool */ SetPageUptodate(page); memcpy(page_address(page) + offset, buf, copylen); - - if (!PagePrivate(page)) { - SetPagePrivate(page); - page_cache_get(page); - } + SetPagePrivate(page); page_cache_release(page); buf += copylen; @@ -114,10 +110,7 @@ static void pad_partial_page(struct logfs_area *area) page = get_mapping_page(sb, index, 0); BUG_ON(!page); /* FIXME: reserve a pool */ memset(page_address(page) + offset, 0xff, len); - if (!PagePrivate(page)) { - SetPagePrivate(page); - page_cache_get(page); - } + SetPagePrivate(page); page_cache_release(page); } } @@ -137,10 +130,7 @@ static void pad_full_pages(struct logfs_area *area) BUG_ON(!page); /* FIXME: reserve a pool */ SetPageUptodate(page); memset(page_address(page), 0xff, PAGE_CACHE_SIZE); - if (!PagePrivate(page)) { - SetPagePrivate(page); - page_cache_get(page); - } + SetPagePrivate(page); page_cache_release(page); index++; no_indizes--; @@ -495,12 +485,8 @@ static void move_btree_to_page(struct inode *inode, struct page *page, mempool_free(item, super->s_alias_pool); } block->page = page; - - if (!PagePrivate(page)) { - SetPagePrivate(page); - page_cache_get(page); - set_page_private(page, (unsigned long) block); - } + SetPagePrivate(page); + page->private = (unsigned long)block; block->ops = &indirect_block_ops; initialize_block_counters(page, block, data, 0); } @@ -550,12 +536,8 @@ void move_page_to_btree(struct page *page) list_add(&item->list, &block->item_list); } block->page = NULL; - - if (PagePrivate(page)) { - ClearPagePrivate(page); - page_cache_release(page); - set_page_private(page, 0); - } + ClearPagePrivate(page); + page->private = 0; block->ops = &btree_block_ops; err = alias_tree_insert(block->sb, block->ino, block->bix, block->level, block); @@ -720,10 +702,7 @@ void freeseg(struct super_block *sb, u32 segno) page = find_get_page(mapping, ofs >> PAGE_SHIFT); if (!page) continue; - if (PagePrivate(page)) { - ClearPagePrivate(page); - page_cache_release(page); - } + ClearPagePrivate(page); page_cache_release(page); } } @@ -862,16 +841,6 @@ static void free_area(struct logfs_area *area) kfree(area); } -void free_areas(struct super_block *sb) -{ - struct logfs_super *super = logfs_super(sb); - int i; - - for_each_area(i) - free_area(super->s_area[i]); - free_area(super->s_journal_area); -} - static struct logfs_area *alloc_area(struct super_block *sb) { struct logfs_area *area; @@ -954,6 +923,10 @@ int logfs_init_areas(struct super_block *sb) void logfs_cleanup_areas(struct super_block *sb) { struct logfs_super *super = logfs_super(sb); + int i; btree_grim_visitor128(&super->s_object_alias_tree, 0, kill_alias); + for_each_area(i) + free_area(super->s_area[i]); + free_area(super->s_journal_area); } diff --git a/trunk/fs/logfs/super.c b/trunk/fs/logfs/super.c index c9ee7f5d1caf..e795c234ea33 100644 --- a/trunk/fs/logfs/super.c +++ b/trunk/fs/logfs/super.c @@ -486,15 +486,14 @@ static void logfs_kill_sb(struct super_block *sb) /* Alias entries slow down mount, so evict as many as possible */ sync_filesystem(sb); logfs_write_anchor(sb); - free_areas(sb); /* * From this point on alias entries are simply dropped - and any * writes to the object store are considered bugs. */ + super->s_flags |= LOGFS_SB_FLAG_SHUTDOWN; log_super("LogFS: Now in shutdown\n"); generic_shutdown_super(sb); - super->s_flags |= LOGFS_SB_FLAG_SHUTDOWN; BUG_ON(super->s_dirty_used_bytes || super->s_dirty_free_bytes); diff --git a/trunk/fs/nilfs2/ioctl.c b/trunk/fs/nilfs2/ioctl.c index 2a70fce70c65..886649627c3d 100644 --- a/trunk/fs/nilfs2/ioctl.c +++ b/trunk/fs/nilfs2/ioctl.c @@ -603,8 +603,6 @@ static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp, nsegs = argv[4].v_nmembs; if (argv[4].v_size != argsz[4]) goto out; - if (nsegs > UINT_MAX / sizeof(__u64)) - goto out; /* * argv[4] points to segment numbers this ioctl cleans. We diff --git a/trunk/fs/proc/base.c b/trunk/fs/proc/base.c index d4548dd49b02..9cde9edf9c4d 100644 --- a/trunk/fs/proc/base.c +++ b/trunk/fs/proc/base.c @@ -198,6 +198,26 @@ static int proc_root_link(struct dentry *dentry, struct path *path) return result; } +static struct mm_struct *mm_access(struct task_struct *task, unsigned int mode) +{ + struct mm_struct *mm; + int err; + + err = mutex_lock_killable(&task->signal->cred_guard_mutex); + if (err) + return ERR_PTR(err); + + mm = get_task_mm(task); + if (mm && mm != current->mm && + !ptrace_may_access(task, mode)) { + mmput(mm); + mm = ERR_PTR(-EACCES); + } + mutex_unlock(&task->signal->cred_guard_mutex); + + return mm; +} + struct mm_struct *mm_for_maps(struct task_struct *task) { return mm_access(task, PTRACE_MODE_READ); @@ -691,13 +711,6 @@ static int mem_open(struct inode* inode, struct file* file) if (IS_ERR(mm)) return PTR_ERR(mm); - if (mm) { - /* ensure this mm_struct can't be freed */ - atomic_inc(&mm->mm_count); - /* but do not pin its memory */ - mmput(mm); - } - /* OK to pass negative loff_t, we can catch out-of-range */ file->f_mode |= FMODE_UNSIGNED_OFFSET; file->private_data = mm; @@ -705,13 +718,13 @@ static int mem_open(struct inode* inode, struct file* file) return 0; } -static ssize_t mem_rw(struct file *file, char __user *buf, - size_t count, loff_t *ppos, int write) +static ssize_t mem_read(struct file * file, char __user * buf, + size_t count, loff_t *ppos) { - struct mm_struct *mm = file->private_data; - unsigned long addr = *ppos; - ssize_t copied; + int ret; char *page; + unsigned long src = *ppos; + struct mm_struct *mm = file->private_data; if (!mm) return 0; @@ -720,55 +733,76 @@ static ssize_t mem_rw(struct file *file, char __user *buf, if (!page) return -ENOMEM; - copied = 0; - if (!atomic_inc_not_zero(&mm->mm_users)) - goto free; - + ret = 0; + while (count > 0) { - int this_len = min_t(int, count, PAGE_SIZE); + int this_len, retval; - if (write && copy_from_user(page, buf, this_len)) { - copied = -EFAULT; + this_len = (count > PAGE_SIZE) ? PAGE_SIZE : count; + retval = access_remote_vm(mm, src, page, this_len, 0); + if (!retval) { + if (!ret) + ret = -EIO; break; } - this_len = access_remote_vm(mm, addr, page, this_len, write); - if (!this_len) { - if (!copied) - copied = -EIO; + if (copy_to_user(buf, page, retval)) { + ret = -EFAULT; break; } + + ret += retval; + src += retval; + buf += retval; + count -= retval; + } + *ppos = src; - if (!write && copy_to_user(buf, page, this_len)) { + free_page((unsigned long) page); + return ret; +} + +static ssize_t mem_write(struct file * file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int copied; + char *page; + unsigned long dst = *ppos; + struct mm_struct *mm = file->private_data; + + if (!mm) + return 0; + + page = (char *)__get_free_page(GFP_TEMPORARY); + if (!page) + return -ENOMEM; + + copied = 0; + while (count > 0) { + int this_len, retval; + + this_len = (count > PAGE_SIZE) ? PAGE_SIZE : count; + if (copy_from_user(page, buf, this_len)) { copied = -EFAULT; break; } - - buf += this_len; - addr += this_len; - copied += this_len; - count -= this_len; + retval = access_remote_vm(mm, dst, page, this_len, 1); + if (!retval) { + if (!copied) + copied = -EIO; + break; + } + copied += retval; + buf += retval; + dst += retval; + count -= retval; } - *ppos = addr; + *ppos = dst; - mmput(mm); -free: free_page((unsigned long) page); return copied; } -static ssize_t mem_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - return mem_rw(file, buf, count, ppos, 0); -} - -static ssize_t mem_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - return mem_rw(file, (char __user*)buf, count, ppos, 1); -} - loff_t mem_lseek(struct file *file, loff_t offset, int orig) { switch (orig) { @@ -788,8 +822,8 @@ loff_t mem_lseek(struct file *file, loff_t offset, int orig) static int mem_release(struct inode *inode, struct file *file) { struct mm_struct *mm = file->private_data; - if (mm) - mmdrop(mm); + + mmput(mm); return 0; } diff --git a/trunk/fs/sysfs/file.c b/trunk/fs/sysfs/file.c index 00012e31829d..62f4fb37789e 100644 --- a/trunk/fs/sysfs/file.c +++ b/trunk/fs/sysfs/file.c @@ -493,12 +493,6 @@ int sysfs_attr_ns(struct kobject *kobj, const struct attribute *attr, const void *ns = NULL; int err; - if (!dir_sd) { - WARN(1, KERN_ERR "sysfs: kobject %s without dirent\n", - kobject_name(kobj)); - return -ENOENT; - } - err = 0; if (!sysfs_ns_type(dir_sd)) goto out; diff --git a/trunk/fs/sysfs/inode.c b/trunk/fs/sysfs/inode.c index 85eb81683a29..4a802b4a9056 100644 --- a/trunk/fs/sysfs/inode.c +++ b/trunk/fs/sysfs/inode.c @@ -318,11 +318,8 @@ int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const void *ns, const cha struct sysfs_addrm_cxt acxt; struct sysfs_dirent *sd; - if (!dir_sd) { - WARN(1, KERN_WARNING "sysfs: can not remove '%s', no directory\n", - name); + if (!dir_sd) return -ENOENT; - } sysfs_addrm_start(&acxt, dir_sd); diff --git a/trunk/fs/xfs/xfs_vnodeops.c b/trunk/fs/xfs/xfs_vnodeops.c index ebdb88840a47..0cf52da9d246 100644 --- a/trunk/fs/xfs/xfs_vnodeops.c +++ b/trunk/fs/xfs/xfs_vnodeops.c @@ -131,8 +131,7 @@ xfs_readlink( __func__, (unsigned long long) ip->i_ino, (long long) pathlen); ASSERT(0); - error = XFS_ERROR(EFSCORRUPTED); - goto out; + return XFS_ERROR(EFSCORRUPTED); } diff --git a/trunk/include/acpi/acpiosxf.h b/trunk/include/acpi/acpiosxf.h index 7c9aebe8a7aa..2fe8639b3ae7 100644 --- a/trunk/include/acpi/acpiosxf.h +++ b/trunk/include/acpi/acpiosxf.h @@ -218,13 +218,9 @@ acpi_status acpi_os_write_port(acpi_io_address address, u32 value, u32 width); */ acpi_status acpi_os_read_memory(acpi_physical_address address, u32 * value, u32 width); -acpi_status -acpi_os_read_memory64(acpi_physical_address address, u64 *value, u32 width); acpi_status acpi_os_write_memory(acpi_physical_address address, u32 value, u32 width); -acpi_status -acpi_os_write_memory64(acpi_physical_address address, u64 value, u32 width); /* * Platform and hardware-independent PCI configuration space access diff --git a/trunk/include/acpi/atomicio.h b/trunk/include/acpi/atomicio.h new file mode 100644 index 000000000000..8b9fb4b0b9ce --- /dev/null +++ b/trunk/include/acpi/atomicio.h @@ -0,0 +1,10 @@ +#ifndef ACPI_ATOMIC_IO_H +#define ACPI_ATOMIC_IO_H + +int acpi_pre_map_gar(struct acpi_generic_address *reg); +int acpi_post_unmap_gar(struct acpi_generic_address *reg); + +int acpi_atomic_read(u64 *val, struct acpi_generic_address *reg); +int acpi_atomic_write(u64 val, struct acpi_generic_address *reg); + +#endif diff --git a/trunk/include/acpi/processor.h b/trunk/include/acpi/processor.h index 8cf7e98a2c7b..610f6fb1bbc2 100644 --- a/trunk/include/acpi/processor.h +++ b/trunk/include/acpi/processor.h @@ -195,7 +195,6 @@ struct acpi_processor_flags { u8 has_cst:1; u8 power_setup_done:1; u8 bm_rld_set:1; - u8 need_hotplug_init:1; }; struct acpi_processor { diff --git a/trunk/include/asm-generic/pci_iomap.h b/trunk/include/asm-generic/pci_iomap.h index e58fcf891370..8de4b73e19e2 100644 --- a/trunk/include/asm-generic/pci_iomap.h +++ b/trunk/include/asm-generic/pci_iomap.h @@ -15,16 +15,6 @@ struct pci_dev; #ifdef CONFIG_PCI /* Create a virtual mapping cookie for a PCI BAR (memory or IO) */ extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max); -/* Create a virtual mapping cookie for a port on a given PCI device. - * Do not call this directly, it exists to make it easier for architectures - * to override */ -#ifdef CONFIG_NO_GENERIC_PCI_IOPORT_MAP -extern void __iomem *__pci_ioport_map(struct pci_dev *dev, unsigned long port, - unsigned int nr); -#else -#define __pci_ioport_map(dev, port, nr) ioport_map((port), (nr)) -#endif - #else static inline void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max) { diff --git a/trunk/include/drm/drmP.h b/trunk/include/drm/drmP.h index 92f0981b5fb8..76caa67c22e2 100644 --- a/trunk/include/drm/drmP.h +++ b/trunk/include/drm/drmP.h @@ -1328,7 +1328,6 @@ extern int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_authmagic(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int drm_remove_magic(struct drm_master *master, drm_magic_t magic); /* Cache management (drm_cache.c) */ void drm_clflush_pages(struct page *pages[], unsigned long num_pages); diff --git a/trunk/include/linux/binfmts.h b/trunk/include/linux/binfmts.h index 0092102db2de..fd88a3945aa1 100644 --- a/trunk/include/linux/binfmts.h +++ b/trunk/include/linux/binfmts.h @@ -18,7 +18,7 @@ struct pt_regs; #define BINPRM_BUF_SIZE 128 #ifdef __KERNEL__ -#include +#include #define CORENAME_MAX_SIZE 128 @@ -58,7 +58,6 @@ struct linux_binprm { unsigned interp_flags; unsigned interp_data; unsigned long loader, exec; - char tcomm[TASK_COMM_LEN]; }; #define BINPRM_FLAGS_ENFORCE_NONDUMP_BIT 0 diff --git a/trunk/include/linux/freezer.h b/trunk/include/linux/freezer.h index d09af4b67cf1..0ab54e16a91f 100644 --- a/trunk/include/linux/freezer.h +++ b/trunk/include/linux/freezer.h @@ -39,7 +39,6 @@ extern bool __refrigerator(bool check_kthr_stop); extern int freeze_processes(void); extern int freeze_kernel_threads(void); extern void thaw_processes(void); -extern void thaw_kernel_threads(void); static inline bool try_to_freeze(void) { @@ -175,7 +174,6 @@ static inline bool __refrigerator(bool check_kthr_stop) { return false; } static inline int freeze_processes(void) { return -ENOSYS; } static inline int freeze_kernel_threads(void) { return -ENOSYS; } static inline void thaw_processes(void) {} -static inline void thaw_kernel_threads(void) {} static inline bool try_to_freeze(void) { return false; } diff --git a/trunk/include/linux/gpio_keys.h b/trunk/include/linux/gpio_keys.h index 004ff33ab38e..b5ca4b2c08ec 100644 --- a/trunk/include/linux/gpio_keys.h +++ b/trunk/include/linux/gpio_keys.h @@ -1,8 +1,6 @@ #ifndef _GPIO_KEYS_H #define _GPIO_KEYS_H -struct device; - struct gpio_keys_button { /* Configuration parameters */ unsigned int code; /* input event code (KEY_*, SW_*) */ 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/lp8727.h b/trunk/include/linux/lp8727.h old mode 100644 new mode 100755 diff --git a/trunk/include/linux/mfd/mcp.h b/trunk/include/linux/mfd/mcp.h index f88c1cc0cb0f..1515e64e3663 100644 --- a/trunk/include/linux/mfd/mcp.h +++ b/trunk/include/linux/mfd/mcp.h @@ -10,6 +10,7 @@ #ifndef MCP_H #define MCP_H +#include #include struct mcp_ops; @@ -26,7 +27,7 @@ struct mcp { dma_device_t dma_telco_rd; dma_device_t dma_telco_wr; struct device attached_device; - int gpio_base; + const char *codec; }; struct mcp_ops { @@ -44,10 +45,11 @@ void mcp_reg_write(struct mcp *, unsigned int, unsigned int); unsigned int mcp_reg_read(struct mcp *, unsigned int); void mcp_enable(struct mcp *); void mcp_disable(struct mcp *); +const struct mcp_device_id *mcp_get_device_id(const struct mcp *mcp); #define mcp_get_sclk_rate(mcp) ((mcp)->sclk_rate) struct mcp *mcp_host_alloc(struct device *, size_t); -int mcp_host_register(struct mcp *); +int mcp_host_register(struct mcp *, void *); void mcp_host_unregister(struct mcp *); struct mcp_driver { @@ -56,6 +58,7 @@ struct mcp_driver { void (*remove)(struct mcp *); int (*suspend)(struct mcp *, pm_message_t); int (*resume)(struct mcp *); + const struct mcp_device_id *id_table; }; int mcp_driver_register(struct mcp_driver *); @@ -64,9 +67,6 @@ void mcp_driver_unregister(struct mcp_driver *); #define mcp_get_drvdata(mcp) dev_get_drvdata(&(mcp)->attached_device) #define mcp_set_drvdata(mcp,d) dev_set_drvdata(&(mcp)->attached_device, d) -static inline void *mcp_priv(struct mcp *mcp) -{ - return mcp + 1; -} +#define mcp_priv(mcp) ((void *)((mcp)+1)) #endif diff --git a/trunk/include/linux/mfd/twl6040.h b/trunk/include/linux/mfd/twl6040.h index 9bc9ac651dad..2463c2619596 100644 --- a/trunk/include/linux/mfd/twl6040.h +++ b/trunk/include/linux/mfd/twl6040.h @@ -187,10 +187,8 @@ struct twl6040 { int rev; u8 vibra_ctrl_cache[2]; - /* PLL configuration */ int pll; unsigned int sysclk; - unsigned int mclk; unsigned int irq; unsigned int irq_base; diff --git a/trunk/include/linux/mfd/ucb1x00.h b/trunk/include/linux/mfd/ucb1x00.h index 4321f044d1e4..bc19e5fb7ea8 100644 --- a/trunk/include/linux/mfd/ucb1x00.h +++ b/trunk/include/linux/mfd/ucb1x00.h @@ -104,6 +104,9 @@ #define UCB_MODE_DYN_VFLAG_ENA (1 << 12) #define UCB_MODE_AUD_OFF_CAN (1 << 13) +struct ucb1x00_plat_data { + int gpio_base; +}; struct ucb1x00_irq { void *devid; @@ -116,7 +119,7 @@ struct ucb1x00 { unsigned int irq; struct semaphore adc_sem; spinlock_t io_lock; - u16 id; + const struct mcp_device_id *id; u16 io_dir; u16 io_out; u16 adc_cr; diff --git a/trunk/include/linux/mod_devicetable.h b/trunk/include/linux/mod_devicetable.h index 83ac0713ed0a..b29e7f6f8fa5 100644 --- a/trunk/include/linux/mod_devicetable.h +++ b/trunk/include/linux/mod_devicetable.h @@ -436,6 +436,17 @@ struct spi_device_id { __attribute__((aligned(sizeof(kernel_ulong_t)))); }; +/* mcp */ + +#define MCP_NAME_SIZE 20 +#define MCP_MODULE_PREFIX "mcp:" + +struct mcp_device_id { + char name[MCP_NAME_SIZE]; + kernel_ulong_t driver_data /* Data private to the driver */ + __attribute__((aligned(sizeof(kernel_ulong_t)))); +}; + /* dmi */ enum dmi_field { DMI_NONE, diff --git a/trunk/include/linux/mpi.h b/trunk/include/linux/mpi.h index d02cca6cc8ce..06f88994ccaa 100644 --- a/trunk/include/linux/mpi.h +++ b/trunk/include/linux/mpi.h @@ -57,6 +57,8 @@ struct gcry_mpi { typedef struct gcry_mpi *MPI; +#define MPI_NULL NULL + #define mpi_get_nlimbs(a) ((a)->nlimbs) #define mpi_is_neg(a) ((a)->sign) diff --git a/trunk/include/linux/mtd/mtd.h b/trunk/include/linux/mtd/mtd.h index d43dc25af82e..1a81fde8f333 100644 --- a/trunk/include/linux/mtd/mtd.h +++ b/trunk/include/linux/mtd/mtd.h @@ -427,7 +427,9 @@ static inline int mtd_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) static inline int mtd_suspend(struct mtd_info *mtd) { - return mtd->suspend ? mtd->suspend(mtd) : 0; + if (!mtd->suspend) + return -EOPNOTSUPP; + return mtd->suspend(mtd); } static inline void mtd_resume(struct mtd_info *mtd) @@ -439,7 +441,7 @@ static inline void mtd_resume(struct mtd_info *mtd) static inline int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs) { if (!mtd->block_isbad) - return 0; + return -EOPNOTSUPP; return mtd->block_isbad(mtd, ofs); } diff --git a/trunk/include/linux/perf_event.h b/trunk/include/linux/perf_event.h index abb2776be1ba..08855613ceb3 100644 --- a/trunk/include/linux/perf_event.h +++ b/trunk/include/linux/perf_event.h @@ -587,7 +587,6 @@ struct hw_perf_event { u64 sample_period; u64 last_period; local64_t period_left; - u64 interrupts_seq; u64 interrupts; u64 freq_time_stamp; diff --git a/trunk/include/linux/pm_qos.h b/trunk/include/linux/pm_qos.h index 4d99e4e6ef83..e5bbcbaa6f57 100644 --- a/trunk/include/linux/pm_qos.h +++ b/trunk/include/linux/pm_qos.h @@ -110,19 +110,7 @@ static inline void pm_qos_remove_request(struct pm_qos_request *req) { return; } static inline int pm_qos_request(int pm_qos_class) -{ - switch (pm_qos_class) { - case PM_QOS_CPU_DMA_LATENCY: - return PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; - case PM_QOS_NETWORK_LATENCY: - return PM_QOS_NETWORK_LAT_DEFAULT_VALUE; - case PM_QOS_NETWORK_THROUGHPUT: - return PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE; - default: - return PM_QOS_DEFAULT_VALUE; - } -} - + { return 0; } static inline int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier) { return 0; } 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/sched.h b/trunk/include/linux/sched.h index 7d379a6bfd88..513f52459872 100644 --- a/trunk/include/linux/sched.h +++ b/trunk/include/linux/sched.h @@ -2090,7 +2090,7 @@ extern struct task_struct *idle_task(int cpu); * is_idle_task - is the specified task an idle task? * @p: the task in question. */ -static inline bool is_idle_task(const struct task_struct *p) +static inline bool is_idle_task(struct task_struct *p) { return p->pid == 0; } @@ -2259,12 +2259,6 @@ static inline void mmdrop(struct mm_struct * mm) extern void mmput(struct mm_struct *); /* Grab a reference to a task's mm, if it is not already going away */ extern struct mm_struct *get_task_mm(struct task_struct *task); -/* - * Grab a reference to a task's mm, if it is not already going away - * and ptrace_may_access with the mode parameter passed to it - * succeeds. - */ -extern struct mm_struct *mm_access(struct task_struct *task, unsigned int mode); /* Remove the current tasks stale references to the old mm_struct */ extern void mm_release(struct task_struct *, struct mm_struct *); /* Allocate a new mm structure and copy contents from tsk->mm */ diff --git a/trunk/include/linux/sh_dma.h b/trunk/include/linux/sh_dma.h index 425450b980b8..8cd7fe59cf1a 100644 --- a/trunk/include/linux/sh_dma.h +++ b/trunk/include/linux/sh_dma.h @@ -70,7 +70,6 @@ struct sh_dmae_pdata { unsigned int needs_tend_set:1; unsigned int no_dmars:1; unsigned int chclr_present:1; - unsigned int slave_only:1; }; /* DMA register */ diff --git a/trunk/include/linux/sysdev.h b/trunk/include/linux/sysdev.h new file mode 100644 index 000000000000..20f63d3e6144 --- /dev/null +++ b/trunk/include/linux/sysdev.h @@ -0,0 +1,164 @@ +/** + * System devices follow a slightly different driver model. + * They don't need to do dynammic driver binding, can't be probed, + * and don't reside on any type of peripheral bus. + * So, we represent and treat them a little differently. + * + * We still have a notion of a driver for a system device, because we still + * want to perform basic operations on these devices. + * + * We also support auxiliary drivers binding to devices of a certain class. + * + * This allows configurable drivers to register themselves for devices of + * a certain type. And, it allows class definitions to reside in generic + * code while arch-specific code can register specific drivers. + * + * Auxiliary drivers registered with a NULL cls are registered as drivers + * for all system devices, and get notification calls for each device. + */ + + +#ifndef _SYSDEV_H_ +#define _SYSDEV_H_ + +#include +#include + + +struct sys_device; +struct sysdev_class_attribute; + +struct sysdev_class { + const char *name; + struct list_head drivers; + struct sysdev_class_attribute **attrs; + struct kset kset; +}; + +struct sysdev_class_attribute { + struct attribute attr; + ssize_t (*show)(struct sysdev_class *, struct sysdev_class_attribute *, + char *); + ssize_t (*store)(struct sysdev_class *, struct sysdev_class_attribute *, + const char *, size_t); +}; + +#define _SYSDEV_CLASS_ATTR(_name,_mode,_show,_store) \ +{ \ + .attr = {.name = __stringify(_name), .mode = _mode }, \ + .show = _show, \ + .store = _store, \ +} + +#define SYSDEV_CLASS_ATTR(_name,_mode,_show,_store) \ + struct sysdev_class_attribute attr_##_name = \ + _SYSDEV_CLASS_ATTR(_name,_mode,_show,_store) + + +extern int sysdev_class_register(struct sysdev_class *); +extern void sysdev_class_unregister(struct sysdev_class *); + +extern int sysdev_class_create_file(struct sysdev_class *, + struct sysdev_class_attribute *); +extern void sysdev_class_remove_file(struct sysdev_class *, + struct sysdev_class_attribute *); +/** + * Auxiliary system device drivers. + */ + +struct sysdev_driver { + struct list_head entry; + int (*add)(struct sys_device *); + int (*remove)(struct sys_device *); +}; + + +extern int sysdev_driver_register(struct sysdev_class *, struct sysdev_driver *); +extern void sysdev_driver_unregister(struct sysdev_class *, struct sysdev_driver *); + + +/** + * sys_devices can be simplified a lot from regular devices, because they're + * simply not as versatile. + */ + +struct sys_device { + u32 id; + struct sysdev_class * cls; + struct kobject kobj; +}; + +extern int sysdev_register(struct sys_device *); +extern void sysdev_unregister(struct sys_device *); + + +struct sysdev_attribute { + struct attribute attr; + ssize_t (*show)(struct sys_device *, struct sysdev_attribute *, char *); + ssize_t (*store)(struct sys_device *, struct sysdev_attribute *, + const char *, size_t); +}; + + +#define _SYSDEV_ATTR(_name, _mode, _show, _store) \ +{ \ + .attr = { .name = __stringify(_name), .mode = _mode }, \ + .show = _show, \ + .store = _store, \ +} + +#define SYSDEV_ATTR(_name, _mode, _show, _store) \ + struct sysdev_attribute attr_##_name = \ + _SYSDEV_ATTR(_name, _mode, _show, _store); + +extern int sysdev_create_file(struct sys_device *, struct sysdev_attribute *); +extern void sysdev_remove_file(struct sys_device *, struct sysdev_attribute *); + +/* Create/remove NULL terminated attribute list */ +static inline int +sysdev_create_files(struct sys_device *d, struct sysdev_attribute **a) +{ + return sysfs_create_files(&d->kobj, (const struct attribute **)a); +} + +static inline void +sysdev_remove_files(struct sys_device *d, struct sysdev_attribute **a) +{ + return sysfs_remove_files(&d->kobj, (const struct attribute **)a); +} + +struct sysdev_ext_attribute { + struct sysdev_attribute attr; + void *var; +}; + +/* + * Support for simple variable sysdev attributes. + * The pointer to the variable is stored in a sysdev_ext_attribute + */ + +/* Add more types as needed */ + +extern ssize_t sysdev_show_ulong(struct sys_device *, struct sysdev_attribute *, + char *); +extern ssize_t sysdev_store_ulong(struct sys_device *, + struct sysdev_attribute *, const char *, size_t); +extern ssize_t sysdev_show_int(struct sys_device *, struct sysdev_attribute *, + char *); +extern ssize_t sysdev_store_int(struct sys_device *, + struct sysdev_attribute *, const char *, size_t); + +#define _SYSDEV_ULONG_ATTR(_name, _mode, _var) \ + { _SYSDEV_ATTR(_name, _mode, sysdev_show_ulong, sysdev_store_ulong), \ + &(_var) } +#define SYSDEV_ULONG_ATTR(_name, _mode, _var) \ + struct sysdev_ext_attribute attr_##_name = \ + _SYSDEV_ULONG_ATTR(_name, _mode, _var); +#define _SYSDEV_INT_ATTR(_name, _mode, _var) \ + { _SYSDEV_ATTR(_name, _mode, sysdev_show_int, sysdev_store_int), \ + &(_var) } +#define SYSDEV_INT_ATTR(_name, _mode, _var) \ + struct sysdev_ext_attribute attr_##_name = \ + _SYSDEV_INT_ATTR(_name, _mode, _var); + +#endif /* _SYSDEV_H_ */ diff --git a/trunk/include/linux/thermal.h b/trunk/include/linux/thermal.h index 796f1ff0388c..47b4a27e6e97 100644 --- a/trunk/include/linux/thermal.h +++ b/trunk/include/linux/thermal.h @@ -152,9 +152,9 @@ struct thermal_cooling_device *thermal_cooling_device_register(char *, void *, void thermal_cooling_device_unregister(struct thermal_cooling_device *); #ifdef CONFIG_NET -extern int thermal_generate_netlink_event(u32 orig, enum events event); +extern int generate_netlink_event(u32 orig, enum events event); #else -static inline int thermal_generate_netlink_event(u32 orig, enum events event) +static inline int generate_netlink_event(u32 orig, enum events event) { return 0; } 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/linux/usb/langwell_otg.h b/trunk/include/linux/usb/langwell_otg.h new file mode 100644 index 000000000000..51f17b16d312 --- /dev/null +++ b/trunk/include/linux/usb/langwell_otg.h @@ -0,0 +1,139 @@ +/* + * Intel Langwell USB OTG transceiver driver + * Copyright (C) 2008 - 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, + * 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 __LANGWELL_OTG_H +#define __LANGWELL_OTG_H + +#include + +#define CI_USBCMD 0x30 +# define USBCMD_RST BIT(1) +# define USBCMD_RS BIT(0) +#define CI_USBSTS 0x34 +# define USBSTS_SLI BIT(8) +# define USBSTS_URI BIT(6) +# define USBSTS_PCI BIT(2) +#define CI_PORTSC1 0x74 +# define PORTSC_PP BIT(12) +# define PORTSC_LS (BIT(11) | BIT(10)) +# define PORTSC_SUSP BIT(7) +# define PORTSC_CCS BIT(0) +#define CI_HOSTPC1 0xb4 +# define HOSTPC1_PHCD BIT(22) +#define CI_OTGSC 0xf4 +# define OTGSC_DPIE BIT(30) +# define OTGSC_1MSE BIT(29) +# define OTGSC_BSEIE BIT(28) +# define OTGSC_BSVIE BIT(27) +# define OTGSC_ASVIE BIT(26) +# define OTGSC_AVVIE BIT(25) +# define OTGSC_IDIE BIT(24) +# define OTGSC_DPIS BIT(22) +# define OTGSC_1MSS BIT(21) +# define OTGSC_BSEIS BIT(20) +# define OTGSC_BSVIS BIT(19) +# define OTGSC_ASVIS BIT(18) +# define OTGSC_AVVIS BIT(17) +# define OTGSC_IDIS BIT(16) +# define OTGSC_DPS BIT(14) +# define OTGSC_1MST BIT(13) +# define OTGSC_BSE BIT(12) +# define OTGSC_BSV BIT(11) +# define OTGSC_ASV BIT(10) +# define OTGSC_AVV BIT(9) +# define OTGSC_ID BIT(8) +# define OTGSC_HABA BIT(7) +# define OTGSC_HADP BIT(6) +# define OTGSC_IDPU BIT(5) +# define OTGSC_DP BIT(4) +# define OTGSC_OT BIT(3) +# define OTGSC_HAAR BIT(2) +# define OTGSC_VC BIT(1) +# define OTGSC_VD BIT(0) +# define OTGSC_INTEN_MASK (0x7f << 24) +# define OTGSC_INT_MASK (0x5f << 24) +# define OTGSC_INTSTS_MASK (0x7f << 16) +#define CI_USBMODE 0xf8 +# define USBMODE_CM (BIT(1) | BIT(0)) +# define USBMODE_IDLE 0 +# define USBMODE_DEVICE 0x2 +# define USBMODE_HOST 0x3 +#define USBCFG_ADDR 0xff10801c +#define USBCFG_LEN 4 +# define USBCFG_VBUSVAL BIT(14) +# define USBCFG_AVALID BIT(13) +# define USBCFG_BVALID BIT(12) +# define USBCFG_SESEND BIT(11) + +#define INTR_DUMMY_MASK (USBSTS_SLI | USBSTS_URI | USBSTS_PCI) + +enum langwell_otg_timer_type { + TA_WAIT_VRISE_TMR, + TA_WAIT_BCON_TMR, + TA_AIDL_BDIS_TMR, + TB_ASE0_BRST_TMR, + TB_SE0_SRP_TMR, + TB_SRP_INIT_TMR, + TB_SRP_FAIL_TMR, + TB_BUS_SUSPEND_TMR +}; + +#define TA_WAIT_VRISE 100 +#define TA_WAIT_BCON 30000 +#define TA_AIDL_BDIS 15000 +#define TB_ASE0_BRST 5000 +#define TB_SE0_SRP 2 +#define TB_SRP_INIT 100 +#define TB_SRP_FAIL 5500 +#define TB_BUS_SUSPEND 500 + +struct langwell_otg_timer { + unsigned long expires; /* Number of count increase to timeout */ + unsigned long count; /* Tick counter */ + void (*function)(unsigned long); /* Timeout function */ + unsigned long data; /* Data passed to function */ + struct list_head list; +}; + +struct langwell_otg { + struct intel_mid_otg_xceiv iotg; + struct device *dev; + + void __iomem *usbcfg; /* SCCBUSB config Reg */ + + unsigned region; + unsigned cfg_region; + + struct work_struct work; + struct workqueue_struct *qwork; + struct timer_list hsm_timer; + + spinlock_t lock; + spinlock_t wq_lock; + + struct notifier_block iotg_notifier; +}; + +static inline +struct langwell_otg *mid_xceiv_to_lnw(struct intel_mid_otg_xceiv *iotg) +{ + return container_of(iotg, struct langwell_otg, iotg); +} + +#endif /* __LANGWELL_OTG_H__ */ 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/sound/core.h b/trunk/include/sound/core.h index cea1b5426dfa..5ab255f196cc 100644 --- a/trunk/include/sound/core.h +++ b/trunk/include/sound/core.h @@ -417,7 +417,6 @@ static inline int __snd_bug_on(int cond) #define gameport_get_port_data(gp) (gp)->port_data #endif -#ifdef CONFIG_PCI /* PCI quirk list helper */ struct snd_pci_quirk { unsigned short subvendor; /* PCI subvendor ID */ @@ -457,6 +456,5 @@ snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list); const struct snd_pci_quirk * snd_pci_quirk_lookup_id(u16 vendor, u16 device, const struct snd_pci_quirk *list); -#endif #endif /* __SOUND_CORE_H */ diff --git a/trunk/include/target/target_core_backend.h b/trunk/include/target/target_core_backend.h index e5e6ff98f0fa..4866499bdeeb 100644 --- a/trunk/include/target/target_core_backend.h +++ b/trunk/include/target/target_core_backend.h @@ -59,7 +59,7 @@ int transport_set_vpd_ident_type(struct t10_vpd *, unsigned char *); int transport_set_vpd_ident(struct t10_vpd *, unsigned char *); /* core helpers also used by command snooping in pscsi */ -void *transport_kmap_data_sg(struct se_cmd *); -void transport_kunmap_data_sg(struct se_cmd *); +void *transport_kmap_first_data_page(struct se_cmd *); +void transport_kunmap_first_data_page(struct se_cmd *); #endif /* TARGET_CORE_BACKEND_H */ diff --git a/trunk/include/target/target_core_base.h b/trunk/include/target/target_core_base.h index dc4e345a0163..daf532bc721a 100644 --- a/trunk/include/target/target_core_base.h +++ b/trunk/include/target/target_core_base.h @@ -582,7 +582,6 @@ struct se_cmd { struct scatterlist *t_data_sg; unsigned int t_data_nents; - void *t_data_vmap; struct scatterlist *t_bidi_data_sg; unsigned int t_bidi_data_nents; diff --git a/trunk/include/target/target_core_fabric.h b/trunk/include/target/target_core_fabric.h index d36fad317e78..523e8bc104d4 100644 --- a/trunk/include/target/target_core_fabric.h +++ b/trunk/include/target/target_core_fabric.h @@ -114,7 +114,7 @@ void transport_init_se_cmd(struct se_cmd *, struct target_core_fabric_ops *, struct se_session *, u32, int, int, unsigned char *); int transport_lookup_cmd_lun(struct se_cmd *, u32); int transport_generic_allocate_tasks(struct se_cmd *, unsigned char *); -void target_submit_cmd(struct se_cmd *, struct se_session *, unsigned char *, +int target_submit_cmd(struct se_cmd *, struct se_session *, unsigned char *, unsigned char *, u32, u32, int, int, int); int transport_handle_cdb_direct(struct se_cmd *); int transport_generic_handle_cdb_map(struct se_cmd *); 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/include/video/omapdss.h b/trunk/include/video/omapdss.h index 483f67caa7ad..062b3b24ff10 100644 --- a/trunk/include/video/omapdss.h +++ b/trunk/include/video/omapdss.h @@ -590,11 +590,6 @@ struct omap_dss_device { int (*get_backlight)(struct omap_dss_device *dssdev); }; -struct omap_dss_hdmi_data -{ - int hpd_gpio; -}; - struct omap_dss_driver { struct device_driver driver; diff --git a/trunk/kernel/events/callchain.c b/trunk/kernel/events/callchain.c index 6581a040f399..057e24b665cf 100644 --- a/trunk/kernel/events/callchain.c +++ b/trunk/kernel/events/callchain.c @@ -115,6 +115,8 @@ int get_callchain_buffers(void) } err = alloc_callchain_buffers(); + if (err) + release_callchain_buffers(); exit: mutex_unlock(&callchain_mutex); diff --git a/trunk/kernel/events/core.c b/trunk/kernel/events/core.c index 1b5c081d8b9f..a8f4ac001a00 100644 --- a/trunk/kernel/events/core.c +++ b/trunk/kernel/events/core.c @@ -815,7 +815,7 @@ static void update_event_times(struct perf_event *event) * here. */ if (is_cgroup_event(event)) - run_end = perf_cgroup_event_time(event); + run_end = perf_event_time(event); else if (ctx->is_active) run_end = ctx->time; else @@ -2300,10 +2300,7 @@ do { \ return div64_u64(dividend, divisor); } -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,40 +2319,22 @@ 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); } } -/* - * combine freq adjustment with unthrottling to avoid two passes over the - * events. At the same time, make sure, having freq events does not change - * the rate of unthrottling as that would introduce bias. - */ -static void perf_adjust_freq_unthr_context(struct perf_event_context *ctx, - int needs_unthr) +static void perf_ctx_adjust_freq(struct perf_event_context *ctx, u64 period) { struct perf_event *event; struct hw_perf_event *hwc; - u64 now, period = TICK_NSEC; + u64 interrupts, now; s64 delta; - /* - * only need to iterate over all events iff: - * - context have events in frequency mode (needs freq adjust) - * - there are events to unthrottle on this cpu - */ - if (!(ctx->nr_freq || needs_unthr)) + if (!ctx->nr_freq) 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) continue; @@ -2365,8 +2344,13 @@ static void perf_adjust_freq_unthr_context(struct perf_event_context *ctx, hwc = &event->hw; - if (needs_unthr && hwc->interrupts == MAX_INTERRUPTS) { - hwc->interrupts = 0; + interrupts = hwc->interrupts; + hwc->interrupts = 0; + + /* + * unthrottle events on the tick + */ + if (interrupts == MAX_INTERRUPTS) { perf_log_throttle(event, 1); event->pmu->start(event, 0); } @@ -2374,30 +2358,14 @@ static void perf_adjust_freq_unthr_context(struct perf_event_context *ctx, if (!event->attr.freq || !event->attr.sample_freq) continue; - /* - * stop the event and update event->count - */ - event->pmu->stop(event, PERF_EF_UPDATE); - + event->pmu->read(event); now = local64_read(&event->count); delta = now - hwc->freq_count_stamp; hwc->freq_count_stamp = now; - /* - * 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); - - event->pmu->start(event, delta > 0 ? PERF_EF_RELOAD : 0); + perf_adjust_period(event, period, delta); } - - perf_pmu_enable(ctx->pmu); - raw_spin_unlock(&ctx->lock); } /* @@ -2420,13 +2388,16 @@ static void rotate_ctx(struct perf_event_context *ctx) */ static void perf_rotate_context(struct perf_cpu_context *cpuctx) { + u64 interval = (u64)cpuctx->jiffies_interval * TICK_NSEC; struct perf_event_context *ctx = NULL; - int rotate = 0, remove = 1; + int rotate = 0, remove = 1, freq = 0; if (cpuctx->ctx.nr_events) { remove = 0; if (cpuctx->ctx.nr_events != cpuctx->ctx.nr_active) rotate = 1; + if (cpuctx->ctx.nr_freq) + freq = 1; } ctx = cpuctx->task_ctx; @@ -2434,26 +2405,37 @@ static void perf_rotate_context(struct perf_cpu_context *cpuctx) remove = 0; if (ctx->nr_events != ctx->nr_active) rotate = 1; + if (ctx->nr_freq) + freq = 1; } - if (!rotate) + if (!rotate && !freq) goto done; perf_ctx_lock(cpuctx, cpuctx->task_ctx); perf_pmu_disable(cpuctx->ctx.pmu); - cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE); - if (ctx) - ctx_sched_out(ctx, cpuctx, EVENT_FLEXIBLE); + if (freq) { + perf_ctx_adjust_freq(&cpuctx->ctx, interval); + if (ctx) + perf_ctx_adjust_freq(ctx, interval); + } - rotate_ctx(&cpuctx->ctx); - if (ctx) - rotate_ctx(ctx); + if (rotate) { + cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE); + if (ctx) + ctx_sched_out(ctx, cpuctx, EVENT_FLEXIBLE); + + rotate_ctx(&cpuctx->ctx); + if (ctx) + rotate_ctx(ctx); - perf_event_sched_in(cpuctx, ctx, current); + perf_event_sched_in(cpuctx, ctx, current); + } perf_pmu_enable(cpuctx->ctx.pmu); perf_ctx_unlock(cpuctx, cpuctx->task_ctx); + done: if (remove) list_del_init(&cpuctx->rotation_list); @@ -2463,22 +2445,10 @@ void perf_event_task_tick(void) { struct list_head *head = &__get_cpu_var(rotation_list); struct perf_cpu_context *cpuctx, *tmp; - struct perf_event_context *ctx; - int throttled; WARN_ON(!irqs_disabled()); - __this_cpu_inc(perf_throttled_seq); - throttled = __this_cpu_xchg(perf_throttled_count, 0); - list_for_each_entry_safe(cpuctx, tmp, head, rotation_list) { - ctx = &cpuctx->ctx; - perf_adjust_freq_unthr_context(ctx, throttled); - - ctx = cpuctx->task_ctx; - if (ctx) - perf_adjust_freq_unthr_context(ctx, throttled); - if (cpuctx->jiffies_interval == 1 || !(jiffies % cpuctx->jiffies_interval)) perf_rotate_context(cpuctx); @@ -4539,7 +4509,6 @@ static int __perf_event_overflow(struct perf_event *event, { int events = atomic_read(&event->event_limit); struct hw_perf_event *hwc = &event->hw; - u64 seq; int ret = 0; /* @@ -4549,20 +4518,14 @@ static int __perf_event_overflow(struct perf_event *event, if (unlikely(!is_sampling_event(event))) return 0; - seq = __this_cpu_read(perf_throttled_seq); - if (seq != hwc->interrupts_seq) { - hwc->interrupts_seq = seq; - hwc->interrupts = 1; - } else { - hwc->interrupts++; - if (unlikely(throttle - && hwc->interrupts >= max_samples_per_tick)) { - __this_cpu_inc(perf_throttled_count); + if (unlikely(hwc->interrupts >= max_samples_per_tick)) { + if (throttle) { hwc->interrupts = MAX_INTERRUPTS; perf_log_throttle(event, 0); ret = 1; } - } + } else + hwc->interrupts++; if (event->attr.freq) { u64 now = perf_clock(); @@ -4571,7 +4534,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/exit.c b/trunk/kernel/exit.c index 4b4042f9bc6a..294b1709170d 100644 --- a/trunk/kernel/exit.c +++ b/trunk/kernel/exit.c @@ -1038,22 +1038,6 @@ void do_exit(long code) if (tsk->nr_dirtied) __this_cpu_add(dirty_throttle_leaks, tsk->nr_dirtied); exit_rcu(); - - /* - * The setting of TASK_RUNNING by try_to_wake_up() may be delayed - * when the following two conditions become true. - * - There is race condition of mmap_sem (It is acquired by - * exit_mm()), and - * - SMI occurs before setting TASK_RUNINNG. - * (or hypervisor of virtual machine switches to other guest) - * As a result, we may become TASK_RUNNING after becoming TASK_DEAD - * - * To avoid it, we have to wait for releasing tsk->pi_lock which - * is held by try_to_wake_up() - */ - smp_mb(); - raw_spin_unlock_wait(&tsk->pi_lock); - /* causes final put_task_struct in finish_task_switch(). */ tsk->state = TASK_DEAD; tsk->flags |= PF_NOFREEZE; /* tell freezer to ignore us */ diff --git a/trunk/kernel/fork.c b/trunk/kernel/fork.c index 1b2ef3c23ae4..051f090d40c1 100644 --- a/trunk/kernel/fork.c +++ b/trunk/kernel/fork.c @@ -647,26 +647,6 @@ struct mm_struct *get_task_mm(struct task_struct *task) } EXPORT_SYMBOL_GPL(get_task_mm); -struct mm_struct *mm_access(struct task_struct *task, unsigned int mode) -{ - struct mm_struct *mm; - int err; - - err = mutex_lock_killable(&task->signal->cred_guard_mutex); - if (err) - return ERR_PTR(err); - - mm = get_task_mm(task); - if (mm && mm != current->mm && - !ptrace_may_access(task, mode)) { - mmput(mm); - mm = ERR_PTR(-EACCES); - } - mutex_unlock(&task->signal->cred_guard_mutex); - - return mm; -} - /* Please note the differences between mmput and mm_release. * mmput is called whenever we stop holding onto a mm_struct, * error success whatever. diff --git a/trunk/kernel/kprobes.c b/trunk/kernel/kprobes.c index 9788c0ec6f43..29f5b65bee29 100644 --- a/trunk/kernel/kprobes.c +++ b/trunk/kernel/kprobes.c @@ -1673,12 +1673,8 @@ static int __kprobes pre_handler_kretprobe(struct kprobe *p, ri->rp = rp; ri->task = current; - if (rp->entry_handler && rp->entry_handler(ri, regs)) { - raw_spin_lock_irqsave(&rp->lock, flags); - hlist_add_head(&ri->hlist, &rp->free_instances); - raw_spin_unlock_irqrestore(&rp->lock, flags); + if (rp->entry_handler && rp->entry_handler(ri, regs)) return 0; - } arch_prepare_kretprobe(ri, regs); diff --git a/trunk/kernel/power/power.h b/trunk/kernel/power/power.h index 21724eee5206..0c4defe6d3b8 100644 --- a/trunk/kernel/power/power.h +++ b/trunk/kernel/power/power.h @@ -231,28 +231,8 @@ extern int pm_test_level; #ifdef CONFIG_SUSPEND_FREEZER static inline int suspend_freeze_processes(void) { - int error; - - error = freeze_processes(); - - /* - * freeze_processes() automatically thaws every task if freezing - * fails. So we need not do anything extra upon error. - */ - if (error) - goto Finish; - - error = freeze_kernel_threads(); - - /* - * freeze_kernel_threads() thaws only kernel threads upon freezing - * failure. So we have to thaw the userspace tasks ourselves. - */ - if (error) - thaw_processes(); - - Finish: - return error; + int error = freeze_processes(); + return error ? : freeze_kernel_threads(); } static inline void suspend_thaw_processes(void) diff --git a/trunk/kernel/power/process.c b/trunk/kernel/power/process.c index 7e426459e60a..77274c9ba2f1 100644 --- a/trunk/kernel/power/process.c +++ b/trunk/kernel/power/process.c @@ -143,10 +143,7 @@ int freeze_processes(void) /** * freeze_kernel_threads - Make freezable kernel threads go to the refrigerator. * - * On success, returns 0. On failure, -errno and only the kernel threads are - * thawed, so as to give a chance to the caller to do additional cleanups - * (if any) before thawing the userspace tasks. So, it is the responsibility - * of the caller to thaw the userspace tasks, when the time is right. + * On success, returns 0. On failure, -errno and system is fully thawed. */ int freeze_kernel_threads(void) { @@ -162,7 +159,7 @@ int freeze_kernel_threads(void) BUG_ON(in_atomic()); if (error) - thaw_kernel_threads(); + thaw_processes(); return error; } @@ -191,22 +188,3 @@ void thaw_processes(void) printk("done.\n"); } -void thaw_kernel_threads(void) -{ - struct task_struct *g, *p; - - pm_nosig_freezing = false; - printk("Restarting kernel threads ... "); - - thaw_workqueues(); - - read_lock(&tasklist_lock); - do_each_thread(g, p) { - if (p->flags & (PF_KTHREAD | PF_WQ_WORKER)) - __thaw_task(p); - } while_each_thread(g, p); - read_unlock(&tasklist_lock); - - schedule(); - printk("done.\n"); -} diff --git a/trunk/kernel/power/user.c b/trunk/kernel/power/user.c index 3e100075b13c..6b1ab7a88522 100644 --- a/trunk/kernel/power/user.c +++ b/trunk/kernel/power/user.c @@ -249,15 +249,13 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, } pm_restore_gfp_mask(); error = hibernation_snapshot(data->platform_support); - if (error) { - thaw_kernel_threads(); - } else { + if (!error) { error = put_user(in_suspend, (int __user *)arg); if (!error && !freezer_test_done) data->ready = 1; if (freezer_test_done) { freezer_test_done = false; - thaw_kernel_threads(); + thaw_processes(); } } break; @@ -276,15 +274,6 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, swsusp_free(); memset(&data->handle, 0, sizeof(struct snapshot_handle)); data->ready = 0; - /* - * It is necessary to thaw kernel threads here, because - * SNAPSHOT_CREATE_IMAGE may be invoked directly after - * SNAPSHOT_FREE. In that case, if kernel threads were not - * thawed, the preallocation of memory carried out by - * hibernation_snapshot() might run into problems (i.e. it - * might fail or even deadlock). - */ - thaw_kernel_threads(); break; case SNAPSHOT_PREF_IMAGE_SIZE: diff --git a/trunk/kernel/rcutorture.c b/trunk/kernel/rcutorture.c index a58ac285fc69..88f17b8a3b1d 100644 --- a/trunk/kernel/rcutorture.c +++ b/trunk/kernel/rcutorture.c @@ -56,8 +56,8 @@ static int nreaders = -1; /* # reader threads, defaults to 2*ncpus */ static int nfakewriters = 4; /* # fake writer threads */ static int stat_interval; /* Interval between stats, in seconds. */ /* Defaults to "only at end of test". */ -static bool verbose; /* Print more debug info. */ -static bool test_no_idle_hz; /* Test RCU's support for tickless idle CPUs. */ +static int verbose; /* Print more debug info. */ +static int test_no_idle_hz; /* Test RCU's support for tickless idle CPUs. */ static int shuffle_interval = 3; /* Interval between shuffles (in sec)*/ static int stutter = 5; /* Start/stop testing interval (in sec) */ static int irqreader = 1; /* RCU readers from irq (timers). */ @@ -1399,7 +1399,7 @@ rcu_torture_shutdown(void *arg) * Execute random CPU-hotplug operations at the interval specified * by the onoff_interval. */ -static int __cpuinit +static int rcu_torture_onoff(void *arg) { int cpu; @@ -1447,7 +1447,7 @@ rcu_torture_onoff(void *arg) return 0; } -static int __cpuinit +static int rcu_torture_onoff_init(void) { if (onoff_interval <= 0) diff --git a/trunk/kernel/sched/core.c b/trunk/kernel/sched/core.c index 5255c9d2e053..df00cb09263e 100644 --- a/trunk/kernel/sched/core.c +++ b/trunk/kernel/sched/core.c @@ -74,7 +74,6 @@ #include #include -#include #ifdef CONFIG_PARAVIRT #include #endif @@ -724,6 +723,9 @@ static void dequeue_task(struct rq *rq, struct task_struct *p, int flags) p->sched_class->dequeue_task(rq, p, flags); } +/* + * activate_task - move a task to the runqueue. + */ void activate_task(struct rq *rq, struct task_struct *p, int flags) { if (task_contributes_to_load(p)) @@ -732,6 +734,9 @@ void activate_task(struct rq *rq, struct task_struct *p, int flags) enqueue_task(rq, p, flags); } +/* + * deactivate_task - remove a task from the runqueue. + */ void deactivate_task(struct rq *rq, struct task_struct *p, int flags) { if (task_contributes_to_load(p)) @@ -4129,7 +4134,7 @@ static int __sched_setscheduler(struct task_struct *p, int policy, on_rq = p->on_rq; running = task_current(rq, p); if (on_rq) - dequeue_task(rq, p, 0); + deactivate_task(rq, p, 0); if (running) p->sched_class->put_prev_task(rq, p); @@ -4142,7 +4147,7 @@ static int __sched_setscheduler(struct task_struct *p, int policy, if (running) p->sched_class->set_curr_task(rq); if (on_rq) - enqueue_task(rq, p, 0); + activate_task(rq, p, 0); check_class_changed(rq, p, prev_class, oldprio); task_rq_unlock(rq, p, &flags); @@ -4993,9 +4998,9 @@ static int __migrate_task(struct task_struct *p, int src_cpu, int dest_cpu) * placed properly. */ if (p->on_rq) { - dequeue_task(rq_src, p, 0); + deactivate_task(rq_src, p, 0); set_task_cpu(p, dest_cpu); - enqueue_task(rq_dest, p, 0); + activate_task(rq_dest, p, 0); check_preempt_curr(rq_dest, p, 0); } done: @@ -7027,10 +7032,10 @@ static void normalize_task(struct rq *rq, struct task_struct *p) on_rq = p->on_rq; if (on_rq) - dequeue_task(rq, p, 0); + deactivate_task(rq, p, 0); __setscheduler(rq, p, SCHED_NORMAL, 0); if (on_rq) { - enqueue_task(rq, p, 0); + activate_task(rq, p, 0); resched_task(rq->curr); } diff --git a/trunk/kernel/sched/fair.c b/trunk/kernel/sched/fair.c index 7c6414fc669d..84adb2d66cbd 100644 --- a/trunk/kernel/sched/fair.c +++ b/trunk/kernel/sched/fair.c @@ -4866,15 +4866,6 @@ static void nohz_balancer_kick(int cpu) return; } -static inline void clear_nohz_tick_stopped(int cpu) -{ - if (unlikely(test_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)))) { - cpumask_clear_cpu(cpu, nohz.idle_cpus_mask); - atomic_dec(&nohz.nr_cpus); - clear_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)); - } -} - static inline void set_cpu_sd_state_busy(void) { struct sched_domain *sd; @@ -4913,12 +4904,6 @@ void select_nohz_load_balancer(int stop_tick) { int cpu = smp_processor_id(); - /* - * If this cpu is going down, then nothing needs to be done. - */ - if (!cpu_active(cpu)) - return; - if (stop_tick) { if (test_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu))) return; @@ -4929,18 +4914,6 @@ void select_nohz_load_balancer(int stop_tick) } return; } - -static int __cpuinit sched_ilb_notifier(struct notifier_block *nfb, - unsigned long action, void *hcpu) -{ - switch (action & ~CPU_TASKS_FROZEN) { - case CPU_DYING: - clear_nohz_tick_stopped(smp_processor_id()); - return NOTIFY_OK; - default: - return NOTIFY_DONE; - } -} #endif static DEFINE_SPINLOCK(balancing); @@ -5097,7 +5070,11 @@ static inline int nohz_kick_needed(struct rq *rq, int cpu) * busy tick after returning from idle, we will update the busy stats. */ set_cpu_sd_state_busy(); - clear_nohz_tick_stopped(cpu); + if (unlikely(test_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)))) { + clear_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)); + cpumask_clear_cpu(cpu, nohz.idle_cpus_mask); + atomic_dec(&nohz.nr_cpus); + } /* * None are in tickless mode and hence no need for NOHZ idle load @@ -5613,7 +5590,6 @@ __init void init_sched_fair_class(void) #ifdef CONFIG_NO_HZ zalloc_cpumask_var(&nohz.idle_cpus_mask, GFP_NOWAIT); - cpu_notifier(sched_ilb_notifier, 0); #endif #endif /* SMP */ diff --git a/trunk/kernel/sched/rt.c b/trunk/kernel/sched/rt.c index f42ae7fb5ec5..3640ebbb466b 100644 --- a/trunk/kernel/sched/rt.c +++ b/trunk/kernel/sched/rt.c @@ -1587,11 +1587,6 @@ static int push_rt_task(struct rq *rq) if (!next_task) return 0; -#ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW - if (unlikely(task_running(rq, next_task))) - return 0; -#endif - retry: if (unlikely(next_task == rq->curr)) { WARN_ON(1); diff --git a/trunk/kernel/watchdog.c b/trunk/kernel/watchdog.c index d117262deba3..1d7bca7f4f52 100644 --- a/trunk/kernel/watchdog.c +++ b/trunk/kernel/watchdog.c @@ -296,7 +296,7 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) if (__this_cpu_read(soft_watchdog_warn) == true) return HRTIMER_RESTART; - printk(KERN_EMERG "BUG: soft lockup - CPU#%d stuck for %us! [%s:%d]\n", + printk(KERN_ERR "BUG: soft lockup - CPU#%d stuck for %us! [%s:%d]\n", smp_processor_id(), duration, current->comm, task_pid_nr(current)); print_modules(); diff --git a/trunk/lib/Kconfig b/trunk/lib/Kconfig index 028aba9e72af..169eb7c598e5 100644 --- a/trunk/lib/Kconfig +++ b/trunk/lib/Kconfig @@ -19,9 +19,6 @@ config RATIONAL config GENERIC_FIND_FIRST_BIT bool -config NO_GENERIC_PCI_IOPORT_MAP - bool - config GENERIC_PCI_IOMAP bool @@ -282,9 +279,6 @@ config AVERAGE If unsure, say N. -config CLZ_TAB - bool - config CORDIC tristate "CORDIC algorithm" help @@ -293,7 +287,6 @@ config CORDIC config MPILIB tristate - select CLZ_TAB help Multiprecision maths library from GnuPG. It is used to implement RSA digital signature verification, diff --git a/trunk/lib/Makefile b/trunk/lib/Makefile index 18515f0267c4..d71aae1b01b3 100644 --- a/trunk/lib/Makefile +++ b/trunk/lib/Makefile @@ -121,8 +121,6 @@ obj-$(CONFIG_DQL) += dynamic_queue_limits.o obj-$(CONFIG_MPILIB) += mpi/ obj-$(CONFIG_SIGNATURE) += digsig.o -obj-$(CONFIG_CLZ_TAB) += clz_tab.o - hostprogs-y := gen_crc32table clean-files := crc32table.h diff --git a/trunk/lib/bug.c b/trunk/lib/bug.c index a28c1415357c..19552096d16b 100644 --- a/trunk/lib/bug.c +++ b/trunk/lib/bug.c @@ -169,7 +169,7 @@ enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs) return BUG_TRAP_TYPE_WARN; } - printk(KERN_DEFAULT "------------[ cut here ]------------\n"); + printk(KERN_EMERG "------------[ cut here ]------------\n"); if (file) printk(KERN_CRIT "kernel BUG at %s:%u!\n", diff --git a/trunk/lib/clz_tab.c b/trunk/lib/clz_tab.c deleted file mode 100644 index 7287b4a991a7..000000000000 --- a/trunk/lib/clz_tab.c +++ /dev/null @@ -1,18 +0,0 @@ -const unsigned char __clz_tab[] = { - 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, -}; diff --git a/trunk/lib/digsig.c b/trunk/lib/digsig.c index 286d558033e2..fd2402f67f89 100644 --- a/trunk/lib/digsig.c +++ b/trunk/lib/digsig.c @@ -34,9 +34,14 @@ static int pkcs_1_v1_5_decode_emsa(const unsigned char *msg, unsigned long msglen, unsigned long modulus_bitlen, unsigned char *out, - unsigned long *outlen) + unsigned long *outlen, + int *is_valid) { unsigned long modulus_len, ps_len, i; + int result; + + /* default to invalid packet */ + *is_valid = 0; modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); @@ -45,30 +50,39 @@ static int pkcs_1_v1_5_decode_emsa(const unsigned char *msg, return -EINVAL; /* separate encoded message */ - if ((msg[0] != 0x00) || (msg[1] != (unsigned char)1)) - return -EINVAL; + if ((msg[0] != 0x00) || (msg[1] != (unsigned char)1)) { + result = -EINVAL; + goto bail; + } for (i = 2; i < modulus_len - 1; i++) if (msg[i] != 0xFF) break; /* separator check */ - if (msg[i] != 0) + if (msg[i] != 0) { /* There was no octet with hexadecimal value 0x00 to separate ps from m. */ - return -EINVAL; + result = -EINVAL; + goto bail; + } ps_len = i - 2; if (*outlen < (msglen - (2 + ps_len + 1))) { *outlen = msglen - (2 + ps_len + 1); - return -EOVERFLOW; + result = -EOVERFLOW; + goto bail; } *outlen = (msglen - (2 + ps_len + 1)); memcpy(out, &msg[2 + ps_len + 1], *outlen); - return 0; + /* valid packet */ + *is_valid = 1; + result = 0; +bail: + return result; } /* @@ -82,7 +96,7 @@ static int digsig_verify_rsa(struct key *key, unsigned long len; unsigned long mlen, mblen; unsigned nret, l; - int head, i; + int valid, head, i; unsigned char *out1 = NULL, *out2 = NULL; MPI in = NULL, res = NULL, pkey[2]; uint8_t *p, *datap, *endp; @@ -91,10 +105,6 @@ static int digsig_verify_rsa(struct key *key, down_read(&key->sem); ukp = key->payload.data; - - if (ukp->datalen < sizeof(*pkh)) - goto err1; - pkh = (struct pubkey_hdr *)ukp->data; if (pkh->version != 1) @@ -107,23 +117,18 @@ static int digsig_verify_rsa(struct key *key, goto err1; datap = pkh->mpi; - endp = ukp->data + ukp->datalen; - - err = -ENOMEM; + endp = datap + ukp->datalen; for (i = 0; i < pkh->nmpi; i++) { unsigned int remaining = endp - datap; pkey[i] = mpi_read_from_buffer(datap, &remaining); - if (!pkey[i]) - goto err; datap += remaining; } mblen = mpi_get_nbits(pkey[0]); mlen = (mblen + 7)/8; - if (mlen == 0) - goto err; + err = -ENOMEM; out1 = kzalloc(mlen, GFP_KERNEL); if (!out1) @@ -162,9 +167,10 @@ static int digsig_verify_rsa(struct key *key, memset(out1, 0, head); memcpy(out1 + head, p, l); - err = pkcs_1_v1_5_decode_emsa(out1, len, mblen, out2, &len); + err = -EINVAL; + pkcs_1_v1_5_decode_emsa(out1, len, mblen, out2, &len, &valid); - if (!err && len == hlen) + if (valid && len == hlen) err = memcmp(out2, h, hlen); err: @@ -172,8 +178,8 @@ static int digsig_verify_rsa(struct key *key, mpi_free(res); kfree(out1); kfree(out2); - while (--i >= 0) - mpi_free(pkey[i]); + mpi_free(pkey[0]); + mpi_free(pkey[1]); err1: up_read(&key->sem); 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/lib/mpi/longlong.h b/trunk/lib/mpi/longlong.h index 29f98624ef93..b87487b40a8b 100644 --- a/trunk/lib/mpi/longlong.h +++ b/trunk/lib/mpi/longlong.h @@ -1200,40 +1200,18 @@ do { \ "r" ((USItype)(v)) \ : "%g1", "%g2" __AND_CLOBBER_CC) #define UMUL_TIME 39 /* 39 instructions */ -/* It's quite necessary to add this much assembler for the sparc. - The default udiv_qrnnd (in C) is more than 10 times slower! */ -#define udiv_qrnnd(q, r, n1, n0, d) \ - __asm__ ("! Inlined udiv_qrnnd\n\t" \ - "mov 32,%%g1\n\t" \ - "subcc %1,%2,%%g0\n\t" \ - "1: bcs 5f\n\t" \ - "addxcc %0,%0,%0 ! shift n1n0 and a q-bit in lsb\n\t" \ - "sub %1,%2,%1 ! this kills msb of n\n\t" \ - "addx %1,%1,%1 ! so this can't give carry\n\t" \ - "subcc %%g1,1,%%g1\n\t" \ - "2: bne 1b\n\t" \ - "subcc %1,%2,%%g0\n\t" \ - "bcs 3f\n\t" \ - "addxcc %0,%0,%0 ! shift n1n0 and a q-bit in lsb\n\t" \ - "b 3f\n\t" \ - "sub %1,%2,%1 ! this kills msb of n\n\t" \ - "4: sub %1,%2,%1\n\t" \ - "5: addxcc %1,%1,%1\n\t" \ - "bcc 2b\n\t" \ - "subcc %%g1,1,%%g1\n\t" \ - "! Got carry from n. Subtract next step to cancel this carry.\n\t" \ - "bne 4b\n\t" \ - "addcc %0,%0,%0 ! shift n1n0 and a 0-bit in lsb\n\t" \ - "sub %1,%2,%1\n\t" \ - "3: xnor %0,0,%0\n\t" \ - "! End of inline udiv_qrnnd\n" \ - : "=&r" ((USItype)(q)), \ - "=&r" ((USItype)(r)) \ - : "r" ((USItype)(d)), \ - "1" ((USItype)(n1)), \ - "0" ((USItype)(n0)) : "%g1", "cc") -#define UDIV_TIME (3+7*32) /* 7 instructions/iteration. 32 iterations. */ #endif +#ifndef udiv_qrnnd +#ifndef LONGLONG_STANDALONE +#define udiv_qrnnd(q, r, n1, n0, d) \ +do { USItype __r; \ + (q) = __udiv_qrnnd(&__r, (n1), (n0), (d)); \ + (r) = __r; \ +} while (0) + extern USItype __udiv_qrnnd(); +#define UDIV_TIME 140 +#endif /* LONGLONG_STANDALONE */ +#endif /* udiv_qrnnd */ #endif /* __sparc__ */ /*************************************** diff --git a/trunk/lib/mpi/mpi-bit.c b/trunk/lib/mpi/mpi-bit.c index 2f526627e4f5..854c9c6da025 100644 --- a/trunk/lib/mpi/mpi-bit.c +++ b/trunk/lib/mpi/mpi-bit.c @@ -21,6 +21,25 @@ #include "mpi-internal.h" #include "longlong.h" +const unsigned char __clz_tab[] = { + 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, +}; + #define A_LIMB_1 ((mpi_limb_t) 1) /**************** diff --git a/trunk/lib/mpi/mpi-div.c b/trunk/lib/mpi/mpi-div.c index f68cbbb4d4a4..c3087d1390ce 100644 --- a/trunk/lib/mpi/mpi-div.c +++ b/trunk/lib/mpi/mpi-div.c @@ -149,9 +149,6 @@ int mpi_tdiv_qr(MPI quot, MPI rem, MPI num, MPI den) mpi_ptr_t marker[5]; int markidx = 0; - if (!dsize) - return -EINVAL; - memset(marker, 0, sizeof(marker)); /* Ensure space is enough for quotient and remainder. @@ -210,8 +207,6 @@ int mpi_tdiv_qr(MPI quot, MPI rem, MPI num, MPI den) * numerator would be gradually overwritten by the quotient limbs. */ if (qp == np) { /* Copy NP object to temporary space. */ np = marker[markidx++] = mpi_alloc_limb_space(nsize); - if (!np) - goto nomem; MPN_COPY(np, qp, nsize); } } else /* Put quotient at top of remainder. */ diff --git a/trunk/lib/mpi/mpi-pow.c b/trunk/lib/mpi/mpi-pow.c index 67f3e79af914..b04a3cf80080 100644 --- a/trunk/lib/mpi/mpi-pow.c +++ b/trunk/lib/mpi/mpi-pow.c @@ -59,7 +59,7 @@ int mpi_powm(MPI res, MPI base, MPI exp, MPI mod) ep = exp->d; if (!msize) - return -EINVAL; + msize = 1 / msize; /* provoke a signal */ if (!esize) { /* Exponent is zero, result is 1 mod MOD, i.e., 1 or 0 diff --git a/trunk/lib/mpi/mpicoder.c b/trunk/lib/mpi/mpicoder.c index f26b41fcb48c..716802b774ea 100644 --- a/trunk/lib/mpi/mpicoder.c +++ b/trunk/lib/mpi/mpicoder.c @@ -20,15 +20,78 @@ #include "mpi-internal.h" +#define DIM(v) (sizeof(v)/sizeof((v)[0])) #define MAX_EXTERN_MPI_BITS 16384 +static uint8_t asn[15] = /* Object ID is 1.3.14.3.2.26 */ +{ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, + 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 +}; + +MPI do_encode_md(const void *sha_buffer, unsigned nbits) +{ + int nframe = (nbits + 7) / 8; + uint8_t *frame, *fr_pt; + int i = 0, n; + size_t asnlen = DIM(asn); + MPI a = MPI_NULL; + + if (SHA1_DIGEST_LENGTH + asnlen + 4 > nframe) + pr_info("MPI: can't encode a %d bit MD into a %d bits frame\n", + (int)(SHA1_DIGEST_LENGTH * 8), (int)nbits); + + /* We encode the MD in this way: + * + * 0 A PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes) + * + * PAD consists of FF bytes. + */ + frame = kmalloc(nframe, GFP_KERNEL); + if (!frame) + return MPI_NULL; + n = 0; + frame[n++] = 0; + frame[n++] = 1; /* block type */ + i = nframe - SHA1_DIGEST_LENGTH - asnlen - 3; + + if (i <= 1) { + pr_info("MPI: message digest encoding failed\n"); + kfree(frame); + return a; + } + + memset(frame + n, 0xff, i); + n += i; + frame[n++] = 0; + memcpy(frame + n, &asn, asnlen); + n += asnlen; + memcpy(frame + n, sha_buffer, SHA1_DIGEST_LENGTH); + n += SHA1_DIGEST_LENGTH; + + i = nframe; + fr_pt = frame; + + if (n != nframe) { + printk + ("MPI: message digest encoding failed, frame length is wrong\n"); + kfree(frame); + return a; + } + + a = mpi_alloc((nframe + BYTES_PER_MPI_LIMB - 1) / BYTES_PER_MPI_LIMB); + mpi_set_buffer(a, frame, nframe, 0); + kfree(frame); + + return a; +} + MPI mpi_read_from_buffer(const void *xbuffer, unsigned *ret_nread) { const uint8_t *buffer = xbuffer; int i, j; unsigned nbits, nbytes, nlimbs, nread = 0; mpi_limb_t a; - MPI val = NULL; + MPI val = MPI_NULL; if (*ret_nread < 2) goto leave; @@ -45,7 +108,7 @@ MPI mpi_read_from_buffer(const void *xbuffer, unsigned *ret_nread) nlimbs = (nbytes + BYTES_PER_MPI_LIMB - 1) / BYTES_PER_MPI_LIMB; val = mpi_alloc(nlimbs); if (!val) - return NULL; + return MPI_NULL; i = BYTES_PER_MPI_LIMB - nbytes % BYTES_PER_MPI_LIMB; i %= BYTES_PER_MPI_LIMB; val->nbits = nbits; @@ -148,6 +211,30 @@ int mpi_fromstr(MPI val, const char *str) } EXPORT_SYMBOL_GPL(mpi_fromstr); +/**************** + * Special function to get the low 8 bytes from an mpi. + * This can be used as a keyid; KEYID is an 2 element array. + * Return the low 4 bytes. + */ +u32 mpi_get_keyid(const MPI a, u32 *keyid) +{ +#if BYTES_PER_MPI_LIMB == 4 + if (keyid) { + keyid[0] = a->nlimbs >= 2 ? a->d[1] : 0; + keyid[1] = a->nlimbs >= 1 ? a->d[0] : 0; + } + return a->nlimbs >= 1 ? a->d[0] : 0; +#elif BYTES_PER_MPI_LIMB == 8 + if (keyid) { + keyid[0] = a->nlimbs ? (u32) (a->d[0] >> 32) : 0; + keyid[1] = a->nlimbs ? (u32) (a->d[0] & 0xffffffff) : 0; + } + return a->nlimbs ? (u32) (a->d[0] & 0xffffffff) : 0; +#else +#error Make this function work with other LIMB sizes +#endif +} + /**************** * Return an allocated buffer with the MPI (msb first). * NBYTES receives the length of this buffer. Caller must free the diff --git a/trunk/lib/mpi/mpih-div.c b/trunk/lib/mpi/mpih-div.c index cde1aaec18da..87ede162dfab 100644 --- a/trunk/lib/mpi/mpih-div.c +++ b/trunk/lib/mpi/mpih-div.c @@ -217,10 +217,6 @@ mpihelp_divrem(mpi_ptr_t qp, mpi_size_t qextra_limbs, case 0: /* We are asked to divide by zero, so go ahead and do it! (To make the compiler not remove this statement, return the value.) */ - /* - * existing clients of this function have been modified - * not to call it with dsize == 0, so this should not happen - */ return 1 / dsize; case 1: diff --git a/trunk/lib/mpi/mpiutil.c b/trunk/lib/mpi/mpiutil.c index 26e4ed31e256..eefc55d6b7f5 100644 --- a/trunk/lib/mpi/mpiutil.c +++ b/trunk/lib/mpi/mpiutil.c @@ -58,9 +58,6 @@ mpi_ptr_t mpi_alloc_limb_space(unsigned nlimbs) { size_t len = nlimbs * sizeof(mpi_limb_t); - if (!len) - return NULL; - return kmalloc(len, GFP_KERNEL); } @@ -138,7 +135,7 @@ int mpi_copy(MPI *copied, const MPI a) size_t i; MPI b; - *copied = NULL; + *copied = MPI_NULL; if (a) { b = mpi_alloc(a->nlimbs); diff --git a/trunk/lib/pci_iomap.c b/trunk/lib/pci_iomap.c index 0d83ea8a9605..4b0fdc22e688 100644 --- a/trunk/lib/pci_iomap.c +++ b/trunk/lib/pci_iomap.c @@ -34,7 +34,7 @@ void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) if (maxlen && len > maxlen) len = maxlen; if (flags & IORESOURCE_IO) - return __pci_ioport_map(dev, start, len); + return ioport_map(start, len); if (flags & IORESOURCE_MEM) { if (flags & IORESOURCE_CACHEABLE) return ioremap(start, len); 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/mm/compaction.c b/trunk/mm/compaction.c index d9ebebe1a2aa..71a58f67f481 100644 --- a/trunk/mm/compaction.c +++ b/trunk/mm/compaction.c @@ -313,34 +313,12 @@ static isolate_migrate_t isolate_migratepages(struct zone *zone, } else if (!locked) spin_lock_irq(&zone->lru_lock); - /* - * migrate_pfn does not necessarily start aligned to a - * pageblock. Ensure that pfn_valid is called when moving - * into a new MAX_ORDER_NR_PAGES range in case of large - * memory holes within the zone - */ - if ((low_pfn & (MAX_ORDER_NR_PAGES - 1)) == 0) { - if (!pfn_valid(low_pfn)) { - low_pfn += MAX_ORDER_NR_PAGES - 1; - continue; - } - } - if (!pfn_valid_within(low_pfn)) continue; nr_scanned++; - /* - * Get the page and ensure the page is within the same zone. - * See the comment in isolate_freepages about overlapping - * nodes. It is deliberate that the new zone lock is not taken - * as memory compaction should not move pages between nodes. - */ + /* Get the page and skip if free */ page = pfn_to_page(low_pfn); - if (page_zone(page) != zone) - continue; - - /* Skip if free */ if (PageBuddy(page)) continue; diff --git a/trunk/mm/filemap.c b/trunk/mm/filemap.c index b66275757c28..97f49ed35bd2 100644 --- a/trunk/mm/filemap.c +++ b/trunk/mm/filemap.c @@ -1400,12 +1400,15 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, unsigned long seg = 0; size_t count; loff_t *ppos = &iocb->ki_pos; + struct blk_plug plug; count = 0; retval = generic_segment_checks(iov, &nr_segs, &count, VERIFY_WRITE); if (retval) return retval; + blk_start_plug(&plug); + /* coalesce the iovecs and go direct-to-BIO for O_DIRECT */ if (filp->f_flags & O_DIRECT) { loff_t size; @@ -1421,12 +1424,8 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, retval = filemap_write_and_wait_range(mapping, pos, pos + iov_length(iov, nr_segs) - 1); if (!retval) { - struct blk_plug plug; - - blk_start_plug(&plug); retval = mapping->a_ops->direct_IO(READ, iocb, iov, pos, nr_segs); - blk_finish_plug(&plug); } if (retval > 0) { *ppos = pos + retval; @@ -1482,6 +1481,7 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, break; } out: + blk_finish_plug(&plug); return retval; } EXPORT_SYMBOL(generic_file_aio_read); diff --git a/trunk/mm/filemap_xip.c b/trunk/mm/filemap_xip.c index a4eb31132229..f91b2f687343 100644 --- a/trunk/mm/filemap_xip.c +++ b/trunk/mm/filemap_xip.c @@ -263,12 +263,7 @@ static int xip_file_fault(struct vm_area_struct *vma, struct vm_fault *vmf) xip_pfn); if (err == -ENOMEM) return VM_FAULT_OOM; - /* - * err == -EBUSY is fine, we've raced against another thread - * that faulted-in the same page - */ - if (err != -EBUSY) - BUG_ON(err); + BUG_ON(err); return VM_FAULT_NOPAGE; } else { int err, ret = VM_FAULT_OOM; diff --git a/trunk/mm/huge_memory.c b/trunk/mm/huge_memory.c index 91d3efb25d15..b3ffc21ce801 100644 --- a/trunk/mm/huge_memory.c +++ b/trunk/mm/huge_memory.c @@ -2083,7 +2083,7 @@ static void collect_mm_slot(struct mm_slot *mm_slot) { struct mm_struct *mm = mm_slot->mm; - VM_BUG_ON(NR_CPUS != 1 && !spin_is_locked(&khugepaged_mm_lock)); + VM_BUG_ON(!spin_is_locked(&khugepaged_mm_lock)); if (khugepaged_test_exit(mm)) { /* free mm_slot */ @@ -2113,7 +2113,7 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages, int progress = 0; VM_BUG_ON(!pages); - VM_BUG_ON(NR_CPUS != 1 && !spin_is_locked(&khugepaged_mm_lock)); + VM_BUG_ON(!spin_is_locked(&khugepaged_mm_lock)); if (khugepaged_scan.mm_slot) mm_slot = khugepaged_scan.mm_slot; diff --git a/trunk/mm/kmemleak.c b/trunk/mm/kmemleak.c index 45eb6217bf38..c833addd94d7 100644 --- a/trunk/mm/kmemleak.c +++ b/trunk/mm/kmemleak.c @@ -1036,7 +1036,7 @@ void __ref kmemleak_scan_area(const void *ptr, size_t size, gfp_t gfp) { pr_debug("%s(0x%p)\n", __func__, ptr); - if (atomic_read(&kmemleak_enabled) && ptr && size && !IS_ERR(ptr)) + if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr)) add_scan_area((unsigned long)ptr, size, gfp); else if (atomic_read(&kmemleak_early_log)) log_early(KMEMLEAK_SCAN_AREA, ptr, size, 0); @@ -1757,7 +1757,6 @@ void __init kmemleak_init(void) #ifdef CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF if (!kmemleak_skip_disable) { - atomic_set(&kmemleak_early_log, 0); kmemleak_disable(); return; } diff --git a/trunk/mm/memblock.c b/trunk/mm/memblock.c index 77b5f227e1d8..2f55f19b7c86 100644 --- a/trunk/mm/memblock.c +++ b/trunk/mm/memblock.c @@ -106,17 +106,14 @@ phys_addr_t __init_memblock memblock_find_in_range_node(phys_addr_t start, if (end == MEMBLOCK_ALLOC_ACCESSIBLE) end = memblock.current_limit; - /* avoid allocating the first page */ - start = max_t(phys_addr_t, start, PAGE_SIZE); + /* adjust @start to avoid underflow and allocating the first page */ + start = max3(start, size, (phys_addr_t)PAGE_SIZE); end = max(start, end); for_each_free_mem_range_reverse(i, nid, &this_start, &this_end, NULL) { this_start = clamp(this_start, start, end); this_end = clamp(this_end, start, end); - if (this_end < size) - continue; - cand = round_down(this_end - size, align); if (cand >= this_start) return cand; diff --git a/trunk/mm/memcontrol.c b/trunk/mm/memcontrol.c index 6728a7ae6f2d..556859fec4ef 100644 --- a/trunk/mm/memcontrol.c +++ b/trunk/mm/memcontrol.c @@ -776,8 +776,7 @@ static void memcg_check_events(struct mem_cgroup *memcg, struct page *page) /* threshold event is triggered in finer grain than soft limit */ if (unlikely(mem_cgroup_event_ratelimit(memcg, MEM_CGROUP_TARGET_THRESH))) { - bool do_softlimit; - bool do_numainfo __maybe_unused; + bool do_softlimit, do_numainfo; do_softlimit = mem_cgroup_event_ratelimit(memcg, MEM_CGROUP_TARGET_SOFTLIMIT); diff --git a/trunk/mm/migrate.c b/trunk/mm/migrate.c index df141f60289e..9871a56d82c3 100644 --- a/trunk/mm/migrate.c +++ b/trunk/mm/migrate.c @@ -445,6 +445,7 @@ void migrate_page_copy(struct page *newpage, struct page *page) ClearPageSwapCache(page); ClearPagePrivate(page); set_page_private(page, 0); + page->mapping = NULL; /* * If any waiters have accumulated on the new page then @@ -666,7 +667,6 @@ static int move_to_new_page(struct page *newpage, struct page *page, } else { if (remap_swapcache) remove_migration_ptes(page, newpage); - page->mapping = NULL; } unlock_page(newpage); diff --git a/trunk/mm/process_vm_access.c b/trunk/mm/process_vm_access.c index c20ff48994c2..e920aa3ce104 100644 --- a/trunk/mm/process_vm_access.c +++ b/trunk/mm/process_vm_access.c @@ -298,18 +298,23 @@ static ssize_t process_vm_rw_core(pid_t pid, const struct iovec *lvec, goto free_proc_pages; } - mm = mm_access(task, PTRACE_MODE_ATTACH); - if (!mm || IS_ERR(mm)) { - rc = IS_ERR(mm) ? PTR_ERR(mm) : -ESRCH; - /* - * Explicitly map EACCES to EPERM as EPERM is a more a - * appropriate error code for process_vw_readv/writev - */ - if (rc == -EACCES) - rc = -EPERM; + task_lock(task); + if (__ptrace_may_access(task, PTRACE_MODE_ATTACH)) { + task_unlock(task); + rc = -EPERM; + goto put_task_struct; + } + mm = task->mm; + + if (!mm || (task->flags & PF_KTHREAD)) { + task_unlock(task); + rc = -EINVAL; goto put_task_struct; } + atomic_inc(&mm->mm_users); + task_unlock(task); + for (i = 0; i < riovcnt && iov_l_curr_idx < liovcnt; i++) { rc = process_vm_rw_single_vec( (unsigned long)rvec[i].iov_base, rvec[i].iov_len, diff --git a/trunk/mm/swap.c b/trunk/mm/swap.c index fff1ff7fb9ad..b0f529b38979 100644 --- a/trunk/mm/swap.c +++ b/trunk/mm/swap.c @@ -659,7 +659,7 @@ void lru_add_page_tail(struct zone* zone, VM_BUG_ON(!PageHead(page)); VM_BUG_ON(PageCompound(page_tail)); VM_BUG_ON(PageLRU(page_tail)); - VM_BUG_ON(NR_CPUS != 1 && !spin_is_locked(&zone->lru_lock)); + VM_BUG_ON(!spin_is_locked(&zone->lru_lock)); SetPageLRU(page_tail); 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/ceph/ceph_common.c b/trunk/net/ceph/ceph_common.c index 761ad9d6cc3b..97f70e50ad3b 100644 --- a/trunk/net/ceph/ceph_common.c +++ b/trunk/net/ceph/ceph_common.c @@ -85,6 +85,8 @@ int ceph_check_fsid(struct ceph_client *client, struct ceph_fsid *fsid) } else { pr_info("client%lld fsid %pU\n", ceph_client_id(client), fsid); memcpy(&client->fsid, fsid, sizeof(*fsid)); + ceph_debugfs_client_init(client); + client->have_fsid = true; } return 0; } diff --git a/trunk/net/ceph/mon_client.c b/trunk/net/ceph/mon_client.c index 1845cde26227..0b62deae42bd 100644 --- a/trunk/net/ceph/mon_client.c +++ b/trunk/net/ceph/mon_client.c @@ -8,8 +8,8 @@ #include #include -#include #include + #include /* @@ -340,19 +340,8 @@ static void ceph_monc_handle_map(struct ceph_mon_client *monc, client->monc.monmap = monmap; kfree(old); - if (!client->have_fsid) { - client->have_fsid = true; - mutex_unlock(&monc->mutex); - /* - * do debugfs initialization without mutex to avoid - * creating a locking dependency - */ - ceph_debugfs_client_init(client); - goto out_unlocked; - } out: mutex_unlock(&monc->mutex); -out_unlocked: wake_up_all(&client->auth_wq); } 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/netpoll.c b/trunk/net/core/netpoll.c index ddefc513b44a..556b08298669 100644 --- a/trunk/net/core/netpoll.c +++ b/trunk/net/core/netpoll.c @@ -194,7 +194,7 @@ static void netpoll_poll_dev(struct net_device *dev) poll_napi(dev); - if (dev->flags & IFF_SLAVE) { + if (dev->priv_flags & IFF_SLAVE) { if (dev->npinfo) { struct net_device *bond_dev = dev->master; struct sk_buff *skb; 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_input.c b/trunk/net/ipv4/tcp_input.c index 53c8ce4046b2..976034f82320 100644 --- a/trunk/net/ipv4/tcp_input.c +++ b/trunk/net/ipv4/tcp_input.c @@ -1307,26 +1307,25 @@ static int tcp_match_skb_to_sack(struct sock *sk, struct sk_buff *skb, return in_sack; } -/* Mark the given newly-SACKed range as such, adjusting counters and hints. */ -static u8 tcp_sacktag_one(struct sock *sk, - struct tcp_sacktag_state *state, u8 sacked, - u32 start_seq, u32 end_seq, +static u8 tcp_sacktag_one(const struct sk_buff *skb, struct sock *sk, + struct tcp_sacktag_state *state, int dup_sack, int pcount) { struct tcp_sock *tp = tcp_sk(sk); + u8 sacked = TCP_SKB_CB(skb)->sacked; int fack_count = state->fack_count; /* Account D-SACK for retransmitted packet. */ if (dup_sack && (sacked & TCPCB_RETRANS)) { if (tp->undo_marker && tp->undo_retrans && - after(end_seq, tp->undo_marker)) + after(TCP_SKB_CB(skb)->end_seq, tp->undo_marker)) tp->undo_retrans--; if (sacked & TCPCB_SACKED_ACKED) state->reord = min(fack_count, state->reord); } /* Nothing to do; acked frame is about to be dropped (was ACKed). */ - if (!after(end_seq, tp->snd_una)) + if (!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una)) return sacked; if (!(sacked & TCPCB_SACKED_ACKED)) { @@ -1345,13 +1344,13 @@ static u8 tcp_sacktag_one(struct sock *sk, /* New sack for not retransmitted frame, * which was in hole. It is reordering. */ - if (before(start_seq, + if (before(TCP_SKB_CB(skb)->seq, tcp_highest_sack_seq(tp))) state->reord = min(fack_count, state->reord); /* SACK enhanced F-RTO (RFC4138; Appendix B) */ - if (!after(end_seq, tp->frto_highmark)) + if (!after(TCP_SKB_CB(skb)->end_seq, tp->frto_highmark)) state->flag |= FLAG_ONLY_ORIG_SACKED; } @@ -1369,7 +1368,8 @@ static u8 tcp_sacktag_one(struct sock *sk, /* Lost marker hint past SACKed? Tweak RFC3517 cnt */ if (!tcp_is_fack(tp) && (tp->lost_skb_hint != NULL) && - before(start_seq, TCP_SKB_CB(tp->lost_skb_hint)->seq)) + before(TCP_SKB_CB(skb)->seq, + TCP_SKB_CB(tp->lost_skb_hint)->seq)) tp->lost_cnt_hint += pcount; if (fack_count > tp->fackets_out) @@ -1388,9 +1388,6 @@ static u8 tcp_sacktag_one(struct sock *sk, return sacked; } -/* Shift newly-SACKed bytes from this skb to the immediately previous - * already-SACKed sk_buff. Mark the newly-SACKed bytes as such. - */ static int tcp_shifted_skb(struct sock *sk, struct sk_buff *skb, struct tcp_sacktag_state *state, unsigned int pcount, int shifted, int mss, @@ -1398,13 +1395,10 @@ static int tcp_shifted_skb(struct sock *sk, struct sk_buff *skb, { struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *prev = tcp_write_queue_prev(sk, skb); - u32 start_seq = TCP_SKB_CB(skb)->seq; /* start of newly-SACKed */ - u32 end_seq = start_seq + shifted; /* end of newly-SACKed */ BUG_ON(!pcount); - /* Adjust hint for FACK. Non-FACK is handled in tcp_sacktag_one(). */ - if (tcp_is_fack(tp) && (skb == tp->lost_skb_hint)) + if (skb == tp->lost_skb_hint) tp->lost_cnt_hint += pcount; TCP_SKB_CB(prev)->end_seq += shifted; @@ -1430,11 +1424,8 @@ static int tcp_shifted_skb(struct sock *sk, struct sk_buff *skb, skb_shinfo(skb)->gso_type = 0; } - /* Adjust counters and hints for the newly sacked sequence range but - * discard the return value since prev is already marked. - */ - tcp_sacktag_one(sk, state, TCP_SKB_CB(skb)->sacked, - start_seq, end_seq, dup_sack, pcount); + /* We discard results */ + tcp_sacktag_one(skb, sk, state, dup_sack, pcount); /* Difference in this won't matter, both ACKed by the same cumul. ACK */ TCP_SKB_CB(prev)->sacked |= (TCP_SKB_CB(skb)->sacked & TCPCB_EVER_RETRANS); @@ -1673,14 +1664,10 @@ static struct sk_buff *tcp_sacktag_walk(struct sk_buff *skb, struct sock *sk, break; if (in_sack) { - TCP_SKB_CB(skb)->sacked = - tcp_sacktag_one(sk, - state, - TCP_SKB_CB(skb)->sacked, - TCP_SKB_CB(skb)->seq, - TCP_SKB_CB(skb)->end_seq, - dup_sack, - tcp_skb_pcount(skb)); + TCP_SKB_CB(skb)->sacked = tcp_sacktag_one(skb, sk, + state, + dup_sack, + tcp_skb_pcount(skb)); if (!before(TCP_SKB_CB(skb)->seq, tcp_highest_sack_seq(tp))) 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/main.c b/trunk/net/mac80211/main.c index 0a0d94ad9b08..b142bd4c2390 100644 --- a/trunk/net/mac80211/main.c +++ b/trunk/net/mac80211/main.c @@ -910,6 +910,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) wiphy_debug(local->hw.wiphy, "Failed to initialize wep: %d\n", result); + ieee80211_led_init(local); + rtnl_lock(); result = ieee80211_init_rate_ctrl_alg(local, @@ -931,8 +933,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) rtnl_unlock(); - ieee80211_led_init(local); - local->network_latency_notifier.notifier_call = ieee80211_max_network_latency; result = pm_qos_add_notifier(PM_QOS_NETWORK_LATENCY, diff --git a/trunk/net/rxrpc/ar-key.c b/trunk/net/rxrpc/ar-key.c index ae3a035f5390..4cba13e46ffd 100644 --- a/trunk/net/rxrpc/ar-key.c +++ b/trunk/net/rxrpc/ar-key.c @@ -232,7 +232,7 @@ static int rxrpc_krb5_decode_principal(struct krb5_principal *princ, if (toklen <= (n_parts + 1) * 4) return -EINVAL; - princ->name_parts = kcalloc(n_parts, sizeof(char *), GFP_KERNEL); + princ->name_parts = kcalloc(sizeof(char *), n_parts, GFP_KERNEL); if (!princ->name_parts) return -ENOMEM; @@ -355,7 +355,7 @@ static int rxrpc_krb5_decode_tagged_array(struct krb5_tagged_data **_td, _debug("n_elem %d", n_elem); - td = kcalloc(n_elem, sizeof(struct krb5_tagged_data), + td = kcalloc(sizeof(struct krb5_tagged_data), n_elem, GFP_KERNEL); if (!td) return -ENOMEM; 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/net/sunrpc/auth_generic.c b/trunk/net/sunrpc/auth_generic.c index 75762f346975..1426ec3d0a53 100644 --- a/trunk/net/sunrpc/auth_generic.c +++ b/trunk/net/sunrpc/auth_generic.c @@ -92,7 +92,6 @@ generic_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) if (gcred->acred.group_info != NULL) get_group_info(gcred->acred.group_info); gcred->acred.machine_cred = acred->machine_cred; - gcred->acred.principal = acred->principal; dprintk("RPC: allocated %s cred %p for uid %d gid %d\n", gcred->acred.machine_cred ? "machine" : "generic", @@ -124,17 +123,6 @@ generic_destroy_cred(struct rpc_cred *cred) call_rcu(&cred->cr_rcu, generic_free_cred_callback); } -static int -machine_cred_match(struct auth_cred *acred, struct generic_cred *gcred, int flags) -{ - if (!gcred->acred.machine_cred || - gcred->acred.principal != acred->principal || - gcred->acred.uid != acred->uid || - gcred->acred.gid != acred->gid) - return 0; - return 1; -} - /* * Match credentials against current process creds. */ @@ -144,12 +132,9 @@ generic_match(struct auth_cred *acred, struct rpc_cred *cred, int flags) struct generic_cred *gcred = container_of(cred, struct generic_cred, gc_base); int i; - if (acred->machine_cred) - return machine_cred_match(acred, gcred, flags); - if (gcred->acred.uid != acred->uid || gcred->acred.gid != acred->gid || - gcred->acred.machine_cred != 0) + gcred->acred.machine_cred != acred->machine_cred) goto out_nomatch; /* Optimisation in the case where pointers are identical... */ diff --git a/trunk/scripts/checkpatch.pl b/trunk/scripts/checkpatch.pl index a3b9782441f9..e3bfcbe8a520 100755 --- a/trunk/scripts/checkpatch.pl +++ b/trunk/scripts/checkpatch.pl @@ -1924,12 +1924,6 @@ sub process { my $pre_ctx = "$1$2"; my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0); - - if ($line =~ /^\+\t{6,}/) { - WARN("DEEP_INDENTATION", - "Too many leading tabs - consider code refactoring\n" . $herecurr); - } - my $ctx_cnt = $realcnt - $#ctx - 1; my $ctx = join("\n", @ctx); diff --git a/trunk/scripts/mod/file2alias.c b/trunk/scripts/mod/file2alias.c index e8c969577768..c0e14b3f2306 100644 --- a/trunk/scripts/mod/file2alias.c +++ b/trunk/scripts/mod/file2alias.c @@ -823,6 +823,16 @@ static int do_spi_entry(const char *filename, struct spi_device_id *id, } ADD_TO_DEVTABLE("spi", struct spi_device_id, do_spi_entry); +/* Looks like: mcp:S */ +static int do_mcp_entry(const char *filename, struct mcp_device_id *id, + char *alias) +{ + sprintf(alias, MCP_MODULE_PREFIX "%s", id->name); + + return 1; +} +ADD_TO_DEVTABLE("mcp", struct mcp_device_id, do_mcp_entry); + static const struct dmifield { const char *prefix; int field; diff --git a/trunk/sound/core/compress_offload.c b/trunk/sound/core/compress_offload.c index a68aed7fce02..dac3633507c9 100644 --- a/trunk/sound/core/compress_offload.c +++ b/trunk/sound/core/compress_offload.c @@ -441,22 +441,19 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) params = kmalloc(sizeof(*params), GFP_KERNEL); if (!params) return -ENOMEM; - if (copy_from_user(params, (void __user *)arg, sizeof(*params))) { - retval = -EFAULT; - goto out; - } + if (copy_from_user(params, (void __user *)arg, sizeof(*params))) + return -EFAULT; retval = snd_compr_allocate_buffer(stream, params); if (retval) { - retval = -ENOMEM; - goto out; + kfree(params); + return -ENOMEM; } retval = stream->ops->set_params(stream, params); if (retval) goto out; stream->runtime->state = SNDRV_PCM_STATE_SETUP; - } else { + } else return -EPERM; - } out: kfree(params); return retval; diff --git a/trunk/sound/isa/sb/emu8000_patch.c b/trunk/sound/isa/sb/emu8000_patch.c index c99c6078be33..e09f144177f5 100644 --- a/trunk/sound/isa/sb/emu8000_patch.c +++ b/trunk/sound/isa/sb/emu8000_patch.c @@ -22,6 +22,7 @@ #include "emu8000_local.h" #include #include +#include static int emu8000_reset_addr; module_param(emu8000_reset_addr, int, 0444); diff --git a/trunk/sound/pci/hda/hda_codec.c b/trunk/sound/pci/hda/hda_codec.c index c2c65f63bf06..4df72c0e8c37 100644 --- a/trunk/sound/pci/hda/hda_codec.c +++ b/trunk/sound/pci/hda/hda_codec.c @@ -1447,7 +1447,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, for (i = 0; i < c->cvt_setups.used; i++) { p = snd_array_elem(&c->cvt_setups, i); if (!p->active && p->stream_tag == stream_tag && - get_wcaps_type(get_wcaps(c, p->nid)) == type) + get_wcaps_type(get_wcaps(codec, p->nid)) == type) p->dirty = 1; } } diff --git a/trunk/sound/pci/hda/hda_jack.c b/trunk/sound/pci/hda/hda_jack.c index 9d819c4b4923..d8a35da0803f 100644 --- a/trunk/sound/pci/hda/hda_jack.c +++ b/trunk/sound/pci/hda/hda_jack.c @@ -282,8 +282,7 @@ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctl); static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, - const struct auto_pin_cfg *cfg, - char *lastname, int *lastidx) + const struct auto_pin_cfg *cfg) { unsigned int def_conf, conn; char name[44]; @@ -299,10 +298,6 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, return 0; snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), &idx); - if (!strcmp(name, lastname) && idx == *lastidx) - idx++; - strncpy(lastname, name, 44); - *lastidx = idx; err = snd_hda_jack_add_kctl(codec, nid, name, idx); if (err < 0) return err; @@ -316,42 +311,41 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { const hda_nid_t *p; - int i, err, lastidx = 0; - char lastname[44] = ""; + int i, err; for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) { - err = add_jack_kctl(codec, *p, cfg, lastname, &lastidx); + err = add_jack_kctl(codec, *p, cfg); if (err < 0) return err; } for (i = 0, p = cfg->hp_pins; i < cfg->hp_outs; i++, p++) { if (*p == *cfg->line_out_pins) /* might be duplicated */ break; - err = add_jack_kctl(codec, *p, cfg, lastname, &lastidx); + err = add_jack_kctl(codec, *p, cfg); if (err < 0) return err; } for (i = 0, p = cfg->speaker_pins; i < cfg->speaker_outs; i++, p++) { if (*p == *cfg->line_out_pins) /* might be duplicated */ break; - err = add_jack_kctl(codec, *p, cfg, lastname, &lastidx); + err = add_jack_kctl(codec, *p, cfg); if (err < 0) return err; } for (i = 0; i < cfg->num_inputs; i++) { - err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg, lastname, &lastidx); + err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg); if (err < 0) return err; } for (i = 0, p = cfg->dig_out_pins; i < cfg->dig_outs; i++, p++) { - err = add_jack_kctl(codec, *p, cfg, lastname, &lastidx); + err = add_jack_kctl(codec, *p, cfg); if (err < 0) return err; } - err = add_jack_kctl(codec, cfg->dig_in_pin, cfg, lastname, &lastidx); + err = add_jack_kctl(codec, cfg->dig_in_pin, cfg); if (err < 0) return err; - err = add_jack_kctl(codec, cfg->mono_out_pin, cfg, lastname, &lastidx); + err = add_jack_kctl(codec, cfg->mono_out_pin, cfg); if (err < 0) return err; return 0; diff --git a/trunk/sound/pci/hda/patch_ca0132.c b/trunk/sound/pci/hda/patch_ca0132.c index 21d91d580da8..35abe3c62908 100644 --- a/trunk/sound/pci/hda/patch_ca0132.c +++ b/trunk/sound/pci/hda/patch_ca0132.c @@ -728,19 +728,18 @@ static int ca0132_hp_switch_put(struct snd_kcontrol *kcontrol, err = chipio_read(codec, REG_CODEC_MUTE, &data); if (err < 0) - goto exit; + return err; /* *valp 0 is mute, 1 is unmute */ data = (data & 0x7f) | (*valp ? 0 : 0x80); - err = chipio_write(codec, REG_CODEC_MUTE, data); + chipio_write(codec, REG_CODEC_MUTE, data); if (err < 0) - goto exit; + return err; spec->curr_hp_switch = *valp; - exit: snd_hda_power_down(codec); - return err < 0 ? err : 1; + return 1; } static int ca0132_speaker_switch_get(struct snd_kcontrol *kcontrol, @@ -771,19 +770,18 @@ static int ca0132_speaker_switch_put(struct snd_kcontrol *kcontrol, err = chipio_read(codec, REG_CODEC_MUTE, &data); if (err < 0) - goto exit; + return err; /* *valp 0 is mute, 1 is unmute */ data = (data & 0xef) | (*valp ? 0 : 0x10); - err = chipio_write(codec, REG_CODEC_MUTE, data); + chipio_write(codec, REG_CODEC_MUTE, data); if (err < 0) - goto exit; + return err; spec->curr_speaker_switch = *valp; - exit: snd_hda_power_down(codec); - return err < 0 ? err : 1; + return 1; } static int ca0132_hp_volume_get(struct snd_kcontrol *kcontrol, @@ -821,26 +819,25 @@ static int ca0132_hp_volume_put(struct snd_kcontrol *kcontrol, err = chipio_read(codec, REG_CODEC_HP_VOL_L, &data); if (err < 0) - goto exit; + return err; val = 31 - left_vol; data = (data & 0xe0) | val; - err = chipio_write(codec, REG_CODEC_HP_VOL_L, data); + chipio_write(codec, REG_CODEC_HP_VOL_L, data); if (err < 0) - goto exit; + return err; val = 31 - right_vol; data = (data & 0xe0) | val; - err = chipio_write(codec, REG_CODEC_HP_VOL_R, data); + chipio_write(codec, REG_CODEC_HP_VOL_R, data); if (err < 0) - goto exit; + return err; spec->curr_hp_volume[0] = left_vol; spec->curr_hp_volume[1] = right_vol; - exit: snd_hda_power_down(codec); - return err < 0 ? err : 1; + return 1; } static int add_hp_switch(struct hda_codec *codec, hda_nid_t nid) @@ -939,8 +936,6 @@ static int ca0132_build_controls(struct hda_codec *codec) if (err < 0) return err; err = add_in_volume(codec, spec->dig_in, "IEC958"); - if (err < 0) - return err; } return 0; } diff --git a/trunk/sound/pci/hda/patch_cirrus.c b/trunk/sound/pci/hda/patch_cirrus.c index bc5a993d1146..0e99357e822c 100644 --- a/trunk/sound/pci/hda/patch_cirrus.c +++ b/trunk/sound/pci/hda/patch_cirrus.c @@ -988,10 +988,8 @@ static void cs_automic(struct hda_codec *codec) change_cur_input(codec, !spec->automic_idx, 0); } else { if (present) { - if (spec->cur_input != spec->automic_idx) { - spec->last_input = spec->cur_input; - spec->cur_input = spec->automic_idx; - } + spec->last_input = spec->cur_input; + spec->cur_input = spec->automic_idx; } else { spec->cur_input = spec->last_input; } diff --git a/trunk/sound/pci/hda/patch_realtek.c b/trunk/sound/pci/hda/patch_realtek.c index 9350f3c3bdf8..c95c8bde12d0 100644 --- a/trunk/sound/pci/hda/patch_realtek.c +++ b/trunk/sound/pci/hda/patch_realtek.c @@ -177,7 +177,6 @@ struct alc_spec { unsigned int detect_lo:1; /* Line-out detection enabled */ unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */ unsigned int automute_lo_possible:1; /* there are line outs and HP */ - unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ /* other flags */ unsigned int no_analog :1; /* digital I/O only */ @@ -496,24 +495,13 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, for (i = 0; i < num_pins; i++) { hda_nid_t nid = pins[i]; - unsigned int val; if (!nid) break; switch (spec->automute_mode) { case ALC_AUTOMUTE_PIN: - /* don't reset VREF value in case it's controlling - * the amp (see alc861_fixup_asus_amp_vref_0f()) - */ - if (spec->keep_vref_in_automute) { - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - val &= ~PIN_HP; - } else - val = 0; - val |= pin_bits; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - val); + pin_bits); break; case ALC_AUTOMUTE_AMP: snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, @@ -1855,8 +1843,6 @@ static const char * const alc_slave_vols[] = { "Speaker Playback Volume", "Mono Playback Volume", "Line-Out Playback Volume", - "CLFE Playback Volume", - "Bass Speaker Playback Volume", "PCM Playback Volume", NULL, }; @@ -1872,8 +1858,6 @@ static const char * const alc_slave_sws[] = { "Mono Playback Switch", "IEC958 Playback Switch", "Line-Out Playback Switch", - "CLFE Playback Switch", - "Bass Speaker Playback Switch", "PCM Playback Switch", NULL, }; @@ -2322,7 +2306,7 @@ static int alc_build_pcms(struct hda_codec *codec) "%s Analog", codec->chip_name); info->name = spec->stream_name_analog; - if (spec->multiout.num_dacs > 0) { + if (spec->multiout.dac_nids > 0) { p = spec->stream_analog_playback; if (!p) p = &alc_pcm_analog_playback; @@ -4751,6 +4735,7 @@ enum { ALC262_FIXUP_FSC_H270, ALC262_FIXUP_HP_Z200, ALC262_FIXUP_TYAN, + ALC262_FIXUP_TOSHIBA_RX1, ALC262_FIXUP_LENOVO_3000, ALC262_FIXUP_BENQ, ALC262_FIXUP_BENQ_T31, @@ -4780,6 +4765,16 @@ static const struct alc_fixup alc262_fixups[] = { { } } }, + [ALC262_FIXUP_TOSHIBA_RX1] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x14, 0x90170110 }, /* speaker */ + { 0x15, 0x0421101f }, /* HP */ + { 0x1a, 0x40f000f0 }, /* N/A */ + { 0x1b, 0x40f000f0 }, /* N/A */ + { 0x1e, 0x40f000f0 }, /* N/A */ + } + }, [ALC262_FIXUP_LENOVO_3000] = { .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { @@ -4812,6 +4807,8 @@ static const struct snd_pci_quirk alc262_fixup_tbl[] = { SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FIXUP_BENQ), SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FIXUP_BENQ), SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_FIXUP_TYAN), + SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1", + ALC262_FIXUP_TOSHIBA_RX1), SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", ALC262_FIXUP_FSC_H270), SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000", ALC262_FIXUP_LENOVO_3000), SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_FIXUP_BENQ), @@ -5380,6 +5377,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A", ALC269_FIXUP_AMIC), SND_PCI_QUIRK(0x1043, 0x1013, "ASUS N61Da", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x1113, "ASUS N63Jn", ALC269_FIXUP_AMIC), SND_PCI_QUIRK(0x1043, 0x1143, "ASUS B53f", ALC269_FIXUP_AMIC), SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_FIXUP_AMIC), SND_PCI_QUIRK(0x1043, 0x1183, "ASUS K72DR", ALC269_FIXUP_AMIC), @@ -5588,28 +5586,8 @@ static const struct hda_amp_list alc861_loopbacks[] = { /* Pin config fixes */ enum { PINFIX_FSC_AMILO_PI1505, - PINFIX_ASUS_A6RP, }; -/* On some laptops, VREF of pin 0x0f is abused for controlling the main amp */ -static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec, - const struct alc_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - unsigned int val; - - if (action != ALC_FIXUP_ACT_INIT) - return; - val = snd_hda_codec_read(codec, 0x0f, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))) - val |= AC_PINCTL_IN_EN; - val |= AC_PINCTL_VREF_50; - snd_hda_codec_write(codec, 0x0f, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, val); - spec->keep_vref_in_automute = 1; -} - static const struct alc_fixup alc861_fixups[] = { [PINFIX_FSC_AMILO_PI1505] = { .type = ALC_FIXUP_PINS, @@ -5619,16 +5597,9 @@ static const struct alc_fixup alc861_fixups[] = { { } } }, - [PINFIX_ASUS_A6RP] = { - .type = ALC_FIXUP_FUNC, - .v.func = alc861_fixup_asus_amp_vref_0f, - }, }; static const struct snd_pci_quirk alc861_fixup_tbl[] = { - SND_PCI_QUIRK_VENDOR(0x1043, "ASUS laptop", PINFIX_ASUS_A6RP), - SND_PCI_QUIRK(0x1584, 0x0000, "Uniwill ECS M31EI", PINFIX_ASUS_A6RP), - SND_PCI_QUIRK(0x1584, 0x2b01, "Haier W18", PINFIX_ASUS_A6RP), SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", PINFIX_FSC_AMILO_PI1505), {} }; diff --git a/trunk/sound/pci/hda/patch_sigmatel.c b/trunk/sound/pci/hda/patch_sigmatel.c index 948f0be2f4f3..336cfcd324f9 100644 --- a/trunk/sound/pci/hda/patch_sigmatel.c +++ b/trunk/sound/pci/hda/patch_sigmatel.c @@ -4870,14 +4870,7 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity) /* BIOS bug: unfilled OEM string */ if (strstr(dev->name, "HP_Mute_LED_P_G")) { set_hp_led_gpio(codec); - switch (codec->subsystem_id) { - case 0x103c148a: - spec->gpio_led_polarity = 0; - break; - default: - spec->gpio_led_polarity = 1; - break; - } + spec->gpio_led_polarity = 1; return 1; } } diff --git a/trunk/sound/pci/hda/patch_via.c b/trunk/sound/pci/hda/patch_via.c index 284e311040fe..03e63fed9caf 100644 --- a/trunk/sound/pci/hda/patch_via.c +++ b/trunk/sound/pci/hda/patch_via.c @@ -199,9 +199,6 @@ struct via_spec { unsigned int no_pin_power_ctl; enum VIA_HDA_CODEC codec_type; - /* analog low-power control */ - bool alc_mode; - /* smart51 setup */ unsigned int smart51_nums; hda_nid_t smart51_pins[2]; @@ -690,15 +687,6 @@ static void via_auto_init_analog_input(struct hda_codec *codec) } } -static void update_power_state(struct hda_codec *codec, hda_nid_t nid, - unsigned int parm) -{ - if (snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_POWER_STATE, 0) == parm) - return; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); -} - static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, unsigned int *affected_parm) { @@ -721,7 +709,7 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, } else parm = AC_PWRST_D3; - update_power_state(codec, nid, parm); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); } static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, @@ -761,7 +749,6 @@ static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, return 0; spec->no_pin_power_ctl = val; set_widgets_power_state(codec); - analog_low_current_mode(codec); return 1; } @@ -1049,19 +1036,13 @@ static bool is_aa_path_mute(struct hda_codec *codec) } /* enter/exit analog low-current mode */ -static void __analog_low_current_mode(struct hda_codec *codec, bool force) +static void analog_low_current_mode(struct hda_codec *codec) { struct via_spec *spec = codec->spec; bool enable; unsigned int verb, parm; - if (spec->no_pin_power_ctl) - enable = false; - else - enable = is_aa_path_mute(codec) && !spec->opened_streams; - if (enable == spec->alc_mode && !force) - return; - spec->alc_mode = enable; + enable = is_aa_path_mute(codec) && (spec->opened_streams != 0); /* decide low current mode's verb & parameter */ switch (spec->codec_type) { @@ -1093,11 +1074,6 @@ static void __analog_low_current_mode(struct hda_codec *codec, bool force) snd_hda_codec_write(codec, codec->afg, 0, verb, parm); } -static void analog_low_current_mode(struct hda_codec *codec) -{ - return __analog_low_current_mode(codec, false); -} - /* * generic initialization of ADC, input mixers and output mixers */ @@ -1470,7 +1446,6 @@ static int via_build_controls(struct hda_codec *codec) struct snd_kcontrol *kctl; int err, i; - spec->no_pin_power_ctl = 1; if (spec->set_widgets_power_state) if (!via_clone_control(spec, &via_pin_power_ctl_enum)) return -ENOMEM; @@ -1524,6 +1499,10 @@ static int via_build_controls(struct hda_codec *codec) return err; } + /* init power states */ + set_widgets_power_state(codec); + analog_low_current_mode(codec); + via_free_kctls(codec); /* no longer needed */ err = snd_hda_jack_add_kctls(codec, &spec->autocfg); @@ -2316,7 +2295,10 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol, if (mux) { /* switch to D0 beofre change index */ - update_power_state(codec, mux, AC_PWRST_D0); + if (snd_hda_codec_read(codec, mux, 0, + AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) + snd_hda_codec_write(codec, mux, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); snd_hda_codec_write(codec, mux, 0, AC_VERB_SET_CONNECT_SEL, spec->inputs[cur].mux_idx); @@ -2794,10 +2776,6 @@ static int via_init(struct hda_codec *codec) for (i = 0; i < spec->num_iverbs; i++) snd_hda_sequence_write(codec, spec->init_verbs[i]); - /* init power states */ - set_widgets_power_state(codec); - __analog_low_current_mode(codec, true); - via_auto_init_multi_out(codec); via_auto_init_hp_out(codec); via_auto_init_speaker_out(codec); @@ -2944,9 +2922,9 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec) if (imux_is_smixer) parm = AC_PWRST_D0; /* SW0 (17h), AIW 0/1 (13h/14h) */ - update_power_state(codec, 0x17, parm); - update_power_state(codec, 0x13, parm); - update_power_state(codec, 0x14, parm); + snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); /* outputs */ /* PW0 (19h), SW1 (18h), AOW1 (11h) */ @@ -2954,8 +2932,8 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec) set_pin_power_state(codec, 0x19, &parm); if (spec->smart51_enabled) set_pin_power_state(codec, 0x1b, &parm); - update_power_state(codec, 0x18, parm); - update_power_state(codec, 0x11, parm); + snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); /* PW6 (22h), SW2 (26h), AOW2 (24h) */ if (is_8ch) { @@ -2963,16 +2941,20 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec) set_pin_power_state(codec, 0x22, &parm); if (spec->smart51_enabled) set_pin_power_state(codec, 0x1a, &parm); - update_power_state(codec, 0x26, parm); - update_power_state(codec, 0x24, parm); + snd_hda_codec_write(codec, 0x26, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x24, 0, + AC_VERB_SET_POWER_STATE, parm); } else if (codec->vendor_id == 0x11064397) { /* PW7(23h), SW2(27h), AOW2(25h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x23, &parm); if (spec->smart51_enabled) set_pin_power_state(codec, 0x1a, &parm); - update_power_state(codec, 0x27, parm); - update_power_state(codec, 0x25, parm); + snd_hda_codec_write(codec, 0x27, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x25, 0, + AC_VERB_SET_POWER_STATE, parm); } /* PW 3/4/7 (1ch/1dh/23h) */ @@ -2984,13 +2966,17 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec) set_pin_power_state(codec, 0x23, &parm); /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ - update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm); - update_power_state(codec, 0x10, parm); + snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, + imux_is_smixer ? AC_PWRST_D0 : parm); + snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); if (is_8ch) { - update_power_state(codec, 0x25, parm); - update_power_state(codec, 0x27, parm); + snd_hda_codec_write(codec, 0x25, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x27, 0, + AC_VERB_SET_POWER_STATE, parm); } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode) - update_power_state(codec, 0x25, parm); + snd_hda_codec_write(codec, 0x25, 0, + AC_VERB_SET_POWER_STATE, parm); } static int patch_vt1708S(struct hda_codec *codec); @@ -3163,10 +3149,10 @@ static void set_widgets_power_state_vt1702(struct hda_codec *codec) if (imux_is_smixer) parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */ /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ - update_power_state(codec, 0x13, parm); - update_power_state(codec, 0x12, parm); - update_power_state(codec, 0x1f, parm); - update_power_state(codec, 0x20, parm); + snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm); /* outputs */ /* PW 3/4 (16h/17h) */ @@ -3174,9 +3160,10 @@ static void set_widgets_power_state_vt1702(struct hda_codec *codec) set_pin_power_state(codec, 0x17, &parm); set_pin_power_state(codec, 0x16, &parm); /* MW0 (1ah), AOW 0/1 (10h/1dh) */ - update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm); - update_power_state(codec, 0x10, parm); - update_power_state(codec, 0x1d, parm); + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, + imux_is_smixer ? AC_PWRST_D0 : parm); + snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); } static int patch_vt1702(struct hda_codec *codec) @@ -3241,48 +3228,52 @@ static void set_widgets_power_state_vt1718S(struct hda_codec *codec) if (imux_is_smixer) parm = AC_PWRST_D0; /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ - update_power_state(codec, 0x1e, parm); - update_power_state(codec, 0x1f, parm); - update_power_state(codec, 0x10, parm); - update_power_state(codec, 0x11, parm); + snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); /* outputs */ /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x27, &parm); - update_power_state(codec, 0x1a, parm); - update_power_state(codec, 0xb, parm); + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm); /* PW2 (26h), AOW2 (ah) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x26, &parm); if (spec->smart51_enabled) set_pin_power_state(codec, 0x2b, &parm); - update_power_state(codec, 0xa, parm); + snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm); /* PW0 (24h), AOW0 (8h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x24, &parm); if (!spec->hp_independent_mode) /* check for redirected HP */ set_pin_power_state(codec, 0x28, &parm); - update_power_state(codec, 0x8, parm); + snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ - update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm); + snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, + imux_is_smixer ? AC_PWRST_D0 : parm); /* PW1 (25h), AOW1 (9h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x25, &parm); if (spec->smart51_enabled) set_pin_power_state(codec, 0x2a, &parm); - update_power_state(codec, 0x9, parm); + snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm); if (spec->hp_independent_mode) { /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x28, &parm); - update_power_state(codec, 0x1b, parm); - update_power_state(codec, 0x34, parm); - update_power_state(codec, 0xc, parm); + snd_hda_codec_write(codec, 0x1b, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x34, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0xc, 0, + AC_VERB_SET_POWER_STATE, parm); } } @@ -3442,8 +3433,8 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) if (imux_is_smixer) parm = AC_PWRST_D0; /* SW0 (17h), AIW0(13h) */ - update_power_state(codec, 0x17, parm); - update_power_state(codec, 0x13, parm); + snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); parm = AC_PWRST_D3; set_pin_power_state(codec, 0x1e, &parm); @@ -3451,11 +3442,12 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) if (spec->dmic_enabled) set_pin_power_state(codec, 0x22, &parm); else - update_power_state(codec, 0x22, AC_PWRST_D3); + snd_hda_codec_write(codec, 0x22, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); /* SW2(26h), AIW1(14h) */ - update_power_state(codec, 0x26, parm); - update_power_state(codec, 0x14, parm); + snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); /* outputs */ /* PW0 (19h), SW1 (18h), AOW1 (11h) */ @@ -3464,8 +3456,8 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) /* Smart 5.1 PW2(1bh) */ if (spec->smart51_enabled) set_pin_power_state(codec, 0x1b, &parm); - update_power_state(codec, 0x18, parm); - update_power_state(codec, 0x11, parm); + snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); /* PW7 (23h), SW3 (27h), AOW3 (25h) */ parm = AC_PWRST_D3; @@ -3473,12 +3465,12 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) /* Smart 5.1 PW1(1ah) */ if (spec->smart51_enabled) set_pin_power_state(codec, 0x1a, &parm); - update_power_state(codec, 0x27, parm); + snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm); /* Smart 5.1 PW5(1eh) */ if (spec->smart51_enabled) set_pin_power_state(codec, 0x1e, &parm); - update_power_state(codec, 0x25, parm); + snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm); /* Mono out */ /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ @@ -3494,9 +3486,9 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) mono_out = 1; } parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; - update_power_state(codec, 0x28, parm); - update_power_state(codec, 0x29, parm); - update_power_state(codec, 0x2a, parm); + snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm); /* PW 3/4 (1ch/1dh) */ parm = AC_PWRST_D3; @@ -3504,12 +3496,15 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) set_pin_power_state(codec, 0x1d, &parm); /* HP Independent Mode, power on AOW3 */ if (spec->hp_independent_mode) - update_power_state(codec, 0x25, parm); + snd_hda_codec_write(codec, 0x25, 0, + AC_VERB_SET_POWER_STATE, parm); /* force to D0 for internal Speaker */ /* MW0 (16h), AOW0 (10h) */ - update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm); - update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm); + snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, + imux_is_smixer ? AC_PWRST_D0 : parm); + snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, + mono_out ? AC_PWRST_D0 : parm); } static int patch_vt1716S(struct hda_codec *codec) @@ -3585,45 +3580,54 @@ static void set_widgets_power_state_vt2002P(struct hda_codec *codec) set_pin_power_state(codec, 0x2b, &parm); parm = AC_PWRST_D0; /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ - update_power_state(codec, 0x1e, parm); - update_power_state(codec, 0x1f, parm); - update_power_state(codec, 0x10, parm); - update_power_state(codec, 0x11, parm); + snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); /* outputs */ /* AOW0 (8h)*/ - update_power_state(codec, 0x8, parm); + snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); if (spec->codec_type == VT1802) { /* PW4 (28h), MW4 (18h), MUX4(38h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x28, &parm); - update_power_state(codec, 0x18, parm); - update_power_state(codec, 0x38, parm); + snd_hda_codec_write(codec, 0x18, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x38, 0, + AC_VERB_SET_POWER_STATE, parm); } else { /* PW4 (26h), MW4 (1ch), MUX4(37h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x26, &parm); - update_power_state(codec, 0x1c, parm); - update_power_state(codec, 0x37, parm); + snd_hda_codec_write(codec, 0x1c, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x37, 0, + AC_VERB_SET_POWER_STATE, parm); } if (spec->codec_type == VT1802) { /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x25, &parm); - update_power_state(codec, 0x15, parm); - update_power_state(codec, 0x35, parm); + snd_hda_codec_write(codec, 0x15, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x35, 0, + AC_VERB_SET_POWER_STATE, parm); } else { /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x25, &parm); - update_power_state(codec, 0x19, parm); - update_power_state(codec, 0x35, parm); + snd_hda_codec_write(codec, 0x19, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x35, 0, + AC_VERB_SET_POWER_STATE, parm); } if (spec->hp_independent_mode) - update_power_state(codec, 0x9, AC_PWRST_D0); + snd_hda_codec_write(codec, 0x9, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); /* Class-D */ /* PW0 (24h), MW0(18h/14h), MUX0(34h) */ @@ -3633,10 +3637,12 @@ static void set_widgets_power_state_vt2002P(struct hda_codec *codec) set_pin_power_state(codec, 0x24, &parm); parm = present ? AC_PWRST_D3 : AC_PWRST_D0; if (spec->codec_type == VT1802) - update_power_state(codec, 0x14, parm); + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_POWER_STATE, parm); else - update_power_state(codec, 0x18, parm); - update_power_state(codec, 0x34, parm); + snd_hda_codec_write(codec, 0x18, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm); /* Mono Out */ present = snd_hda_jack_detect(codec, 0x26); @@ -3644,20 +3650,28 @@ static void set_widgets_power_state_vt2002P(struct hda_codec *codec) parm = present ? AC_PWRST_D3 : AC_PWRST_D0; if (spec->codec_type == VT1802) { /* PW15 (33h), MW8(1ch), MUX8(3ch) */ - update_power_state(codec, 0x33, parm); - update_power_state(codec, 0x1c, parm); - update_power_state(codec, 0x3c, parm); + snd_hda_codec_write(codec, 0x33, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x1c, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x3c, 0, + AC_VERB_SET_POWER_STATE, parm); } else { /* PW15 (31h), MW8(17h), MUX8(3bh) */ - update_power_state(codec, 0x31, parm); - update_power_state(codec, 0x17, parm); - update_power_state(codec, 0x3b, parm); + snd_hda_codec_write(codec, 0x31, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x17, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x3b, 0, + AC_VERB_SET_POWER_STATE, parm); } /* MW9 (21h) */ if (imux_is_smixer || !is_aa_path_mute(codec)) - update_power_state(codec, 0x21, AC_PWRST_D0); + snd_hda_codec_write(codec, 0x21, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); else - update_power_state(codec, 0x21, AC_PWRST_D3); + snd_hda_codec_write(codec, 0x21, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); } /* patch for vt2002P */ @@ -3717,28 +3731,30 @@ static void set_widgets_power_state_vt1812(struct hda_codec *codec) set_pin_power_state(codec, 0x2b, &parm); parm = AC_PWRST_D0; /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ - update_power_state(codec, 0x1e, parm); - update_power_state(codec, 0x1f, parm); - update_power_state(codec, 0x10, parm); - update_power_state(codec, 0x11, parm); + snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); /* outputs */ /* AOW0 (8h)*/ - update_power_state(codec, 0x8, AC_PWRST_D0); + snd_hda_codec_write(codec, 0x8, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); /* PW4 (28h), MW4 (18h), MUX4(38h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x28, &parm); - update_power_state(codec, 0x18, parm); - update_power_state(codec, 0x38, parm); + snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm); /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x25, &parm); - update_power_state(codec, 0x15, parm); - update_power_state(codec, 0x35, parm); + snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm); if (spec->hp_independent_mode) - update_power_state(codec, 0x9, AC_PWRST_D0); + snd_hda_codec_write(codec, 0x9, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); /* Internal Speaker */ /* PW0 (24h), MW0(14h), MUX0(34h) */ @@ -3747,11 +3763,15 @@ static void set_widgets_power_state_vt1812(struct hda_codec *codec) parm = AC_PWRST_D3; set_pin_power_state(codec, 0x24, &parm); if (present) { - update_power_state(codec, 0x14, AC_PWRST_D3); - update_power_state(codec, 0x34, AC_PWRST_D3); + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + snd_hda_codec_write(codec, 0x34, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); } else { - update_power_state(codec, 0x14, AC_PWRST_D0); - update_power_state(codec, 0x34, AC_PWRST_D0); + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + snd_hda_codec_write(codec, 0x34, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); } @@ -3762,20 +3782,26 @@ static void set_widgets_power_state_vt1812(struct hda_codec *codec) parm = AC_PWRST_D3; set_pin_power_state(codec, 0x31, &parm); if (present) { - update_power_state(codec, 0x1c, AC_PWRST_D3); - update_power_state(codec, 0x3c, AC_PWRST_D3); - update_power_state(codec, 0x3e, AC_PWRST_D3); + snd_hda_codec_write(codec, 0x1c, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + snd_hda_codec_write(codec, 0x3c, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + snd_hda_codec_write(codec, 0x3e, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); } else { - update_power_state(codec, 0x1c, AC_PWRST_D0); - update_power_state(codec, 0x3c, AC_PWRST_D0); - update_power_state(codec, 0x3e, AC_PWRST_D0); + snd_hda_codec_write(codec, 0x1c, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + snd_hda_codec_write(codec, 0x3c, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + snd_hda_codec_write(codec, 0x3e, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); } /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x33, &parm); - update_power_state(codec, 0x1d, parm); - update_power_state(codec, 0x3d, parm); + snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm); } diff --git a/trunk/sound/pci/oxygen/oxygen_mixer.c b/trunk/sound/pci/oxygen/oxygen_mixer.c index c0dbb52d45be..26c7e8bcb229 100644 --- a/trunk/sound/pci/oxygen/oxygen_mixer.c +++ b/trunk/sound/pci/oxygen/oxygen_mixer.c @@ -618,12 +618,9 @@ static int ac97_volume_get(struct snd_kcontrol *ctl, mutex_lock(&chip->mutex); reg = oxygen_read_ac97(chip, codec, index); mutex_unlock(&chip->mutex); - if (!stereo) { - value->value.integer.value[0] = 31 - (reg & 0x1f); - } else { - value->value.integer.value[0] = 31 - ((reg >> 8) & 0x1f); - value->value.integer.value[1] = 31 - (reg & 0x1f); - } + value->value.integer.value[0] = 31 - (reg & 0x1f); + if (stereo) + value->value.integer.value[1] = 31 - ((reg >> 8) & 0x1f); return 0; } @@ -639,14 +636,14 @@ static int ac97_volume_put(struct snd_kcontrol *ctl, mutex_lock(&chip->mutex); oldreg = oxygen_read_ac97(chip, codec, index); - if (!stereo) { - newreg = oldreg & ~0x1f; - newreg |= 31 - (value->value.integer.value[0] & 0x1f); - } else { - newreg = oldreg & ~0x1f1f; - newreg |= (31 - (value->value.integer.value[0] & 0x1f)) << 8; - newreg |= 31 - (value->value.integer.value[1] & 0x1f); - } + newreg = oldreg; + newreg = (newreg & ~0x1f) | + (31 - (value->value.integer.value[0] & 0x1f)); + if (stereo) + newreg = (newreg & ~0x1f00) | + ((31 - (value->value.integer.value[1] & 0x1f)) << 8); + else + newreg = (newreg & ~0x1f00) | ((newreg & 0x1f) << 8); change = newreg != oldreg; if (change) oxygen_write_ac97(chip, codec, index, newreg); diff --git a/trunk/sound/pci/ymfpci/ymfpci.c b/trunk/sound/pci/ymfpci/ymfpci.c index 94ab728f5ca8..e57b89e8aa89 100644 --- a/trunk/sound/pci/ymfpci/ymfpci.c +++ b/trunk/sound/pci/ymfpci/ymfpci.c @@ -286,22 +286,17 @@ static int __devinit snd_card_ymfpci_probe(struct pci_dev *pci, snd_card_free(card); return err; } - err = snd_ymfpci_mixer(chip, rear_switch[dev]); - if (err < 0) { + if ((err = snd_ymfpci_pcm_4ch(chip, 2, NULL)) < 0) { snd_card_free(card); return err; } - if (chip->ac97->ext_id & AC97_EI_SDAC) { - err = snd_ymfpci_pcm_4ch(chip, 2, NULL); - if (err < 0) { - snd_card_free(card); - return err; - } - err = snd_ymfpci_pcm2(chip, 3, NULL); - if (err < 0) { - snd_card_free(card); - return err; - } + if ((err = snd_ymfpci_pcm2(chip, 3, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ymfpci_mixer(chip, rear_switch[dev])) < 0) { + snd_card_free(card); + return err; } if ((err = snd_ymfpci_timer(chip, 0)) < 0) { snd_card_free(card); diff --git a/trunk/sound/pci/ymfpci/ymfpci_main.c b/trunk/sound/pci/ymfpci/ymfpci_main.c index 12a9a2b03387..03ee4e365311 100644 --- a/trunk/sound/pci/ymfpci/ymfpci_main.c +++ b/trunk/sound/pci/ymfpci/ymfpci_main.c @@ -1614,14 +1614,6 @@ static int snd_ymfpci_put_dup4ch(struct snd_kcontrol *kcontrol, struct snd_ctl_e return change; } -static struct snd_kcontrol_new snd_ymfpci_dup4ch __devinitdata = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "4ch Duplication", - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, - .info = snd_ymfpci_info_dup4ch, - .get = snd_ymfpci_get_dup4ch, - .put = snd_ymfpci_put_dup4ch, -}; static struct snd_kcontrol_new snd_ymfpci_controls[] __devinitdata = { { @@ -1650,6 +1642,13 @@ YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,VOLUME), 1, YDSXGR_SPDIFLOOPVOL), YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), 0, YDSXGR_SPDIFOUTCTRL, 0), YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, YDSXGR_SPDIFINCTRL, 0), YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("Loop",NONE,NONE), 0, YDSXGR_SPDIFINCTRL, 4), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "4ch Duplication", + .info = snd_ymfpci_info_dup4ch, + .get = snd_ymfpci_get_dup4ch, + .put = snd_ymfpci_put_dup4ch, +}, }; @@ -1839,12 +1838,6 @@ int __devinit snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch) if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_controls[idx], chip))) < 0) return err; } - if (chip->ac97->ext_id & AC97_EI_SDAC) { - kctl = snd_ctl_new1(&snd_ymfpci_dup4ch, chip); - err = snd_ctl_add(chip->card, kctl); - if (err < 0) - return err; - } /* add S/PDIF control */ if (snd_BUG_ON(!chip->pcm_spdif)) diff --git a/trunk/sound/soc/codecs/cs42l73.c b/trunk/sound/soc/codecs/cs42l73.c index 78979b3e0e95..9d38db8f1919 100644 --- a/trunk/sound/soc/codecs/cs42l73.c +++ b/trunk/sound/soc/codecs/cs42l73.c @@ -1113,7 +1113,7 @@ static int cs42l73_pcm_hw_params(struct snd_pcm_substream *substream, priv->config[id].mmcc &= 0xC0; priv->config[id].mmcc |= cs42l73_mclk_coeffs[mclk_coeff].mmcc; priv->config[id].spc &= 0xFC; - priv->config[id].spc |= MCK_SCLK_MCLK; + priv->config[id].spc &= MCK_SCLK_64FS; } else { /* CS42L73 Slave */ priv->config[id].spc &= 0xFC; diff --git a/trunk/sound/soc/codecs/sgtl5000.c b/trunk/sound/soc/codecs/sgtl5000.c index 7f4ba819a9f6..f8863ebb4304 100644 --- a/trunk/sound/soc/codecs/sgtl5000.c +++ b/trunk/sound/soc/codecs/sgtl5000.c @@ -987,12 +987,12 @@ static int sgtl5000_restore_regs(struct snd_soc_codec *codec) /* restore regular registers */ for (reg = 0; reg <= SGTL5000_CHIP_SHORT_CTRL; reg += 2) { - /* These regs should restore in particular order */ + /* this regs depends on the others */ if (reg == SGTL5000_CHIP_ANA_POWER || reg == SGTL5000_CHIP_CLK_CTRL || reg == SGTL5000_CHIP_LINREG_CTRL || reg == SGTL5000_CHIP_LINE_OUT_CTRL || - reg == SGTL5000_CHIP_REF_CTRL) + reg == SGTL5000_CHIP_CLK_CTRL) continue; snd_soc_write(codec, reg, cache[reg]); @@ -1003,17 +1003,8 @@ static int sgtl5000_restore_regs(struct snd_soc_codec *codec) snd_soc_write(codec, reg, cache[reg]); /* - * restore these regs according to the power setting sequence in - * sgtl5000_set_power_regs() and clock setting sequence in - * sgtl5000_set_clock(). - * - * The order of restore is: - * 1. SGTL5000_CHIP_CLK_CTRL MCLK_FREQ bits (1:0) should be restore after - * SGTL5000_CHIP_ANA_POWER PLL bits set - * 2. SGTL5000_CHIP_LINREG_CTRL should be set before - * SGTL5000_CHIP_ANA_POWER LINREG_D restored - * 3. SGTL5000_CHIP_REF_CTRL controls Analog Ground Voltage, - * prefer to resotre it after SGTL5000_CHIP_ANA_POWER restored + * restore power and other regs according + * to set_power() and set_clock() */ snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL, cache[SGTL5000_CHIP_LINREG_CTRL]); diff --git a/trunk/sound/soc/codecs/tlv320aic32x4.c b/trunk/sound/soc/codecs/tlv320aic32x4.c index 372b0b83bd9f..eb401ef021fb 100644 --- a/trunk/sound/soc/codecs/tlv320aic32x4.c +++ b/trunk/sound/soc/codecs/tlv320aic32x4.c @@ -60,6 +60,7 @@ struct aic32x4_rate_divs { struct aic32x4_priv { u32 sysclk; + s32 master; u8 page_no; void *control_data; u32 power_cfg; @@ -368,6 +369,7 @@ static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai, static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); u8 iface_reg_1; u8 iface_reg_2; u8 iface_reg_3; @@ -382,9 +384,11 @@ static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: + aic32x4->master = 1; iface_reg_1 |= AIC32X4_BCLKMASTER | AIC32X4_WCLKMASTER; break; case SND_SOC_DAIFMT_CBS_CFS: + aic32x4->master = 0; break; default: printk(KERN_ERR "aic32x4: invalid DAI master/slave interface\n"); @@ -522,58 +526,64 @@ static int aic32x4_mute(struct snd_soc_dai *dai, int mute) static int aic32x4_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + switch (level) { case SND_SOC_BIAS_ON: - /* Switch on PLL */ - snd_soc_update_bits(codec, AIC32X4_PLLPR, - AIC32X4_PLLEN, AIC32X4_PLLEN); - - /* Switch on NDAC Divider */ - snd_soc_update_bits(codec, AIC32X4_NDAC, - AIC32X4_NDACEN, AIC32X4_NDACEN); - - /* Switch on MDAC Divider */ - snd_soc_update_bits(codec, AIC32X4_MDAC, - AIC32X4_MDACEN, AIC32X4_MDACEN); - - /* Switch on NADC Divider */ - snd_soc_update_bits(codec, AIC32X4_NADC, - AIC32X4_NADCEN, AIC32X4_NADCEN); - - /* Switch on MADC Divider */ - snd_soc_update_bits(codec, AIC32X4_MADC, - AIC32X4_MADCEN, AIC32X4_MADCEN); - - /* Switch on BCLK_N Divider */ - snd_soc_update_bits(codec, AIC32X4_BCLKN, - AIC32X4_BCLKEN, AIC32X4_BCLKEN); + if (aic32x4->master) { + /* Switch on PLL */ + snd_soc_update_bits(codec, AIC32X4_PLLPR, + AIC32X4_PLLEN, AIC32X4_PLLEN); + + /* Switch on NDAC Divider */ + snd_soc_update_bits(codec, AIC32X4_NDAC, + AIC32X4_NDACEN, AIC32X4_NDACEN); + + /* Switch on MDAC Divider */ + snd_soc_update_bits(codec, AIC32X4_MDAC, + AIC32X4_MDACEN, AIC32X4_MDACEN); + + /* Switch on NADC Divider */ + snd_soc_update_bits(codec, AIC32X4_NADC, + AIC32X4_NADCEN, AIC32X4_NADCEN); + + /* Switch on MADC Divider */ + snd_soc_update_bits(codec, AIC32X4_MADC, + AIC32X4_MADCEN, AIC32X4_MADCEN); + + /* Switch on BCLK_N Divider */ + snd_soc_update_bits(codec, AIC32X4_BCLKN, + AIC32X4_BCLKEN, AIC32X4_BCLKEN); + } break; case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - /* Switch off PLL */ - snd_soc_update_bits(codec, AIC32X4_PLLPR, - AIC32X4_PLLEN, 0); - - /* Switch off NDAC Divider */ - snd_soc_update_bits(codec, AIC32X4_NDAC, - AIC32X4_NDACEN, 0); - - /* Switch off MDAC Divider */ - snd_soc_update_bits(codec, AIC32X4_MDAC, - AIC32X4_MDACEN, 0); - - /* Switch off NADC Divider */ - snd_soc_update_bits(codec, AIC32X4_NADC, - AIC32X4_NADCEN, 0); - - /* Switch off MADC Divider */ - snd_soc_update_bits(codec, AIC32X4_MADC, - AIC32X4_MADCEN, 0); - - /* Switch off BCLK_N Divider */ - snd_soc_update_bits(codec, AIC32X4_BCLKN, - AIC32X4_BCLKEN, 0); + if (aic32x4->master) { + /* Switch off PLL */ + snd_soc_update_bits(codec, AIC32X4_PLLPR, + AIC32X4_PLLEN, 0); + + /* Switch off NDAC Divider */ + snd_soc_update_bits(codec, AIC32X4_NDAC, + AIC32X4_NDACEN, 0); + + /* Switch off MDAC Divider */ + snd_soc_update_bits(codec, AIC32X4_MDAC, + AIC32X4_MDACEN, 0); + + /* Switch off NADC Divider */ + snd_soc_update_bits(codec, AIC32X4_NADC, + AIC32X4_NADCEN, 0); + + /* Switch off MADC Divider */ + snd_soc_update_bits(codec, AIC32X4_MADC, + AIC32X4_MADCEN, 0); + + /* Switch off BCLK_N Divider */ + snd_soc_update_bits(codec, AIC32X4_BCLKN, + AIC32X4_BCLKEN, 0); + } break; case SND_SOC_BIAS_OFF: break; @@ -641,11 +651,9 @@ static int aic32x4_probe(struct snd_soc_codec *codec) if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE) { snd_soc_write(codec, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE); } - - tmp_reg = (aic32x4->power_cfg & AIC32X4_PWR_AIC32X4_LDO_ENABLE) ? - AIC32X4_LDOCTLEN : 0; - snd_soc_write(codec, AIC32X4_LDOCTL, tmp_reg); - + if (aic32x4->power_cfg & AIC32X4_PWR_AIC32X4_LDO_ENABLE) { + snd_soc_write(codec, AIC32X4_LDOCTL, AIC32X4_LDOCTLEN); + } tmp_reg = snd_soc_read(codec, AIC32X4_CMMODE); if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36) { tmp_reg |= AIC32X4_LDOIN_18_36; diff --git a/trunk/sound/soc/codecs/wm2000.c b/trunk/sound/soc/codecs/wm2000.c index a75c3766aede..c2880907fced 100644 --- a/trunk/sound/soc/codecs/wm2000.c +++ b/trunk/sound/soc/codecs/wm2000.c @@ -733,9 +733,8 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c, struct wm2000_priv *wm2000; struct wm2000_platform_data *pdata; const char *filename; - const struct firmware *fw = NULL; - int ret; - int reg; + const struct firmware *fw; + int reg, ret; u16 id; wm2000 = devm_kzalloc(&i2c->dev, sizeof(struct wm2000_priv), @@ -752,7 +751,7 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c, ret = PTR_ERR(wm2000->regmap); dev_err(&i2c->dev, "Failed to allocate register map: %d\n", ret); - goto out; + goto err; } /* Verify that this is a WM2000 */ @@ -764,7 +763,7 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c, if (id != 0x2000) { dev_err(&i2c->dev, "Device is not a WM2000 - ID %x\n", id); ret = -ENODEV; - goto out_regmap_exit; + goto err_regmap; } reg = wm2000_read(i2c, WM2000_REG_REVISON); @@ -783,7 +782,7 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c, ret = request_firmware(&fw, filename, &i2c->dev); if (ret != 0) { dev_err(&i2c->dev, "Failed to acquire ANC data: %d\n", ret); - goto out_regmap_exit; + goto err_regmap; } /* Pre-cook the concatenation of the register address onto the image */ @@ -794,13 +793,15 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c, if (wm2000->anc_download == NULL) { dev_err(&i2c->dev, "Out of memory\n"); ret = -ENOMEM; - goto out_regmap_exit; + goto err_fw; } wm2000->anc_download[0] = 0x80; wm2000->anc_download[1] = 0x00; memcpy(wm2000->anc_download + 2, fw->data, fw->size); + release_firmware(fw); + wm2000->anc_eng_ena = 1; wm2000->anc_active = 1; wm2000->spk_ena = 1; @@ -808,14 +809,18 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c, wm2000_reset(wm2000); - ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm2000, NULL, 0); - if (!ret) - goto out; + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm2000, + NULL, 0); + if (ret != 0) + goto err_fw; -out_regmap_exit: - regmap_exit(wm2000->regmap); -out: + return 0; + +err_fw: release_firmware(fw); +err_regmap: + regmap_exit(wm2000->regmap); +err: return ret; } diff --git a/trunk/sound/soc/codecs/wm5100.c b/trunk/sound/soc/codecs/wm5100.c index 89f2af77b1c3..8b24323d6b2c 100644 --- a/trunk/sound/soc/codecs/wm5100.c +++ b/trunk/sound/soc/codecs/wm5100.c @@ -1377,7 +1377,6 @@ static int wm5100_set_bias_level(struct snd_soc_codec *codec, switch (wm5100->rev) { case 0: - regcache_cache_bypass(wm5100->regmap, true); snd_soc_write(codec, 0x11, 0x3); snd_soc_write(codec, 0x203, 0xc); snd_soc_write(codec, 0x206, 0); @@ -1393,7 +1392,6 @@ static int wm5100_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, wm5100_reva_patches[i].reg, wm5100_reva_patches[i].val); - regcache_cache_bypass(wm5100->regmap, false); break; default: break; @@ -1404,8 +1402,6 @@ static int wm5100_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_OFF: - regcache_cache_only(wm5100->regmap, true); - regcache_mark_dirty(wm5100->regmap); if (wm5100->pdata.ldo_ena) gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0); regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies), @@ -2184,7 +2180,6 @@ static void wm5100_micd_irq(struct snd_soc_codec *codec) if (wm5100->jack_detecting) { dev_dbg(codec->dev, "Microphone detected\n"); wm5100->jack_mic = true; - wm5100->jack_detecting = false; snd_soc_jack_report(wm5100->jack, SND_JACK_HEADSET, SND_JACK_HEADSET | SND_JACK_BTN_0); @@ -2223,7 +2218,6 @@ static void wm5100_micd_irq(struct snd_soc_codec *codec) SND_JACK_BTN_0); } else if (wm5100->jack_detecting) { dev_dbg(codec->dev, "Headphone detected\n"); - wm5100->jack_detecting = false; snd_soc_jack_report(wm5100->jack, SND_JACK_HEADPHONE, SND_JACK_HEADPHONE); @@ -2613,13 +2607,6 @@ static const struct regmap_config wm5100_regmap = { .cache_type = REGCACHE_RBTREE, }; -static const unsigned int wm5100_mic_ctrl_reg[] = { - WM5100_IN1L_CONTROL, - WM5100_IN2L_CONTROL, - WM5100_IN3L_CONTROL, - WM5100_IN4L_CONTROL, -}; - static __devinit int wm5100_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -2752,7 +2739,7 @@ static __devinit int wm5100_i2c_probe(struct i2c_client *i2c, } for (i = 0; i < ARRAY_SIZE(wm5100->pdata.in_mode); i++) { - regmap_update_bits(wm5100->regmap, wm5100_mic_ctrl_reg[i], + regmap_update_bits(wm5100->regmap, WM5100_IN1L_CONTROL, WM5100_IN1_MODE_MASK | WM5100_IN1_DMIC_SUP_MASK, (wm5100->pdata.in_mode[i] << diff --git a/trunk/sound/soc/codecs/wm8958-dsp2.c b/trunk/sound/soc/codecs/wm8958-dsp2.c index 40ac888faf3d..8d4ea43d40a3 100644 --- a/trunk/sound/soc/codecs/wm8958-dsp2.c +++ b/trunk/sound/soc/codecs/wm8958-dsp2.c @@ -55,7 +55,7 @@ static int wm8958_dsp2_fw(struct snd_soc_codec *codec, const char *name, return 0; if (fw->size < 32) { - dev_err(codec->dev, "%s: firmware too short (%zd bytes)\n", + dev_err(codec->dev, "%s: firmware too short (%d bytes)\n", name, fw->size); goto err; } diff --git a/trunk/sound/soc/codecs/wm8962.c b/trunk/sound/soc/codecs/wm8962.c index 29c4b02c4790..296de4e30d26 100644 --- a/trunk/sound/soc/codecs/wm8962.c +++ b/trunk/sound/soc/codecs/wm8962.c @@ -96,7 +96,7 @@ static int wm8962_regulator_event_##n(struct notifier_block *nb, \ struct wm8962_priv *wm8962 = container_of(nb, struct wm8962_priv, \ disable_nb[n]); \ if (event & REGULATOR_EVENT_DISABLE) { \ - regcache_mark_dirty(wm8962->regmap); \ + regcache_cache_only(wm8962->regmap, true); \ } \ return 0; \ } @@ -3159,13 +3159,13 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream, case SNDRV_PCM_FORMAT_S16_LE: break; case SNDRV_PCM_FORMAT_S20_3LE: - aif0 |= 0x4; + aif0 |= 0x40; break; case SNDRV_PCM_FORMAT_S24_LE: - aif0 |= 0x8; + aif0 |= 0x80; break; case SNDRV_PCM_FORMAT_S32_LE: - aif0 |= 0xc; + aif0 |= 0xc0; break; default: return -EINVAL; diff --git a/trunk/sound/soc/codecs/wm8994.c b/trunk/sound/soc/codecs/wm8994.c index ec69a6c152fe..93d27b660257 100644 --- a/trunk/sound/soc/codecs/wm8994.c +++ b/trunk/sound/soc/codecs/wm8994.c @@ -770,8 +770,6 @@ static void vmid_reference(struct snd_soc_codec *codec) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); - pm_runtime_get_sync(codec->dev); - wm8994->vmid_refcount++; dev_dbg(codec->dev, "Referencing VMID, refcount is now %d\n", @@ -785,12 +783,7 @@ static void vmid_reference(struct snd_soc_codec *codec) WM8994_VMID_RAMP_MASK, WM8994_STARTUP_BIAS_ENA | WM8994_VMID_BUF_ENA | - (0x3 << WM8994_VMID_RAMP_SHIFT)); - - /* Remove discharge for line out */ - snd_soc_update_bits(codec, WM8994_ANTIPOP_1, - WM8994_LINEOUT1_DISCH | - WM8994_LINEOUT2_DISCH, 0); + (0x11 << WM8994_VMID_RAMP_SHIFT)); /* Main bias enable, VMID=2x40k */ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1, @@ -844,8 +837,6 @@ static void vmid_dereference(struct snd_soc_codec *codec) WM8994_VMID_BUF_ENA | WM8994_VMID_RAMP_MASK, 0); } - - pm_runtime_put(codec->dev); } static int vmid_event(struct snd_soc_dapm_widget *w, @@ -2762,6 +2753,11 @@ static int wm8994_resume(struct snd_soc_codec *codec) codec->cache_only = 0; } + /* Restore the registers */ + ret = snd_soc_cache_sync(codec); + if (ret != 0) + dev_err(codec->dev, "Failed to sync cache: %d\n", ret); + wm8994_set_bias_level(codec, SND_SOC_BIAS_STANDBY); for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) { diff --git a/trunk/sound/soc/codecs/wm8996.c b/trunk/sound/soc/codecs/wm8996.c index 61f7daa4d0e6..d8da10fe5b52 100644 --- a/trunk/sound/soc/codecs/wm8996.c +++ b/trunk/sound/soc/codecs/wm8996.c @@ -108,7 +108,7 @@ static int wm8996_regulator_event_##n(struct notifier_block *nb, \ struct wm8996_priv *wm8996 = container_of(nb, struct wm8996_priv, \ disable_nb[n]); \ if (event & REGULATOR_EVENT_DISABLE) { \ - regcache_mark_dirty(wm8996->regmap); \ + regcache_cache_only(wm8996->regmap, true); \ } \ return 0; \ } @@ -1120,8 +1120,7 @@ SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, WM8996_AIF_CLOCKING_1, 0, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("SYSDSPCLK", 2, WM8996_CLOCKING_1, 1, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("AIFCLK", 2, WM8996_CLOCKING_1, 2, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("Charge Pump", 2, WM8996_CHARGE_PUMP_1, 15, 0, cp_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("Bandgap", SND_SOC_NOPM, 0, 0, bg_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("LDO2", WM8996_POWER_MANAGEMENT_2, 1, 0, NULL, 0), @@ -2008,7 +2007,6 @@ static int wm8996_set_sysclk(struct snd_soc_dai *dai, struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); int lfclk = 0; int ratediv = 0; - int sync = WM8996_REG_SYNC; int src; int old; @@ -2053,7 +2051,6 @@ static int wm8996_set_sysclk(struct snd_soc_dai *dai, case 32000: case 32768: lfclk = WM8996_LFCLK_ENA; - sync = 0; break; default: dev_warn(codec->dev, "Unsupported clock rate %dHz\n", @@ -2067,8 +2064,6 @@ static int wm8996_set_sysclk(struct snd_soc_dai *dai, WM8996_SYSCLK_SRC_MASK | WM8996_SYSCLK_DIV_MASK, src << WM8996_SYSCLK_SRC_SHIFT | ratediv); snd_soc_update_bits(codec, WM8996_CLOCKING_1, WM8996_LFCLK_ENA, lfclk); - snd_soc_update_bits(codec, WM8996_CONTROL_INTERFACE_1, - WM8996_REG_SYNC, sync); snd_soc_update_bits(codec, WM8996_AIF_CLOCKING_1, WM8996_SYSCLK_ENA, old); diff --git a/trunk/sound/soc/codecs/wm8996.h b/trunk/sound/soc/codecs/wm8996.h index de9ac3e44aec..0fde643194ce 100644 --- a/trunk/sound/soc/codecs/wm8996.h +++ b/trunk/sound/soc/codecs/wm8996.h @@ -1567,10 +1567,6 @@ int wm8996_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, /* * R257 (0x101) - Control Interface (1) */ -#define WM8996_REG_SYNC 0x8000 /* REG_SYNC */ -#define WM8996_REG_SYNC_MASK 0x8000 /* REG_SYNC */ -#define WM8996_REG_SYNC_SHIFT 15 /* REG_SYNC */ -#define WM8996_REG_SYNC_WIDTH 1 /* REG_SYNC */ #define WM8996_AUTO_INC 0x0004 /* AUTO_INC */ #define WM8996_AUTO_INC_MASK 0x0004 /* AUTO_INC */ #define WM8996_AUTO_INC_SHIFT 2 /* AUTO_INC */ diff --git a/trunk/sound/soc/codecs/wm_hubs.c b/trunk/sound/soc/codecs/wm_hubs.c index 8a68cea4a3ee..2a61094075f8 100644 --- a/trunk/sound/soc/codecs/wm_hubs.c +++ b/trunk/sound/soc/codecs/wm_hubs.c @@ -586,14 +586,14 @@ SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER1, 0, 1, 0), }; static const struct snd_kcontrol_new line2_mix[] = { -SOC_DAPM_SINGLE("IN1L Switch", WM8993_LINE_MIXER2, 2, 1, 0), -SOC_DAPM_SINGLE("IN1R Switch", WM8993_LINE_MIXER2, 1, 1, 0), +SOC_DAPM_SINGLE("IN2R Switch", WM8993_LINE_MIXER2, 2, 1, 0), +SOC_DAPM_SINGLE("IN2L Switch", WM8993_LINE_MIXER2, 1, 1, 0), SOC_DAPM_SINGLE("Output Switch", WM8993_LINE_MIXER2, 0, 1, 0), }; static const struct snd_kcontrol_new line2n_mix[] = { -SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER2, 5, 1, 0), -SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER2, 6, 1, 0), +SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER2, 6, 1, 0), +SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER2, 5, 1, 0), }; static const struct snd_kcontrol_new line2p_mix[] = { @@ -613,8 +613,6 @@ SND_SOC_DAPM_INPUT("IN2RP:VXRP"), SND_SOC_DAPM_SUPPLY("MICBIAS2", WM8993_POWER_MANAGEMENT_1, 5, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("MICBIAS1", WM8993_POWER_MANAGEMENT_1, 4, 0, NULL, 0), -SND_SOC_DAPM_SUPPLY("LINEOUT_VMID_BUF", WM8993_ANTIPOP1, 7, 0, NULL, 0), - SND_SOC_DAPM_MIXER("IN1L PGA", WM8993_POWER_MANAGEMENT_2, 6, 0, in1l_pga, ARRAY_SIZE(in1l_pga)), SND_SOC_DAPM_MIXER("IN1R PGA", WM8993_POWER_MANAGEMENT_2, 4, 0, @@ -836,11 +834,9 @@ static const struct snd_soc_dapm_route lineout1_diff_routes[] = { }; static const struct snd_soc_dapm_route lineout1_se_routes[] = { - { "LINEOUT1N Mixer", NULL, "LINEOUT_VMID_BUF" }, { "LINEOUT1N Mixer", "Left Output Switch", "Left Output PGA" }, { "LINEOUT1N Mixer", "Right Output Switch", "Right Output PGA" }, - { "LINEOUT1P Mixer", NULL, "LINEOUT_VMID_BUF" }, { "LINEOUT1P Mixer", "Left Output Switch", "Left Output PGA" }, { "LINEOUT1N Driver", NULL, "LINEOUT1N Mixer" }, @@ -848,8 +844,8 @@ static const struct snd_soc_dapm_route lineout1_se_routes[] = { }; static const struct snd_soc_dapm_route lineout2_diff_routes[] = { - { "LINEOUT2 Mixer", "IN1L Switch", "IN1L PGA" }, - { "LINEOUT2 Mixer", "IN1R Switch", "IN1R PGA" }, + { "LINEOUT2 Mixer", "IN2L Switch", "IN2L PGA" }, + { "LINEOUT2 Mixer", "IN2R Switch", "IN2R PGA" }, { "LINEOUT2 Mixer", "Output Switch", "Right Output PGA" }, { "LINEOUT2N Driver", NULL, "LINEOUT2 Mixer" }, @@ -857,11 +853,9 @@ static const struct snd_soc_dapm_route lineout2_diff_routes[] = { }; static const struct snd_soc_dapm_route lineout2_se_routes[] = { - { "LINEOUT2N Mixer", NULL, "LINEOUT_VMID_BUF" }, { "LINEOUT2N Mixer", "Left Output Switch", "Left Output PGA" }, { "LINEOUT2N Mixer", "Right Output Switch", "Right Output PGA" }, - { "LINEOUT2P Mixer", NULL, "LINEOUT_VMID_BUF" }, { "LINEOUT2P Mixer", "Right Output Switch", "Right Output PGA" }, { "LINEOUT2N Driver", NULL, "LINEOUT2N Mixer" }, diff --git a/trunk/sound/soc/mxs/mxs-saif.c b/trunk/sound/soc/mxs/mxs-saif.c index f204dbac11d4..dccfb37a9626 100644 --- a/trunk/sound/soc/mxs/mxs-saif.c +++ b/trunk/sound/soc/mxs/mxs-saif.c @@ -124,8 +124,6 @@ static int mxs_saif_set_clk(struct mxs_saif *saif, * * If MCLK is not used, we just set saif clk to 512*fs. */ - clk_prepare_enable(master_saif->clk); - if (master_saif->mclk_in_use) { if (mclk % 32 == 0) { scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; @@ -135,7 +133,6 @@ static int mxs_saif_set_clk(struct mxs_saif *saif, ret = clk_set_rate(master_saif->clk, 384 * rate); } else { /* SAIF MCLK should be either 32x or 48x */ - clk_disable_unprepare(master_saif->clk); return -EINVAL; } } else { @@ -143,8 +140,6 @@ static int mxs_saif_set_clk(struct mxs_saif *saif, scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; } - clk_disable_unprepare(master_saif->clk); - if (ret) return ret; diff --git a/trunk/sound/soc/samsung/neo1973_wm8753.c b/trunk/sound/soc/samsung/neo1973_wm8753.c index c6012ff5bd3e..7ac0ba2025c3 100644 --- a/trunk/sound/soc/samsung/neo1973_wm8753.c +++ b/trunk/sound/soc/samsung/neo1973_wm8753.c @@ -230,6 +230,8 @@ static const struct snd_kcontrol_new neo1973_wm8753_controls[] = { /* GTA02 specific routes and controls */ +#ifdef CONFIG_MACH_NEO1973_GTA02 + static int gta02_speaker_enabled; static int lm4853_set_spk(struct snd_kcontrol *kcontrol, @@ -309,6 +311,10 @@ static int neo1973_gta02_wm8753_init(struct snd_soc_codec *codec) return 0; } +#else +static int neo1973_gta02_wm8753_init(struct snd_soc_code *codec) { return 0; } +#endif + static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; @@ -316,6 +322,10 @@ static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd) int ret; /* set up NC codec pins */ + if (machine_is_neo1973_gta01()) { + snd_soc_dapm_nc_pin(dapm, "LOUT2"); + snd_soc_dapm_nc_pin(dapm, "ROUT2"); + } snd_soc_dapm_nc_pin(dapm, "OUT3"); snd_soc_dapm_nc_pin(dapm, "OUT4"); snd_soc_dapm_nc_pin(dapm, "LINE1"); @@ -360,6 +370,50 @@ static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd) return 0; } +/* GTA01 specific controls */ + +#ifdef CONFIG_MACH_NEO1973_GTA01 + +static const struct snd_soc_dapm_route neo1973_lm4857_routes[] = { + {"Amp IN", NULL, "ROUT1"}, + {"Amp IN", NULL, "LOUT1"}, + + {"Handset Spk", NULL, "Amp EP"}, + {"Stereo Out", NULL, "Amp LS"}, + {"Headphone", NULL, "Amp HP"}, +}; + +static const struct snd_soc_dapm_widget neo1973_lm4857_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Handset Spk", NULL), + SND_SOC_DAPM_SPK("Stereo Out", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), +}; + +static int neo1973_lm4857_init(struct snd_soc_dapm_context *dapm) +{ + int ret; + + ret = snd_soc_dapm_new_controls(dapm, neo1973_lm4857_dapm_widgets, + ARRAY_SIZE(neo1973_lm4857_dapm_widgets)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, neo1973_lm4857_routes, + ARRAY_SIZE(neo1973_lm4857_routes)); + if (ret) + return ret; + + snd_soc_dapm_ignore_suspend(dapm, "Stereo Out"); + snd_soc_dapm_ignore_suspend(dapm, "Handset Spk"); + snd_soc_dapm_ignore_suspend(dapm, "Headphone"); + + return 0; +} + +#else +static int neo1973_lm4857_init(struct snd_soc_dapm_context *dapm) { return 0; }; +#endif + static struct snd_soc_dai_link neo1973_dai[] = { { /* Hifi Playback - for similatious use with voice below */ .name = "WM8753", @@ -386,6 +440,11 @@ static struct snd_soc_aux_dev neo1973_aux_devs[] = { .name = "dfbmcs320", .codec_name = "dfbmcs320.0", }, + { + .name = "lm4857", + .codec_name = "lm4857.0-007c", + .init = neo1973_lm4857_init, + }, }; static struct snd_soc_codec_conf neo1973_codec_conf[] = { @@ -395,10 +454,14 @@ static struct snd_soc_codec_conf neo1973_codec_conf[] = { }, }; +#ifdef CONFIG_MACH_NEO1973_GTA02 static const struct gpio neo1973_gta02_gpios[] = { { GTA02_GPIO_HP_IN, GPIOF_OUT_INIT_HIGH, "GTA02_HP_IN" }, { GTA02_GPIO_AMP_SHUT, GPIOF_OUT_INIT_HIGH, "GTA02_AMP_SHUT" }, }; +#else +static const struct gpio neo1973_gta02_gpios[] = {}; +#endif static struct snd_soc_card neo1973 = { .name = "neo1973", @@ -417,7 +480,7 @@ static int __init neo1973_init(void) { int ret; - if (!machine_is_neo1973_gta02()) + if (!machine_is_neo1973_gta01() && !machine_is_neo1973_gta02()) return -ENODEV; if (machine_is_neo1973_gta02()) { diff --git a/trunk/sound/soc/soc-core.c b/trunk/sound/soc/soc-core.c index 92cee24ed2dc..b5ecf6d23214 100644 --- a/trunk/sound/soc/soc-core.c +++ b/trunk/sound/soc/soc-core.c @@ -567,17 +567,6 @@ int snd_soc_suspend(struct device *dev) if (!codec->suspended && codec->driver->suspend) { switch (codec->dapm.bias_level) { case SND_SOC_BIAS_STANDBY: - /* - * If the CODEC is capable of idle - * bias off then being in STANDBY - * means it's doing something, - * otherwise fall through. - */ - if (codec->dapm.idle_bias_off) { - dev_dbg(codec->dev, - "idle_bias_off CODEC on over suspend\n"); - break; - } case SND_SOC_BIAS_OFF: codec->driver->suspend(codec); codec->suspended = 1; diff --git a/trunk/sound/usb/quirks-table.h b/trunk/sound/usb/quirks-table.h index d89ab4c7d44b..8edc5035fc8f 100644 --- a/trunk/sound/usb/quirks-table.h +++ b/trunk/sound/usb/quirks-table.h @@ -1617,14 +1617,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, -{ - /* Edirol UM-3G */ - USB_DEVICE_VENDOR_SPEC(0x0582, 0x0108), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .ifnum = 0, - .type = QUIRK_MIDI_STANDARD_INTERFACE - } -}, { /* Boss JS-8 Jam Station */ USB_DEVICE(0x0582, 0x0109), diff --git a/trunk/tools/perf/Makefile b/trunk/tools/perf/Makefile index 7c12650165ae..ac86d67b636e 100644 --- a/trunk/tools/perf/Makefile +++ b/trunk/tools/perf/Makefile @@ -104,7 +104,7 @@ endif CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 $(CFLAGS_WERROR) $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) EXTLIBS = -lpthread -lrt -lelf -lm -ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE +ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 ALL_LDFLAGS = $(LDFLAGS) STRIP ?= strip @@ -168,7 +168,10 @@ endif ### --- END CONFIGURATION SECTION --- -BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE +# Those must not be GNU-specific; they are shared with perl/ which may +# be built by a different compiler. (Note that this is an artifact now +# but it still might be nice to keep that distinction.) +BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include BASIC_LDFLAGS = # Guard against environment variables 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/builtin-probe.c b/trunk/tools/perf/builtin-probe.c index fb8566181f27..59d43abfbfec 100644 --- a/trunk/tools/perf/builtin-probe.c +++ b/trunk/tools/perf/builtin-probe.c @@ -20,6 +20,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ +#define _GNU_SOURCE #include #include #include @@ -30,6 +31,7 @@ #include #include +#undef _GNU_SOURCE #include "perf.h" #include "builtin.h" #include "util/util.h" diff --git a/trunk/tools/perf/builtin-top.c b/trunk/tools/perf/builtin-top.c index dd162aa24baa..8f80df896038 100644 --- a/trunk/tools/perf/builtin-top.c +++ b/trunk/tools/perf/builtin-top.c @@ -89,6 +89,8 @@ void get_term_dimensions(struct winsize *ws) static void perf_top__update_print_entries(struct perf_top *top) { + top->print_entries = top->winsize.ws_row; + if (top->print_entries > 9) top->print_entries -= 9; } @@ -98,13 +100,6 @@ static void perf_top__sig_winch(int sig __used, siginfo_t *info __used, void *ar struct perf_top *top = arg; get_term_dimensions(&top->winsize); - if (!top->print_entries - || (top->print_entries+4) > top->winsize.ws_row) { - top->print_entries = top->winsize.ws_row; - } else { - top->print_entries += 4; - top->winsize.ws_row = top->print_entries; - } perf_top__update_print_entries(top); } @@ -458,10 +453,8 @@ static void perf_top__handle_keypress(struct perf_top *top, int c) }; perf_top__sig_winch(SIGWINCH, NULL, top); sigaction(SIGWINCH, &act, NULL); - } else { - perf_top__sig_winch(SIGWINCH, NULL, top); + } else signal(SIGWINCH, SIG_DFL); - } break; case 'E': if (top->evlist->nr_entries > 1) { 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) diff --git a/trunk/tools/perf/util/header.c b/trunk/tools/perf/util/header.c index ecd7f4dd7eea..3e7e0b09c12c 100644 --- a/trunk/tools/perf/util/header.c +++ b/trunk/tools/perf/util/header.c @@ -2105,7 +2105,7 @@ int perf_event__synthesize_event_type(struct perf_tool *tool, strncpy(ev.event_type.event_type.name, name, MAX_EVENT_NAME - 1); ev.event_type.header.type = PERF_RECORD_HEADER_EVENT_TYPE; - size = strlen(ev.event_type.event_type.name); + size = strlen(name); size = ALIGN(size, sizeof(u64)); ev.event_type.header.size = sizeof(ev.event_type) - (sizeof(ev.event_type.event_type.name) - size); diff --git a/trunk/tools/perf/util/probe-event.c b/trunk/tools/perf/util/probe-event.c index 29cb65459811..eb25900e2211 100644 --- a/trunk/tools/perf/util/probe-event.c +++ b/trunk/tools/perf/util/probe-event.c @@ -19,6 +19,7 @@ * */ +#define _GNU_SOURCE #include #include #include @@ -32,6 +33,7 @@ #include #include +#undef _GNU_SOURCE #include "util.h" #include "event.h" #include "string.h" diff --git a/trunk/tools/perf/util/symbol.c b/trunk/tools/perf/util/symbol.c index 0975438c3e72..215d50f2042e 100644 --- a/trunk/tools/perf/util/symbol.c +++ b/trunk/tools/perf/util/symbol.c @@ -1,3 +1,4 @@ +#define _GNU_SOURCE #include #include #include diff --git a/trunk/tools/perf/util/trace-event-parse.c b/trunk/tools/perf/util/trace-event-parse.c index 1a8d4dc4f386..6c164dc9ee95 100644 --- a/trunk/tools/perf/util/trace-event-parse.c +++ b/trunk/tools/perf/util/trace-event-parse.c @@ -21,13 +21,14 @@ * The parts for function graph printing was taken and modified from the * Linux Kernel that were written by Frederic Weisbecker. */ - +#define _GNU_SOURCE #include #include #include #include #include +#undef _GNU_SOURCE #include "../perf.h" #include "util.h" #include "trace-event.h" diff --git a/trunk/tools/perf/util/ui/browsers/hists.c b/trunk/tools/perf/util/ui/browsers/hists.c index e81aef1f2569..1212a386a033 100644 --- a/trunk/tools/perf/util/ui/browsers/hists.c +++ b/trunk/tools/perf/util/ui/browsers/hists.c @@ -1,4 +1,6 @@ +#define _GNU_SOURCE #include +#undef _GNU_SOURCE #include "../libslang.h" #include #include diff --git a/trunk/tools/perf/util/ui/helpline.c b/trunk/tools/perf/util/ui/helpline.c index 4f48f5901b30..6ef3c5691762 100644 --- a/trunk/tools/perf/util/ui/helpline.c +++ b/trunk/tools/perf/util/ui/helpline.c @@ -1,3 +1,4 @@ +#define _GNU_SOURCE #include #include #include diff --git a/trunk/tools/perf/util/util.h b/trunk/tools/perf/util/util.h index ecf9898169c8..b9c530cce79a 100644 --- a/trunk/tools/perf/util/util.h +++ b/trunk/tools/perf/util/util.h @@ -40,6 +40,7 @@ #define decimal_length(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1) #define _ALL_SOURCE 1 +#define _GNU_SOURCE 1 #define _BSD_SOURCE 1 #define HAS_BOOL diff --git a/trunk/virt/kvm/kvm_main.c b/trunk/virt/kvm/kvm_main.c index a91f980077d8..7287bf5d1c9e 100644 --- a/trunk/virt/kvm/kvm_main.c +++ b/trunk/virt/kvm/kvm_main.c @@ -1543,7 +1543,7 @@ void mark_page_dirty_in_slot(struct kvm *kvm, struct kvm_memory_slot *memslot, if (memslot && memslot->dirty_bitmap) { unsigned long rel_gfn = gfn - memslot->base_gfn; - if (!test_and_set_bit_le(rel_gfn, memslot->dirty_bitmap)) + if (!__test_and_set_bit_le(rel_gfn, memslot->dirty_bitmap)) memslot->nr_dirty_pages++; } }